agentix-cli 0.69.3 → 0.69.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-IDAWXA3K.js +2 -0
- package/dist/{board-dashboard-U6IZQA4W.js → board-dashboard-MAP6A2N7.js} +2 -2
- package/dist/{chunk-NO2HOK4B.js → chunk-23A5K5UW.js} +3 -3
- package/dist/chunk-23A5K5UW.js.map +1 -0
- package/dist/{chunk-RN5NOIBH.js → chunk-2UYV24UD.js} +2 -2
- package/dist/{chunk-EQTSOK7L.js → chunk-535MLOSB.js} +15 -15
- package/dist/chunk-535MLOSB.js.map +1 -0
- package/dist/{chunk-SZJMS7K4.js → chunk-5MUJZODV.js} +19 -19
- package/dist/chunk-5MUJZODV.js.map +1 -0
- package/dist/{chunk-SXKOI2IF.js → chunk-5ONI54E6.js} +5 -5
- package/dist/{chunk-E7OACKES.js → chunk-BBIWAJRX.js} +2 -2
- package/dist/{chunk-Y57CULGE.js → chunk-C245D4BZ.js} +2 -2
- package/dist/{chunk-DW5EHX3M.js → chunk-CKBAVCGU.js} +2 -2
- package/dist/{chunk-VVMOTM3V.js → chunk-DQQ3BCJW.js} +2 -2
- package/dist/{chunk-B7S66EK7.js → chunk-IU6L4V3A.js} +14 -14
- package/dist/chunk-IU6L4V3A.js.map +1 -0
- package/dist/{chunk-IULWZA3J.js → chunk-J7HA7ZTU.js} +6 -6
- package/dist/chunk-J7HA7ZTU.js.map +1 -0
- package/dist/chunk-JHCI6XCJ.js +5 -0
- package/dist/chunk-JHCI6XCJ.js.map +1 -0
- package/dist/chunk-K63IOY6A.js +152 -0
- package/dist/chunk-K63IOY6A.js.map +1 -0
- package/dist/{chunk-KPPGT5FJ.js → chunk-M6GPRMCQ.js} +2 -2
- package/dist/{chunk-USMCN2X3.js → chunk-OHRTBD7U.js} +12 -12
- package/dist/{chunk-FGFDPYL4.js → chunk-P2RYUODK.js} +2 -2
- package/dist/chunk-P2RYUODK.js.map +1 -0
- package/dist/{chunk-JYBSYW7T.js → chunk-V2TLFYKH.js} +2 -2
- package/dist/{chunk-3FWJXI7H.js → chunk-YBF2QVZ7.js} +2 -2
- package/dist/cli.js +1 -1
- package/dist/{compaction-H5U32UU6.js → compaction-MECSN36J.js} +4 -4
- package/dist/compaction-MECSN36J.js.map +1 -0
- package/dist/{context-planner-LJT5IKH6.js → context-planner-JU7CNXCB.js} +3 -3
- package/dist/context-planner-JU7CNXCB.js.map +1 -0
- package/dist/heal-CMQL5SG4.js +2 -0
- package/dist/index.d.ts +21 -2
- package/dist/index.js +2 -2
- package/dist/loader-YFVSSYSL.js +2 -0
- package/dist/memory-extract-YCNCARZ2.js +2 -0
- package/dist/program-T275Y2V6.js +2 -0
- package/dist/{promote-7SU6IJDC.js → promote-VZAUTV6E.js} +2 -2
- package/dist/{providers-NRPX2FHP.js → providers-ES5KVLI4.js} +2 -2
- package/dist/query-7QQZPMXM.js +2 -0
- package/dist/{registry-K76DGYLW.js → registry-WQ65MQEY.js} +2 -2
- package/dist/{request-planner-YIG5RUN2.js → request-planner-NMOAOZIM.js} +2 -2
- package/dist/{review-candidates-2JFQ2M53.js → review-candidates-FLY6HZEN.js} +2 -2
- package/dist/{routing-3CIQQZJ4.js → routing-SMIZ77CT.js} +2 -2
- package/dist/{seat-HMF3G6BR.js → seat-F3S2HG5G.js} +2 -2
- package/dist/{select-2P73MN6O.js → select-5GC7CTPQ.js} +2 -2
- package/dist/wiki-J4MMCONT.js +2 -0
- package/package.json +1 -1
- package/dist/agent-OJQKD5AI.js +0 -2
- package/dist/chunk-B7S66EK7.js.map +0 -1
- package/dist/chunk-EQTSOK7L.js.map +0 -1
- package/dist/chunk-FGFDPYL4.js.map +0 -1
- package/dist/chunk-IULWZA3J.js.map +0 -1
- package/dist/chunk-JQXG2OO6.js +0 -152
- package/dist/chunk-JQXG2OO6.js.map +0 -1
- package/dist/chunk-NO2HOK4B.js.map +0 -1
- package/dist/chunk-SZJMS7K4.js.map +0 -1
- package/dist/chunk-VDMYBPYB.js +0 -5
- package/dist/chunk-VDMYBPYB.js.map +0 -1
- package/dist/compaction-H5U32UU6.js.map +0 -1
- package/dist/context-planner-LJT5IKH6.js.map +0 -1
- package/dist/heal-HVYQD3EZ.js +0 -2
- package/dist/loader-EVHZ2R73.js +0 -2
- package/dist/memory-extract-F76BE6KA.js +0 -2
- package/dist/program-O67GMIWX.js +0 -2
- package/dist/query-CY7F5ZFR.js +0 -2
- package/dist/wiki-VXTUHSRH.js +0 -2
- /package/dist/{agent-OJQKD5AI.js.map → agent-IDAWXA3K.js.map} +0 -0
- /package/dist/{board-dashboard-U6IZQA4W.js.map → board-dashboard-MAP6A2N7.js.map} +0 -0
- /package/dist/{chunk-RN5NOIBH.js.map → chunk-2UYV24UD.js.map} +0 -0
- /package/dist/{chunk-SXKOI2IF.js.map → chunk-5ONI54E6.js.map} +0 -0
- /package/dist/{chunk-E7OACKES.js.map → chunk-BBIWAJRX.js.map} +0 -0
- /package/dist/{chunk-Y57CULGE.js.map → chunk-C245D4BZ.js.map} +0 -0
- /package/dist/{chunk-DW5EHX3M.js.map → chunk-CKBAVCGU.js.map} +0 -0
- /package/dist/{chunk-VVMOTM3V.js.map → chunk-DQQ3BCJW.js.map} +0 -0
- /package/dist/{chunk-KPPGT5FJ.js.map → chunk-M6GPRMCQ.js.map} +0 -0
- /package/dist/{chunk-USMCN2X3.js.map → chunk-OHRTBD7U.js.map} +0 -0
- /package/dist/{chunk-JYBSYW7T.js.map → chunk-V2TLFYKH.js.map} +0 -0
- /package/dist/{chunk-3FWJXI7H.js.map → chunk-YBF2QVZ7.js.map} +0 -0
- /package/dist/{heal-HVYQD3EZ.js.map → heal-CMQL5SG4.js.map} +0 -0
- /package/dist/{loader-EVHZ2R73.js.map → loader-YFVSSYSL.js.map} +0 -0
- /package/dist/{memory-extract-F76BE6KA.js.map → memory-extract-YCNCARZ2.js.map} +0 -0
- /package/dist/{program-O67GMIWX.js.map → program-T275Y2V6.js.map} +0 -0
- /package/dist/{promote-7SU6IJDC.js.map → promote-VZAUTV6E.js.map} +0 -0
- /package/dist/{providers-NRPX2FHP.js.map → providers-ES5KVLI4.js.map} +0 -0
- /package/dist/{query-CY7F5ZFR.js.map → query-7QQZPMXM.js.map} +0 -0
- /package/dist/{registry-K76DGYLW.js.map → registry-WQ65MQEY.js.map} +0 -0
- /package/dist/{request-planner-YIG5RUN2.js.map → request-planner-NMOAOZIM.js.map} +0 -0
- /package/dist/{review-candidates-2JFQ2M53.js.map → review-candidates-FLY6HZEN.js.map} +0 -0
- /package/dist/{routing-3CIQQZJ4.js.map → routing-SMIZ77CT.js.map} +0 -0
- /package/dist/{seat-HMF3G6BR.js.map → seat-F3S2HG5G.js.map} +0 -0
- /package/dist/{select-2P73MN6O.js.map → select-5GC7CTPQ.js.map} +0 -0
- /package/dist/{wiki-VXTUHSRH.js.map → wiki-J4MMCONT.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/wiki/capture-filter.ts","../src/agents/memory-extract.ts"],"sourcesContent":["// What deserves to become a raw wiki entry.\n//\n// Until now the only gate was `response.content.length > 50`. Every agent\n// task was captured, with the task's own message as the \"User:\" half — so\n// a cron's prompt became an entry, a role brief became an entry, and a\n// \"[MISSED RUN]\" notice became an entry.\n//\n// Measured over the 10,703 entries that produced: 30% arrived on a\n// machine-origin channel, 33% are prompt-shaped, and 47.5% are one or\n// both. That is the bulk of a corpus that has yielded 257 articles and is\n// read about three times a quarter.\n//\n// These rules are deterministic and free. They are deliberately\n// conservative — each one describes a shape that cannot contain knowledge\n// about the world, rather than guessing at value. Judging whether the\n// REMAINDER is worth keeping is a different question, and belongs to the\n// entry-triage seat, which costs money and can be wrong.\n\n/** Channels where both sides of the exchange are machines. A cron's\n * prompt is configuration, and its reply is a status report. */\nconst MACHINE_CHANNELS = new Set([\"cron\", \"a2a\", \"workflow\", \"selftest\"])\n\n/** Shapes that are an instruction to an agent, not a record of anything.\n * Anchored at the start of the captured content, which always begins\n * \"User: \" followed by the task message. */\nconst PROMPT_SHAPES: Array<[RegExp, string]> = [\n [/^User:\\s*You are\\s/i, \"role brief\"],\n [/^User:\\s*(Run|Execute)\\s/i, \"run instruction\"],\n [/^User:\\s*\\[MISSED RUN/i, \"missed cron run\"],\n // The bracketed prefix varies: some arrive as \"[Group, 09:43]: [Recent\n // group conversation]\". Anchoring on the bare marker missed 367 entries\n // in the first purge, so allow one leading bracketed segment.\n [/^User:\\s*(\\[[^\\]]{0,40}\\]:?\\s*)?\\[Recent group conversation\\]/i, \"group chat dump\"],\n]\n\nexport interface CaptureDecision {\n capture: boolean\n /** Why it was rejected, for logging and for counting what the filter\n * actually removes once it is live. */\n reason?: string\n}\n\nexport function shouldCaptureEntry(input: {\n channel: string | undefined\n content: string\n responseLength: number\n}): CaptureDecision {\n if (input.responseLength <= 50) return { capture: false, reason: \"response too short\" }\n\n const channel = (input.channel ?? \"\").toLowerCase()\n // Channels carry a node suffix: \"telegram@peer-server\".\n const base = channel.split(\"@\")[0]\n if (MACHINE_CHANNELS.has(base)) return { capture: false, reason: `machine channel: ${base}` }\n\n const head = input.content.slice(0, 400)\n for (const [shape, reason] of PROMPT_SHAPES) {\n if (shape.test(head)) return { capture: false, reason }\n }\n\n return { capture: true }\n}\n","import type { MemoryStore } from \"./memory-store\"\nimport { shouldCaptureEntry } from \"@/wiki/capture-filter\"\nimport { containsSecret, trustForChannel } from \"./memory-trust\"\n\n// --- Haiku-powered memory extraction ---\n// Fire-and-forget after each agent response.\n// Extracts memorable facts and writes them to the persistent store.\n\nconst EXTRACTION_MODEL = \"claude-haiku-4-20250514\"\n\nconst EXTRACTION_PROMPT = `You are a memory extraction system for an AI agent. Given a conversation exchange, extract facts worth remembering for future conversations across different chat sessions.\n\nExtract ONLY facts that would help the agent in FUTURE, DIFFERENT conversations:\n- User preferences and instructions (\"never do X\", \"always use Y\")\n- Commitments the agent made (\"I will deploy by Friday\")\n- Relationships (\"Alex is the admin\", \"Marketing handles content\")\n- Task state (\"GitLab tokens need deploying to peer-server\")\n- Important facts about infrastructure, config, or processes\n\nSKIP:\n- Credentials of any kind: passwords, tokens, API keys, private keys, connection strings. Never repeat one, not even partly. At most note that a credential exists and where it is kept, without its value.\n- Routine greetings, acknowledgments, status updates\n- Information derivable from code or config files\n- Transient conversation flow (\"let me check\", \"here's what I found\")\n- Facts already obvious from the agent's system prompt\n\nFor each fact, output a JSON array:\n[{\"category\":\"fact|preference|commitment|task-state\",\"content\":\"concise fact, 1-2 sentences\",\"keywords\":[\"keyword1\",\"keyword2\"]}]\n\nIf nothing worth remembering, output: []`\n\nexport async function extractMemories(\n agentId: string,\n userMessage: string,\n agentResponse: string,\n source: { channel: string; chatId: string; sender: string },\n store: MemoryStore,\n): Promise<void> {\n // Skip very short exchanges (unlikely to contain memorable facts)\n if (userMessage.length < 20 && agentResponse.length < 50) return\n\n // Same gate as wiki capture: machine channels (cron, a2a, workflow) and\n // prompt-shaped messages hold configuration, not facts about the world.\n const gate = shouldCaptureEntry({\n channel: source.channel,\n content: `User: ${userMessage}`,\n responseLength: agentResponse.length,\n })\n if (!gate.capture) return\n\n const { createProvider } = await import(\"@/agent/providers\")\n const provider = createProvider(\"claude\")\n\n const result = await provider.generate(\n [\n { role: \"system\", content: EXTRACTION_PROMPT },\n {\n role: \"user\",\n content: `${source.sender} (via ${source.channel}): ${userMessage}\\n\\nAgent: ${agentResponse}`,\n },\n ],\n { model: EXTRACTION_MODEL, maxTokens: 512 },\n )\n\n // Parse JSON array from response\n const content = result.content.trim()\n const jsonMatch = content.match(/\\[[\\s\\S]*\\]/)\n if (!jsonMatch) return\n\n const facts = JSON.parse(jsonMatch[0]) as Array<{\n category: string\n content: string\n keywords: string[]\n }>\n\n if (!Array.isArray(facts) || facts.length === 0) return\n\n const date = new Date().toISOString().slice(0, 10)\n const trust = trustForChannel(source.channel)\n\n for (const fact of facts) {\n if (typeof fact?.content !== \"string\") continue\n // The prompt says never to extract credentials; this holds when the\n // model does it anyway. addMemory refuses them too.\n if (fact.category === \"secret\" || containsSecret(fact.content)) continue\n // Skip duplicates\n if (store.hasSimilar(agentId, fact.content)) continue\n\n store.addMemory(agentId, {\n agentId,\n category: fact.category as any,\n content: fact.content,\n keywords: fact.keywords || [],\n source: { ...source, date, trust },\n expiresAt: fact.category === \"task-state\"\n ? new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString()\n : undefined,\n })\n }\n\n // Prune occasionally (every ~50 calls, probabilistic)\n if (Math.random() < 0.02) {\n store.prune(agentId)\n }\n}\n"],"mappings":"+CAoBA,IAAMA,EAAmB,IAAI,IAAI,CAAC,OAAQ,MAAO,WAAY,UAAU,CAAC,EAKlEC,EAAyC,CAC7C,CAAC,sBAAuB,YAAY,EACpC,CAAC,4BAA6B,iBAAiB,EAC/C,CAAC,yBAA0B,iBAAiB,EAI5C,CAAC,iEAAkE,iBAAiB,CACtF,EASO,SAASC,EAAmBC,EAIf,CAClB,GAAIA,EAAM,gBAAkB,GAAI,MAAO,CAAE,QAAS,GAAO,OAAQ,oBAAqB,EAItF,IAAMC,GAFWD,EAAM,SAAW,IAAI,YAAY,EAE7B,MAAM,GAAG,EAAE,CAAC,EACjC,GAAIH,EAAiB,IAAII,CAAI,EAAG,MAAO,CAAE,QAAS,GAAO,OAAQ,oBAAoBA,GAAO,EAE5F,IAAMC,EAAOF,EAAM,QAAQ,MAAM,EAAG,GAAG,EACvC,OAAW,CAACG,EAAOC,CAAM,IAAKN,EAC5B,GAAIK,EAAM,KAAKD,CAAI,EAAG,MAAO,CAAE,QAAS,GAAO,OAAAE,CAAO,EAGxD,MAAO,CAAE,QAAS,EAAK,CACzB,CCpDA,IAAMC,EAAmB,0BAEnBC,EAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0CAqB1B,eAAsBC,EACpBC,EACAC,EACAC,EACAC,EACAC,EACe,CAWf,GATIH,EAAY,OAAS,IAAMC,EAAc,OAAS,IASlD,CALSG,EAAmB,CAC9B,QAASF,EAAO,QAChB,QAAS,SAASF,IAClB,eAAgBC,EAAc,MAChC,CAAC,EACS,QAAS,OAEnB,GAAM,CAAE,eAAAI,CAAe,EAAI,KAAM,QAAO,yBAAmB,EAgBrDC,GAbS,MAFED,EAAe,QAAQ,EAEV,SAC5B,CACE,CAAE,KAAM,SAAU,QAASR,CAAkB,EAC7C,CACE,KAAM,OACN,QAAS,GAAGK,EAAO,eAAeA,EAAO,aAAaF;AAAA;AAAA,SAAyBC,GACjF,CACF,EACA,CAAE,MAAOL,EAAkB,UAAW,GAAI,CAC5C,GAGuB,QAAQ,KAAK,EACV,MAAM,aAAa,EAC7C,GAAI,CAACU,EAAW,OAEhB,IAAMC,EAAQ,KAAK,MAAMD,EAAU,CAAC,CAAC,EAMrC,GAAI,CAAC,MAAM,QAAQC,CAAK,GAAKA,EAAM,SAAW,EAAG,OAEjD,IAAMC,EAAO,IAAI,KAAK,EAAE,YAAY,EAAE,MAAM,EAAG,EAAE,EAC3CC,EAAQC,EAAgBR,EAAO,OAAO,EAE5C,QAAWS,KAAQJ,EACb,OAAOI,GAAM,SAAY,WAGzBA,EAAK,WAAa,UAAYC,EAAeD,EAAK,OAAO,GAEzDR,EAAM,WAAWJ,EAASY,EAAK,OAAO,GAE1CR,EAAM,UAAUJ,EAAS,CACvB,QAAAA,EACA,SAAUY,EAAK,SACf,QAASA,EAAK,QACd,SAAUA,EAAK,UAAY,CAAC,EAC5B,OAAQ,CAAE,GAAGT,EAAQ,KAAAM,EAAM,MAAAC,CAAM,EACjC,UAAWE,EAAK,WAAa,aACzB,IAAI,KAAK,KAAK,IAAI,EAAI,EAAI,GAAK,GAAK,GAAK,GAAI,EAAE,YAAY,EAC3D,MACN,CAAC,GAIC,KAAK,OAAO,EAAI,KAClBR,EAAM,MAAMJ,CAAO,CAEvB","names":["MACHINE_CHANNELS","PROMPT_SHAPES","shouldCaptureEntry","input","base","head","shape","reason","EXTRACTION_MODEL","EXTRACTION_PROMPT","extractMemories","agentId","userMessage","agentResponse","source","store","shouldCaptureEntry","createProvider","jsonMatch","facts","date","trust","trustForChannel","fact","containsSecret"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/auth-store.ts","../src/agent/tools/definitions.ts","../src/agent/providers/claude.ts","../src/agent/providers/claude-code.ts","../src/agent/providers/openai.ts","../src/agent/providers/demo.ts","../src/agent/providers/index.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, mkdirSync } from \"fs\"\nimport path from \"path\"\nimport os from \"os\"\nimport chalk from \"chalk\"\nimport prompts from \"prompts\"\nimport { logger } from \"@/utils/logger\"\n\n// --- Auth config stored at ~/.agentx/auth.json ---\n\nexport interface AuthConfig {\n provider: \"claude\" | \"claude-code\"\n authType: \"api-key\" | \"oauth\"\n token: string\n model: string\n}\n\nconst AUTH_DIR = path.join(os.homedir(), \".agentx\")\nconst AUTH_FILE = path.join(AUTH_DIR, \"auth.json\")\n\nexport function loadAuthConfig(): AuthConfig | null {\n if (!existsSync(AUTH_FILE)) return null\n try {\n const data = JSON.parse(readFileSync(AUTH_FILE, \"utf8\"))\n if (data.provider && data.authType && data.token && data.model) {\n return data as AuthConfig\n }\n return null\n } catch {\n return null\n }\n}\n\nexport function saveAuthConfig(config: AuthConfig): void {\n if (!existsSync(AUTH_DIR)) {\n mkdirSync(AUTH_DIR, { recursive: true })\n }\n writeFileSync(AUTH_FILE, JSON.stringify(config, null, 2), \"utf8\")\n}\n\n/**\n * Resolve a token for the Anthropic API.\n * Priority: (1) explicit token, (2) env vars, (3) ~/.agentx/auth.json, (4) OpenClaw auth stores\n */\nexport function resolveToken(explicitKey?: string): {\n token: string\n authType: \"api-key\" | \"oauth\"\n} | null {\n // 1. Explicit CLI flag\n if (explicitKey?.trim()) {\n const type = explicitKey.startsWith(\"sk-ant-oat\") ? \"oauth\" : \"api-key\"\n return { token: explicitKey.trim(), authType: type }\n }\n\n // 2. Environment variables\n const envOAuth = process.env.ANTHROPIC_OAUTH_TOKEN\n if (envOAuth?.trim()) {\n return { token: envOAuth.trim(), authType: \"oauth\" }\n }\n\n // Claude Code's native env var (sk-ant-oat01-...). Same OAuth token format\n // as ANTHROPIC_OAUTH_TOKEN but Claude Code sets it under its own name, so\n // without this fallback the SDK providers fail to resolve even though the\n // `claude` CLI in the same shell works fine. Enables the context planner\n // to use the fast SDK path instead of cold-spawning a subprocess.\n const envClaudeCodeOAuth = process.env.CLAUDE_CODE_OAUTH_TOKEN\n if (envClaudeCodeOAuth?.trim()) {\n return { token: envClaudeCodeOAuth.trim(), authType: \"oauth\" }\n }\n\n const envApiKey = process.env.ANTHROPIC_API_KEY\n if (envApiKey?.trim()) {\n return { token: envApiKey.trim(), authType: \"api-key\" }\n }\n\n // 3. Stored config\n const stored = loadAuthConfig()\n if (stored) {\n return { token: stored.token, authType: stored.authType }\n }\n\n // 4. OpenClaw auth-profiles store (legacy fallback)\n const homeDir = os.homedir()\n const storePaths = [\n path.join(homeDir, \".openclaw\", \"agents\", \"main\", \"agent\", \"auth-profiles.json\"),\n path.join(homeDir, \".openclaw\", \"auth-profiles.json\"),\n path.join(homeDir, \".openclaw\", \"credentials\", \"oauth.json\"),\n path.join(homeDir, \".claude\", \"oauth.json\"),\n path.join(homeDir, \".config\", \"claude\", \"oauth.json\"),\n path.join(homeDir, \".config\", \"anthropic\", \"oauth.json\"),\n ]\n\n for (const p of storePaths) {\n const t = readTokenFromAuthProfiles(p)\n if (t) {\n return { token: t, authType: \"oauth\" }\n }\n }\n\n return null\n}\n\n/**\n * Read an Anthropic OAuth/token credential from an auth-profiles or oauth JSON file.\n */\nfunction readTokenFromAuthProfiles(filePath: string): string {\n if (!existsSync(filePath)) return \"\"\n\n try {\n const data = JSON.parse(readFileSync(filePath, \"utf8\"))\n\n // Modern auth-profiles format: { profiles: { \"anthropic:xxx\": { type, access/token } } }\n if (data.profiles) {\n let fallbackToken = \"\"\n for (const [id, cred] of Object.entries(data.profiles) as [string, any][]) {\n if (!id.startsWith(\"anthropic\")) continue\n if (cred.type === \"oauth\" && cred.access) {\n if (cred.expires && Date.now() > cred.expires) {\n continue\n }\n return cred.access\n }\n if (cred.type === \"token\" && cred.token) {\n fallbackToken = cred.token\n }\n }\n if (fallbackToken) return fallbackToken\n }\n\n // Legacy oauth.json format: { anthropic: { access: \"...\" } }\n if (data.anthropic?.access) return data.anthropic.access\n if (data.anthropic?.token) return data.anthropic.token\n\n return \"\"\n } catch {\n return \"\"\n }\n}\n\n// --- Model setup & credentials ---\n\nconst ANTHROPIC_MODELS = [\n { id: \"claude-sonnet-4-20250514\", label: \"anthropic/claude-sonnet-4\", hint: \"Claude Sonnet 4 · ctx 200k · recommended\" },\n { id: \"claude-opus-4-20250514\", label: \"anthropic/claude-opus-4-5\", hint: \"Claude Opus 4.5 · ctx 200k · reasoning\" },\n { id: \"claude-haiku-4-20250514\", label: \"anthropic/claude-haiku-4\", hint: \"Claude Haiku 4 · ctx 200k · fast\" },\n]\n\nasync function handleApiKey(): Promise<{ token: string; authType: \"api-key\" } | null> {\n const envKey = process.env.ANTHROPIC_API_KEY\n if (envKey?.trim()) {\n const preview = envKey.slice(0, 10) + \"...\" + envKey.slice(-4)\n const { useExisting } = await prompts({\n type: \"confirm\",\n name: \"useExisting\",\n message: `Use existing ANTHROPIC_API_KEY (env, ${preview})?`,\n initial: true,\n })\n\n if (useExisting) {\n return { token: envKey.trim(), authType: \"api-key\" }\n }\n }\n\n const stored = loadAuthConfig()\n if (stored?.authType === \"api-key\" && stored.token) {\n const preview = stored.token.slice(0, 10) + \"...\" + stored.token.slice(-4)\n const { useExisting } = await prompts({\n type: \"confirm\",\n name: \"useExisting\",\n message: `Use stored API key (${preview})?`,\n initial: true,\n })\n\n if (useExisting) {\n return { token: stored.token, authType: \"api-key\" }\n }\n }\n\n const { apiKey } = await prompts({\n type: \"password\",\n name: \"apiKey\",\n message: \"Enter Anthropic API key\",\n validate: (v: string) => {\n if (!v.startsWith(\"sk-ant-api\")) return \"API key must start with sk-ant-api\"\n if (v.length < 40) return \"API key seems too short\"\n return true\n },\n })\n\n if (!apiKey) return null\n return { token: apiKey, authType: \"api-key\" }\n}\n\nasync function handleSetupToken(): Promise<{ token: string; authType: \"oauth\" } | null> {\n logger.break()\n const boxWidth = 46\n const line1 = \"Run `claude setup-token` in your terminal.\"\n const line2 = \"Then paste the generated token below.\"\n console.log(` ${chalk.cyan(\"Anthropic setup-token\")} ${\"─\".repeat(boxWidth - \"Anthropic setup-token\".length - 1)}╮`)\n console.log(` ${\" \".repeat(boxWidth)}│`)\n console.log(` ${line1}${\" \".repeat(boxWidth - line1.length)}│`)\n console.log(` ${line2}${\" \".repeat(boxWidth - line2.length)}│`)\n console.log(` ${\" \".repeat(boxWidth)}│`)\n console.log(` ${\"─\".repeat(boxWidth)}╯`)\n logger.break()\n\n const { token } = await prompts({\n type: \"password\",\n name: \"token\",\n message: \"Paste Anthropic setup-token\",\n validate: (v: string) => {\n if (!v.trim()) return \"Token is required\"\n if (v.length < 40) return \"Token seems too short\"\n return true\n },\n })\n\n if (!token) return null\n\n const { tokenName } = await prompts({\n type: \"text\",\n name: \"tokenName\",\n message: \"Token name (blank = default)\",\n })\n\n return { token, authType: \"oauth\" }\n}\n\ntype AuthResult = { token: string; authType: \"api-key\" | \"oauth\" } | null\n\nasync function selectAuthMethod(): Promise<AuthResult> {\n while (true) {\n const { provider } = await prompts({\n type: \"select\",\n name: \"provider\",\n message: \"Model/auth provider\",\n choices: [\n { title: \"Anthropic\", value: \"anthropic\", description: \"setup-token + API key\" },\n ],\n })\n\n if (provider === undefined) return null\n\n const { method } = await prompts({\n type: \"select\",\n name: \"method\",\n message: \"Anthropic auth method\",\n choices: [\n { title: \"Anthropic token (paste setup-token)\", value: \"setup-token\", description: \"run `claude setup-token` elsewhere, then paste the token here\" },\n { title: \"Anthropic API key\", value: \"api-key\" },\n { title: chalk.dim(\"Back\"), value: \"back\" },\n ],\n })\n\n if (method === undefined) return null\n if (method === \"back\") continue\n\n if (method === \"api-key\") return handleApiKey()\n if (method === \"setup-token\") return handleSetupToken()\n\n return null\n }\n}\n\nasync function selectModel(): Promise<string | null> {\n const stored = loadAuthConfig()\n\n const choices = ANTHROPIC_MODELS.map((m) => ({\n title: m.label,\n value: m.id,\n description: m.hint + (stored?.model === m.id ? \" · current\" : \"\"),\n }))\n\n if (stored?.model) {\n const current = ANTHROPIC_MODELS.find((m) => m.id === stored.model)\n const label = current ? current.label : stored.model\n choices.unshift({\n title: `Keep current (${label})`,\n value: stored.model,\n description: \"no change\",\n })\n }\n\n const { modelId } = await prompts({\n type: \"select\",\n name: \"modelId\",\n message: \"Default model\",\n choices,\n })\n\n return modelId || null\n}\n\n/**\n * Reusable interactive setup flow. Returns the saved config, or null if the user cancelled.\n */\nexport async function runModelSetup(): Promise<AuthConfig | null> {\n const auth = await selectAuthMethod()\n if (!auth) return null\n\n const modelId = await selectModel()\n if (!modelId) return null\n\n const config: AuthConfig = {\n provider: auth.authType === \"oauth\" ? \"claude-code\" : \"claude\",\n authType: auth.authType,\n token: auth.token,\n model: modelId,\n }\n\n saveAuthConfig(config)\n\n logger.break()\n logger.success(\"Configuration saved to ~/.agentx/auth.json\")\n console.log(` Provider: ${chalk.bold(\"anthropic\")} (${config.authType})`)\n console.log(` Model: ${chalk.bold(ANTHROPIC_MODELS.find((m) => m.id === config.model)?.label || config.model)}`)\n\n return config\n}\n\n/**\n * Ensure credentials are available. If not, auto-prompt the interactive setup.\n * Returns true if credentials are ready, false if user cancelled.\n */\nexport async function ensureCredentials(explicitKey?: string): Promise<boolean> {\n const resolved = resolveToken(explicitKey)\n if (resolved) return true\n\n logger.warn(\"No AI credentials configured.\")\n logger.break()\n\n const result = await runModelSetup()\n return result !== null\n}\n","// --- Agent tool definitions in Anthropic tool_use format ---\n\nexport interface ToolDefinition {\n name: string\n description: string\n input_schema: Record<string, unknown>\n permission?: \"file-read\" | \"file-write\" | \"command\" | \"none\"\n}\n\nconst TOOL_DEFINITIONS: ToolDefinition[] = [\n {\n name: \"create_files\",\n description:\n \"Create one or more files as output. Use this when you need to generate code, documents, configs, or any file-based output.\",\n input_schema: {\n type: \"object\",\n properties: {\n files: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n path: {\n type: \"string\",\n description:\n \"Relative file path from project root (e.g., src/components/Button.tsx)\",\n },\n content: {\n type: \"string\",\n description: \"The full content of the file\",\n },\n language: {\n type: \"string\",\n description: \"Programming language or file type\",\n },\n description: {\n type: \"string\",\n description: \"Brief description of what this file does\",\n },\n },\n required: [\"path\", \"content\"],\n },\n },\n summary: {\n type: \"string\",\n description: \"Brief summary of all generated files\",\n },\n },\n required: [\"files\"],\n },\n permission: \"file-write\",\n },\n {\n name: \"ask_user\",\n description:\n \"Ask the user a clarifying question when you need more information to proceed. Use this when the request is ambiguous or you need to confirm important decisions.\",\n input_schema: {\n type: \"object\",\n properties: {\n question: {\n type: \"string\",\n description: \"The question to ask the user\",\n },\n options: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Optional list of choices for the user\",\n },\n },\n required: [\"question\"],\n },\n permission: \"none\",\n },\n {\n name: \"read_file\",\n description:\n \"Read the contents of a file. Use this to inspect existing code, understand patterns, check implementations, or gather context before generating code.\",\n input_schema: {\n type: \"object\",\n properties: {\n path: {\n type: \"string\",\n description: \"Relative file path from project root\",\n },\n max_lines: {\n type: \"number\",\n description:\n \"Maximum number of lines to read. Defaults to 500. Use for large files.\",\n },\n },\n required: [\"path\"],\n },\n permission: \"file-read\",\n },\n {\n name: \"search_files\",\n description:\n \"Search for files matching a pattern and optionally search their content with a regex. Use this to find relevant code, understand project structure, or locate specific patterns.\",\n input_schema: {\n type: \"object\",\n properties: {\n pattern: {\n type: \"string\",\n description:\n \"Glob pattern to match files (e.g., 'src/**/*.ts', '*.json')\",\n },\n content_regex: {\n type: \"string\",\n description:\n \"Optional regex to search within matched files. Returns matching lines.\",\n },\n max_results: {\n type: \"number\",\n description: \"Maximum number of results to return. Default: 50.\",\n },\n },\n required: [\"pattern\"],\n },\n permission: \"none\",\n },\n {\n name: \"list_directory\",\n description:\n \"List files and directories at a given path. Use this to explore project structure.\",\n input_schema: {\n type: \"object\",\n properties: {\n path: {\n type: \"string\",\n description:\n \"Relative directory path from project root. Defaults to '.' (root).\",\n },\n recursive: {\n type: \"boolean\",\n description: \"List recursively. Default: false.\",\n },\n max_depth: {\n type: \"number\",\n description: \"Maximum depth for recursive listing. Default: 3.\",\n },\n },\n },\n permission: \"none\",\n },\n {\n name: \"run_command\",\n description:\n \"Execute a shell command. Use this to run build tools, test commands, linters, or inspect the environment. Commands run in the project root directory.\",\n input_schema: {\n type: \"object\",\n properties: {\n command: {\n type: \"string\",\n description: \"The shell command to execute\",\n },\n timeout: {\n type: \"number\",\n description:\n \"Timeout in milliseconds. Default: 30000 (30 seconds).\",\n },\n },\n required: [\"command\"],\n },\n permission: \"command\",\n },\n {\n name: \"edit_file\",\n description:\n \"Apply search-and-replace edits to an existing file. Use this for targeted modifications to existing code rather than rewriting entire files.\",\n input_schema: {\n type: \"object\",\n properties: {\n path: {\n type: \"string\",\n description: \"Relative file path from project root\",\n },\n edits: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n old_text: {\n type: \"string\",\n description: \"The exact text to find in the file\",\n },\n new_text: {\n type: \"string\",\n description: \"The replacement text\",\n },\n },\n required: [\"old_text\", \"new_text\"],\n },\n description: \"List of search/replace pairs to apply in order\",\n },\n },\n required: [\"path\", \"edits\"],\n },\n permission: \"file-write\",\n },\n {\n name: \"spawn_agent\",\n description:\n \"Spawn a sub-agent to handle a task in the background. The sub-agent runs in its own session and returns results. Use this for parallel research, long-running tasks, or delegating work to a specialized agent.\",\n input_schema: {\n type: \"object\",\n properties: {\n agent_id: {\n type: \"string\",\n description: \"The ID of the agent to spawn (must be a registered agent)\",\n },\n prompt: {\n type: \"string\",\n description: \"The task description for the sub-agent\",\n },\n timeout_seconds: {\n type: \"number\",\n description: \"Maximum time to wait for completion in seconds. Default: 300 (5 minutes).\",\n },\n },\n required: [\"agent_id\", \"prompt\"],\n },\n permission: \"none\",\n },\n]\n\n/**\n * Get all tool definitions in Anthropic API format (for generateRaw()).\n * Optionally filter by enabled tool names.\n */\nexport function getAnthropicTools(enabledTools?: string[]): Array<{\n name: string\n description: string\n input_schema: Record<string, unknown>\n}> {\n const defs = enabledTools\n ? TOOL_DEFINITIONS.filter((t) => enabledTools.includes(t.name))\n : TOOL_DEFINITIONS\n\n return defs.map(({ name, description, input_schema }) => ({\n name,\n description,\n input_schema,\n }))\n}\n\n/**\n * Get the legacy tools (create_files + ask_user) for backward compatibility.\n */\nexport function getLegacyTools(): Array<{\n name: string\n description: string\n input_schema: Record<string, unknown>\n}> {\n return getAnthropicTools([\"create_files\", \"ask_user\"])\n}\n\n/**\n * Format tool descriptions for inclusion in a system prompt.\n * Used when the `claude` CLI binary handles its own tools —\n * we describe our additional tools as text so the binary's built-in\n * capabilities (read, write, bash) handle them natively.\n */\nexport function formatToolsForSystemPrompt(): string {\n const agenticTools = TOOL_DEFINITIONS.filter(\n (t) => t.name !== \"create_files\" && t.name !== \"ask_user\"\n )\n\n if (agenticTools.length === 0) return \"\"\n\n const lines = [\n \"# Available Capabilities\",\n \"In addition to generating files, you have the following capabilities:\",\n \"\",\n ]\n\n for (const tool of agenticTools) {\n lines.push(`## ${tool.name}`)\n lines.push(tool.description)\n lines.push(\"\")\n }\n\n return lines.join(\"\\n\")\n}\n\nexport const ALL_TOOL_NAMES = TOOL_DEFINITIONS.map((t) => t.name)\n","import type {\n AgentProvider,\n GenerationMessage,\n GenerationResult,\n GeneratedFile,\n ProviderOptions,\n StreamEvent,\n AnthropicMessage,\n RawGenerationResult,\n RawStreamEvent,\n ContentBlock,\n} from \"./types\"\nimport { resolveToken } from \"@/utils/auth-store\"\nimport { getLegacyTools } from \"../tools/definitions\"\n\n// --- Claude provider (default) using Anthropic SDK ---\n\nconst DEFAULT_MODEL = \"claude-sonnet-4-20250514\"\nconst DEFAULT_MAX_TOKENS = 8192\n// Min tokens for Anthropic prompt-cache eligibility (varies by model:\n// 1024 Haiku / 2048 Sonnet / 4096 Opus). Below threshold the API\n// silently skips caching. We wrap unconditionally — cheap when it\n// doesn't fit, big win when it does. Voice/text on acme-public have\n// 40K-token system prompts so caching always kicks in there.\nconst CACHE_MIN_CHARS = 4 * 1024 // ~1k tokens at 4 char/tok\n\n/**\n * Wrap a system-prompt string in an Anthropic structured-block array\n * with `cache_control: { type: \"ephemeral\" }`. Returns a plain string\n * when the prompt is too short to be cache-eligible (saves a request-\n * shape change that wouldn't help). Cache TTL is the default 5 minutes;\n * suitable for back-to-back chat turns within a session.\n */\nfunction wrapSystemForCache(systemPrompt: string): unknown {\n if (!systemPrompt || systemPrompt.length < CACHE_MIN_CHARS) {\n return systemPrompt\n }\n return [\n {\n type: \"text\",\n text: systemPrompt,\n cache_control: { type: \"ephemeral\" },\n },\n ]\n}\n\ninterface AnthropicResponse {\n id: string\n content: Array<{\n type: \"text\" | \"tool_use\" | \"tool_result\"\n text?: string\n id?: string\n name?: string\n input?: Record<string, unknown>\n tool_use_id?: string\n content?: string\n }>\n model: string\n stop_reason: string\n usage: { input_tokens: number; output_tokens: number }\n}\n\nexport class ClaudeProvider implements AgentProvider {\n name = \"claude\"\n private apiKey: string\n private authType: \"api-key\" | \"oauth\" = \"api-key\"\n\n constructor(apiKey?: string) {\n this.apiKey = apiKey || process.env.ANTHROPIC_API_KEY || \"\"\n if (!this.apiKey) {\n // Fallback to auth store\n const resolved = resolveToken()\n if (resolved) {\n this.apiKey = resolved.token\n this.authType = resolved.authType\n }\n }\n if (!this.apiKey) {\n throw new Error(\n \"Anthropic API key required. Run `agentx model` to configure, set ANTHROPIC_API_KEY, or pass --api-key.\"\n )\n }\n }\n\n async generate(\n messages: GenerationMessage[],\n options?: ProviderOptions\n ): Promise<GenerationResult> {\n const model = options?.model || DEFAULT_MODEL\n const maxTokens = options?.maxTokens || DEFAULT_MAX_TOKENS\n\n // Extract system message\n const systemMsg = messages.find((m) => m.role === \"system\")\n const conversationMsgs = messages\n .filter((m) => m.role !== \"system\")\n .map((m) => ({\n role: m.role as \"user\" | \"assistant\",\n content: m.content,\n }))\n\n const body: Record<string, unknown> = {\n model,\n max_tokens: maxTokens,\n messages: conversationMsgs,\n tools: getLegacyTools(),\n }\n\n if (systemMsg) {\n body.system = wrapSystemForCache(systemMsg.content)\n }\n\n if (options?.temperature !== undefined) {\n body.temperature = options.temperature\n }\n\n const response = await this.callApi(body, options)\n\n return this.parseResponse(response)\n }\n\n async generateRaw(\n messages: AnthropicMessage[],\n systemPrompt: string,\n tools: Array<{ name: string; description: string; input_schema: Record<string, unknown> }>,\n options?: ProviderOptions\n ): Promise<RawGenerationResult> {\n const model = options?.model || DEFAULT_MODEL\n const maxTokens = options?.maxTokens || DEFAULT_MAX_TOKENS\n\n const body: Record<string, unknown> = {\n model,\n max_tokens: maxTokens,\n system: wrapSystemForCache(systemPrompt),\n messages,\n tools,\n }\n\n if (options?.temperature !== undefined) {\n body.temperature = options.temperature\n }\n\n if (options?.toolChoice) {\n body.tool_choice = { type: \"tool\", name: options.toolChoice.name }\n }\n\n const response = await this.callApi(body, options)\n\n // Map response content blocks to our ContentBlock type\n const content: ContentBlock[] = response.content.map((block) => {\n if (block.type === \"text\") {\n return { type: \"text\" as const, text: block.text || \"\" }\n }\n if (block.type === \"tool_use\") {\n return {\n type: \"tool_use\" as const,\n id: block.id || \"\",\n name: block.name || \"\",\n input: block.input || {},\n }\n }\n return { type: \"text\" as const, text: \"\" }\n })\n\n return {\n content,\n stop_reason: response.stop_reason as RawGenerationResult[\"stop_reason\"],\n usage: response.usage,\n }\n }\n\n async *stream(\n messages: GenerationMessage[],\n options?: ProviderOptions\n ): AsyncIterable<StreamEvent> {\n const model = options?.model || DEFAULT_MODEL\n const maxTokens = options?.maxTokens || DEFAULT_MAX_TOKENS\n\n const systemMsg = messages.find((m) => m.role === \"system\")\n const conversationMsgs = messages\n .filter((m) => m.role !== \"system\")\n .map((m) => ({\n role: m.role as \"user\" | \"assistant\",\n content: m.content,\n }))\n\n const body: Record<string, unknown> = {\n model,\n max_tokens: maxTokens,\n messages: conversationMsgs,\n stream: true,\n tools: getLegacyTools(),\n }\n\n if (systemMsg) {\n body.system = wrapSystemForCache(systemMsg.content)\n }\n\n const headers = this.buildHeaders()\n\n const res = await fetch(\"https://api.anthropic.com/v1/messages\", {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: options?.abortSignal,\n })\n\n if (!res.ok) {\n const errorText = await res.text()\n yield { type: \"error\", error: `Anthropic API error (${res.status}): ${errorText}` }\n return\n }\n\n const reader = res.body?.getReader()\n if (!reader) {\n yield { type: \"error\", error: \"No response body\" }\n return\n }\n\n const decoder = new TextDecoder()\n let buffer = \"\"\n const files: GeneratedFile[] = []\n let content = \"\"\n let followUp: string | undefined\n let tokensUsed = 0\n let activeTool: { name: string; id: string; json: string } | null = null\n\n const inferFollowUpFromText = (text: string): string | undefined => {\n const cleaned = (text || \"\").trim()\n if (!cleaned) return undefined\n if (cleaned.includes(\"```\")) return undefined\n\n const lines = cleaned.replace(/\\r\\n/g, \"\\n\").split(\"\\n\")\n const tail = lines.slice(Math.max(0, lines.length - 20))\n const tailText = tail.join(\"\\n\")\n\n const looksLikePlanApproval =\n /\\b(plan|proposal)\\b/i.test(tailText) &&\n /(ready for your review|review (the )?plan|requesting plan approval|awaiting approval|waiting for (your )?approval|approve (the )?plan|approval to proceed)/i.test(\n tailText\n )\n\n if (looksLikePlanApproval) {\n return (\n \"The provider is requesting plan approval.\\n\" +\n \"Reply with:\\n\" +\n \"- approve\\n\" +\n \"- revise: <what to change>\\n\" +\n \"- cancel\"\n )\n }\n\n const isIntro = (l: string) =>\n /^(question|clarification|clarify|i need|need more|before i proceed|to proceed|please (confirm|clarify)|which|what|where|when|how|do you)/i.test(\n l.trim()\n )\n\n let startIdx = -1\n for (let i = tail.length - 1; i >= 0; i--) {\n const l = tail[i].trim()\n if (!l) continue\n if (isIntro(l) || l.includes(\"?\")) {\n startIdx = i\n break\n }\n }\n if (startIdx === -1) return undefined\n\n const out: string[] = []\n for (let i = startIdx; i < tail.length && out.length < 8; i++) {\n const l = tail[i]\n const t = l.trim()\n if (out.length > 0 && !t) break\n if (\n out.length > 0 &&\n !/^(options?:|[-*]\\s|\\d+[\\).]\\s)/i.test(t) &&\n !t.includes(\"?\")\n ) {\n break\n }\n out.push(l.trimEnd())\n }\n\n const candidate = out.join(\"\\n\").trim()\n if (candidate.length < 5) return undefined\n if (candidate.length > 800) return candidate.slice(0, 800).trimEnd()\n return candidate\n }\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split(\"\\n\")\n buffer = lines.pop() || \"\"\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue\n const data = line.slice(6).trim()\n if (data === \"[DONE]\") continue\n\n try {\n const event = JSON.parse(data)\n\n if (event.type === \"content_block_start\") {\n if (event.content_block?.type === \"tool_use\") {\n // Finalize any previous tool block if the stream didn't send a stop.\n if (activeTool) {\n yield { type: \"tool_use_end\", name: activeTool.name }\n try {\n const input = JSON.parse(activeTool.json || \"{}\") as any\n if (activeTool.name === \"create_files\" && input) {\n if (Array.isArray(input.files)) {\n files.push(...(input.files as GeneratedFile[]))\n }\n if (typeof input.summary === \"string\" && input.summary.trim()) {\n content += `\\n${input.summary}`\n }\n }\n if (activeTool.name === \"ask_user\" && input) {\n if (typeof input.question === \"string\" && input.question.trim()) {\n followUp = input.question\n if (Array.isArray(input.options) && input.options.length) {\n followUp += `\\nOptions: ${input.options.join(\", \")}`\n }\n }\n }\n } catch {\n // Ignore invalid tool JSON\n } finally {\n activeTool = null\n }\n }\n\n activeTool = {\n name: event.content_block.name,\n id: event.content_block.id,\n json: \"\",\n }\n yield {\n type: \"tool_use_start\",\n name: event.content_block.name,\n id: event.content_block.id,\n }\n }\n }\n\n if (event.type === \"content_block_delta\") {\n if (event.delta?.type === \"text_delta\") {\n content += event.delta.text\n yield { type: \"text_delta\", text: event.delta.text }\n }\n if (event.delta?.type === \"input_json_delta\") {\n if (activeTool) activeTool.json += event.delta.partial_json || \"\"\n yield { type: \"tool_use_delta\", json: event.delta.partial_json }\n }\n }\n\n if (event.type === \"content_block_stop\") {\n // Tool use blocks are complete\n if (activeTool) {\n // Manually inline finalize to avoid generator gymnastics.\n yield { type: \"tool_use_end\", name: activeTool.name }\n try {\n const input = JSON.parse(activeTool.json || \"{}\") as any\n if (activeTool.name === \"create_files\" && input) {\n if (Array.isArray(input.files)) {\n files.push(...(input.files as GeneratedFile[]))\n }\n if (typeof input.summary === \"string\" && input.summary.trim()) {\n content += `\\n${input.summary}`\n }\n }\n if (activeTool.name === \"ask_user\" && input) {\n if (typeof input.question === \"string\" && input.question.trim()) {\n followUp = input.question\n if (Array.isArray(input.options) && input.options.length) {\n followUp += `\\nOptions: ${input.options.join(\", \")}`\n }\n }\n }\n } catch {\n // Ignore invalid tool JSON\n } finally {\n activeTool = null\n }\n }\n }\n\n if (event.type === \"message_delta\") {\n if (event.usage) {\n tokensUsed = (event.usage.input_tokens || 0) + (event.usage.output_tokens || 0)\n }\n }\n\n if (event.type === \"message_stop\") {\n // Message complete\n }\n } catch {\n // Skip unparseable lines\n }\n }\n }\n } finally {\n reader.releaseLock()\n }\n\n // Flush any trailing tool input.\n if (activeTool) {\n yield { type: \"tool_use_end\", name: activeTool.name }\n try {\n const input = JSON.parse(activeTool.json || \"{}\") as any\n if (activeTool.name === \"create_files\" && input) {\n if (Array.isArray(input.files)) {\n files.push(...(input.files as GeneratedFile[]))\n }\n if (typeof input.summary === \"string\" && input.summary.trim()) {\n content += `\\n${input.summary}`\n }\n }\n if (activeTool.name === \"ask_user\" && input) {\n if (typeof input.question === \"string\" && input.question.trim()) {\n followUp = input.question\n if (Array.isArray(input.options) && input.options.length) {\n followUp += `\\nOptions: ${input.options.join(\", \")}`\n }\n }\n }\n } catch {\n // Ignore\n }\n activeTool = null\n }\n\n if (!followUp && files.length === 0) {\n followUp = inferFollowUpFromText(content)\n }\n\n yield {\n type: \"done\",\n result: { content, files, followUp, tokensUsed },\n }\n }\n\n /**\n * Streaming variant of generateRaw — yields text_delta events per\n * chunk so callers can pipe per-token to a UI, AND assembles the full\n * RawGenerationResult (with tool_use blocks intact, stop_reason set)\n * so the agentic loop's tool dispatch keeps working unchanged.\n *\n * Implementation mirrors stream() but:\n * - Targets generateRaw's signature (anthropicMessages + systemPrompt +\n * tools as parameters, not interleaved messages).\n * - Builds a ContentBlock[] with both text and tool_use blocks in the\n * order they arrived, so the agentic loop's `result.content.filter(\n * b => b.type === \"tool_use\")` still finds them.\n * - Yields one terminal `raw_result` event the loop consumes.\n */\n async *generateRawStream(\n messages: AnthropicMessage[],\n systemPrompt: string,\n tools: Array<{ name: string; description: string; input_schema: Record<string, unknown> }>,\n options?: ProviderOptions,\n ): AsyncIterable<RawStreamEvent> {\n const model = options?.model || DEFAULT_MODEL\n const maxTokens = options?.maxTokens || DEFAULT_MAX_TOKENS\n\n const body: Record<string, unknown> = {\n model,\n max_tokens: maxTokens,\n system: wrapSystemForCache(systemPrompt),\n messages,\n tools,\n stream: true,\n }\n if (options?.temperature !== undefined) body.temperature = options.temperature\n\n const headers = this.buildHeaders()\n let res: Response\n try {\n res = await fetch(\"https://api.anthropic.com/v1/messages\", {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: options?.abortSignal,\n })\n } catch (err: any) {\n yield { type: \"error\", error: `Anthropic stream init failed: ${err?.message ?? err}` }\n return\n }\n\n if (!res.ok) {\n const errText = await res.text().catch(() => \"\")\n yield { type: \"error\", error: `Anthropic API error (${res.status}): ${errText.slice(0, 500)}` }\n return\n }\n\n const reader = res.body?.getReader()\n if (!reader) {\n yield { type: \"error\", error: \"No response body on Anthropic stream\" }\n return\n }\n\n const decoder = new TextDecoder()\n let buffer = \"\"\n const blocks: ContentBlock[] = []\n let activeBlockIdx: number | null = null\n let stopReason: RawGenerationResult[\"stop_reason\"] = \"end_turn\"\n let inputTokens = 0\n let outputTokens = 0\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split(\"\\n\")\n buffer = lines.pop() || \"\"\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue\n const data = line.slice(6).trim()\n if (!data || data === \"[DONE]\") continue\n\n let event: any\n try { event = JSON.parse(data) } catch { continue }\n\n if (event.type === \"message_start\" && event.message?.usage) {\n inputTokens = event.message.usage.input_tokens || 0\n }\n\n if (event.type === \"content_block_start\") {\n const cb = event.content_block\n if (cb?.type === \"text\") {\n activeBlockIdx = blocks.length\n blocks.push({ type: \"text\", text: \"\" })\n } else if (cb?.type === \"tool_use\") {\n activeBlockIdx = blocks.length\n blocks.push({\n type: \"tool_use\",\n id: cb.id || \"\",\n name: cb.name || \"\",\n input: {},\n })\n // We'll accumulate the input JSON across input_json_delta\n // events and parse once content_block_stop fires.\n ;(blocks[activeBlockIdx] as any)._rawJson = \"\"\n } else {\n activeBlockIdx = null\n }\n }\n\n if (event.type === \"content_block_delta\" && activeBlockIdx != null) {\n const block = blocks[activeBlockIdx]\n if (event.delta?.type === \"text_delta\" && block.type === \"text\") {\n block.text += event.delta.text\n yield { type: \"text_delta\", text: event.delta.text }\n } else if (event.delta?.type === \"input_json_delta\" && block.type === \"tool_use\") {\n ;(block as any)._rawJson += event.delta.partial_json || \"\"\n }\n }\n\n if (event.type === \"content_block_stop\" && activeBlockIdx != null) {\n const block = blocks[activeBlockIdx]\n if (block.type === \"tool_use\") {\n const raw = (block as any)._rawJson || \"{}\"\n try {\n block.input = JSON.parse(raw) as Record<string, unknown>\n } catch {\n block.input = {}\n }\n delete (block as any)._rawJson\n }\n activeBlockIdx = null\n }\n\n if (event.type === \"message_delta\") {\n if (event.delta?.stop_reason) {\n stopReason = event.delta.stop_reason as RawGenerationResult[\"stop_reason\"]\n }\n if (event.usage) {\n outputTokens = event.usage.output_tokens || 0\n }\n }\n }\n }\n } catch (err: any) {\n yield { type: \"error\", error: `Anthropic stream read failed: ${err?.message ?? err}` }\n return\n } finally {\n reader.releaseLock()\n }\n\n yield {\n type: \"raw_result\",\n result: {\n content: blocks,\n stop_reason: stopReason,\n usage: { input_tokens: inputTokens, output_tokens: outputTokens },\n },\n }\n }\n\n private buildHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"anthropic-version\": \"2023-06-01\",\n }\n\n if (this.authType === \"oauth\") {\n headers[\"Authorization\"] = `Bearer ${this.apiKey}`\n headers[\"anthropic-beta\"] = \"claude-code-20250219,oauth-2025-04-20\"\n headers[\"user-agent\"] = \"claude-cli/2.1.2 (external, cli)\"\n headers[\"x-app\"] = \"cli\"\n headers[\"anthropic-dangerous-direct-browser-access\"] = \"true\"\n } else {\n headers[\"x-api-key\"] = this.apiKey\n }\n\n return headers\n }\n\n private async callApi(\n body: Record<string, unknown>,\n options?: ProviderOptions,\n ): Promise<AnthropicResponse> {\n const headers = this.buildHeaders()\n\n // Every request here is cancellable. Without the signal an operator\n // cancel only marks the task aborted in the registry while the HTTP\n // request runs to completion, and a caller's own AbortController — the\n // shape used by src/graph/classifier.ts and src/agents/context-planner.ts\n // — does nothing at all.\n const res = await fetch(\"https://api.anthropic.com/v1/messages\", {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: options?.abortSignal,\n })\n\n if (!res.ok) {\n const errorText = await res.text()\n throw new Error(`Anthropic API error (${res.status}): ${errorText}`)\n }\n\n return (await res.json()) as AnthropicResponse\n }\n\n private parseResponse(response: AnthropicResponse): GenerationResult {\n const files: GeneratedFile[] = []\n let content = \"\"\n let followUp: string | undefined\n\n for (const block of response.content) {\n if (block.type === \"text\") {\n content += block.text || \"\"\n }\n\n if (block.type === \"tool_use\") {\n if (block.name === \"create_files\" && block.input) {\n const input = block.input as {\n files: GeneratedFile[]\n summary?: string\n }\n files.push(...(input.files || []))\n if (input.summary) {\n content += `\\n${input.summary}`\n }\n }\n\n if (block.name === \"ask_user\" && block.input) {\n const input = block.input as { question: string; options?: string[] }\n followUp = input.question\n if (input.options?.length) {\n followUp += `\\nOptions: ${input.options.join(\", \")}`\n }\n }\n }\n }\n\n return {\n content,\n files,\n followUp,\n tokensUsed: response.usage.input_tokens + response.usage.output_tokens,\n }\n }\n}\n","import type {\n AgentProvider,\n GenerationMessage,\n GenerationResult,\n GeneratedFile,\n ProviderOptions,\n StreamEvent,\n AnthropicMessage,\n RawGenerationResult,\n ContentBlock,\n} from \"./types\"\nimport { resolveToken, loadAuthConfig } from \"@/utils/auth-store\"\nimport { execa } from \"execa\"\nimport { getLegacyTools } from \"../tools/definitions\"\n\n// --- Claude Code provider: uses Claude CLI (subscription) or direct API (API key) ---\n\nconst DEFAULT_MODEL = \"claude-sonnet-4-20250514\"\nconst DEFAULT_MAX_TOKENS = 8192\n\n// Model ID → claude CLI alias\nconst CLI_MODEL_ALIASES: Record<string, string> = {\n \"claude-sonnet-4-20250514\": \"sonnet\",\n \"claude-opus-4-20250514\": \"opus\",\n \"claude-haiku-4-20250514\": \"haiku\",\n}\n\ntype AuthCredential = { type: \"oauth\"; token: string } | { type: \"api-key\"; token: string }\n\ninterface AnthropicResponse {\n id: string\n content: Array<{\n type: \"text\" | \"tool_use\" | \"tool_result\"\n text?: string\n id?: string\n name?: string\n input?: Record<string, unknown>\n }>\n model: string\n stop_reason: string\n usage: { input_tokens: number; output_tokens: number }\n}\n\nexport class ClaudeCodeProvider implements AgentProvider {\n name = \"claude-code\"\n private credential: AuthCredential\n\n constructor() {\n // Stored config takes priority — if user ran `agentx model` and chose OAuth,\n // we use that regardless of ANTHROPIC_API_KEY env var (same as OpenClaw's clearEnv).\n const stored = loadAuthConfig()\n if (stored) {\n this.credential = { type: stored.authType, token: stored.token }\n return\n }\n\n const resolved = resolveToken()\n if (!resolved) {\n throw new Error(\n \"No Claude credentials found.\\n\" +\n \"Options:\\n\" +\n \" 1. Run `agentx model` to configure credentials\\n\" +\n \" 2. Set ANTHROPIC_API_KEY environment variable\\n\" +\n \" 3. Set ANTHROPIC_OAUTH_TOKEN environment variable\"\n )\n }\n this.credential = { type: resolved.authType, token: resolved.token }\n }\n\n async generate(\n messages: GenerationMessage[],\n options?: ProviderOptions\n ): Promise<GenerationResult> {\n // OAuth tokens → use claude CLI (subscription billing)\n // API keys → call API directly\n if (this.credential.type === \"oauth\") {\n return this.generateViaCli(messages, options)\n }\n return this.generateViaApi(messages, options)\n }\n\n /**\n * generateRaw() for the agentic tool_result loop.\n * Only available for API key auth — the CLI binary has its own agent loop.\n */\n async generateRaw(\n messages: AnthropicMessage[],\n systemPrompt: string,\n tools: Array<{ name: string; description: string; input_schema: Record<string, unknown> }>,\n options?: ProviderOptions\n ): Promise<RawGenerationResult> {\n // OAuth path: not supported — the claude CLI has its own built-in tools\n if (this.credential.type === \"oauth\") {\n throw new Error(\"generateRaw() not available for OAuth/CLI mode\")\n }\n\n const model = options?.model || DEFAULT_MODEL\n const maxTokens = options?.maxTokens || DEFAULT_MAX_TOKENS\n\n const body: Record<string, unknown> = {\n model,\n max_tokens: maxTokens,\n system: systemPrompt,\n messages,\n tools,\n }\n\n if (options?.temperature !== undefined) {\n body.temperature = options.temperature\n }\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"anthropic-version\": \"2023-06-01\",\n \"x-api-key\": this.credential.token,\n }\n\n const res = await fetch(\"https://api.anthropic.com/v1/messages\", {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n })\n\n if (!res.ok) {\n const errorText = await res.text()\n throw new Error(`Anthropic API error (${res.status}): ${errorText}`)\n }\n\n const response = (await res.json()) as AnthropicResponse\n\n const content: ContentBlock[] = response.content.map((block) => {\n if (block.type === \"text\") {\n return { type: \"text\" as const, text: block.text || \"\" }\n }\n if (block.type === \"tool_use\") {\n return {\n type: \"tool_use\" as const,\n id: block.id || \"\",\n name: block.name || \"\",\n input: block.input || {},\n }\n }\n return { type: \"text\" as const, text: \"\" }\n })\n\n return {\n content,\n stop_reason: response.stop_reason as RawGenerationResult[\"stop_reason\"],\n usage: response.usage,\n }\n }\n\n /**\n * Returns true if this provider can use the agentic tool loop.\n * Only available for API key auth (CLI mode falls back to legacy loop).\n */\n get supportsAgenticLoop(): boolean {\n return this.credential.type === \"api-key\"\n }\n\n /**\n * Generate via the `claude` CLI binary.\n * This is how subscription billing works — the CLI handles auth internally.\n */\n private async generateViaCli(\n messages: GenerationMessage[],\n options?: ProviderOptions\n ): Promise<GenerationResult> {\n const model = options?.model || loadAuthConfig()?.model || DEFAULT_MODEL\n const cliModel = CLI_MODEL_ALIASES[model] || model\n\n const systemMsg = messages.find((m) => m.role === \"system\")\n const userMsgs = messages.filter((m) => m.role === \"user\")\n const prompt = userMsgs.map((m) => m.content).join(\"\\n\\n\")\n\n const args = [\n \"-p\",\n \"--output-format\", \"json\",\n \"--model\", cliModel,\n \"--dangerously-skip-permissions\",\n ]\n\n if (systemMsg) {\n args.push(\"--append-system-prompt\", systemMsg.content)\n }\n\n // Prompt goes as the last positional argument\n args.push(prompt)\n\n // Clear ANTHROPIC_API_KEY to force CLI to use subscription auth\n const env = { ...process.env }\n delete env.ANTHROPIC_API_KEY\n delete env.ANTHROPIC_API_KEY_OLD\n\n const result = await execa(\"claude\", args, {\n env,\n extendEnv: false,\n reject: false,\n timeout: 300_000,\n stdin: \"ignore\",\n })\n\n if (result.exitCode !== 0) {\n const err = result.stderr || result.stdout || \"Claude CLI failed\"\n throw new Error(`Claude CLI error: ${err}`)\n }\n\n // Claude CLI may return success exit code but with is_error in JSON\n const output = result.stdout.trim()\n try {\n const json = JSON.parse(output)\n if (json.is_error && json.result) {\n throw new Error(json.result)\n }\n } catch (e: any) {\n if (e.message && !e.message.includes(\"JSON\")) throw e\n // Not JSON — that's fine, parseCliOutput will handle it\n }\n\n return this.parseCliOutput(output)\n }\n\n /**\n * Generate via direct Anthropic API call (for API key auth).\n */\n private async generateViaApi(\n messages: GenerationMessage[],\n options?: ProviderOptions\n ): Promise<GenerationResult> {\n const model = options?.model || DEFAULT_MODEL\n const maxTokens = options?.maxTokens || DEFAULT_MAX_TOKENS\n\n const systemMsg = messages.find((m) => m.role === \"system\")\n const conversationMsgs = messages\n .filter((m) => m.role !== \"system\")\n .map((m) => ({\n role: m.role as \"user\" | \"assistant\",\n content: m.content,\n }))\n\n const body: Record<string, unknown> = {\n model,\n max_tokens: maxTokens,\n messages: conversationMsgs,\n tools: getLegacyTools(),\n }\n\n if (systemMsg) {\n body.system = systemMsg.content\n }\n\n if (options?.temperature !== undefined) {\n body.temperature = options.temperature\n }\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"anthropic-version\": \"2023-06-01\",\n \"x-api-key\": this.credential.token,\n }\n\n const res = await fetch(\"https://api.anthropic.com/v1/messages\", {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n })\n\n if (!res.ok) {\n const errorText = await res.text()\n throw new Error(`Anthropic API error (${res.status}): ${errorText}`)\n }\n\n const response = (await res.json()) as AnthropicResponse\n return this.parseApiResponse(response)\n }\n\n /**\n * Stream responses. For OAuth, uses CLI with --output-format stream-json.\n * For API keys, uses SSE streaming.\n */\n async *stream(\n messages: GenerationMessage[],\n options?: ProviderOptions\n ): AsyncIterable<StreamEvent> {\n if (this.credential.type === \"oauth\") {\n // CLI streaming via --output-format stream-json\n yield* this.streamViaCli(messages, options)\n } else {\n // API streaming via SSE (same as ClaudeProvider.stream)\n yield* this.streamViaApi(messages, options)\n }\n }\n\n private async *streamViaCli(\n messages: GenerationMessage[],\n options?: ProviderOptions\n ): AsyncIterable<StreamEvent> {\n const model = options?.model || loadAuthConfig()?.model || DEFAULT_MODEL\n const cliModel = CLI_MODEL_ALIASES[model] || model\n\n const systemMsg = messages.find((m) => m.role === \"system\")\n const userMsgs = messages.filter((m) => m.role === \"user\")\n const prompt = userMsgs.map((m) => m.content).join(\"\\n\\n\")\n\n const args = [\n \"-p\",\n \"--output-format\", \"stream-json\",\n \"--model\", cliModel,\n \"--dangerously-skip-permissions\",\n ]\n\n if (systemMsg) {\n args.push(\"--append-system-prompt\", systemMsg.content)\n }\n\n args.push(prompt)\n\n const env = { ...process.env }\n delete env.ANTHROPIC_API_KEY\n delete env.ANTHROPIC_API_KEY_OLD\n\n const child = execa(\"claude\", args, {\n env,\n extendEnv: false,\n reject: false,\n timeout: 300_000,\n stdin: \"ignore\",\n })\n\n let fullContent = \"\"\n let stderr = \"\"\n let fatalError: string | undefined\n\n if (child.stderr) {\n child.stderr.on(\"data\", (chunk: any) => {\n stderr += Buffer.isBuffer(chunk) ? chunk.toString(\"utf8\") : String(chunk)\n })\n }\n\n if (child.stdout) {\n const decoder = new TextDecoder()\n const readable = child.stdout as unknown as AsyncIterable<Uint8Array>\n\n for await (const chunk of readable) {\n const text = typeof chunk === \"string\" ? chunk : decoder.decode(chunk, { stream: true })\n // Stream JSON format: each line is a JSON object\n const lines = text.split(\"\\n\").filter(Boolean)\n for (const line of lines) {\n try {\n const event = JSON.parse(line)\n // Some claude stream-json builds emit error events.\n if (event.type === \"error\") {\n fatalError = String(event.error || event.message || \"Claude CLI error\")\n continue\n }\n if (event.is_error && (event.result || event.message)) {\n fatalError = String(event.result || event.message)\n continue\n }\n if (event.type === \"assistant\" && event.message) {\n fullContent += event.message\n yield { type: \"text_delta\", text: event.message }\n } else if (event.type === \"result\") {\n fullContent = event.result || fullContent\n }\n } catch {\n // Not JSON, treat as raw text\n fullContent += line\n yield { type: \"text_delta\", text: line }\n }\n }\n }\n }\n\n const res = await child\n if (res.exitCode !== 0 && !fatalError) {\n fatalError = (stderr || res.stderr || res.stdout || \"Claude CLI failed\").toString().trim()\n }\n\n if (fatalError) {\n yield { type: \"error\", error: `Claude CLI error: ${fatalError}` }\n return\n }\n\n const files = this.extractFilesFromText(fullContent)\n const followUp =\n files.length === 0 ? this.inferFollowUpFromText(fullContent) : undefined\n yield {\n type: \"done\",\n result: { content: fullContent, files, followUp, tokensUsed: 0 },\n }\n }\n\n private async *streamViaApi(\n messages: GenerationMessage[],\n options?: ProviderOptions\n ): AsyncIterable<StreamEvent> {\n const model = options?.model || DEFAULT_MODEL\n const maxTokens = options?.maxTokens || DEFAULT_MAX_TOKENS\n\n const systemMsg = messages.find((m) => m.role === \"system\")\n const conversationMsgs = messages\n .filter((m) => m.role !== \"system\")\n .map((m) => ({ role: m.role as \"user\" | \"assistant\", content: m.content }))\n\n const body: Record<string, unknown> = {\n model,\n max_tokens: maxTokens,\n messages: conversationMsgs,\n tools: getLegacyTools(),\n stream: true,\n }\n\n if (systemMsg) body.system = systemMsg.content\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"anthropic-version\": \"2023-06-01\",\n \"x-api-key\": this.credential.token,\n }\n\n const res = await fetch(\"https://api.anthropic.com/v1/messages\", {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n })\n\n if (!res.ok) {\n const errorText = await res.text()\n yield { type: \"error\", error: `Anthropic API error (${res.status}): ${errorText}` }\n return\n }\n\n const reader = res.body?.getReader()\n if (!reader) {\n yield { type: \"error\", error: \"No response body\" }\n return\n }\n\n const decoder = new TextDecoder()\n let buffer = \"\"\n let content = \"\"\n let tokensUsed = 0\n const files: GeneratedFile[] = []\n let followUp: string | undefined\n let activeTool: { name: string; id: string; json: string } | null = null\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split(\"\\n\")\n buffer = lines.pop() || \"\"\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue\n const data = line.slice(6).trim()\n if (data === \"[DONE]\") continue\n\n try {\n const event = JSON.parse(data)\n if (event.type === \"content_block_start\") {\n if (event.content_block?.type === \"tool_use\") {\n // Finalize any previous tool block if the stream didn't send a stop.\n if (activeTool) {\n yield { type: \"tool_use_end\", name: activeTool.name }\n try {\n const input = JSON.parse(activeTool.json || \"{}\") as any\n if (activeTool.name === \"create_files\" && input) {\n if (Array.isArray(input.files)) {\n files.push(...(input.files as GeneratedFile[]))\n }\n if (typeof input.summary === \"string\" && input.summary.trim()) {\n content += `\\n${input.summary}`\n }\n }\n if (activeTool.name === \"ask_user\" && input) {\n if (typeof input.question === \"string\" && input.question.trim()) {\n followUp = input.question\n if (Array.isArray(input.options) && input.options.length) {\n followUp += `\\nOptions: ${input.options.join(\", \")}`\n }\n }\n }\n } catch {\n // Ignore invalid tool JSON\n } finally {\n activeTool = null\n }\n }\n\n activeTool = {\n name: event.content_block.name,\n id: event.content_block.id,\n json: \"\",\n }\n yield { type: \"tool_use_start\", name: activeTool.name, id: activeTool.id }\n }\n }\n\n if (event.type === \"content_block_delta\" && event.delta?.type === \"text_delta\") {\n content += event.delta.text\n yield { type: \"text_delta\", text: event.delta.text }\n }\n if (event.type === \"content_block_delta\" && event.delta?.type === \"input_json_delta\") {\n if (activeTool) activeTool.json += event.delta.partial_json || \"\"\n yield { type: \"tool_use_delta\", json: event.delta.partial_json }\n }\n if (event.type === \"content_block_stop\") {\n if (activeTool) {\n yield { type: \"tool_use_end\", name: activeTool.name }\n try {\n const input = JSON.parse(activeTool.json || \"{}\") as any\n if (activeTool.name === \"create_files\" && input) {\n if (Array.isArray(input.files)) {\n files.push(...(input.files as GeneratedFile[]))\n }\n if (typeof input.summary === \"string\" && input.summary.trim()) {\n content += `\\n${input.summary}`\n }\n }\n if (activeTool.name === \"ask_user\" && input) {\n if (typeof input.question === \"string\" && input.question.trim()) {\n followUp = input.question\n if (Array.isArray(input.options) && input.options.length) {\n followUp += `\\nOptions: ${input.options.join(\", \")}`\n }\n }\n }\n } catch {\n // Ignore invalid tool JSON\n } finally {\n activeTool = null\n }\n }\n }\n if (event.type === \"message_delta\" && event.usage) {\n tokensUsed = (event.usage.input_tokens || 0) + (event.usage.output_tokens || 0)\n }\n } catch {\n // Skip\n }\n }\n }\n } finally {\n reader.releaseLock()\n }\n\n // Flush any trailing tool input.\n if (activeTool) {\n yield { type: \"tool_use_end\", name: activeTool.name }\n try {\n const input = JSON.parse(activeTool.json || \"{}\") as any\n if (activeTool.name === \"create_files\" && input) {\n if (Array.isArray(input.files)) {\n files.push(...(input.files as GeneratedFile[]))\n }\n if (typeof input.summary === \"string\" && input.summary.trim()) {\n content += `\\n${input.summary}`\n }\n }\n if (activeTool.name === \"ask_user\" && input) {\n if (typeof input.question === \"string\" && input.question.trim()) {\n followUp = input.question\n if (Array.isArray(input.options) && input.options.length) {\n followUp += `\\nOptions: ${input.options.join(\", \")}`\n }\n }\n }\n } catch {\n // Ignore\n }\n activeTool = null\n }\n\n // If the model didn't use tools (or we couldn't parse them), fall back to fenced-code extraction.\n if (files.length === 0) {\n files.push(...this.extractFilesFromText(content))\n }\n if (!followUp && files.length === 0) {\n followUp = this.inferFollowUpFromText(content)\n }\n yield {\n type: \"done\",\n result: { content, files, followUp, tokensUsed },\n }\n }\n\n /**\n * Parse JSON output from the `claude` CLI.\n */\n private parseCliOutput(stdout: string): GenerationResult {\n let parsed: any\n try {\n parsed = JSON.parse(stdout)\n } catch {\n // CLI returned plain text\n const inferred = this.inferFollowUpFromText(stdout.trim())\n return {\n content: stdout.trim(),\n files: [],\n followUp: inferred,\n tokensUsed: 0,\n }\n }\n\n // Claude CLI JSON format: { result: \"...\", session_id: \"...\", ... }\n const text = parsed.result || parsed.text || parsed.content || \"\"\n\n // Extract files from the text if it contains code blocks with file paths\n const files = this.extractFilesFromText(text)\n const followUp = files.length === 0 ? this.inferFollowUpFromText(text) : undefined\n\n return {\n content: text,\n files,\n followUp,\n tokensUsed: parsed.usage\n ? (parsed.usage.input_tokens || 0) + (parsed.usage.output_tokens || 0)\n : 0,\n }\n }\n\n /**\n * Extract file blocks from CLI text output.\n * Looks for patterns like: ```path/to/file.ts ... ```\n */\n private extractFilesFromText(text: string): GeneratedFile[] {\n const files: GeneratedFile[] = []\n // Match fenced code blocks with a file path hint on the opening line\n const pattern = /```[\\w]*\\s*([\\w/._-]+\\.\\w+)\\n([\\s\\S]*?)```/g\n let match: RegExpExecArray | null\n while ((match = pattern.exec(text)) !== null) {\n const filePath = match[1]\n const content = match[2]\n if (filePath && content) {\n files.push({ path: filePath, content: content.trimEnd() })\n }\n }\n return files\n }\n\n /**\n * Claude CLI (OAuth/subscription) doesn't always emit structured ask_user tool blocks.\n * When it's obviously asking for clarification, infer a follow-up question from the tail.\n */\n private inferFollowUpFromText(text: string): string | undefined {\n const cleaned = (text || \"\").trim()\n if (!cleaned) return undefined\n\n // If it looks like it produced file blocks, don't treat it as a follow-up.\n if (cleaned.includes(\"```\")) return undefined\n\n const lines = cleaned.replace(/\\r\\n/g, \"\\n\").split(\"\\n\")\n const tail = lines.slice(Math.max(0, lines.length - 20))\n\n const tailText = tail.join(\"\\n\")\n const looksLikePlanApproval =\n /\\b(plan|proposal)\\b/i.test(tailText) &&\n /(ready for your review|review (the )?plan|requesting plan approval|awaiting approval|waiting for (your )?approval|approve (the )?plan|approval to proceed)/i.test(\n tailText\n )\n\n if (looksLikePlanApproval) {\n return (\n \"The provider is requesting plan approval.\\n\" +\n \"Reply with:\\n\" +\n \"- approve\\n\" +\n \"- revise: <what to change>\\n\" +\n \"- cancel\"\n )\n }\n\n const isIntro = (l: string) =>\n /^(question|clarification|clarify|i need|need more|before i proceed|to proceed|please (confirm|clarify)|which|what|where|when|how|do you)/i.test(\n l.trim()\n )\n\n // Find the last strong candidate line.\n let startIdx = -1\n for (let i = tail.length - 1; i >= 0; i--) {\n const l = tail[i].trim()\n if (!l) continue\n if (isIntro(l) || l.includes(\"?\")) {\n startIdx = i\n break\n }\n }\n if (startIdx === -1) return undefined\n\n // Collect the question + a small option/list block that follows it.\n const out: string[] = []\n for (let i = startIdx; i < tail.length && out.length < 8; i++) {\n const l = tail[i]\n const t = l.trim()\n if (out.length > 0 && !t) break\n\n if (\n out.length > 0 &&\n !/^(options?:|[-*]\\s|\\d+[\\).]\\s)/i.test(t) &&\n !t.includes(\"?\")\n ) {\n break\n }\n\n out.push(l.trimEnd())\n }\n\n const candidate = out.join(\"\\n\").trim()\n if (candidate.length < 5) return undefined\n if (candidate.length > 800) return candidate.slice(0, 800).trimEnd()\n return candidate\n }\n\n /**\n * Parse Anthropic API response (tool-use format).\n */\n private parseApiResponse(response: AnthropicResponse): GenerationResult {\n const files: GeneratedFile[] = []\n let content = \"\"\n let followUp: string | undefined\n\n for (const block of response.content) {\n if (block.type === \"text\") {\n content += block.text || \"\"\n }\n\n if (block.type === \"tool_use\") {\n if (block.name === \"create_files\" && block.input) {\n const input = block.input as {\n files: GeneratedFile[]\n summary?: string\n }\n files.push(...(input.files || []))\n if (input.summary) {\n content += `\\n${input.summary}`\n }\n }\n\n if (block.name === \"ask_user\" && block.input) {\n const input = block.input as { question: string; options?: string[] }\n followUp = input.question\n if (input.options?.length) {\n followUp += `\\nOptions: ${input.options.join(\", \")}`\n }\n }\n }\n }\n\n return {\n content,\n files,\n followUp,\n tokensUsed: response.usage.input_tokens + response.usage.output_tokens,\n }\n }\n}\n","import type {\n AgentProvider,\n GenerationMessage,\n GenerationResult,\n GeneratedFile,\n ProviderOptions,\n StreamEvent,\n AnthropicMessage,\n RawGenerationResult,\n RawStreamEvent,\n ContentBlock,\n} from \"./types\"\nimport { getLegacyTools } from \"../tools/definitions\"\n\n// --- OpenAI-compatible provider (OpenAI, DeepSeek, OpenRouter, Together,\n// vLLM, llama.cpp, anything that speaks the Chat Completions API).\n//\n// Zero new deps — uses raw `fetch` to mirror the ClaudeProvider pattern.\n// Routes through OPENAI_BASE_URL so the same class works for every\n// OpenAI-shape backend; defaults to api.openai.com/v1.\n//\n// Environment knobs:\n// OPENAI_BASE_URL e.g. https://api.deepseek.com/v1 (defaults to OpenAI)\n// OPENAI_API_KEY bearer token for the upstream\n// OPENAI_MODEL default model id (per-call override via options.model)\n//\n// The agentx-side tool catalog uses Anthropic's `input_schema` shape and\n// the rest of the runtime expects RawGenerationResult in Anthropic shape\n// (ContentBlock[] with stop_reason: \"end_turn\"|\"tool_use\"|…). We translate\n// at the boundary so the tool/agentic loop above is provider-agnostic.\n\nconst DEFAULT_MODEL = \"gpt-4o-mini\"\nconst DEFAULT_MAX_TOKENS = 8192\n\ninterface OpenAIToolCall {\n id: string\n type: \"function\"\n function: { name: string; arguments: string }\n}\n\ninterface OpenAIResponseMessage {\n role: \"assistant\"\n content: string | null\n tool_calls?: OpenAIToolCall[]\n // DeepSeek V4 reasoning output (\"thinking mode\"). When present, MUST\n // be echoed back on subsequent requests of the same turn or DeepSeek\n // 400s with \"The reasoning_content in the thinking mode must be\n // passed back to the API.\" We surface it as a {type:\"reasoning\"}\n // ContentBlock so the agentic loop carries it through.\n reasoning_content?: string\n}\n\ninterface OpenAIChatResponse {\n id: string\n model: string\n choices: Array<{\n index: number\n message: OpenAIResponseMessage\n finish_reason: \"stop\" | \"length\" | \"tool_calls\" | \"content_filter\" | \"function_call\" | null\n }>\n usage?: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number }\n}\n\nexport interface OpenAIProviderOptions {\n /** Toggle \"thinking mode\" on backends that support it (DeepSeek V4\n * `reasoning_content`). Default `true` for DeepSeek baseUrls so the\n * agent surfaces its reasoning live in the dashboard; set to `false`\n * via `providers.<name>.thinking` in agentx.json if a particular\n * agent wants the extra latency / cost back. Ignored on backends\n * that don't speak the thinking-mode protocol (vanilla OpenAI,\n * vLLM, Together, …). */\n thinking?: boolean\n}\n\nexport class OpenAIProvider implements AgentProvider {\n name = \"openai\"\n private apiKey: string\n private baseUrl: string\n /** Resolved thinking flag — null when the backend isn't a known\n * thinking-mode endpoint and the option is irrelevant. */\n private thinkingEnabled: boolean | null\n\n constructor(apiKey?: string, baseUrl?: string, opts: OpenAIProviderOptions = {}) {\n this.apiKey = apiKey || process.env.OPENAI_API_KEY || \"\"\n this.baseUrl = (\n baseUrl ||\n process.env.OPENAI_BASE_URL ||\n \"https://api.openai.com/v1\"\n ).replace(/\\/$/, \"\")\n // DeepSeek defaults to thinking ON — that's the model's own default\n // behavior. Operator can flip it off via providers.<name>.thinking=false.\n // Non-DeepSeek backends ignore the flag entirely.\n const isDeepseek = this.baseUrl.includes(\"deepseek.com\")\n this.thinkingEnabled = isDeepseek\n ? (opts.thinking === false ? false : true)\n : null\n if (!this.apiKey) {\n throw new Error(\n \"OpenAI-compatible API key required. Set OPENAI_API_KEY (or pass --api-key). \" +\n \"For DeepSeek, also set OPENAI_BASE_URL=https://api.deepseek.com/v1.\",\n )\n }\n }\n\n async generate(\n messages: GenerationMessage[],\n options?: ProviderOptions,\n ): Promise<GenerationResult> {\n const model = options?.model || process.env.OPENAI_MODEL || DEFAULT_MODEL\n const maxTokens = options?.maxTokens || DEFAULT_MAX_TOKENS\n\n // OpenAI takes system as just another message with role:\"system\" at the top.\n const oaMessages = messages.map((m) => ({ role: m.role, content: m.content }))\n\n const body: Record<string, unknown> = {\n model,\n max_tokens: maxTokens,\n messages: oaMessages,\n tools: this.legacyToolsAsOpenAI(),\n }\n if (options?.temperature !== undefined) body.temperature = options.temperature\n\n const data = await this.callApi(body)\n return this.parseHighLevelResponse(data)\n }\n\n async generateRaw(\n messages: AnthropicMessage[],\n systemPrompt: string,\n tools: Array<{ name: string; description: string; input_schema: Record<string, unknown> }>,\n options?: ProviderOptions,\n ): Promise<RawGenerationResult> {\n const body = this.buildRawBody(messages, systemPrompt, tools, options)\n const data = await this.callApi(body, options?.abortSignal)\n return this.parseRawResponse(data)\n }\n\n /**\n * Streaming variant of generateRaw. Yields text_delta and thinking_delta\n * events as the model speaks, then a terminal raw_result with the full\n * content blocks (text, tool_use, and — for DeepSeek thinking mode —\n * a `reasoning` block at the head). The agentic loop's tool-dispatch\n * and reasoning-round-trip logic then operates on identical data to\n * the non-streaming path.\n */\n async *generateRawStream(\n messages: AnthropicMessage[],\n systemPrompt: string,\n tools: Array<{ name: string; description: string; input_schema: Record<string, unknown> }>,\n options?: ProviderOptions,\n ): AsyncIterable<RawStreamEvent> {\n const body = { ...this.buildRawBody(messages, systemPrompt, tools, options), stream: true, stream_options: { include_usage: true } }\n\n let res: Response\n try {\n res = await fetch(`${this.baseUrl}/chat/completions`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${this.apiKey}` },\n body: JSON.stringify(this.maybeDeepseekExtras(body)),\n // Forward operator cancel down to the HTTP layer so Stop\n // actually terminates the in-flight request. Without this,\n // the abort fires in the registry but fetch keeps draining\n // tokens.\n signal: options?.abortSignal as any,\n })\n } catch (err: any) {\n // AbortError surfaces here when the operator cancelled before\n // the connection completed. Treat as cancelled, not a real\n // error — the registry's outer handler already records the\n // cancellation.\n if (err?.name === \"AbortError\" || options?.abortSignal?.aborted) {\n yield { type: \"error\", error: \"cancelled\" }\n return\n }\n yield { type: \"error\", error: `OpenAI-compat raw stream init failed: ${err?.message ?? err}` }\n return\n }\n if (!res.ok) {\n const errText = await res.text().catch(() => \"\")\n yield { type: \"error\", error: `OpenAI-compat API error (${res.status}): ${errText.slice(0, 500)}` }\n return\n }\n const reader = res.body?.getReader()\n if (!reader) { yield { type: \"error\", error: \"No response body on OpenAI-compat raw stream\" }; return }\n\n // Wire the operator's abort signal into reader.cancel() so an abort\n // mid-stream actually closes the underlying TCP socket. Without this,\n // undici sometimes keeps reader.read() pending forever even after\n // fetch's signal aborts — DeepSeek's reasoning_content can stream for\n // many minutes, and the orchestrator's wall-clock timer fired into a\n // signal that no one was actually watching here. Production saw 58+\n // min hangs past the 15-min maxExecutionMinutes cap on acme-public.\n const onAbortCancelReader = () => {\n reader.cancel(new Error(\"aborted\")).catch(() => { /* already closed */ })\n }\n if (options?.abortSignal) {\n if (options.abortSignal.aborted) onAbortCancelReader()\n else options.abortSignal.addEventListener(\"abort\", onAbortCancelReader, { once: true })\n }\n\n const decoder = new TextDecoder()\n let buffer = \"\"\n let textOut = \"\"\n let reasoningOut = \"\"\n let inputTokens = 0\n let outputTokens = 0\n let finishReason: string | null = null\n interface ToolAcc { id: string; name: string; argumentsRaw: string }\n const toolAcc: Record<number, ToolAcc> = {}\n\n try {\n while (true) {\n // Belt-and-braces: bail between chunks too, in case reader.cancel()\n // didn't unblock the pending read (some SSE proxies hold the\n // connection open even after upstream close).\n if (options?.abortSignal?.aborted) {\n yield { type: \"error\", error: \"cancelled\" }\n return\n }\n const { done, value } = await reader.read()\n if (done) break\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split(\"\\n\")\n buffer = lines.pop() || \"\"\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue\n const data = line.slice(6).trim()\n if (!data || data === \"[DONE]\") continue\n let chunk: any\n try { chunk = JSON.parse(data) } catch { continue }\n const choice = chunk.choices?.[0]\n const delta = choice?.delta\n if (choice?.finish_reason) finishReason = choice.finish_reason\n\n if (typeof delta?.reasoning_content === \"string\" && delta.reasoning_content) {\n reasoningOut += delta.reasoning_content\n yield { type: \"thinking_delta\", text: delta.reasoning_content }\n }\n if (typeof delta?.content === \"string\" && delta.content) {\n textOut += delta.content\n yield { type: \"text_delta\", text: delta.content }\n }\n if (Array.isArray(delta?.tool_calls)) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index\n if (idx == null) continue\n const acc = (toolAcc[idx] ||= { id: \"\", name: \"\", argumentsRaw: \"\" })\n if (tc.id) acc.id = tc.id\n if (tc.function?.name) acc.name = tc.function.name\n if (tc.function?.arguments) acc.argumentsRaw += tc.function.arguments\n }\n }\n if (chunk.usage) {\n inputTokens = chunk.usage.prompt_tokens || 0\n outputTokens = chunk.usage.completion_tokens || 0\n }\n }\n }\n } catch (err: any) {\n if (err?.name === \"AbortError\" || options?.abortSignal?.aborted) {\n yield { type: \"error\", error: \"cancelled\" }\n return\n }\n yield { type: \"error\", error: `OpenAI-compat raw stream read failed: ${err?.message ?? err}` }\n return\n } finally {\n if (options?.abortSignal) {\n options.abortSignal.removeEventListener(\"abort\", onAbortCancelReader)\n }\n }\n\n // Assemble blocks in the same order parseRawResponse uses so the\n // agentic loop's round-trip stays consistent.\n const blocks: ContentBlock[] = []\n if (reasoningOut) blocks.push({ type: \"reasoning\", text: reasoningOut })\n if (textOut) blocks.push({ type: \"text\", text: textOut })\n for (const acc of Object.values(toolAcc)) {\n if (!acc.id || !acc.name) continue\n let input: Record<string, unknown> = {}\n try { input = acc.argumentsRaw ? JSON.parse(acc.argumentsRaw) : {} } catch { /* keep empty input */ }\n blocks.push({ type: \"tool_use\", id: acc.id, name: acc.name, input })\n }\n\n let stop_reason: RawGenerationResult[\"stop_reason\"] = \"end_turn\"\n switch (finishReason) {\n case \"tool_calls\":\n case \"function_call\":\n stop_reason = \"tool_use\"; break\n case \"length\":\n stop_reason = \"max_tokens\"; break\n }\n\n yield {\n type: \"raw_result\",\n result: { content: blocks, stop_reason, usage: { input_tokens: inputTokens, output_tokens: outputTokens } },\n }\n }\n\n /** Shared request-body builder for generateRaw / generateRawStream — both\n * need the same message translation, tool list, and provider extras. */\n private buildRawBody(\n messages: AnthropicMessage[],\n systemPrompt: string,\n tools: Array<{ name: string; description: string; input_schema: Record<string, unknown> }>,\n options?: ProviderOptions,\n ): Record<string, unknown> {\n const model = options?.model || process.env.OPENAI_MODEL || DEFAULT_MODEL\n const maxTokens = options?.maxTokens || DEFAULT_MAX_TOKENS\n\n // Translate Anthropic-shape messages (with tool_use / tool_result content blocks)\n // into OpenAI Chat Completions message shape (with role:\"tool\" turns and\n // assistant.tool_calls). System prompt goes in as messages[0].\n const oaMessages: any[] = systemPrompt\n ? [{ role: \"system\", content: systemPrompt }]\n : []\n\n for (const m of messages) {\n const blocks = Array.isArray(m.content) ? m.content : [{ type: \"text\" as const, text: m.content }]\n if (m.role === \"user\") {\n const toolResults = blocks.filter((b) => (b as any).type === \"tool_result\") as Array<{\n type: \"tool_result\"; tool_use_id: string; content: string; is_error?: boolean;\n }>\n const textParts = blocks\n .filter((b) => (b as any).type === \"text\")\n .map((b) => (b as any).text)\n .join(\"\")\n for (const tr of toolResults) {\n oaMessages.push({\n role: \"tool\",\n tool_call_id: tr.tool_use_id,\n content: tr.is_error ? `[error] ${tr.content}` : tr.content,\n })\n }\n if (textParts) {\n oaMessages.push({ role: \"user\", content: textParts })\n } else if (toolResults.length === 0) {\n oaMessages.push({ role: \"user\", content: m.content as any })\n }\n } else {\n // Assistant turns may have text + tool_use + reasoning blocks;\n // OpenAI wants `content` + `tool_calls` on a single message,\n // and DeepSeek V4 also wants the prior `reasoning_content` so\n // thinking-mode round-trip works.\n const toolUses = blocks.filter((b) => (b as any).type === \"tool_use\") as Array<{\n type: \"tool_use\"; id: string; name: string; input: Record<string, unknown>;\n }>\n const text = blocks\n .filter((b) => (b as any).type === \"text\")\n .map((b) => (b as any).text)\n .join(\"\")\n const reasoning = blocks\n .filter((b) => (b as any).type === \"reasoning\")\n .map((b) => (b as any).text)\n .join(\"\")\n const msg: any = { role: \"assistant\", content: text || null }\n if (toolUses.length > 0) {\n msg.tool_calls = toolUses.map((tu) => ({\n id: tu.id,\n type: \"function\" as const,\n function: { name: tu.name, arguments: JSON.stringify(tu.input || {}) },\n }))\n }\n if (reasoning) {\n msg.reasoning_content = reasoning\n }\n oaMessages.push(msg)\n }\n }\n\n const body: Record<string, unknown> = {\n model,\n max_tokens: maxTokens,\n messages: oaMessages,\n tools: tools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.name,\n description: t.description,\n parameters: t.input_schema,\n },\n })),\n tool_choice: \"auto\",\n }\n if (options?.temperature !== undefined) body.temperature = options.temperature\n if (options?.toolChoice) {\n body.tool_choice = { type: \"function\", function: { name: options.toolChoice.name } }\n }\n if (options?.responseFormat) {\n body.response_format = {\n type: \"json_schema\",\n json_schema: {\n name: options.responseFormat.name,\n schema: options.responseFormat.schema,\n strict: true,\n },\n }\n }\n if (options?.logprobs?.enabled) {\n body.logprobs = true\n body.top_logprobs = options.logprobs.topK\n }\n return body\n }\n\n async *stream(\n messages: GenerationMessage[],\n options?: ProviderOptions,\n ): AsyncIterable<StreamEvent> {\n const model = options?.model || process.env.OPENAI_MODEL || DEFAULT_MODEL\n const maxTokens = options?.maxTokens || DEFAULT_MAX_TOKENS\n\n const oaMessages = messages.map((m) => ({ role: m.role, content: m.content }))\n\n const body: Record<string, unknown> = {\n model,\n max_tokens: maxTokens,\n messages: oaMessages,\n tools: this.legacyToolsAsOpenAI(),\n stream: true,\n stream_options: { include_usage: true },\n }\n if (options?.temperature !== undefined) body.temperature = options.temperature\n\n let res: Response\n try {\n res = await fetch(`${this.baseUrl}/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(this.maybeDeepseekExtras(body)),\n })\n } catch (err: any) {\n yield { type: \"error\", error: `OpenAI-compat stream init failed: ${err.message}` }\n return\n }\n\n if (!res.ok) {\n const errText = await res.text().catch(() => \"\")\n yield {\n type: \"error\",\n error: `OpenAI-compat API error (${res.status}): ${errText.slice(0, 500)}`,\n }\n return\n }\n\n const reader = res.body?.getReader()\n if (!reader) {\n yield { type: \"error\", error: \"No response body on OpenAI-compat stream\" }\n return\n }\n\n // Same SSE parsing approach as ClaudeProvider.stream — read chunks, split\n // on lines, accumulate tool_calls by index. Tool args arrive as\n // many small string fragments and must be joined before JSON.parse.\n const decoder = new TextDecoder()\n let buffer = \"\"\n let content = \"\"\n let followUp: string | undefined\n let tokensUsed = 0\n const files: GeneratedFile[] = []\n interface ToolAcc { id: string; name: string; argumentsRaw: string }\n const toolAcc: Record<number, ToolAcc> = {}\n let activeToolEmittedIdx: Record<number, boolean> = {}\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split(\"\\n\")\n buffer = lines.pop() || \"\"\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue\n const data = line.slice(6).trim()\n if (data === \"[DONE]\") continue\n\n let chunk: any\n try { chunk = JSON.parse(data) } catch { continue }\n const choice = chunk.choices?.[0]\n const delta = choice?.delta\n\n // Reasoning deltas (DeepSeek V4 thinking mode). Emit as a\n // separate event so the consumer can render them inline as\n // the agent thinks, without polluting `content`. The high-\n // level stream() path doesn't carry reasoning into the\n // final `GenerationResult` (it's not part of the visible\n // answer) — only generateRawStream does, so the agentic\n // round-trip can echo it back.\n if (typeof delta?.reasoning_content === \"string\" && delta.reasoning_content) {\n yield { type: \"thinking_delta\", text: delta.reasoning_content }\n }\n\n // Text deltas — stream to caller and accumulate.\n if (typeof delta?.content === \"string\" && delta.content) {\n content += delta.content\n yield { type: \"text_delta\", text: delta.content }\n }\n\n // Tool call deltas — accumulate per index. Emit start once per\n // tool, emit delta fragments as input_json comes in.\n if (Array.isArray(delta?.tool_calls)) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index\n if (idx == null) continue\n const acc = (toolAcc[idx] ||= { id: \"\", name: \"\", argumentsRaw: \"\" })\n if (tc.id) acc.id = tc.id\n if (tc.function?.name) acc.name = tc.function.name\n if (tc.function?.arguments) acc.argumentsRaw += tc.function.arguments\n\n if (acc.id && acc.name && !activeToolEmittedIdx[idx]) {\n activeToolEmittedIdx[idx] = true\n yield { type: \"tool_use_start\", name: acc.name, id: acc.id }\n }\n if (activeToolEmittedIdx[idx] && tc.function?.arguments) {\n yield { type: \"tool_use_delta\", json: tc.function.arguments }\n }\n }\n }\n\n // Usage chunk (OpenAI sends a final chunk with `usage` when\n // stream_options.include_usage=true; DeepSeek/others may not).\n if (chunk.usage) {\n tokensUsed =\n (chunk.usage.prompt_tokens || 0) + (chunk.usage.completion_tokens || 0)\n }\n }\n }\n } catch (err: any) {\n yield { type: \"error\", error: `OpenAI-compat stream read failed: ${err.message}` }\n return\n }\n\n // Finalize tool_calls: parse the joined arguments JSON and map\n // create_files / ask_user the same way ClaudeProvider does.\n for (const acc of Object.values(toolAcc)) {\n if (!acc.name) continue\n yield { type: \"tool_use_end\", name: acc.name }\n try {\n const input = acc.argumentsRaw ? JSON.parse(acc.argumentsRaw) : {}\n if (acc.name === \"create_files\" && Array.isArray(input.files)) {\n files.push(...(input.files as GeneratedFile[]))\n if (typeof input.summary === \"string\" && input.summary.trim()) {\n content += (content ? \"\\n\" : \"\") + input.summary\n }\n } else if (acc.name === \"ask_user\" && typeof input.question === \"string\") {\n followUp = input.question\n if (Array.isArray(input.options) && input.options.length) {\n followUp += `\\nOptions: ${input.options.join(\", \")}`\n }\n }\n } catch {\n // Malformed tool args — surface as missing, don't crash the stream.\n }\n }\n\n yield { type: \"done\", result: { content, files, followUp, tokensUsed } }\n }\n\n // --- helpers ---\n\n private async callApi(body: Record<string, unknown>, abortSignal?: AbortSignal): Promise<OpenAIChatResponse> {\n const res = await fetch(`${this.baseUrl}/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(this.maybeDeepseekExtras(body)),\n signal: abortSignal as any,\n })\n if (!res.ok) {\n const errorText = await res.text().catch(() => \"\")\n throw new Error(`OpenAI-compatible API error (${res.status}) ${this.baseUrl}: ${errorText.slice(0, 500)}`)\n }\n return (await res.json()) as OpenAIChatResponse\n }\n\n /**\n * DeepSeek v4 enables \"thinking mode\" by default — the model emits a\n * `reasoning_content` block that the API expects to be echoed back on\n * the next request. The agentic loop now handles that round-trip\n * (assistant turns with a `reasoning` content block get folded into\n * `msg.reasoning_content` in the next request), so we no longer need\n * to globally disable it. Operator can still opt out per provider via\n * `providers.<name>.thinking: false` in agentx.json — the constructor\n * resolves `this.thinkingEnabled` and we only emit `enable_thinking:\n * false` when explicitly disabled.\n */\n private maybeDeepseekExtras(body: Record<string, unknown>): Record<string, unknown> {\n if (this.thinkingEnabled === null) return body // not a thinking-mode backend\n if (this.thinkingEnabled === false) return { ...body, enable_thinking: false }\n return body // thinking ON — DeepSeek's default, no extra flag needed\n }\n\n private parseHighLevelResponse(response: OpenAIChatResponse): GenerationResult {\n const choice = response.choices?.[0]\n const message = choice?.message\n const files: GeneratedFile[] = []\n let content = message?.content || \"\"\n let followUp: string | undefined\n\n // Map legacy tools (create_files, ask_user) the same way ClaudeProvider\n // does — these are the only tools getLegacyTools() exposes today.\n for (const tc of message?.tool_calls || []) {\n let parsedArgs: any = {}\n try {\n parsedArgs = JSON.parse(tc.function.arguments || \"{}\")\n } catch {\n /* ignore malformed tool args */\n }\n if (tc.function.name === \"create_files\" && Array.isArray(parsedArgs.files)) {\n files.push(...parsedArgs.files)\n if (typeof parsedArgs.summary === \"string\" && parsedArgs.summary.trim()) {\n content += (content ? \"\\n\" : \"\") + parsedArgs.summary\n }\n } else if (tc.function.name === \"ask_user\" && typeof parsedArgs.question === \"string\") {\n followUp = parsedArgs.question\n if (Array.isArray(parsedArgs.options) && parsedArgs.options.length) {\n followUp += `\\nOptions: ${parsedArgs.options.join(\", \")}`\n }\n }\n }\n\n const usage = response.usage\n return {\n content,\n files,\n followUp,\n tokensUsed: usage ? (usage.prompt_tokens || 0) + (usage.completion_tokens || 0) : undefined,\n }\n }\n\n private parseRawResponse(response: OpenAIChatResponse): RawGenerationResult {\n const choice = response.choices?.[0]\n const message = choice?.message\n const blocks: ContentBlock[] = []\n\n // Reasoning_content goes FIRST so the order matches what the\n // upstream returned (DeepSeek emits reasoning then content). The\n // agentic loop preserves block order when round-tripping.\n if (message?.reasoning_content) {\n blocks.push({ type: \"reasoning\", text: message.reasoning_content })\n }\n if (message?.content) {\n blocks.push({ type: \"text\", text: message.content })\n }\n for (const tc of message?.tool_calls || []) {\n let input: Record<string, unknown> = {}\n try {\n input = tc.function.arguments ? JSON.parse(tc.function.arguments) : {}\n } catch {\n // Malformed JSON — surface as empty input rather than crashing the loop.\n }\n blocks.push({ type: \"tool_use\", id: tc.id, name: tc.function.name, input })\n }\n\n // Translate OpenAI finish_reason → Anthropic stop_reason vocabulary.\n let stop_reason: RawGenerationResult[\"stop_reason\"] = \"end_turn\"\n switch (choice?.finish_reason) {\n case \"tool_calls\":\n case \"function_call\":\n stop_reason = \"tool_use\"\n break\n case \"length\":\n stop_reason = \"max_tokens\"\n break\n case \"stop\":\n case null:\n case undefined:\n default:\n stop_reason = \"end_turn\"\n }\n\n const usage = response.usage\n return {\n content: blocks,\n stop_reason,\n usage: {\n input_tokens: usage?.prompt_tokens || 0,\n output_tokens: usage?.completion_tokens || 0,\n },\n }\n }\n\n private legacyToolsAsOpenAI() {\n // ClaudeProvider passes Anthropic-shaped legacy tools; translate to OpenAI's\n // function-tool shape so generate() still surfaces create_files / ask_user.\n return getLegacyTools().map((t: any) => ({\n type: \"function\" as const,\n function: {\n name: t.name,\n description: t.description,\n parameters: t.input_schema,\n },\n }))\n }\n}\n","// --- Demo provider: canned replies, real protocol ---\n//\n// Powers `agentx demo` — a zero-key walkthrough where every daemon,\n// mesh hop, ledger row, and dashboard view is real and only the LLM is\n// replaced by a scripted transcript. The provider reads a script file\n// (path in AGENTX_DEMO_SCRIPT) and answers each request with the first\n// step whose `match` regex hits the latest user message, streamed with\n// human-ish pacing so the live views have something to show.\n//\n// Deliberately boring: no tools, no files, one text block per turn.\n// The point of the demo is the plumbing around the model, not the model.\n\nimport { readFileSync } from \"fs\"\nimport type {\n AgentProvider,\n GenerationMessage,\n GenerationResult,\n ProviderOptions,\n StreamEvent,\n AnthropicMessage,\n RawGenerationResult,\n RawStreamEvent,\n} from \"./types\"\n\ninterface DemoStep {\n /** Regex tested (case-insensitive) against the latest user message. */\n match: string\n /** Optional \"thinking\" text streamed as thinking_delta before the reply. */\n thinking?: string\n reply: string\n /** Pause before the first token. Default 700ms. */\n delayMs?: number\n /** Pause between streamed chunks. Default 35ms. */\n chunkMs?: number\n}\n\ninterface DemoScript {\n steps: DemoStep[]\n fallback?: Pick<DemoStep, \"reply\" | \"thinking\" | \"delayMs\" | \"chunkMs\">\n}\n\nconst DEFAULT_FALLBACK: DemoScript[\"fallback\"] = {\n reply:\n \"This is demo mode — I answer from a script, not a live model. \" +\n \"Run `agentx setup` to connect a real provider.\",\n}\n\nfunction loadScript(): DemoScript {\n const path = process.env.AGENTX_DEMO_SCRIPT\n if (!path) return { steps: [], fallback: DEFAULT_FALLBACK }\n try {\n const parsed = JSON.parse(readFileSync(path, \"utf-8\")) as DemoScript\n return {\n steps: Array.isArray(parsed.steps) ? parsed.steps : [],\n fallback: parsed.fallback || DEFAULT_FALLBACK,\n }\n } catch (e: any) {\n return {\n steps: [],\n fallback: { reply: `Demo script unreadable (${e.message}) — check AGENTX_DEMO_SCRIPT.` },\n }\n }\n}\n\nfunction latestUserText(messages: Array<{ role: string; content: unknown }>): string {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role !== \"user\") continue\n const c = messages[i].content\n if (typeof c === \"string\") return c\n if (Array.isArray(c)) {\n return c\n .map((b: any) => (b?.type === \"text\" ? b.text : b?.type === \"tool_result\" ? b.content : \"\"))\n .filter(Boolean)\n .join(\"\\n\")\n }\n }\n return \"\"\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(new Error(\"aborted\"))\n const t = setTimeout(resolve, ms)\n signal?.addEventListener(\"abort\", () => { clearTimeout(t); reject(new Error(\"aborted\")) }, { once: true })\n })\n}\n\n/** Split into word-ish chunks so streaming looks like generation, not a paste. */\nfunction chunks(text: string): string[] {\n return text.match(/\\S+\\s*/g) || [text]\n}\n\nconst approxTokens = (s: string) => Math.max(1, Math.round(s.length / 4))\n\nexport class DemoProvider implements AgentProvider {\n name = \"demo\"\n private script: DemoScript\n\n constructor() {\n this.script = loadScript()\n }\n\n private pick(userText: string): Required<Pick<DemoStep, \"reply\" | \"delayMs\" | \"chunkMs\">> & Pick<DemoStep, \"thinking\"> {\n // The orchestrator prepends conversation history to the task text, so\n // an early scene's trigger phrase stays present in every later turn.\n // Prefer the step whose regex matches LATEST in the text — the newest\n // part of the conversation wins, which is how a real model would read it.\n let best: DemoStep | undefined\n let bestIndex = -1\n for (const step of this.script.steps) {\n try {\n const re = new RegExp(step.match, \"ig\")\n let m: RegExpExecArray | null\n let last = -1\n while ((m = re.exec(userText)) !== null) {\n last = m.index\n if (m.index === re.lastIndex) re.lastIndex++ // zero-width guard\n }\n if (last > bestIndex) {\n bestIndex = last\n best = step\n }\n } catch { /* bad regex in a step — skip it */ }\n }\n if (best) {\n return { reply: best.reply, thinking: best.thinking, delayMs: best.delayMs ?? 700, chunkMs: best.chunkMs ?? 35 }\n }\n const f = this.script.fallback || DEFAULT_FALLBACK\n return { reply: f!.reply, thinking: f!.thinking, delayMs: f!.delayMs ?? 700, chunkMs: f!.chunkMs ?? 35 }\n }\n\n async generate(messages: GenerationMessage[], options?: ProviderOptions): Promise<GenerationResult> {\n const step = this.pick(latestUserText(messages))\n await sleep(step.delayMs, options?.abortSignal)\n return { content: step.reply, files: [], tokensUsed: approxTokens(step.reply) }\n }\n\n async *stream(messages: GenerationMessage[], options?: ProviderOptions): AsyncIterable<StreamEvent> {\n const step = this.pick(latestUserText(messages))\n await sleep(step.delayMs, options?.abortSignal)\n if (step.thinking) {\n for (const c of chunks(step.thinking)) {\n yield { type: \"thinking_delta\", text: c }\n await sleep(step.chunkMs, options?.abortSignal)\n }\n }\n for (const c of chunks(step.reply)) {\n yield { type: \"text_delta\", text: c }\n await sleep(step.chunkMs, options?.abortSignal)\n }\n yield { type: \"done\", result: { content: step.reply, files: [], tokensUsed: approxTokens(step.reply) } }\n }\n\n async generateRaw(\n messages: AnthropicMessage[],\n _systemPrompt: string,\n _tools: Array<{ name: string; description: string; input_schema: Record<string, unknown> }>,\n options?: ProviderOptions,\n ): Promise<RawGenerationResult> {\n const step = this.pick(latestUserText(messages))\n await sleep(step.delayMs, options?.abortSignal)\n return {\n content: [{ type: \"text\", text: step.reply }],\n stop_reason: \"end_turn\",\n usage: { input_tokens: approxTokens(latestUserText(messages)), output_tokens: approxTokens(step.reply) },\n }\n }\n\n async *generateRawStream(\n messages: AnthropicMessage[],\n _systemPrompt: string,\n _tools: Array<{ name: string; description: string; input_schema: Record<string, unknown> }>,\n options?: ProviderOptions,\n ): AsyncIterable<RawStreamEvent> {\n const step = this.pick(latestUserText(messages))\n await sleep(step.delayMs, options?.abortSignal)\n if (step.thinking) {\n for (const c of chunks(step.thinking)) {\n yield { type: \"thinking_delta\", text: c }\n await sleep(step.chunkMs, options?.abortSignal)\n }\n }\n for (const c of chunks(step.reply)) {\n yield { type: \"text_delta\", text: c }\n await sleep(step.chunkMs, options?.abortSignal)\n }\n yield {\n type: \"raw_result\",\n result: {\n content: [{ type: \"text\", text: step.reply }],\n stop_reason: \"end_turn\",\n usage: { input_tokens: approxTokens(latestUserText(messages)), output_tokens: approxTokens(step.reply) },\n },\n }\n }\n}\n","import type { AgentProvider } from \"./types\"\nimport { ClaudeProvider } from \"./claude\"\nimport { ClaudeCodeProvider } from \"./claude-code\"\nimport { OpenAIProvider } from \"./openai\"\nimport { DemoProvider } from \"./demo\"\nimport { loadAuthConfig } from \"@/utils/auth-store\"\n\n// `deepseek` is a label on top of the OpenAI-compatible provider — it\n// resolves to OpenAIProvider with the DeepSeek baseUrl + DEEPSEEK_API_KEY\n// so users can declare `provider: \"deepseek\"` on an agent without having to\n// also fiddle with OPENAI_BASE_URL. Same pattern for any other preset.\nexport type ProviderName = \"claude-code\" | \"claude\" | \"openai\" | \"deepseek\" | \"ollama\" | \"demo\" | \"custom\"\n\nconst DEEPSEEK_PRESET = {\n baseUrl: \"https://api.deepseek.com/v1\",\n envKey: \"DEEPSEEK_API_KEY\",\n}\n\nexport interface CreateProviderOptions {\n /** Toggle DeepSeek thinking-mode (`reasoning_content` blocks). Default\n * `true` for DeepSeek baseUrls. Wire this from\n * `providers.<name>.thinking` in agentx.json. */\n thinking?: boolean\n}\n\nexport function createProvider(\n name: ProviderName = \"claude-code\",\n apiKey?: string,\n opts: CreateProviderOptions = {},\n): AgentProvider {\n // Auto-detect provider from stored config when using the default\n let resolvedName = name\n if (name === \"claude-code\" && !apiKey) {\n const stored = loadAuthConfig()\n if (stored) {\n resolvedName = stored.provider\n }\n }\n\n switch (resolvedName) {\n case \"claude-code\":\n return new ClaudeCodeProvider()\n case \"claude\":\n return new ClaudeProvider(apiKey)\n case \"openai\":\n // OpenAI-compatible. Defaults to api.openai.com/v1 + OPENAI_API_KEY;\n // override the host via OPENAI_BASE_URL so the same class talks to\n // any compatible backend (vLLM, OpenRouter, Together, llama.cpp).\n return new OpenAIProvider(apiKey, undefined, { thinking: opts.thinking })\n case \"deepseek\":\n // Convenience preset — forces the DeepSeek baseUrl and pulls the key\n // from DEEPSEEK_API_KEY (falls back to apiKey arg or OPENAI_API_KEY).\n return new OpenAIProvider(\n apiKey || process.env[DEEPSEEK_PRESET.envKey] || process.env.OPENAI_API_KEY,\n DEEPSEEK_PRESET.baseUrl,\n { thinking: opts.thinking },\n )\n case \"demo\":\n // Scripted provider behind `agentx demo` — canned replies, real\n // daemons/mesh/ledger. Never selected implicitly.\n return new DemoProvider()\n case \"ollama\":\n throw new Error(\n \"Ollama provider coming soon. Workaround: set provider to 'openai' with OPENAI_BASE_URL=http://localhost:11434/v1 (Ollama exposes an OpenAI-compatible endpoint). Or contribute at github.com/anis-marrouchi/agentx\",\n )\n default:\n throw new Error(`Unknown provider: ${resolvedName}. Supported: claude-code, claude, openai, deepseek`)\n }\n}\n\nexport { ClaudeProvider } from \"./claude\"\nexport { ClaudeCodeProvider } from \"./claude-code\"\nexport { OpenAIProvider } from \"./openai\"\nexport type {\n AgentProvider,\n GenerationMessage,\n GenerationResult,\n GeneratedFile,\n ProviderOptions,\n StreamEvent,\n AnthropicMessage,\n ContentBlock,\n RawGenerationResult,\n} from \"./types\"\n"],"mappings":"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QACN,MAAO,CACL,KAAM,SACN,WAAY,CACV,KAAM,CACJ,KAAM,SACN,YACE,wEACJ,EACA,QAAS,CACP,KAAM,SACN,YAAa,8BACf,EACA,SAAU,CACR,KAAM,SACN,YAAa,mCACf,EACA,YAAa,CACX,KAAM,SACN,YAAa,0CACf,CACF,EACA,SAAU,CAAC,OAAQ,SAAS,CAC9B,CACF,EACA,QAAS,CACP,KAAM,SACN,YAAa,sCACf,CACF,EACA,SAAU,CAAC,OAAO,CACpB,EACA,WAAY,YACd,EACA,CACE,KAAM,WACN,YACE,mKACF,aAAc,CACZ,KAAM,SACN,WAAY,CACV,SAAU,CACR,KAAM,SACN,YAAa,8BACf,EACA,QAAS,CACP,KAAM,QACN,MAAO,CAAE,KAAM,QAAS,EACxB,YAAa,uCACf,CACF,EACA,SAAU,CAAC,UAAU,CACvB,EACA,WAAY,MACd,EACA,CACE,KAAM,YACN,YACE,wJACF,aAAc,CACZ,KAAM,SACN,WAAY,CACV,KAAM,CACJ,KAAM,SACN,YAAa,sCACf,EACA,UAAW,CACT,KAAM,SACN,YACE,wEACJ,CACF,EACA,SAAU,CAAC,MAAM,CACnB,EACA,WAAY,WACd,EACA,CACE,KAAM,eACN,YACE,mLACF,aAAc,CACZ,KAAM,SACN,WAAY,CACV,QAAS,CACP,KAAM,SACN,YACE,6DACJ,EACA,cAAe,CACb,KAAM,SACN,YACE,wEACJ,EACA,YAAa,CACX,KAAM,SACN,YAAa,mDACf,CACF,EACA,SAAU,CAAC,SAAS,CACtB,EACA,WAAY,MACd,EACA,CACE,KAAM,iBACN,YACE,qFACF,aAAc,CACZ,KAAM,SACN,WAAY,CACV,KAAM,CACJ,KAAM,SACN,YACE,oEACJ,EACA,UAAW,CACT,KAAM,UACN,YAAa,mCACf,EACA,UAAW,CACT,KAAM,SACN,YAAa,kDACf,CACF,CACF,EACA,WAAY,MACd,EACA,CACE,KAAM,cACN,YACE,wJACF,aAAc,CACZ,KAAM,SACN,WAAY,CACV,QAAS,CACP,KAAM,SACN,YAAa,8BACf,EACA,QAAS,CACP,KAAM,SACN,YACE,uDACJ,C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","names":["existsSync","readFileSync","writeFileSync","mkdirSync","path","os","chalk","prompts","loadAuthConfig","AUTH_FILE","data","saveAuthConfig","config","AUTH_DIR","resolveToken","explicitKey","type","envOAuth","envClaudeCodeOAuth","envApiKey","stored","homeDir","storePaths","t","readTokenFromAuthProfiles","filePath","fallbackToken","id","cred","handleApiKey","envKey","preview","useExisting","apiKey","v","handleSetupToken","logger","boxWidth","line1","line2","token","tokenName","selectAuthMethod","provider","method","selectModel","choices","ANTHROPIC_MODELS","m","current","label","modelId","runModelSetup","auth","ensureCredentials","init_auth_store","__esmMin","init_logger","getAnthropicTools","enabledTools","TOOL_DEFINITIONS","name","description","input_schema","getLegacyTools","formatToolsForSystemPrompt","agenticTools","lines","tool","ALL_TOOL_NAMES","init_definitions","__esmMin","t","wrapSystemForCache","systemPrompt","CACHE_MIN_CHARS","DEFAULT_MODEL","DEFAULT_MAX_TOKENS","ClaudeProvider","init_claude","__esmMin","init_auth_store","init_definitions","apiKey","resolved","resolveToken","messages","options","model","maxTokens","systemMsg","m","conversationMsgs","body","getLegacyTools","response","tools","block","headers","res","errorText","reader","decoder","buffer","files","content","followUp","tokensUsed","activeTool","inferFollowUpFromText","text","cleaned","lines","tail","tailText","isIntro","l","startIdx","i","out","t","candidate","done","value","line","data","event","input","err","errText","blocks","activeBlockIdx","stopReason","inputTokens","outputTokens","cb","raw","execa","DEFAULT_MODEL","DEFAULT_MAX_TOKENS","CLI_MODEL_ALIASES","ClaudeCodeProvider","init_claude_code","__esmMin","init_auth_store","init_definitions","stored","loadAuthConfig","resolved","resolveToken","messages","options","systemPrompt","tools","model","maxTokens","body","headers","res","errorText","response","block","cliModel","systemMsg","m","prompt","args","env","result","err","output","json","e","conversationMsgs","getLegacyTools","child","fullContent","stderr","fatalError","chunk","decoder","readable","lines","line","event","files","followUp","reader","buffer","content","tokensUsed","activeTool","done","value","data","input","stdout","parsed","inferred","text","pattern","match","filePath","cleaned","tail","tailText","isIntro","l","startIdx","i","out","t","candidate","DEFAULT_MODEL","DEFAULT_MAX_TOKENS","OpenAIProvider","init_openai","__esmMin","init_definitions","apiKey","baseUrl","opts","isDeepseek","messages","options","model","maxTokens","oaMessages","body","data","systemPrompt","tools","res","err","errText","reader","onAbortCancelReader","decoder","buffer","textOut","reasoningOut","inputTokens","outputTokens","finishReason","toolAcc","done","value","lines","line","chunk","choice","delta","tc","idx","acc","blocks","input","stop_reason","m","toolResults","b","textParts","tr","toolUses","text","reasoning","msg","tu","t","content","followUp","tokensUsed","files","activeToolEmittedIdx","abortSignal","errorText","response","message","parsedArgs","usage","getLegacyTools","readFileSync","loadScript","path","DEFAULT_FALLBACK","parsed","latestUserText","messages","i","c","b","sleep","ms","signal","resolve","reject","t","chunks","text","approxTokens","DemoProvider","init_demo","__esmMin","s","userText","best","bestIndex","step","re","m","last","f","options","_systemPrompt","_tools","createProvider","name","apiKey","opts","resolvedName","stored","loadAuthConfig","ClaudeCodeProvider","ClaudeProvider","OpenAIProvider","DEEPSEEK_PRESET","DemoProvider","init_providers","__esmMin","init_claude","init_claude_code","init_openai","init_demo","init_auth_store"]}
|
package/dist/chunk-VDMYBPYB.js
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import{existsSync as k,promises as w}from"fs";import u from"path";import x from"fast-glob";import L from"js-yaml";import{z as r}from"zod";var h=r.object({name:r.string(),description:r.string(),version:r.string().optional(),author:r.string().optional(),tags:r.array(r.string()).optional(),globs:r.array(r.string()).optional(),triggers:r.array(r.object({pattern:r.string(),description:r.string().optional()})).optional(),autoInject:r.boolean().optional(),references:r.array(r.string()).optional(),delegatesTo:r.array(r.string()).optional(),category:r.string().optional()});var j=[".skills",".claude/skills","skills"],d="SKILL.md";async function P(c){let e=[];for(let t of j){let l=u.resolve(c,t);if(!k(l))continue;let f=await x.glob(`**/${d}`,{cwd:l,deep:3,caseSensitiveMatch:!1});for(let s of f){let n=u.resolve(l,s),i=await S(n);i&&(i.source="local",i.path=n,e.push(i))}}let o=u.resolve(c,d);if(k(o)){let t=await S(o);t&&(t.source="local",t.path=o,e.push(t))}return e}async function S(c){try{let e=await w.readFile(c,"utf8");return F(e)}catch{return null}}function F(c){try{let e=c.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);if(!e)return{frontmatter:{name:"unnamed",description:"No description"},instructions:c.trim(),source:"local"};let[,o,t]=e,l=M(o);return{frontmatter:h.parse(l),instructions:t.trim(),source:"local"}}catch{return null}}function M(c){let e=L.load(c);if(e===null||typeof e!="object"||Array.isArray(e))return{};let o=e,t=o.metadata;return t&&typeof t=="object"&&(!o.tags&&t.tags&&(typeof t.tags=="string"?o.tags=t.tags.split(",").map(l=>l.trim()).filter(Boolean):Array.isArray(t.tags)&&(o.tags=t.tags)),!o.version&&t.version&&(o.version=t.version)),o}function b(c,e,o){let t=[],l=e.toLowerCase(),f=new Set(l.split(/\s+/));for(let s of c){let n=0,i="";if(s.frontmatter.triggers)for(let a of s.frontmatter.triggers)try{new RegExp(a.pattern,"i").test(e)&&(n=Math.max(n,.9),i=`Trigger match: ${a.description||a.pattern}`)}catch{l.includes(a.pattern.toLowerCase())&&(n=Math.max(n,.7),i=`Keyword match: ${a.pattern}`)}if(s.frontmatter.tags){let a=s.frontmatter.tags.filter(m=>f.has(m.toLowerCase())||l.includes(m.toLowerCase()));if(a.length){let m=Math.min(a.length*.3,.8);m>n&&(n=m,i=`Tag match: ${a.join(", ")}`)}}let g=s.frontmatter.name.toLowerCase().split(/[-_\s]+/),y=s.frontmatter.description.toLowerCase().split(/\s+/),v=new Set([...g,...y]),p=[...f].filter(a=>v.has(a)&&a.length>3);if(p.length>0){let a=Math.min(p.length*.2,.6);a>n&&(n=a,i=`Content match: ${p.join(", ")}`)}n>.1&&t.push({skill:s,relevance:n,matchReason:i})}return t.sort((s,n)=>n.relevance-s.relevance)}function W(c,e,o=2e3){let t=c.filter(i=>i.frontmatter.autoInject);if(t.length===0)return"";let l=b(t,e);if(l.length===0)return"";let f=o*4,s=["[Auto-Injected Skills \u2014 matched to current task]"],n=s[0].length;for(let i of l){let g=`
|
|
2
|
-
## ${i.skill.frontmatter.name} (${Math.round(i.relevance*100)}% match)
|
|
3
|
-
${i.skill.instructions}`;if(n+g.length>f)break;s.push(g),n+=g.length}return s.length===1?"":s.join(`
|
|
4
|
-
`)}export{P as a,S as b,F as c,b as d,W as e};
|
|
5
|
-
//# sourceMappingURL=chunk-VDMYBPYB.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/agent/skills/loader.ts","../src/agent/skills/types.ts"],"sourcesContent":["import { existsSync, promises as fs } from \"fs\"\nimport path from \"path\"\nimport fg from \"fast-glob\"\nimport yaml from \"js-yaml\"\nimport { logger } from \"@/utils/logger\"\nimport type { Skill, SkillFrontmatter, SkillMatch } from \"./types\"\nimport { skillFrontmatterSchema } from \"./types\"\n\n// --- Load skills from local files and remote packages ---\n\nconst SKILL_DIRS = [\".skills\", \".claude/skills\", \"skills\"]\nconst SKILL_FILE = \"SKILL.md\"\n\nexport async function loadLocalSkills(cwd: string): Promise<Skill[]> {\n const skills: Skill[] = []\n\n for (const dir of SKILL_DIRS) {\n const skillDir = path.resolve(cwd, dir)\n if (!existsSync(skillDir)) continue\n\n const skillFiles = await fg.glob(`**/${SKILL_FILE}`, {\n cwd: skillDir,\n deep: 3,\n caseSensitiveMatch: false,\n })\n\n for (const file of skillFiles) {\n const fullPath = path.resolve(skillDir, file)\n const skill = await parseSkillFile(fullPath)\n if (skill) {\n skill.source = \"local\"\n skill.path = fullPath\n skills.push(skill)\n }\n }\n }\n\n // Also check for a single SKILL.md at project root\n const rootSkill = path.resolve(cwd, SKILL_FILE)\n if (existsSync(rootSkill)) {\n const skill = await parseSkillFile(rootSkill)\n if (skill) {\n skill.source = \"local\"\n skill.path = rootSkill\n skills.push(skill)\n }\n }\n\n return skills\n}\n\nexport async function parseSkillFile(filePath: string): Promise<Skill | null> {\n try {\n const content = await fs.readFile(filePath, \"utf8\")\n return parseSkillContent(content)\n } catch {\n return null\n }\n}\n\nexport function parseSkillContent(content: string): Skill | null {\n try {\n // Parse YAML frontmatter\n const frontmatterMatch = content.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/)\n\n if (!frontmatterMatch) {\n // No frontmatter - treat entire content as instructions with minimal metadata\n return {\n frontmatter: { name: \"unnamed\", description: \"No description\" },\n instructions: content.trim(),\n source: \"local\",\n }\n }\n\n const [, frontmatterRaw, instructions] = frontmatterMatch\n const frontmatter = parseYamlFrontmatter(frontmatterRaw)\n\n const validated = skillFrontmatterSchema.parse(frontmatter)\n\n return {\n frontmatter: validated,\n instructions: instructions.trim(),\n source: \"local\",\n }\n } catch {\n return null\n }\n}\n\nfunction parseYamlFrontmatter(raw: string): Record<string, unknown> {\n // Real YAML — handles arrays (block + flow), nested objects (e.g. `metadata:`\n // sub-block used by some skills), quoted strings, and comma-separated tag\n // strings. The previous primitive parser silently dropped nested keys, which\n // caused valid skills (initech-pm, hotmail, initech-v1-coder) to fail Zod validation\n // and load as null.\n const parsed = yaml.load(raw)\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return {}\n }\n const result = parsed as Record<string, unknown>\n // Some skills nest tags under `metadata.tags` as a comma-separated string.\n // Lift it to the top-level shape Zod expects (array of strings) so the\n // schema accepts the skill.\n const meta = result.metadata as Record<string, unknown> | undefined\n if (meta && typeof meta === \"object\") {\n if (!result.tags && meta.tags) {\n if (typeof meta.tags === \"string\") {\n result.tags = meta.tags.split(\",\").map(t => t.trim()).filter(Boolean)\n } else if (Array.isArray(meta.tags)) {\n result.tags = meta.tags\n }\n }\n if (!result.version && meta.version) result.version = meta.version\n }\n return result\n}\n\nexport function matchSkillsToTask(\n skills: Skill[],\n taskDescription: string,\n outputType?: string\n): SkillMatch[] {\n const matches: SkillMatch[] = []\n const taskLower = taskDescription.toLowerCase()\n const taskWords = new Set(taskLower.split(/\\s+/))\n\n for (const skill of skills) {\n let relevance = 0\n let matchReason = \"\"\n\n // Check trigger patterns\n if (skill.frontmatter.triggers) {\n for (const trigger of skill.frontmatter.triggers) {\n try {\n const regex = new RegExp(trigger.pattern, \"i\")\n if (regex.test(taskDescription)) {\n relevance = Math.max(relevance, 0.9)\n matchReason = `Trigger match: ${trigger.description || trigger.pattern}`\n }\n } catch {\n // Invalid regex - try simple string match\n if (taskLower.includes(trigger.pattern.toLowerCase())) {\n relevance = Math.max(relevance, 0.7)\n matchReason = `Keyword match: ${trigger.pattern}`\n }\n }\n }\n }\n\n // Check tags overlap\n if (skill.frontmatter.tags) {\n const tagOverlap = skill.frontmatter.tags.filter(\n (tag) => taskWords.has(tag.toLowerCase()) || taskLower.includes(tag.toLowerCase())\n )\n if (tagOverlap.length) {\n const tagRelevance = Math.min(tagOverlap.length * 0.3, 0.8)\n if (tagRelevance > relevance) {\n relevance = tagRelevance\n matchReason = `Tag match: ${tagOverlap.join(\", \")}`\n }\n }\n }\n\n // Check name/description overlap\n const nameWords = skill.frontmatter.name.toLowerCase().split(/[-_\\s]+/)\n const descWords = skill.frontmatter.description.toLowerCase().split(/\\s+/)\n const allSkillWords = new Set([...nameWords, ...descWords])\n\n const overlap = [...taskWords].filter((w) => allSkillWords.has(w) && w.length > 3)\n if (overlap.length > 0) {\n const wordRelevance = Math.min(overlap.length * 0.2, 0.6)\n if (wordRelevance > relevance) {\n relevance = wordRelevance\n matchReason = `Content match: ${overlap.join(\", \")}`\n }\n }\n\n if (relevance > 0.1) {\n matches.push({ skill, relevance, matchReason })\n }\n }\n\n return matches.sort((a, b) => b.relevance - a.relevance)\n}\n\n/**\n * Find auto-injectable skills that match the current message.\n * Only skills with `autoInject: true` in frontmatter participate.\n * Returns skill content to inject into context, capped at maxTokens.\n */\nexport function getAutoInjectSkills(\n skills: Skill[],\n message: string,\n maxTokens: number = 2000,\n): string {\n const autoSkills = skills.filter(s => s.frontmatter.autoInject)\n if (autoSkills.length === 0) return \"\"\n\n const matches = matchSkillsToTask(autoSkills, message)\n if (matches.length === 0) return \"\"\n\n // Inject matched skills within token budget\n const maxChars = maxTokens * 4\n const sections: string[] = [\"[Auto-Injected Skills — matched to current task]\"]\n let totalChars = sections[0].length\n\n for (const match of matches) {\n const content = `\\n## ${match.skill.frontmatter.name} (${Math.round(match.relevance * 100)}% match)\\n${match.skill.instructions}`\n if (totalChars + content.length > maxChars) break\n sections.push(content)\n totalChars += content.length\n }\n\n if (sections.length === 1) return \"\" // no skills fit in budget\n return sections.join(\"\\n\")\n}\n","import { z } from \"zod\"\n\n// --- Skill types aligned with skills.sh / Agent Skills spec ---\n\nexport const skillFrontmatterSchema = z.object({\n name: z.string(),\n description: z.string(),\n version: z.string().optional(),\n author: z.string().optional(),\n tags: z.array(z.string()).optional(),\n globs: z.array(z.string()).optional(),\n // When to automatically apply this skill\n triggers: z\n .array(\n z.object({\n pattern: z.string(), // regex or keyword pattern\n description: z.string().optional(),\n })\n )\n .optional(),\n /** If true, skill is auto-injected into context when triggers match (per-turn) */\n autoInject: z.boolean().optional(),\n /** Reference card ids cited by this skill (e.g. \"initech.gitlab.project.initech-v2\").\n * Audit lint flags FAILING when any id does not resolve. */\n references: z.array(z.string()).optional(),\n /** Skills this one delegates to (e.g. initech-cx-email → [hotmail]). Audit lint\n * flags FAILING when a delegate has no SKILL.md. */\n delegatesTo: z.array(z.string()).optional(),\n /** Free-form category — used by recipes and audit rules. Common values:\n * deploy, ssh, email, gitlab, ops, content, marketing. */\n category: z.string().optional(),\n})\n\nexport type SkillFrontmatter = z.infer<typeof skillFrontmatterSchema>\n\nexport interface Skill {\n frontmatter: SkillFrontmatter\n instructions: string // Markdown body\n source: \"local\" | \"remote\" | \"generated\"\n path?: string // Local file path\n packageId?: string // e.g., \"intellectronica/agent-skills\"\n}\n\nexport interface SkillMatch {\n skill: Skill\n relevance: number // 0-1 how relevant to current task\n matchReason: string\n}\n\nexport interface SkillPackage {\n owner: string\n repo: string\n skills: Skill[]\n}\n"],"mappings":"AAAA,OAAS,cAAAA,EAAY,YAAYC,MAAU,KAC3C,OAAOC,MAAU,OACjB,OAAOC,MAAQ,YACf,OAAOC,MAAU,UCHjB,OAAS,KAAAC,MAAS,MAIX,IAAMC,EAAyBD,EAAE,OAAO,CAC7C,KAAMA,EAAE,OAAO,EACf,YAAaA,EAAE,OAAO,EACtB,QAASA,EAAE,OAAO,EAAE,SAAS,EAC7B,OAAQA,EAAE,OAAO,EAAE,SAAS,EAC5B,KAAMA,EAAE,MAAMA,EAAE,OAAO,CAAC,EAAE,SAAS,EACnC,MAAOA,EAAE,MAAMA,EAAE,OAAO,CAAC,EAAE,SAAS,EAEpC,SAAUA,EACP,MACCA,EAAE,OAAO,CACP,QAASA,EAAE,OAAO,EAClB,YAAaA,EAAE,OAAO,EAAE,SAAS,CACnC,CAAC,CACH,EACC,SAAS,EAEZ,WAAYA,EAAE,QAAQ,EAAE,SAAS,EAGjC,WAAYA,EAAE,MAAMA,EAAE,OAAO,CAAC,EAAE,SAAS,EAGzC,YAAaA,EAAE,MAAMA,EAAE,OAAO,CAAC,EAAE,SAAS,EAG1C,SAAUA,EAAE,OAAO,EAAE,SAAS,CAChC,CAAC,EDrBD,IAAME,EAAa,CAAC,UAAW,iBAAkB,QAAQ,EACnDC,EAAa,WAEnB,eAAsBC,EAAgBC,EAA+B,CACnE,IAAMC,EAAkB,CAAC,EAEzB,QAAWC,KAAOL,EAAY,CAC5B,IAAMM,EAAWC,EAAK,QAAQJ,EAAKE,CAAG,EACtC,GAAI,CAACG,EAAWF,CAAQ,EAAG,SAE3B,IAAMG,EAAa,MAAMC,EAAG,KAAK,MAAMT,IAAc,CACnD,IAAKK,EACL,KAAM,EACN,mBAAoB,EACtB,CAAC,EAED,QAAWK,KAAQF,EAAY,CAC7B,IAAMG,EAAWL,EAAK,QAAQD,EAAUK,CAAI,EACtCE,EAAQ,MAAMC,EAAeF,CAAQ,EACvCC,IACFA,EAAM,OAAS,QACfA,EAAM,KAAOD,EACbR,EAAO,KAAKS,CAAK,IAMvB,IAAME,EAAYR,EAAK,QAAQJ,EAAKF,CAAU,EAC9C,GAAIO,EAAWO,CAAS,EAAG,CACzB,IAAMF,EAAQ,MAAMC,EAAeC,CAAS,EACxCF,IACFA,EAAM,OAAS,QACfA,EAAM,KAAOE,EACbX,EAAO,KAAKS,CAAK,GAIrB,OAAOT,CACT,CAEA,eAAsBU,EAAeE,EAAyC,CAC5E,GAAI,CACF,IAAMC,EAAU,MAAMC,EAAG,SAASF,EAAU,MAAM,EAClD,OAAOG,EAAkBF,CAAO,CAClC,MAAE,CACA,OAAO,IACT,CACF,CAEO,SAASE,EAAkBF,EAA+B,CAC/D,GAAI,CAEF,IAAMG,EAAmBH,EAAQ,MAAM,mCAAmC,EAE1E,GAAI,CAACG,EAEH,MAAO,CACL,YAAa,CAAE,KAAM,UAAW,YAAa,gBAAiB,EAC9D,aAAcH,EAAQ,KAAK,EAC3B,OAAQ,OACV,EAGF,GAAM,CAAC,CAAEI,EAAgBC,CAAY,EAAIF,EACnCG,EAAcC,EAAqBH,CAAc,EAIvD,MAAO,CACL,YAHgBI,EAAuB,MAAMF,CAAW,EAIxD,aAAcD,EAAa,KAAK,EAChC,OAAQ,OACV,CACF,MAAE,CACA,OAAO,IACT,CACF,CAEA,SAASE,EAAqBE,EAAsC,CAMlE,IAAMC,EAASC,EAAK,KAAKF,CAAG,EAC5B,GAAIC,IAAW,MAAQ,OAAOA,GAAW,UAAY,MAAM,QAAQA,CAAM,EACvE,MAAO,CAAC,EAEV,IAAME,EAASF,EAITG,EAAOD,EAAO,SACpB,OAAIC,GAAQ,OAAOA,GAAS,WACtB,CAACD,EAAO,MAAQC,EAAK,OACnB,OAAOA,EAAK,MAAS,SACvBD,EAAO,KAAOC,EAAK,KAAK,MAAM,GAAG,EAAE,IAAIC,GAAKA,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,EAC3D,MAAM,QAAQD,EAAK,IAAI,IAChCD,EAAO,KAAOC,EAAK,OAGnB,CAACD,EAAO,SAAWC,EAAK,UAASD,EAAO,QAAUC,EAAK,UAEtDD,CACT,CAEO,SAASG,EACd5B,EACA6B,EACAC,EACc,CACd,IAAMC,EAAwB,CAAC,EACzBC,EAAYH,EAAgB,YAAY,EACxCI,EAAY,IAAI,IAAID,EAAU,MAAM,KAAK,CAAC,EAEhD,QAAWvB,KAAST,EAAQ,CAC1B,IAAIkC,EAAY,EACZC,EAAc,GAGlB,GAAI1B,EAAM,YAAY,SACpB,QAAW2B,KAAW3B,EAAM,YAAY,SACtC,GAAI,CACY,IAAI,OAAO2B,EAAQ,QAAS,GAAG,EACnC,KAAKP,CAAe,IAC5BK,EAAY,KAAK,IAAIA,EAAW,EAAG,EACnCC,EAAc,kBAAkBC,EAAQ,aAAeA,EAAQ,UAEnE,MAAE,CAEIJ,EAAU,SAASI,EAAQ,QAAQ,YAAY,CAAC,IAClDF,EAAY,KAAK,IAAIA,EAAW,EAAG,EACnCC,EAAc,kBAAkBC,EAAQ,UAE5C,CAKJ,GAAI3B,EAAM,YAAY,KAAM,CAC1B,IAAM4B,EAAa5B,EAAM,YAAY,KAAK,OACvC6B,GAAQL,EAAU,IAAIK,EAAI,YAAY,CAAC,GAAKN,EAAU,SAASM,EAAI,YAAY,CAAC,CACnF,EACA,GAAID,EAAW,OAAQ,CACrB,IAAME,EAAe,KAAK,IAAIF,EAAW,OAAS,GAAK,EAAG,EACtDE,EAAeL,IACjBA,EAAYK,EACZJ,EAAc,cAAcE,EAAW,KAAK,IAAI,MAMtD,IAAMG,EAAY/B,EAAM,YAAY,KAAK,YAAY,EAAE,MAAM,SAAS,EAChEgC,EAAYhC,EAAM,YAAY,YAAY,YAAY,EAAE,MAAM,KAAK,EACnEiC,EAAgB,IAAI,IAAI,CAAC,GAAGF,EAAW,GAAGC,CAAS,CAAC,EAEpDE,EAAU,CAAC,GAAGV,CAAS,EAAE,OAAQW,GAAMF,EAAc,IAAIE,CAAC,GAAKA,EAAE,OAAS,CAAC,EACjF,GAAID,EAAQ,OAAS,EAAG,CACtB,IAAME,EAAgB,KAAK,IAAIF,EAAQ,OAAS,GAAK,EAAG,EACpDE,EAAgBX,IAClBA,EAAYW,EACZV,EAAc,kBAAkBQ,EAAQ,KAAK,IAAI,KAIjDT,EAAY,IACdH,EAAQ,KAAK,CAAE,MAAAtB,EAAO,UAAAyB,EAAW,YAAAC,CAAY,CAAC,EAIlD,OAAOJ,EAAQ,KAAK,CAACe,EAAGC,IAAMA,EAAE,UAAYD,EAAE,SAAS,CACzD,CAOO,SAASE,EACdhD,EACAiD,EACAC,EAAoB,IACZ,CACR,IAAMC,EAAanD,EAAO,OAAOoD,GAAKA,EAAE,YAAY,UAAU,EAC9D,GAAID,EAAW,SAAW,EAAG,MAAO,GAEpC,IAAMpB,EAAUH,EAAkBuB,EAAYF,CAAO,EACrD,GAAIlB,EAAQ,SAAW,EAAG,MAAO,GAGjC,IAAMsB,EAAWH,EAAY,EACvBI,EAAqB,CAAC,uDAAkD,EAC1EC,EAAaD,EAAS,CAAC,EAAE,OAE7B,QAAWE,KAASzB,EAAS,CAC3B,IAAMlB,EAAU;AAAA,KAAQ2C,EAAM,MAAM,YAAY,SAAS,KAAK,MAAMA,EAAM,UAAY,GAAG;AAAA,EAAcA,EAAM,MAAM,eACnH,GAAID,EAAa1C,EAAQ,OAASwC,EAAU,MAC5CC,EAAS,KAAKzC,CAAO,EACrB0C,GAAc1C,EAAQ,OAGxB,OAAIyC,EAAS,SAAW,EAAU,GAC3BA,EAAS,KAAK;AAAA,CAAI,CAC3B","names":["existsSync","fs","path","fg","yaml","z","skillFrontmatterSchema","SKILL_DIRS","SKILL_FILE","loadLocalSkills","cwd","skills","dir","skillDir","path","existsSync","skillFiles","fg","file","fullPath","skill","parseSkillFile","rootSkill","filePath","content","fs","parseSkillContent","frontmatterMatch","frontmatterRaw","instructions","frontmatter","parseYamlFrontmatter","skillFrontmatterSchema","raw","parsed","yaml","result","meta","t","matchSkillsToTask","taskDescription","outputType","matches","taskLower","taskWords","relevance","matchReason","trigger","tagOverlap","tag","tagRelevance","nameWords","descWords","allSkillWords","overlap","w","wordRelevance","a","b","getAutoInjectSkills","message","maxTokens","autoSkills","s","maxChars","sections","totalChars","match"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/agents/drift-detection.ts","../src/agents/compaction.ts"],"sourcesContent":["import { tokenize } from \"../memory/bm25\"\n\n// --- Behavioral Drift Detection ---\n//\n// Detects silent behavioral shifts after compaction or session boundaries.\n// Three signals, all zero-AI (pure signal processing):\n//\n// 1. Ghost lexicon decay — domain vocabulary disappearing after compaction\n// 2. Tool-call sequence shift — Jaccard distance on tool usage patterns\n// 3. Semantic drift — keyword overlap decline across session boundaries\n//\n// Inspired by CrewAI #5155 and agent-morrow/compression-monitor.\n\nexport interface BehavioralFingerprint {\n /** Domain vocabulary: word → frequency */\n lexicon: Map<string, number>\n /** Tool call sequence: tool names in order */\n toolCalls: string[]\n /** Top keywords by frequency (for quick comparison) */\n topKeywords: string[]\n /** Timestamp of fingerprint capture */\n capturedAt: string\n /** Number of messages in the sample */\n messageCount: number\n}\n\nexport interface DriftReport {\n /** 0-1 score: 0 = no drift, 1 = complete drift */\n overallScore: number\n /** Ghost lexicon decay: percentage of domain words that disappeared */\n lexiconDecay: number\n /** Tool-call Jaccard distance: 0 = identical patterns, 1 = completely different */\n toolShift: number\n /** Keyword overlap decline: percentage of shared keywords lost */\n semanticDrift: number\n /** Domain words that disappeared */\n lostWords: string[]\n /** New tool calls that weren't in the baseline */\n newTools: string[]\n /** Missing tool calls that were in the baseline */\n missingTools: string[]\n}\n\n/**\n * Build a behavioral fingerprint from agent messages.\n * Call this BEFORE compaction to establish a baseline.\n */\nexport function buildFingerprint(\n messages: Array<{ role: string; content: string; name?: string }>,\n toolCalls?: string[],\n): BehavioralFingerprint {\n const lexicon = new Map<string, number>()\n\n // Build vocabulary from agent responses\n for (const msg of messages) {\n if (msg.role !== \"agent\") continue\n const words = tokenize(msg.content)\n for (const word of words) {\n lexicon.set(word, (lexicon.get(word) || 0) + 1)\n }\n }\n\n // Top keywords by frequency (domain vocabulary)\n const topKeywords = [...lexicon.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 50)\n .map(([word]) => word)\n\n return {\n lexicon,\n toolCalls: toolCalls || [],\n topKeywords,\n capturedAt: new Date().toISOString(),\n messageCount: messages.length,\n }\n}\n\n/**\n * Compare two fingerprints and produce a drift report.\n * Call this AFTER compaction with the new session's fingerprint.\n */\nexport function detectDrift(\n baseline: BehavioralFingerprint,\n current: BehavioralFingerprint,\n): DriftReport {\n // 1. Ghost lexicon decay: how many baseline domain words are missing?\n const lexiconDecay = computeLexiconDecay(baseline, current)\n\n // 2. Tool-call sequence shift: Jaccard distance on tool usage\n const { distance: toolShift, newTools, missingTools } = computeToolShift(baseline, current)\n\n // 3. Semantic drift: keyword overlap decline\n const semanticDrift = computeSemanticDrift(baseline, current)\n\n // Overall score: weighted average (lexicon decay matters most)\n const overallScore = Math.min(1, (lexiconDecay * 0.4) + (toolShift * 0.3) + (semanticDrift * 0.3))\n\n // Lost domain words\n const lostWords = baseline.topKeywords.filter(\n word => !current.lexicon.has(word) || (current.lexicon.get(word) || 0) === 0,\n ).slice(0, 10)\n\n return {\n overallScore: Math.round(overallScore * 100) / 100,\n lexiconDecay: Math.round(lexiconDecay * 100) / 100,\n toolShift: Math.round(toolShift * 100) / 100,\n semanticDrift: Math.round(semanticDrift * 100) / 100,\n lostWords,\n newTools,\n missingTools,\n }\n}\n\n/**\n * Ghost lexicon decay: percentage of baseline's top domain words\n * that disappeared from the current fingerprint.\n */\nfunction computeLexiconDecay(\n baseline: BehavioralFingerprint,\n current: BehavioralFingerprint,\n): number {\n if (baseline.topKeywords.length === 0) return 0\n\n let missing = 0\n for (const word of baseline.topKeywords) {\n if (!current.lexicon.has(word)) missing++\n }\n\n return missing / baseline.topKeywords.length\n}\n\n/**\n * Tool-call sequence shift: Jaccard distance between tool usage sets.\n * J(A,B) = |A ∩ B| / |A ∪ B|, distance = 1 - J\n */\nfunction computeToolShift(\n baseline: BehavioralFingerprint,\n current: BehavioralFingerprint,\n): { distance: number; newTools: string[]; missingTools: string[] } {\n const baseSet = new Set(baseline.toolCalls)\n const currSet = new Set(current.toolCalls)\n\n if (baseSet.size === 0 && currSet.size === 0) {\n return { distance: 0, newTools: [], missingTools: [] }\n }\n\n const intersection = [...baseSet].filter(t => currSet.has(t))\n const union = new Set([...baseSet, ...currSet])\n const jaccard = intersection.length / union.size\n const distance = 1 - jaccard\n\n const newTools = [...currSet].filter(t => !baseSet.has(t))\n const missingTools = [...baseSet].filter(t => !currSet.has(t))\n\n return { distance, newTools, missingTools }\n}\n\n/**\n * Semantic drift: keyword overlap decline.\n * Compares top keywords between baseline and current.\n */\nfunction computeSemanticDrift(\n baseline: BehavioralFingerprint,\n current: BehavioralFingerprint,\n): number {\n if (baseline.topKeywords.length === 0) return 0\n\n const currentSet = new Set(current.topKeywords)\n let overlap = 0\n\n for (const word of baseline.topKeywords) {\n if (currentSet.has(word)) overlap++\n }\n\n // Drift = 1 - overlap ratio\n return 1 - (overlap / baseline.topKeywords.length)\n}\n\n// --- Per-agent fingerprint persistence ---\n\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from \"fs\"\nimport { resolve } from \"path\"\n\nconst DRIFT_DIR = \".agentx/drift\"\n\n/**\n * Save a behavioral fingerprint for an agent (pre-compaction baseline).\n */\nexport function saveFingerprint(\n agentId: string,\n fingerprint: BehavioralFingerprint,\n baseDir: string = process.cwd(),\n): void {\n const dir = resolve(baseDir, DRIFT_DIR)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n\n const serialized = {\n ...fingerprint,\n lexicon: Object.fromEntries(fingerprint.lexicon),\n }\n\n writeFileSync(\n resolve(dir, `${agentId}.json`),\n JSON.stringify(serialized, null, 2),\n )\n}\n\n/**\n * Load the saved fingerprint for an agent.\n */\nexport function loadFingerprint(\n agentId: string,\n baseDir: string = process.cwd(),\n): BehavioralFingerprint | null {\n const file = resolve(baseDir, DRIFT_DIR, `${agentId}.json`)\n if (!existsSync(file)) return null\n\n try {\n const data = JSON.parse(readFileSync(file, \"utf-8\"))\n return {\n ...data,\n lexicon: new Map(Object.entries(data.lexicon)),\n }\n } catch {\n return null\n }\n}\n","import type { SessionMessage } from \"./sessions\"\nimport type { MemoryStore } from \"./memory-store\"\nimport { buildFingerprint, detectDrift, saveFingerprint, loadFingerprint, type DriftReport } from \"./drift-detection\"\n\n// --- Context Compaction ---\n//\n// Summarizes conversation history when it grows too large,\n// preventing context loss in long-running sessions.\n//\n// Strategy:\n// 1. Memory flush: extract memorable facts BEFORE compacting\n// 2. Split-aware: keep tool_use/tool_result pairs intact\n// 3. Uses cheaper model (Haiku) for summarization\n// 4. Preserves recent messages verbatim, compacts older ones\n\nconst COMPACTION_MODEL = \"claude-haiku-4-20250514\"\nconst MAX_HISTORY_CHARS = 60_000\n// Start compacting when stored history exceeds this. Bumped 10K → 50K\n// because the original threshold tripped on trivial multi-message bursts\n// (project-bot queues, daily-tick storms), spending Haiku tokens to reduce\n// stored history that Claude itself never reads — Claude --resume replays\n// from its own copy. The cost-relevant thresholds are tier-2 token rotation\n// (~200K tokens / ~800K chars of input) and max-turns; compaction is just\n// for our local storage hygiene + cross-session summary seeding, so a\n// generous threshold is fine.\nconst COMPACT_THRESHOLD = 50_000\nconst KEEP_RECENT = 6 // Always keep last N messages verbatim\nconst MIN_MESSAGES_TO_COMPACT = 8 // Don't compact if fewer than this\n\nconst COMPACTION_PROMPT = `You are a conversation summarizer. Given a conversation history between a user and an AI agent, produce a concise summary that preserves:\n\n1. Key decisions made and their reasoning\n2. Important facts learned (names, IDs, paths, URLs, credentials shared)\n3. Commitments and action items (what was promised, deadlines)\n4. Current task state (what's in progress, what's done, what's blocked)\n5. User preferences expressed during the conversation\n\nFormat as a structured summary:\n[Session Summary]\n- Decisions: ...\n- Facts: ...\n- In progress: ...\n- Done: ...\n- Preferences: ...\n\nKeep it under 500 words. Prioritize actionable information over conversational flow.\nDo NOT include greetings, acknowledgments, or meta-commentary about the conversation.`\n\nexport interface CompactionResult {\n /** Summarized text replacing older messages */\n summary: string\n /** Messages that were compacted (removed from history) */\n compactedCount: number\n /** Messages kept verbatim (recent) */\n keptCount: number\n /** Whether memory flush was performed before compaction */\n memoryFlushed: boolean\n /** Quality score: percentage of key entities preserved in summary (0-100) */\n qualityScore?: number\n /** Entities that were lost in compaction */\n lostEntities?: string[]\n /** Behavioral drift report (compared to pre-compaction baseline) */\n drift?: DriftReport\n}\n\n/**\n * Check whether a session's history needs compaction.\n */\nexport function needsCompaction(messages: SessionMessage[]): boolean {\n if (messages.length < MIN_MESSAGES_TO_COMPACT) return false\n const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0)\n return totalChars > COMPACT_THRESHOLD\n}\n\n/**\n * Compact a session's conversation history.\n *\n * Flow:\n * 1. Flush memories from the about-to-be-compacted messages\n * 2. Summarize older messages using Haiku\n * 3. Return summary + preserved recent messages\n */\nexport async function compactSession(\n messages: SessionMessage[],\n agentId: string,\n memoryStore?: MemoryStore,\n /** The session's chat, so flushed facts carry its channel's trust. */\n origin?: { channel: string; chatId: string },\n): Promise<CompactionResult> {\n if (messages.length < MIN_MESSAGES_TO_COMPACT) {\n return {\n summary: \"\",\n compactedCount: 0,\n keptCount: messages.length,\n memoryFlushed: false,\n }\n }\n\n // Split: older messages to compact, recent to keep\n const splitIdx = Math.max(0, messages.length - KEEP_RECENT)\n const toCompact = messages.slice(0, splitIdx)\n const toKeep = messages.slice(splitIdx)\n\n if (toCompact.length === 0) {\n return {\n summary: \"\",\n compactedCount: 0,\n keptCount: toKeep.length,\n memoryFlushed: false,\n }\n }\n\n // Step 0: Capture behavioral fingerprint BEFORE compaction (drift baseline)\n const preFingerprint = buildFingerprint(toCompact)\n saveFingerprint(agentId, preFingerprint)\n\n // Step 1: Memory flush — extract facts from messages about to be compacted\n let memoryFlushed = false\n if (memoryStore) {\n try {\n await flushMemories(agentId, toCompact, memoryStore, origin)\n memoryFlushed = true\n } catch {\n // Best-effort memory flush\n }\n }\n\n // Step 2: Summarize older messages\n const historyText = formatMessagesForSummary(toCompact)\n const summary = await summarize(historyText)\n\n // Step 3: Verify compaction quality — check entity preservation\n const { qualityScore, lostEntities } = verifyCompactionQuality(historyText, summary)\n\n // Step 4: Check for behavioral drift — compare summary against pre-compaction baseline\n let drift: DriftReport | undefined\n const summaryMessages: SessionMessage[] = [{\n role: \"agent\" as const,\n content: summary,\n timestamp: new Date().toISOString(),\n }]\n const postFingerprint = buildFingerprint(summaryMessages)\n const baseline = loadFingerprint(agentId)\n if (baseline) {\n drift = detectDrift(baseline, postFingerprint)\n }\n\n return {\n summary,\n compactedCount: toCompact.length,\n keptCount: toKeep.length,\n memoryFlushed,\n qualityScore,\n lostEntities: lostEntities.length > 0 ? lostEntities : undefined,\n drift: drift && drift.overallScore > 0.1 ? drift : undefined,\n }\n}\n\n/**\n * Verify compaction quality by checking entity preservation.\n * Extracts key entities (names, IDs, URLs, decisions) from the original\n * and checks how many survived in the summary.\n */\nfunction verifyCompactionQuality(\n original: string,\n summary: string,\n): { qualityScore: number; lostEntities: string[] } {\n const entities = extractEntities(original)\n if (entities.length === 0) return { qualityScore: 100, lostEntities: [] }\n\n const summaryLower = summary.toLowerCase()\n const preserved: string[] = []\n const lost: string[] = []\n\n for (const entity of entities) {\n if (summaryLower.includes(entity.toLowerCase())) {\n preserved.push(entity)\n } else {\n lost.push(entity)\n }\n }\n\n const score = Math.round((preserved.length / entities.length) * 100)\n return { qualityScore: score, lostEntities: lost }\n}\n\n/**\n * Extract key entities from text: names, IDs, URLs, file paths, decisions.\n */\nfunction extractEntities(text: string): string[] {\n const entities: string[] = []\n const seen = new Set<string>()\n\n const add = (entity: string) => {\n const key = entity.toLowerCase().trim()\n if (key.length < 3 || seen.has(key)) return\n seen.add(key)\n entities.push(entity.trim())\n }\n\n // URLs\n const urls = text.match(/https?:\\/\\/[^\\s<>)\"]+/g) || []\n for (const url of urls) add(url.slice(0, 80))\n\n // File paths\n const paths = text.match(/(?:\\/[\\w.-]+){2,}/g) || []\n for (const p of paths) add(p)\n\n // Ticket/issue references (#123, !456)\n const refs = text.match(/[#!]\\d{2,}/g) || []\n for (const r of refs) add(r)\n\n // Email addresses\n const emails = text.match(/[\\w.-]+@[\\w.-]+\\.\\w+/g) || []\n for (const e of emails) add(e)\n\n // Quoted strings (potential names, IDs, keys)\n const quoted = text.match(/\"([^\"]{3,40})\"/g) || []\n for (const q of quoted) add(q.slice(1, -1))\n\n // Capitalized proper nouns (2+ words starting with uppercase)\n const properNouns = text.match(/[A-Z][a-z]+(?:\\s+[A-Z][a-z]+)+/g) || []\n for (const n of properNouns) add(n)\n\n // UUIDs and hashes\n const ids = text.match(/[a-f0-9]{8,}/gi) || []\n for (const id of ids.slice(0, 5)) add(id) // cap to avoid noise\n\n return entities.slice(0, 30) // max 30 entities to check\n}\n\n/**\n * Apply compaction result to a session's messages.\n * Replaces older messages with a single summary message and keeps recent ones.\n */\nexport function applyCompaction(\n messages: SessionMessage[],\n result: CompactionResult,\n): SessionMessage[] {\n if (result.compactedCount === 0) return messages\n\n const kept = messages.slice(-result.keptCount)\n\n // Prepend summary as a synthetic \"system\" message\n const summaryMessage: SessionMessage = {\n role: \"agent\",\n name: \"system\",\n content: `[Compacted conversation summary — ${result.compactedCount} earlier messages]\\n\\n${result.summary}`,\n timestamp: new Date().toISOString(),\n }\n\n return [summaryMessage, ...kept]\n}\n\n/**\n * Flush extractable memories from messages before they get compacted.\n * This ensures important facts survive compaction.\n */\nasync function flushMemories(\n agentId: string,\n messages: SessionMessage[],\n store: MemoryStore,\n origin?: { channel: string; chatId: string },\n): Promise<void> {\n // Build a conversation block from the messages\n const userMessages: string[] = []\n const agentMessages: string[] = []\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n userMessages.push(`${msg.name || \"User\"}: ${msg.content}`)\n } else {\n agentMessages.push(msg.content)\n }\n }\n\n if (userMessages.length === 0 || agentMessages.length === 0) return\n\n // Use the existing memory extraction on a combined block\n const { extractMemories } = await import(\"./memory-extract\")\n await extractMemories(\n agentId,\n userMessages.join(\"\\n\"),\n agentMessages.join(\"\\n\"),\n { channel: origin?.channel ?? \"compaction\", chatId: origin?.chatId ?? \"flush\", sender: \"compaction\" },\n store,\n )\n}\n\n/**\n * Format messages into a readable transcript for the summarizer.\n */\nfunction formatMessagesForSummary(messages: SessionMessage[]): string {\n return messages\n .map((m) => {\n const time = m.timestamp.slice(11, 16)\n const speaker = m.role === \"user\" ? (m.name || \"User\") : (m.name || \"Agent\")\n return `[${time}] ${speaker}: ${m.content}`\n })\n .join(\"\\n\\n\")\n}\n\n/**\n * Summarize a conversation transcript using a cheap/fast model.\n */\nasync function summarize(transcript: string): Promise<string> {\n // Truncate extremely long transcripts to avoid exceeding Haiku's context\n const maxInputChars = 20000\n const truncated = transcript.length > maxInputChars\n ? transcript.slice(-maxInputChars)\n : transcript\n\n try {\n const { createProvider } = await import(\"@/agent/providers\")\n const provider = createProvider(\"claude\")\n\n const result = await provider.generate(\n [\n { role: \"system\", content: COMPACTION_PROMPT },\n { role: \"user\", content: truncated },\n ],\n { model: COMPACTION_MODEL, maxTokens: 1024 },\n )\n\n return result.content.trim()\n } catch (error: any) {\n // Fallback: simple truncation if summarization fails\n const lines = transcript.split(\"\\n\")\n const kept = lines.slice(-20)\n return `[Summary unavailable — keeping last ${kept.length} lines]\\n${kept.join(\"\\n\")}`\n }\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","existsSync","mkdirSync","resolve","buildFingerprint","messages","toolCalls","lexicon","msg","words","tokenize","word","topKeywords","a","b","detectDrift","baseline","current","lexiconDecay","computeLexiconDecay","toolShift","newTools","missingTools","computeToolShift","semanticDrift","computeSemanticDrift","overallScore","lostWords","missing","baseSet","currSet","intersection","t","union","distance","currentSet","overlap","DRIFT_DIR","saveFingerprint","agentId","fingerprint","baseDir","dir","serialized","loadFingerprint","file","data","COMPACTION_MODEL","COMPACT_THRESHOLD","KEEP_RECENT","MIN_MESSAGES_TO_COMPACT","COMPACTION_PROMPT","needsCompaction","messages","sum","m","compactSession","agentId","memoryStore","origin","splitIdx","toCompact","toKeep","preFingerprint","buildFingerprint","saveFingerprint","memoryFlushed","flushMemories","historyText","formatMessagesForSummary","summary","summarize","qualityScore","lostEntities","verifyCompactionQuality","drift","summaryMessages","postFingerprint","baseline","loadFingerprint","detectDrift","original","entities","extractEntities","summaryLower","preserved","lost","entity","text","seen","add","key","urls","url","paths","p","refs","r","emails","e","quoted","q","properNouns","n","ids","id","applyCompaction","result","kept","store","userMessages","agentMessages","msg","extractMemories","time","speaker","transcript","truncated","createProvider","COMPACTION_MODEL"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/agents/context-planner.ts"],"sourcesContent":["// --- Context Planner ---\n//\n// A Haiku pre-call that decides what context to retrieve for the current\n// message, replacing the blind \"stack every layer\" approach of the layered\n// strategy. Drops --resume bloat by avoiding the full session history blob\n// on every turn; instead pulls only the specific facts / memory / cross-chat\n// the current message actually needs.\n//\n// Always-on core (handled by registry, not the planner): channel, scope,\n// landscape, identity, intent, and a small `recentTurns`-sized tail of the\n// session verbatim. The planner decides: how many tail turns to keep, and\n// whether to pull memory or cross-chat on top.\n//\n// Fail-open: any planner error returns null and the registry falls back to\n// the layered strategy. This keeps the feature safe to enable even when\n// the planner model is misbehaving.\n//\n// Cost model: one small Haiku call per message (~300 input / ~80 output\n// tokens, ~$0.0005). Win threshold is when it avoids shipping ≥5K tokens\n// of layered history/memory the agent didn't need — typical for short\n// \"ok / yes / do it\" messages where the layered path still dumps the\n// whole 12K-char session blob.\n\nimport type { SessionStore, SessionMessage } from \"./sessions\"\nimport { extractJson } from \"@/utils/extract-json\"\nimport type { MemoryFact, MemoryStore } from \"./memory-store\"\n\n/** Planner output. When `null`, the caller falls back to the layered\n * strategy. Otherwise each field overrides the corresponding context\n * layer — an empty string means \"skip this layer entirely\". */\nexport interface ContextPlan {\n /** Rendered session history (same shape buildHistoryContext returns),\n * truncated to the last N turns the planner picked. */\n sessionHistory: string\n /** Rendered memory context, or \"\" when the planner decided memory\n * isn't relevant to this message. */\n memoryContext: string\n /** The memory facts rendered into memoryContext. */\n memoryFacts: MemoryFact[]\n /** Rendered cross-chat summary, or \"\" when not needed. */\n crossChatContext: string\n /** Debug metadata — surfaced in logs + bench output so operators can\n * see what the planner chose. Not sent to the main agent. */\n debug: {\n recentTurns: number\n memoryIncluded: boolean\n memoryQuery?: string\n crossChatIncluded: boolean\n reasoning?: string\n planLatencyMs: number\n }\n}\n\nexport interface PlanContextInput {\n agentId: string\n channel: string\n chatId: string\n message: string\n sessions: SessionStore\n memoryStore: MemoryStore\n /** Caller-controlled timeout for the Haiku planner call. Defaults to\n * 8s — the planner is in the critical path before the main agent\n * runs, so we can't afford a long timeout. Fail-open if exceeded. */\n timeoutMs?: number\n}\n\nconst PLANNER_MODEL = \"claude-haiku-4-5-20251001\"\nconst PLANNER_PROMPT = `You are a context planner for an AI agent. Given the user's current message and recent conversation tail, decide what historical context the agent needs.\n\nOutput ONE JSON object on one line, no prose, no fences:\n { \"recentTurns\": number, \"memory\": { \"include\": boolean, \"query\": string }, \"crossChat\": boolean, \"reasoning\": string }\n\nRules:\n- \"recentTurns\" in [0,6]. Short acknowledgments (\"ok\", \"yes\", \"do it\", \"continue\") need 2-3 turns to know what they refer to. New topics need 0. Technical follow-ups need 3-5.\n- \"memory.include\" = true only when the message likely references a person, past event, decision, or project the agent should recall. \"memory.query\" = 3-8 words distilling what to search for (leave \"\" when include=false).\n- \"crossChat\" = true only when the message references another chat (\"DM\", \"the group\", \"earlier conversation\", a peer agent's name). Most messages = false.\n- \"reasoning\" = one short phrase explaining the plan.\n\nBe conservative — unneeded context costs tokens. When in doubt, exclude.`\n\nexport async function planContext(input: PlanContextInput): Promise<ContextPlan | null> {\n const start = Date.now()\n const timeoutMs = input.timeoutMs ?? 8_000\n\n // Snapshot the session tail for the planner prompt. Don't send the\n // whole session — the planner only needs enough to judge whether the\n // message is a follow-up, and we pay tokens for every line we ship.\n const recentTail = sessionTail(input.sessions, input.agentId, input.channel, input.chatId, 4)\n\n const userPrompt = [\n `Agent: ${input.agentId}`,\n `Channel: ${input.channel}`,\n recentTail ? `Recent conversation:\\n${recentTail}` : \"Recent conversation: (none — first message)\",\n ``,\n `Current message: ${input.message.slice(0, 1500)}`,\n ].join(\"\\n\")\n\n // Use the Anthropic SDK path — warm HTTP client, no subprocess cold-start.\n // resolveToken now recognizes CLAUDE_CODE_OAUTH_TOKEN so this works out\n // of the box for any operator who already has Claude CLI auth set up.\n // Planner timeout is tight (8s default) because plan latency is in the\n // critical path before the main agent runs.\n let planJson: any\n try {\n const { createProvider } = await import(\"@/agent/providers\")\n const provider = createProvider(\"claude\")\n const ac = new AbortController()\n const timer = setTimeout(() => ac.abort(), timeoutMs)\n try {\n const result = await provider.generate(\n [\n { role: \"system\", content: PLANNER_PROMPT },\n { role: \"user\", content: userPrompt },\n ],\n { model: PLANNER_MODEL, maxTokens: 256 },\n )\n planJson = extractJson(result.content)\n } finally {\n clearTimeout(timer)\n }\n } catch (err: any) {\n // Temporary: surface planner errors for debugging. Will silence once\n // we're confident the SDK path is stable.\n if (process.env.AGENTX_PLANNER_DEBUG) {\n // eslint-disable-next-line no-console\n console.error(`[planner] error: ${err?.message ?? err}`)\n }\n return null\n }\n\n if (!planJson || typeof planJson !== \"object\") return null\n\n const recentTurns = clampInt(planJson.recentTurns, 0, 6, 3)\n const memoryInclude = Boolean(planJson.memory?.include)\n const memoryQuery: string = typeof planJson.memory?.query === \"string\" ? planJson.memory.query : \"\"\n const crossChatInclude = Boolean(planJson.crossChat)\n const reasoning = typeof planJson.reasoning === \"string\" ? planJson.reasoning : undefined\n\n // Resolve the plan into actual retrieval strings.\n const sessionHistory = renderSessionTail(\n input.sessions,\n input.agentId,\n input.channel,\n input.chatId,\n recentTurns,\n )\n\n let memoryContext = \"\"\n let memoryFacts: MemoryFact[] = []\n if (memoryInclude && memoryQuery) {\n try {\n const relevant = input.memoryStore.findRelevant(memoryQuery, input.agentId, 5)\n memoryContext = input.memoryStore.buildContext(relevant)\n memoryFacts = input.memoryStore.contextFacts(relevant)\n } catch {\n // Memory retrieval is best-effort; planner proceeds with empty memory.\n }\n }\n\n let crossChatContext = \"\"\n if (crossChatInclude) {\n // Force-include via empty message arg — the planner already decided.\n crossChatContext = input.sessions.getCrossSessionSummary(\n input.agentId, input.channel, input.chatId, \"\",\n )\n }\n\n return {\n sessionHistory,\n memoryContext,\n memoryFacts,\n crossChatContext,\n debug: {\n recentTurns,\n memoryIncluded: memoryInclude,\n memoryQuery: memoryQuery || undefined,\n crossChatIncluded: crossChatInclude,\n reasoning,\n planLatencyMs: Date.now() - start,\n },\n }\n}\n\n/** Render the last N message pairs as a compact prompt tail. Returns \"\" when\n * there's no session on disk yet (first message). Format mirrors\n * SessionStore.buildHistoryContext but with an N-turn cap — the planner's\n * whole point is to cap this instead of dumping the full 12K-char log. */\nfunction renderSessionTail(\n sessions: SessionStore,\n agentId: string,\n channel: string,\n chatId: string,\n turns: number,\n): string {\n if (turns <= 0) return \"\"\n const session = sessions.getSession(agentId, channel, chatId)\n if (session.messages.length === 0) return \"\"\n // N \"turns\" = N*2 messages (user+agent pairs). Round to nearest message\n // count so a partial trailing user message still gets included.\n const msgCount = Math.min(session.messages.length, turns * 2)\n const tail = session.messages.slice(-msgCount)\n const lines: string[] = [`[Recent exchange — last ${tail.length} messages]`]\n for (const msg of tail) {\n const name = msg.name || (msg.role === \"user\" ? \"User\" : \"Agent\")\n lines.push(`${name}: ${msg.content}`)\n }\n lines.push(\"[End recent exchange — respond to the latest message above]\")\n lines.push(\"\")\n return lines.join(\"\\n\")\n}\n\n/** Very small tail for the planner's own prompt — enough for the planner to\n * decide whether this message is a follow-up, not enough to bloat the\n * planner call itself. Distinct from renderSessionTail (used for the main\n * agent) because we cap more aggressively here. */\nfunction sessionTail(\n sessions: SessionStore,\n agentId: string,\n channel: string,\n chatId: string,\n maxMessages: number,\n): string {\n const session = sessions.getSession(agentId, channel, chatId)\n if (session.messages.length === 0) return \"\"\n const tail = session.messages.slice(-maxMessages)\n return tail\n .map((m: SessionMessage) => {\n const name = m.name || (m.role === \"user\" ? \"User\" : \"Agent\")\n const content = m.content.length > 200 ? m.content.slice(0, 200) + \"…\" : m.content\n return ` ${name}: ${content}`\n })\n .join(\"\\n\")\n}\n\n/** Extract the first JSON object from a text response. Haiku sometimes\n * wraps output in backticks or prose despite the instructions, so we\n * scan for the first `{...}` span. */\n\nfunction clampInt(v: unknown, min: number, max: number, fallback: number): number {\n const n = typeof v === \"number\" ? Math.floor(v) : Number.parseInt(String(v ?? \"\"), 10)\n if (!Number.isFinite(n)) return fallback\n return Math.min(max, Math.max(min, n))\n}\n"],"mappings":"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","names":["init_extract_json","PLANNER_MODEL","PLANNER_PROMPT","planContext","input","start","timeoutMs","recentTail","sessionTail","userPrompt","planJson","createProvider","provider","ac","timer","result","extractJson","err","recentTurns","clampInt","memoryInclude","memoryQuery","crossChatInclude","reasoning","sessionHistory","renderSessionTail","memoryContext","memoryFacts","relevant","crossChatContext","sessions","agentId","channel","chatId","turns","session","msgCount","tail","lines","msg","name","maxMessages","m","content","v","min","max","fallback","n"]}
|
package/dist/heal-HVYQD3EZ.js
DELETED
package/dist/loader-EVHZ2R73.js
DELETED