graft-comboagent
This documents the local-host combo agent client for Cursor / Claude Code / Codex / Trae over HTTPS. For the cloud builtin graft skill see skills/agentic/graft; for mechanics see Graft.
Problems it solves
Engineers split time between:
- Cloud combo agent: heavy analysis (ASPICE traces, requirement audits, baseline deltas, novelty search) with artifacts in the cloud KB
- Local IDE: coding, tests, PRs, docs
Bridging is more than pasting session blurbs. graft-comboagent packages three usage modes:
| Mode | Solves | Primary actions |
|---|---|---|
| A · KB search | Treat the cloud KB as RAG for local agents—no active session required | list_kbs · kb_detail · list_documents · get_doc_profile · list_chunks · search --source document |
| B · Session graft | Pull cloud digests, rounds, and artifacts into local context | list_sessions · get_digest · get_round · search --source round · read_file · download · list_files · search_by_artifact · grep_file |
| C · Dispatch | Queue a new instruction on a long-lived cloud session (heavy tools not local) | dispatch_task |
A is pure RAG (no session spin-up). B is read-only graft. C is the only write path—it cannot create/delete sessions or files; it only appends a turn to an existing session.
Architecture
- Server:
api/apps/graft_app.pyexposes user-scoped HTTPS endpoints - Auth: reuse UI
signed_auth_token(itsdangerous) after/v1/user/login - Client: Python helpers (
login/whoami/logout/call) plus bundledpublic.pem - Writes such as
register_artifact,copy_kb_document,save_*blocked client- and server-side—onlydispatch_taskescapes read-only posture
Install
graft-comboagent lives under skills/graft-comboagent/ (Nox-Lumen-tech/combo-skills). Copy or symlink into the host:
| Host | Commands | Docs |
|---|---|---|
| Cursor | cp -r skills/graft-comboagent ~/.cursor/skills/or ln -s $(pwd)/skills/graft-comboagent ~/.cursor/skills/graft-comboagent | cursor.com/docs/skills |
| Claude Code | cp -r skills/graft-comboagent ~/.claude/skills/ | code.claude.com/docs/en/skills |
| Trae | cp -r skills/graft-comboagent ~/.trae/skills/ | docs.trae.ai/ide/skills |
| Codex | cp -r skills/graft-comboagent ~/.codex/skills/ | Repo SKILL.md standard |
Cursor also scans
~/.claude/skills/and~/.codex/skills/, so one symlink under~/.cursor/skills/often covers every host.
Restart after first install (Claude watches running sessions live, yet new root folders still need restart).
Dependencies:
Legacy
pycryptoinstalls still work—login.pyfalls back automatically.
First run (three steps)
Step 1 — login
Interactive prompt:
Default hostname is
https://xipnex.nox-lumen.com; override if you self-host.
Behind the scenes:
- Reads
public.pembesideSKILL.md - RSA-encrypts the password (same routine as the web UI
crypt()) POST /v1/user/login→signed_auth_token- Writes
~/.config/graft-comboagent/token.jsonwithchmod 0600
Step 2 — verify
Sample output:
Step 3 — exercise all three modes
Action catalog
Everything routes through scripts/call.py; tokens load automatically from ~/.config/graft-comboagent/token.json.
The pack ships
references/action-decision-tree.md—branch on user intent (“find a doc”, “inspect a session”, “offload work to cloud”) before choosing actions.
A · KB search (session optional)
| Action | Role | Example |
|---|---|---|
list_kbs | Discover visible KBs | call.py list_kbs |
kb_detail | Metadata (description, doc counts, vector config) | call.py kb_detail --kb-id "ASPICE engineering KB" |
list_documents | Paginated doc listing + query | call.py list_documents --kb-id "ASPICE engineering KB" --query cooling |
get_doc_profile | Single-doc profile (pages, parse state) | call.py get_doc_profile --doc-id <id> |
list_chunks | Chunk inventory for large docs | call.py list_chunks --doc-id <id> |
search | --source document hybrid search (mirrors chat “Ask” button) | call.py search --query <kw> --source document --kb-ids "ASPICE engineering KB" |
search --source documentfans out to platformunified_search(BM25 + vectors + rerank)—same stack as the product chat box.
B · Session graft (read-only)
| Action | Role | Example |
|---|---|---|
list_sessions | Discover sessions (self + shared) | call.py list_sessions --query <kw> |
get_digest | Narrative recap of rounds + artifacts | call.py get_digest --session-id <x> |
get_round | Exact transcript for one round | call.py get_round --session-id <x> --round-id N |
search | --source round full-text over sessions (--session-id "*") | call.py search --query <kw> --session-id "*" --source round |
search_by_artifact | Find files by artifact name | call.py search_by_artifact --query <name> --session-id <x> |
grep_file | Server-side file grep | call.py grep_file --query <kw> |
read_file | markitdown conversion to markdown (throttled) | call.py read_file --path <p> [--center-line N --context-lines K] [--find <kw>] |
download | Raw bytes to disk (open in Office / pandas) | call.py download --path <p> --out ./report.docx |
list_files | Enumerate session outputs | call.py list_files --session-id <x> |
Focused read_file modes
Default behavior returns the whole markdownized document. For >20K-token files, narrow the window:
C · Dispatch (only write path)
| Action | Role | Example |
|---|---|---|
dispatch_task | Enqueue another user prompt against an existing session | call.py dispatch_task --session-id NAME-OR-UUID --prompt "..." |
Mechanics:
- Server queues a new conversational round
- HTTP returns immediately (non-blocking IDE)
- Cloud worker executes asynchronously
- Poll with
list_sessions,get_digest, or newestget_round
Limits: cannot spawn/delete sessions/files—only append turns.
Names vs. UUIDs for session_id / kb_id
Aligned with tenant kb_ids / naming rules—either UUIDs or human labels resolve server-side:
If multiple names collide, ask the user—never guess.
Response envelope
Stdout is JSON:
Failures:
LLMs should read code + hint before blind retries.
Context throttling rules
Local windows are tighter than cloud Agents. Follow SKILL.md discipline:
- Start with
get_digestfor session recon (≈5–20K tokens) - Drill into
get_roundonly when digest points to critical rounds - Never dump entire binaries into prompts—use
read_file --find/--center-line;downloadgoes to disk, not chat - KB queries default
--source document—don’t scan all rounds unless you intend graft mode B - No brute-force scraping—forbid looping all rounds, entire chunk lists, or mass
downloads
Breaking these bursts local context budgets after a handful of calls.
Three cookbook flows
Flow A · Cloud KB as local RAG
“While coding an EEA init routine locally, cite canonical samples from cloud ‘EEA platform KB’.”
Key: locate a few chunks, then --find; don’t hydrate whole libraries.
Flow B · Graft cloud session conclusions
“Using cloud session ‘ASPICE study’ DFMEA output, audit local
src/for uncovered SRS clauses.”
Flow C · Cloud continues heavy lifting
“I pushed fixes—have the cloud session re-run ASPICE trace; I’ll poll later.”
Dispatch keeps the IDE unblocked—queue analysis, resume coding, fetch conclusions minutes later.
Environment variables
| Var | Purpose | Default |
|---|---|---|
GRAFT_COMBOAGENT_SERVER | Default login host | https://xipnex.nox-lumen.com |
GRAFT_COMBOAGENT_EMAIL | Default email for login.py | — |
GRAFT_COMBOAGENT_TOKEN | Token JSON path | ~/.config/graft-comboagent/token.json |
GRAFT_COMBOAGENT_DL_DIR | Default download folder | ./.graft/downloads/ |
Server requirements
Rolling clusters need:
/v1/user/loginembeddingsigned_auth_token(api/apps/user_app.py)api/apps/graft_app.pyroutes:POST /v1/graft/memory/unified_search— modes A+B JSON actionsGET /v1/graft/memory/download— binary streamingPOST /v1/graft/dispatch— enqueue prompts
All endpoints sit behind @login_required sharing _verify_graft_access (tenant + ACL + mutation guard).
Clients pass Authorization: Bearer <signed_auth_token>.
Known limitations
- Mostly read-only aside from
dispatch_task; even dispatch cannot mutate session lifecycle or arbitrary files—only enqueue turns - Single profile: one ragbase hostname per login; switch hosts via
logout.py+ login or alternate token paths - No browser SSO: email/password today (OAuth requests go through PM)
- Downloads ≤200 MB enforced server-side
- Dispatch lacks synchronous results—always poll afterward
Troubleshooting
| Symptom | Cause | Mitigation |
|---|---|---|
[FATAL] public.pem missing | Pack incomplete | Copy conf/public.pem beside SKILL.md |
[FATAL] signed_auth_token missing | Backend not patched | Deploy ragbase with graft login changes |
[ERR] not logged in | Token absent | python scripts/login.py |
| HTTP 401 | Rotated credential elsewhere | Login again |
ACCESS_DENIED | Session/KB not visible | Confirm sharing; adjust IDs |
NOT_FOUND | Bad artifact path | list_files / list_documents / grep_file first |
ACTION_NOT_ALLOWED | Attempted forbidden write APIs | Stick to documented actions |
FILE_TOO_LARGE | >200 MB artifact | Chunk/split externally |
| Dispatch no-op | Session paused/closed | get_digest for status before retry |
Cloud builtin graft comparison
| Dimension | Cloud agentic/graft | Local graft-comboagent |
|---|---|---|
| Caller | Platform Agent | Local IDE Agents |
| Transport | Native FunctionTool | HTTPS scripts |
| Auth | Implicit session token | Local token.json |
| Scope | Session graft only (mode B-ish) | Modes A+B+C |
| Large files | Read inside Agent context | read_file windows or download + local tools |
| Writes | Other builtins handle persistence | dispatch_task only, no lifecycle edits |