agentix-cli 0.16.0 → 0.17.0
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-4AOMVXRJ.js +2 -0
- package/dist/board-dashboard-CXIVJKDP.js +7282 -0
- package/dist/board-dashboard-CXIVJKDP.js.map +1 -0
- package/dist/chunk-34NG7UDX.js +11 -0
- package/dist/chunk-34NG7UDX.js.map +1 -0
- package/dist/chunk-4HP6UC2X.js +13 -0
- package/dist/chunk-4HP6UC2X.js.map +1 -0
- package/dist/chunk-5WNGHDFC.js +164 -0
- package/dist/chunk-5WNGHDFC.js.map +1 -0
- package/dist/{chunk-7MBTMCKY.js → chunk-63UYRJIC.js} +2 -2
- package/dist/chunk-6PPIFDOQ.js +1742 -0
- package/dist/chunk-6PPIFDOQ.js.map +1 -0
- package/dist/chunk-ABCHHNG6.js +625 -0
- package/dist/chunk-ABCHHNG6.js.map +1 -0
- package/dist/chunk-AFRHKA7B.js +2 -0
- package/dist/chunk-AFRHKA7B.js.map +1 -0
- package/dist/chunk-CDNWCYF7.js +2 -0
- package/dist/chunk-CDNWCYF7.js.map +1 -0
- package/dist/{chunk-6MKAXEE5.js → chunk-EKTPCSN4.js} +2 -2
- package/dist/{chunk-6MKAXEE5.js.map → chunk-EKTPCSN4.js.map} +1 -1
- package/dist/chunk-EW27SCCC.js +2 -0
- package/dist/chunk-EW27SCCC.js.map +1 -0
- package/dist/chunk-HCFSHGK4.js +46 -0
- package/dist/chunk-HCFSHGK4.js.map +1 -0
- package/dist/chunk-HHJGKD5B.js +71 -0
- package/dist/chunk-HHJGKD5B.js.map +1 -0
- package/dist/{chunk-YI2T5QYB.js → chunk-MBEQASP3.js} +2 -2
- package/dist/chunk-N67JZAXU.js +2 -0
- package/dist/chunk-N67JZAXU.js.map +1 -0
- package/dist/{chunk-FLOGK2U7.js → chunk-SRHLXZRF.js} +2 -2
- package/dist/chunk-TT2Q7YMZ.js +118 -0
- package/dist/chunk-TT2Q7YMZ.js.map +1 -0
- package/dist/{chunk-HI24KAEY.js → chunk-WJXU5F4Z.js} +3 -4
- package/dist/{chunk-HI24KAEY.js.map → chunk-WJXU5F4Z.js.map} +1 -1
- package/dist/chunk-XDJSYQST.js +6 -0
- package/dist/chunk-XDJSYQST.js.map +1 -0
- package/dist/chunk-YPOELN6Z.js +798 -0
- package/dist/chunk-YPOELN6Z.js.map +1 -0
- package/dist/{chunk-QZP65I2H.js → chunk-ZE2YFX4K.js} +2 -2
- package/dist/cli.js +1 -320
- package/dist/cli.js.map +1 -1
- package/dist/{compaction-JBZZ7I7Y.js → compaction-X46EQEPS.js} +6 -6
- package/dist/compaction-X46EQEPS.js.map +1 -0
- package/dist/{config-LN6JEHMF.js → config-KCYHMKCT.js} +2 -2
- package/dist/config-mutator-QP4GLBOE.js +2 -0
- package/dist/context-planner-G2J7FNNU.js +17 -0
- package/dist/context-planner-G2J7FNNU.js.map +1 -0
- package/dist/editor-chat-R54E65W5.js +137 -0
- package/dist/editor-chat-R54E65W5.js.map +1 -0
- package/dist/heal-JDXUPRET.js +2 -0
- package/dist/inbox-L5ZZ623Y.js +315 -0
- package/dist/inbox-L5ZZ623Y.js.map +1 -0
- package/dist/index.d.ts +2917 -1781
- package/dist/index.js +23 -53
- package/dist/index.js.map +1 -1
- package/dist/ingest-whatsapp-NS4OWDOU.js +6 -0
- package/dist/ingest-whatsapp-NS4OWDOU.js.map +1 -0
- package/dist/memory-extract-WH4FZ456.js +2 -0
- package/dist/processes-XF5CV4LK.js +256 -0
- package/dist/processes-XF5CV4LK.js.map +1 -0
- package/dist/program-FFWEILAS.js +2 -0
- package/dist/providers-AKQ4HTB4.js +2 -0
- package/dist/registry-IFMDJWQ7.js +2 -0
- package/dist/scheduler-63D5KFO7.js +2 -0
- package/dist/slack-PSQLVTGB.js +3 -0
- package/dist/slack-PSQLVTGB.js.map +1 -0
- package/dist/store-5MJOO74U.js +2 -0
- package/dist/store-5MJOO74U.js.map +1 -0
- package/dist/task-store-NM7VKNLE.js +2 -0
- package/dist/task-store-NM7VKNLE.js.map +1 -0
- package/dist/telegram-W4SLFMNY.js +3 -0
- package/dist/telegram-W4SLFMNY.js.map +1 -0
- package/dist/timers-PCB4ZIBD.js +2 -0
- package/dist/timers-PCB4ZIBD.js.map +1 -0
- package/dist/types-C7IFH42Z.js +2 -0
- package/dist/types-C7IFH42Z.js.map +1 -0
- package/dist/usage-dashboard-AXPXB2KP.js +803 -0
- package/dist/usage-dashboard-AXPXB2KP.js.map +1 -0
- package/dist/web/workflow-editor.global.js +1427 -0
- package/dist/web/workflow-editor.global.js.map +1 -0
- package/dist/whatsapp-DCHRBRVV.js +3 -0
- package/dist/whatsapp-DCHRBRVV.js.map +1 -0
- package/dist/workspace-setup-C4VB3QZV.js +2 -0
- package/dist/workspace-setup-C4VB3QZV.js.map +1 -0
- package/package.json +9 -2
- package/dist/agent-BCDPSPKU.js +0 -2
- package/dist/board-dashboard-LA7WW56M.js +0 -5668
- package/dist/board-dashboard-LA7WW56M.js.map +0 -1
- package/dist/chunk-6U2XOQHU.js +0 -7
- package/dist/chunk-6U2XOQHU.js.map +0 -1
- package/dist/chunk-7FJQGD6U.js +0 -6
- package/dist/chunk-7FJQGD6U.js.map +0 -1
- package/dist/chunk-ABDP7TDL.js +0 -71
- package/dist/chunk-ABDP7TDL.js.map +0 -1
- package/dist/chunk-CXY2RYNI.js +0 -11
- package/dist/chunk-CXY2RYNI.js.map +0 -1
- package/dist/chunk-HVC6FJNP.js +0 -108
- package/dist/chunk-HVC6FJNP.js.map +0 -1
- package/dist/chunk-K7BE4E35.js +0 -138
- package/dist/chunk-K7BE4E35.js.map +0 -1
- package/dist/chunk-KXJSLLSQ.js +0 -199
- package/dist/chunk-KXJSLLSQ.js.map +0 -1
- package/dist/chunk-KXZMYLHQ.js +0 -46
- package/dist/chunk-KXZMYLHQ.js.map +0 -1
- package/dist/chunk-M7HKBG3V.js +0 -2
- package/dist/chunk-M7HKBG3V.js.map +0 -1
- package/dist/chunk-RB7265LO.js +0 -12
- package/dist/chunk-RB7265LO.js.map +0 -1
- package/dist/compaction-JBZZ7I7Y.js.map +0 -1
- package/dist/config-mutator-24TH7PHM.js +0 -2
- package/dist/heal-WKY5I4KB.js +0 -2
- package/dist/memory-extract-AD7KMRPC.js +0 -2
- package/dist/providers-VUIWO3ZK.js +0 -2
- package/dist/registry-ASC2QS5K.js +0 -2
- package/dist/scheduler-A3TQOCKT.js +0 -2
- package/dist/usage-dashboard-QX3CF6KK.js +0 -404
- package/dist/usage-dashboard-QX3CF6KK.js.map +0 -1
- package/dist/workspace-setup-ZWTT42XT.js +0 -2
- /package/dist/{agent-BCDPSPKU.js.map → agent-4AOMVXRJ.js.map} +0 -0
- /package/dist/{chunk-7MBTMCKY.js.map → chunk-63UYRJIC.js.map} +0 -0
- /package/dist/{chunk-YI2T5QYB.js.map → chunk-MBEQASP3.js.map} +0 -0
- /package/dist/{chunk-FLOGK2U7.js.map → chunk-SRHLXZRF.js.map} +0 -0
- /package/dist/{chunk-QZP65I2H.js.map → chunk-ZE2YFX4K.js.map} +0 -0
- /package/dist/{config-LN6JEHMF.js.map → config-KCYHMKCT.js.map} +0 -0
- /package/dist/{config-mutator-24TH7PHM.js.map → config-mutator-QP4GLBOE.js.map} +0 -0
- /package/dist/{heal-WKY5I4KB.js.map → heal-JDXUPRET.js.map} +0 -0
- /package/dist/{memory-extract-AD7KMRPC.js.map → memory-extract-WH4FZ456.js.map} +0 -0
- /package/dist/{providers-VUIWO3ZK.js.map → program-FFWEILAS.js.map} +0 -0
- /package/dist/{registry-ASC2QS5K.js.map → providers-AKQ4HTB4.js.map} +0 -0
- /package/dist/{scheduler-A3TQOCKT.js.map → registry-IFMDJWQ7.js.map} +0 -0
- /package/dist/{workspace-setup-ZWTT42XT.js.map → scheduler-63D5KFO7.js.map} +0 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{existsSync as a,mkdirSync as d,readdirSync as l,readFileSync as h,unlinkSync as o,writeFileSync as m}from"fs";import{resolve as s}from"path";import{randomUUID as f}from"crypto";import{z as n}from"zod";var u=n.object({id:n.string(),runId:n.string(),workflowId:n.string(),nodeId:n.string(),fireAt:n.string(),cancelKey:n.string().optional(),createdAt:n.string()}),c=class{baseDir;tickIntervalMs;log;timer;callback;firedIds=new Set;constructor(e={}){let t=e.baseDir??s(process.cwd(),".agentx/workflows");this.baseDir=s(t,"_timers"),d(this.baseDir,{recursive:!0}),this.tickIntervalMs=e.tickIntervalMs??1e3,this.log=e.log??(()=>{})}onFire(e){this.callback=e}start(){this.timer||(this.log(`[timers] ticking every ${this.tickIntervalMs}ms \u2014 scanning ${this.baseDir}`),this.tick(),this.timer=setInterval(()=>this.tick(),this.tickIntervalMs),typeof this.timer.unref=="function"&&this.timer.unref())}stop(){this.timer&&(clearInterval(this.timer),this.timer=void 0)}schedule(e){let t={id:f(),runId:e.runId,workflowId:e.workflowId,nodeId:e.nodeId,fireAt:e.fireAt,cancelKey:e.cancelKey,createdAt:new Date().toISOString()};return m(this.pathFor(t.id),JSON.stringify(t,null,2)),t}cancel(e){let t=0;for(let i of this.list())if(!(e.runId&&i.runId!==e.runId)&&!(e.nodeId&&i.nodeId!==e.nodeId)&&!(e.cancelKey&&i.cancelKey!==e.cancelKey))try{o(this.pathFor(i.id)),t++}catch{}return t}list(){if(!a(this.baseDir))return[];let e=[];for(let t of l(this.baseDir,{withFileTypes:!0}))if(!(!t.isFile()||!t.name.endsWith(".json")))try{let i=JSON.parse(h(s(this.baseDir,t.name),"utf-8")),r=u.safeParse(i);r.success&&e.push(r.data)}catch{}return e}pathFor(e){return s(this.baseDir,`${e}.json`)}tick(){if(!this.callback)return;let e=Date.now();for(let t of this.list()){if(this.firedIds.has(t.id))continue;let i=Date.parse(t.fireAt);if(!(Number.isNaN(i)||i>e)){this.firedIds.add(t.id);try{o(this.pathFor(t.id))}catch{}try{Promise.resolve(this.callback(t)).catch(r=>this.log(`[timers] fire callback threw: ${r?.message??r}`))}catch(r){this.log(`[timers] fire dispatch failed: ${r?.message??r}`)}}}}};export{u as a,c as b};
|
|
2
|
+
//# sourceMappingURL=chunk-AFRHKA7B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/workflows/timers.ts"],"sourcesContent":["import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { randomUUID } from \"crypto\"\nimport { z } from \"zod\"\n\n// --- Durable timer service ---\n//\n// Persists pending timers under .agentx/workflows/_timers/<id>.json so a\n// daemon restart doesn't forget them. A 1s tick loop fires due timers by\n// invoking a registered callback. The workflow dispatcher registers its\n// resume-on-timer callback at boot.\n//\n// Timer model — v1:\n// - Each timer is keyed to a (runId, nodeId) pair so `timer.boundary`\n// nodes can be cancelled when the attached node completes before the\n// timer elapses.\n// - Fire-once semantics. No repeating timers at this layer (use\n// trigger.cron at the workflow level for recurrence).\n// - Best-effort delivery: a missed tick (daemon crash, clock skew) is\n// honoured on next startup by scanning persisted timers with fireAt\n// in the past.\n\nexport const timerRecordSchema = z.object({\n id: z.string(),\n runId: z.string(),\n workflowId: z.string(),\n nodeId: z.string(),\n /** ISO-8601 instant when the timer should fire. */\n fireAt: z.string(),\n /** Optional key to cancel matching timers (e.g., when the attached node\n * completes normally and the escalation timer should be cancelled). */\n cancelKey: z.string().optional(),\n createdAt: z.string(),\n})\nexport type TimerRecord = z.infer<typeof timerRecordSchema>\n\nexport interface TimerCallback {\n (t: TimerRecord): void | Promise<void>\n}\n\nexport interface TimerServiceOptions {\n baseDir?: string\n /** Tick interval, in ms. Default 1000. */\n tickIntervalMs?: number\n log?: (msg: string) => void\n}\n\nexport class TimerService {\n readonly baseDir: string\n private readonly tickIntervalMs: number\n private readonly log: (msg: string) => void\n private timer?: ReturnType<typeof setInterval>\n private callback?: TimerCallback\n private firedIds: Set<string> = new Set()\n\n constructor(opts: TimerServiceOptions = {}) {\n const root = opts.baseDir ?? resolve(process.cwd(), \".agentx/workflows\")\n this.baseDir = resolve(root, \"_timers\")\n mkdirSync(this.baseDir, { recursive: true })\n this.tickIntervalMs = opts.tickIntervalMs ?? 1000\n this.log = opts.log ?? (() => {})\n }\n\n /** Register the fire callback. Overwrites any prior callback. Typical\n * caller is the dispatcher's \"resume on timer\" path. */\n onFire(cb: TimerCallback): void {\n this.callback = cb\n }\n\n start(): void {\n if (this.timer) return\n this.log(`[timers] ticking every ${this.tickIntervalMs}ms — scanning ${this.baseDir}`)\n // Honour missed ticks on startup: scan all persisted timers once.\n this.tick()\n this.timer = setInterval(() => this.tick(), this.tickIntervalMs)\n // Don't hold the event loop open on daemon shutdown.\n if (typeof this.timer.unref === \"function\") this.timer.unref()\n }\n\n stop(): void {\n if (!this.timer) return\n clearInterval(this.timer)\n this.timer = undefined\n }\n\n schedule(args: {\n runId: string\n workflowId: string\n nodeId: string\n fireAt: string\n cancelKey?: string\n }): TimerRecord {\n const record: TimerRecord = {\n id: randomUUID(),\n runId: args.runId,\n workflowId: args.workflowId,\n nodeId: args.nodeId,\n fireAt: args.fireAt,\n cancelKey: args.cancelKey,\n createdAt: new Date().toISOString(),\n }\n writeFileSync(this.pathFor(record.id), JSON.stringify(record, null, 2))\n return record\n }\n\n /** Cancel any timers matching the filter. Returns how many were removed. */\n cancel(filter: { runId?: string; nodeId?: string; cancelKey?: string }): number {\n let removed = 0\n for (const t of this.list()) {\n if (filter.runId && t.runId !== filter.runId) continue\n if (filter.nodeId && t.nodeId !== filter.nodeId) continue\n if (filter.cancelKey && t.cancelKey !== filter.cancelKey) continue\n try { unlinkSync(this.pathFor(t.id)); removed++ } catch { /* already gone */ }\n }\n return removed\n }\n\n list(): TimerRecord[] {\n if (!existsSync(this.baseDir)) return []\n const out: TimerRecord[] = []\n for (const entry of readdirSync(this.baseDir, { withFileTypes: true })) {\n if (!entry.isFile() || !entry.name.endsWith(\".json\")) continue\n try {\n const raw = JSON.parse(readFileSync(resolve(this.baseDir, entry.name), \"utf-8\"))\n const parsed = timerRecordSchema.safeParse(raw)\n if (parsed.success) out.push(parsed.data)\n } catch { /* skip malformed */ }\n }\n return out\n }\n\n private pathFor(id: string): string {\n return resolve(this.baseDir, `${id}.json`)\n }\n\n private tick(): void {\n if (!this.callback) return\n const now = Date.now()\n for (const t of this.list()) {\n if (this.firedIds.has(t.id)) continue\n const fireAtMs = Date.parse(t.fireAt)\n if (Number.isNaN(fireAtMs) || fireAtMs > now) continue\n // Mark fired BEFORE calling the callback so a callback that loops\n // back into the walk (and re-schedules siblings) doesn't re-trigger.\n this.firedIds.add(t.id)\n try { unlinkSync(this.pathFor(t.id)) } catch { /* may race with cancel */ }\n try {\n void Promise.resolve(this.callback(t)).catch((e: any) => this.log(`[timers] fire callback threw: ${e?.message ?? e}`))\n } catch (e: any) {\n this.log(`[timers] fire dispatch failed: ${e?.message ?? e}`)\n }\n }\n }\n}\n"],"mappings":"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","names":["existsSync","mkdirSync","readdirSync","readFileSync","unlinkSync","writeFileSync","resolve","randomUUID","z","timerRecordSchema","TimerService","opts","root","cb","args","record","filter","removed","t","out","entry","raw","parsed","id","now","fireAtMs","e"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{z as e}from"zod";var t=e.enum(["text","long-text","number","boolean","date","select","multi-select","file"]),o=e.object({min:e.number().optional(),max:e.number().optional(),pattern:e.string().optional()}).strict(),i=e.object({key:e.string().min(1).regex(/^[a-zA-Z_][a-zA-Z0-9_-]*$/,"field key must be identifier-safe"),label:e.string().min(1),type:t,required:e.boolean().default(!1),options:e.array(e.string()).optional(),hint:e.string().optional(),defaultValue:e.unknown().optional(),validate:o.optional()}),r=e.object({key:e.string().min(1),label:e.string().min(1)}),a=e.object({id:e.string().optional(),title:e.string().min(1),description:e.string().optional(),fields:e.array(i).default([]),submitLabel:e.string().default("Submit"),secondaryAction:r.optional()}),m=e.object({action:e.enum(["primary","secondary"]).default("primary"),values:e.record(e.unknown()).default({})});export{t as a,o as b,i as c,r as d,a as e,m as f};
|
|
2
|
+
//# sourceMappingURL=chunk-CDNWCYF7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/forms/types.ts"],"sourcesContent":["import { z } from \"zod\"\n\n// --- Form schema ---\n//\n// Forms are the structured-input primitive for user tasks. A form has a\n// title, a list of typed fields, and optional primary/secondary action\n// labels (for approve/reject-style flows).\n//\n// Forms can live inline on a userTask node's config OR be factored out to\n// .agentx/forms/<id>.json and referenced by id.\n//\n// Rendering happens in channel adapters (Telegram inline keyboards, Slack\n// Block Kit, WhatsApp list messages) and on the web inbox. Capability\n// negotiation is per-renderer — a date field on a channel without native\n// date input falls back to a validated text prompt.\n\nexport const formFieldTypeSchema = z.enum([\n \"text\",\n \"long-text\",\n \"number\",\n \"boolean\",\n \"date\",\n \"select\",\n \"multi-select\",\n \"file\",\n])\nexport type FormFieldType = z.infer<typeof formFieldTypeSchema>\n\nexport const formFieldValidateSchema = z.object({\n min: z.number().optional(),\n max: z.number().optional(),\n pattern: z.string().optional(),\n}).strict()\nexport type FormFieldValidate = z.infer<typeof formFieldValidateSchema>\n\nexport const formFieldSchema = z.object({\n key: z.string().min(1).regex(/^[a-zA-Z_][a-zA-Z0-9_-]*$/, \"field key must be identifier-safe\"),\n label: z.string().min(1),\n type: formFieldTypeSchema,\n required: z.boolean().default(false),\n /** Options for select / multi-select. Ignored for other types. */\n options: z.array(z.string()).optional(),\n hint: z.string().optional(),\n defaultValue: z.unknown().optional(),\n validate: formFieldValidateSchema.optional(),\n})\nexport type FormField = z.infer<typeof formFieldSchema>\n\nexport const formActionSchema = z.object({\n key: z.string().min(1),\n label: z.string().min(1),\n})\nexport type FormAction = z.infer<typeof formActionSchema>\n\nexport const formSchemaSchema = z.object({\n id: z.string().optional(),\n title: z.string().min(1),\n description: z.string().optional(),\n fields: z.array(formFieldSchema).default([]),\n /** Primary button label. Default \"Submit\". */\n submitLabel: z.string().default(\"Submit\"),\n /** Optional secondary action — used for approve/reject-style flows. */\n secondaryAction: formActionSchema.optional(),\n})\nexport type FormSchema = z.infer<typeof formSchemaSchema>\n\n// --- Submission ---\n\nexport const formSubmissionSchema = z.object({\n /** Which button the user clicked: \"primary\" (default) or \"secondary\". */\n action: z.enum([\"primary\", \"secondary\"]).default(\"primary\"),\n /** Field values keyed by field.key. Types match FormField.type. */\n values: z.record(z.unknown()).default({}),\n})\nexport type FormSubmission = z.infer<typeof formSubmissionSchema>\n"],"mappings":"AAAA,OAAS,KAAAA,MAAS,MAgBX,IAAMC,EAAsBD,EAAE,KAAK,CACxC,OACA,YACA,SACA,UACA,OACA,SACA,eACA,MACF,CAAC,EAGYE,EAA0BF,EAAE,OAAO,CAC9C,IAAKA,EAAE,OAAO,EAAE,SAAS,EACzB,IAAKA,EAAE,OAAO,EAAE,SAAS,EACzB,QAASA,EAAE,OAAO,EAAE,SAAS,CAC/B,CAAC,EAAE,OAAO,EAGGG,EAAkBH,EAAE,OAAO,CACtC,IAAKA,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,4BAA6B,mCAAmC,EAC7F,MAAOA,EAAE,OAAO,EAAE,IAAI,CAAC,EACvB,KAAMC,EACN,SAAUD,EAAE,QAAQ,EAAE,QAAQ,EAAK,EAEnC,QAASA,EAAE,MAAMA,EAAE,OAAO,CAAC,EAAE,SAAS,EACtC,KAAMA,EAAE,OAAO,EAAE,SAAS,EAC1B,aAAcA,EAAE,QAAQ,EAAE,SAAS,EACnC,SAAUE,EAAwB,SAAS,CAC7C,CAAC,EAGYE,EAAmBJ,EAAE,OAAO,CACvC,IAAKA,EAAE,OAAO,EAAE,IAAI,CAAC,EACrB,MAAOA,EAAE,OAAO,EAAE,IAAI,CAAC,CACzB,CAAC,EAGYK,EAAmBL,EAAE,OAAO,CACvC,GAAIA,EAAE,OAAO,EAAE,SAAS,EACxB,MAAOA,EAAE,OAAO,EAAE,IAAI,CAAC,EACvB,YAAaA,EAAE,OAAO,EAAE,SAAS,EACjC,OAAQA,EAAE,MAAMG,CAAe,EAAE,QAAQ,CAAC,CAAC,EAE3C,YAAaH,EAAE,OAAO,EAAE,QAAQ,QAAQ,EAExC,gBAAiBI,EAAiB,SAAS,CAC7C,CAAC,EAKYE,EAAuBN,EAAE,OAAO,CAE3C,OAAQA,EAAE,KAAK,CAAC,UAAW,WAAW,CAAC,EAAE,QAAQ,SAAS,EAE1D,OAAQA,EAAE,OAAOA,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC,CAC1C,CAAC","names":["z","formFieldTypeSchema","formFieldValidateSchema","formFieldSchema","formActionSchema","formSchemaSchema","formSubmissionSchema"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a as w,b as y,c as x}from"./chunk-SFQUP3BP.js";import{g}from"./chunk-
|
|
1
|
+
import{a as w,b as y,c as x}from"./chunk-SFQUP3BP.js";import{m as g}from"./chunk-HCFSHGK4.js";import{existsSync as d,promises as h}from"fs";import c from"path";import{execa as C}from"execa";var b={enabled:!0,maxAttempts:3,provider:"claude-code"},v=class{constructor(e,r,i){this.cwd=e;this.config={...b,...r},this.memory=i||new x(e)}config;memory;async detectAndHeal(e,r,i){if(!this.config.enabled)return{healed:!1,attempts:0,error:"Heal disabled",filesChanged:[]};let t=await this.runVerification();if(!t)return{healed:!0,attempts:0,error:"",filesChanged:[]};let n=0,s=t,a=[];for(;n<this.config.maxAttempts;){n++;let l=await this.generateFix(s,e,r,n);if(!l.files.length)break;for(let o of l.files){let m=c.isAbsolute(o.path)?o.path:c.resolve(this.cwd,o.path),p=c.dirname(m);await h.mkdir(p,{recursive:!0}),await h.writeFile(m,o.content,"utf8"),a.push(o.path)}let f=await this.runVerification();if(!f)return i&&await this.memory.recordHeal(i,t,!0,a),{healed:!0,attempts:n,error:t,fix:l.explanation,filesChanged:a};s=f}return i&&await this.memory.recordHeal(i,t,!1,a),{healed:!1,attempts:n,error:s,filesChanged:a}}async runVerification(){let e=[this.config.buildCommand,this.config.lintCommand,this.config.testCommand].filter(Boolean);if(!e.length){let r=await this.detectCommands();e.push(...r)}for(let r of e)try{let[i,...t]=r.split(" ");await C(i,t,{cwd:this.cwd,timeout:6e4,reject:!0})}catch(i){let t=i.stderr||"",n=i.stdout||"",s=i.message||"";return k(`Command failed: ${r}
|
|
2
2
|
${t}
|
|
3
3
|
${n}
|
|
4
4
|
${s}`)}return null}async detectCommands(){let e=[],r=c.resolve(this.cwd,"package.json");if(d(r))try{let t=JSON.parse(await h.readFile(r,"utf8")).scripts||{};t.build&&e.push("npm run build"),t.typecheck&&e.push("npm run typecheck"),t.lint&&e.push("npm run lint"),t.test&&e.push("npm run test")}catch{}return d(c.resolve(this.cwd,"pyproject.toml"))&&d(c.resolve(this.cwd,"conftest.py"))&&e.push("python -m pytest --tb=short -q"),e}async generateFix(e,r,i,t){let n=g(this.config.provider,this.config.apiKey),s=[];for(let o of r.slice(0,5)){let m=c.resolve(this.cwd,o);if(d(m)){let p=await h.readFile(m,"utf8");s.push(`## ${o}
|
|
@@ -29,4 +29,4 @@ Fix the error. Use create_files with the corrected file content.`}],f=await n.ge
|
|
|
29
29
|
`)+`
|
|
30
30
|
... (truncated)`:e.join(`
|
|
31
31
|
`)}export{v as a};
|
|
32
|
-
//# sourceMappingURL=chunk-
|
|
32
|
+
//# sourceMappingURL=chunk-EKTPCSN4.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime/heal.ts"],"sourcesContent":["import { existsSync, promises as fs } from \"fs\"\nimport path from \"path\"\nimport { execa } from \"execa\"\nimport { createProvider, type ProviderName } from \"@/agent/providers\"\nimport type { GenerationMessage, GeneratedFile } from \"@/agent/providers/types\"\nimport { detectTechStack, formatTechStack } from \"@/agent/context/tech-stack\"\nimport { Memory } from \"./memory\"\n\n// --- Auto-Heal Engine: detect errors, diagnose, fix, verify ---\n// When generated code fails (build error, test failure, runtime crash),\n// the heal engine reads the error, understands the context, generates\n// a fix, and re-verifies it works.\n\nexport interface HealConfig {\n enabled: boolean\n testCommand?: string\n buildCommand?: string\n lintCommand?: string\n maxAttempts: number\n provider: ProviderName\n model?: string\n apiKey?: string\n}\n\nexport interface HealResult {\n healed: boolean\n attempts: number\n error: string\n fix?: string\n filesChanged: string[]\n}\n\nconst DEFAULT_HEAL_CONFIG: HealConfig = {\n enabled: true,\n maxAttempts: 3,\n provider: \"claude-code\",\n}\n\nexport class HealEngine {\n private config: HealConfig\n private memory: Memory\n\n constructor(\n private cwd: string,\n config?: Partial<HealConfig>,\n memory?: Memory\n ) {\n this.config = { ...DEFAULT_HEAL_CONFIG, ...config }\n this.memory = memory || new Memory(cwd)\n }\n\n async detectAndHeal(\n generatedFiles: string[],\n originalTask: string,\n entryId?: string\n ): Promise<HealResult> {\n if (!this.config.enabled) {\n return { healed: false, attempts: 0, error: \"Heal disabled\", filesChanged: [] }\n }\n\n // 1. Run verification commands\n const error = await this.runVerification()\n if (!error) {\n return { healed: true, attempts: 0, error: \"\", filesChanged: [] }\n }\n\n // 2. Attempt to heal\n let attempts = 0\n let currentError = error\n const allChangedFiles: string[] = []\n\n while (attempts < this.config.maxAttempts) {\n attempts++\n\n const fix = await this.generateFix(\n currentError,\n generatedFiles,\n originalTask,\n attempts\n )\n\n if (!fix.files.length) {\n break // Agent couldn't generate a fix\n }\n\n // Apply the fix\n for (const file of fix.files) {\n const filePath = path.isAbsolute(file.path)\n ? file.path\n : path.resolve(this.cwd, file.path)\n\n const dir = path.dirname(filePath)\n await fs.mkdir(dir, { recursive: true })\n await fs.writeFile(filePath, file.content, \"utf8\")\n allChangedFiles.push(file.path)\n }\n\n // Re-verify\n const newError = await this.runVerification()\n if (!newError) {\n // Healed successfully\n if (entryId) {\n await this.memory.recordHeal(entryId, error, true, allChangedFiles)\n }\n return {\n healed: true,\n attempts,\n error,\n fix: fix.explanation,\n filesChanged: allChangedFiles,\n }\n }\n\n currentError = newError\n }\n\n // Failed to heal\n if (entryId) {\n await this.memory.recordHeal(entryId, error, false, allChangedFiles)\n }\n\n return {\n healed: false,\n attempts,\n error: currentError,\n filesChanged: allChangedFiles,\n }\n }\n\n private async runVerification(): Promise<string | null> {\n const commands = [\n this.config.buildCommand,\n this.config.lintCommand,\n this.config.testCommand,\n ].filter(Boolean) as string[]\n\n // Auto-detect commands if not configured\n if (!commands.length) {\n const detected = await this.detectCommands()\n commands.push(...detected)\n }\n\n for (const cmd of commands) {\n try {\n const [bin, ...args] = cmd.split(\" \")\n await execa(bin, args, {\n cwd: this.cwd,\n timeout: 60000,\n reject: true,\n })\n } catch (error: any) {\n const stderr = error.stderr || \"\"\n const stdout = error.stdout || \"\"\n const message = error.message || \"\"\n return truncateError(`Command failed: ${cmd}\\n${stderr}\\n${stdout}\\n${message}`)\n }\n }\n\n return null // All passed\n }\n\n private async detectCommands(): Promise<string[]> {\n const commands: string[] = []\n const pkgPath = path.resolve(this.cwd, \"package.json\")\n\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(await fs.readFile(pkgPath, \"utf8\"))\n const scripts = pkg.scripts || {}\n if (scripts.build) commands.push(\"npm run build\")\n if (scripts.typecheck) commands.push(\"npm run typecheck\")\n if (scripts.lint) commands.push(\"npm run lint\")\n if (scripts.test) commands.push(\"npm run test\")\n } catch {\n // Skip if can't read package.json\n }\n }\n\n // Python projects\n if (existsSync(path.resolve(this.cwd, \"pyproject.toml\"))) {\n if (existsSync(path.resolve(this.cwd, \"conftest.py\"))) {\n commands.push(\"python -m pytest --tb=short -q\")\n }\n }\n\n return commands\n }\n\n private async generateFix(\n error: string,\n affectedFiles: string[],\n originalTask: string,\n attempt: number\n ): Promise<{ files: GeneratedFile[]; explanation: string }> {\n const provider = createProvider(this.config.provider, this.config.apiKey)\n\n // Read affected files\n const fileContents: string[] = []\n for (const file of affectedFiles.slice(0, 5)) {\n const filePath = path.resolve(this.cwd, file)\n if (existsSync(filePath)) {\n const content = await fs.readFile(filePath, \"utf8\")\n fileContents.push(`## ${file}\\n\\`\\`\\`\\n${content.slice(0, 3000)}\\n\\`\\`\\``)\n }\n }\n\n const techStack = await detectTechStack(this.cwd)\n\n const messages: GenerationMessage[] = [\n {\n role: \"system\",\n content: `You are agentx auto-heal. You fix errors in generated code.\n\nProject tech stack: ${formatTechStack(techStack)}\n\nRULES:\n- Fix ONLY the error — do not refactor or add features\n- Output the COMPLETE fixed file content via create_files\n- Be minimal — smallest change that fixes the error\n- This is attempt ${attempt} — ${attempt > 1 ? \"previous fix attempts failed, try a different approach\" : \"analyze carefully\"}`,\n },\n {\n role: \"user\",\n content: `The following code was generated for task: \"${originalTask}\"\n\n## Error\n\\`\\`\\`\n${error}\n\\`\\`\\`\n\n## Affected Files\n${fileContents.join(\"\\n\\n\")}\n\nFix the error. Use create_files with the corrected file content.`,\n },\n ]\n\n const result = await provider.generate(messages, {\n model: this.config.model,\n maxTokens: 8192,\n })\n\n return {\n files: result.files,\n explanation: result.content,\n }\n }\n}\n\nfunction truncateError(error: string): string {\n const lines = error.split(\"\\n\").filter((l) => l.trim())\n if (lines.length > 30) {\n return lines.slice(0, 30).join(\"\\n\") + \"\\n... (truncated)\"\n }\n return lines.join(\"\\n\")\n}\n"],"mappings":"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","names":["existsSync","fs","path","execa","DEFAULT_HEAL_CONFIG","HealEngine","cwd","config","memory","Memory","generatedFiles","originalTask","entryId","error","attempts","currentError","allChangedFiles","fix","file","filePath","path","dir","fs","newError","commands","detected","cmd","bin","args","execa","stderr","stdout","message","truncateError","pkgPath","existsSync","scripts","affectedFiles","attempt","provider","createProvider","fileContents","content","techStack","detectTechStack","messages","formatTechStack","result","lines","l"]}
|
|
1
|
+
{"version":3,"sources":["../src/runtime/heal.ts"],"sourcesContent":["import { existsSync, promises as fs } from \"fs\"\nimport path from \"path\"\nimport { execa } from \"execa\"\nimport { createProvider, type ProviderName } from \"@/agent/providers\"\nimport type { GenerationMessage, GeneratedFile } from \"@/agent/providers/types\"\nimport { detectTechStack, formatTechStack } from \"@/agent/context/tech-stack\"\nimport { Memory } from \"./memory\"\n\n// --- Auto-Heal Engine: detect errors, diagnose, fix, verify ---\n// When generated code fails (build error, test failure, runtime crash),\n// the heal engine reads the error, understands the context, generates\n// a fix, and re-verifies it works.\n\nexport interface HealConfig {\n enabled: boolean\n testCommand?: string\n buildCommand?: string\n lintCommand?: string\n maxAttempts: number\n provider: ProviderName\n model?: string\n apiKey?: string\n}\n\nexport interface HealResult {\n healed: boolean\n attempts: number\n error: string\n fix?: string\n filesChanged: string[]\n}\n\nconst DEFAULT_HEAL_CONFIG: HealConfig = {\n enabled: true,\n maxAttempts: 3,\n provider: \"claude-code\",\n}\n\nexport class HealEngine {\n private config: HealConfig\n private memory: Memory\n\n constructor(\n private cwd: string,\n config?: Partial<HealConfig>,\n memory?: Memory\n ) {\n this.config = { ...DEFAULT_HEAL_CONFIG, ...config }\n this.memory = memory || new Memory(cwd)\n }\n\n async detectAndHeal(\n generatedFiles: string[],\n originalTask: string,\n entryId?: string\n ): Promise<HealResult> {\n if (!this.config.enabled) {\n return { healed: false, attempts: 0, error: \"Heal disabled\", filesChanged: [] }\n }\n\n // 1. Run verification commands\n const error = await this.runVerification()\n if (!error) {\n return { healed: true, attempts: 0, error: \"\", filesChanged: [] }\n }\n\n // 2. Attempt to heal\n let attempts = 0\n let currentError = error\n const allChangedFiles: string[] = []\n\n while (attempts < this.config.maxAttempts) {\n attempts++\n\n const fix = await this.generateFix(\n currentError,\n generatedFiles,\n originalTask,\n attempts\n )\n\n if (!fix.files.length) {\n break // Agent couldn't generate a fix\n }\n\n // Apply the fix\n for (const file of fix.files) {\n const filePath = path.isAbsolute(file.path)\n ? file.path\n : path.resolve(this.cwd, file.path)\n\n const dir = path.dirname(filePath)\n await fs.mkdir(dir, { recursive: true })\n await fs.writeFile(filePath, file.content, \"utf8\")\n allChangedFiles.push(file.path)\n }\n\n // Re-verify\n const newError = await this.runVerification()\n if (!newError) {\n // Healed successfully\n if (entryId) {\n await this.memory.recordHeal(entryId, error, true, allChangedFiles)\n }\n return {\n healed: true,\n attempts,\n error,\n fix: fix.explanation,\n filesChanged: allChangedFiles,\n }\n }\n\n currentError = newError\n }\n\n // Failed to heal\n if (entryId) {\n await this.memory.recordHeal(entryId, error, false, allChangedFiles)\n }\n\n return {\n healed: false,\n attempts,\n error: currentError,\n filesChanged: allChangedFiles,\n }\n }\n\n private async runVerification(): Promise<string | null> {\n const commands = [\n this.config.buildCommand,\n this.config.lintCommand,\n this.config.testCommand,\n ].filter(Boolean) as string[]\n\n // Auto-detect commands if not configured\n if (!commands.length) {\n const detected = await this.detectCommands()\n commands.push(...detected)\n }\n\n for (const cmd of commands) {\n try {\n const [bin, ...args] = cmd.split(\" \")\n await execa(bin, args, {\n cwd: this.cwd,\n timeout: 60000,\n reject: true,\n })\n } catch (error: any) {\n const stderr = error.stderr || \"\"\n const stdout = error.stdout || \"\"\n const message = error.message || \"\"\n return truncateError(`Command failed: ${cmd}\\n${stderr}\\n${stdout}\\n${message}`)\n }\n }\n\n return null // All passed\n }\n\n private async detectCommands(): Promise<string[]> {\n const commands: string[] = []\n const pkgPath = path.resolve(this.cwd, \"package.json\")\n\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(await fs.readFile(pkgPath, \"utf8\"))\n const scripts = pkg.scripts || {}\n if (scripts.build) commands.push(\"npm run build\")\n if (scripts.typecheck) commands.push(\"npm run typecheck\")\n if (scripts.lint) commands.push(\"npm run lint\")\n if (scripts.test) commands.push(\"npm run test\")\n } catch {\n // Skip if can't read package.json\n }\n }\n\n // Python projects\n if (existsSync(path.resolve(this.cwd, \"pyproject.toml\"))) {\n if (existsSync(path.resolve(this.cwd, \"conftest.py\"))) {\n commands.push(\"python -m pytest --tb=short -q\")\n }\n }\n\n return commands\n }\n\n private async generateFix(\n error: string,\n affectedFiles: string[],\n originalTask: string,\n attempt: number\n ): Promise<{ files: GeneratedFile[]; explanation: string }> {\n const provider = createProvider(this.config.provider, this.config.apiKey)\n\n // Read affected files\n const fileContents: string[] = []\n for (const file of affectedFiles.slice(0, 5)) {\n const filePath = path.resolve(this.cwd, file)\n if (existsSync(filePath)) {\n const content = await fs.readFile(filePath, \"utf8\")\n fileContents.push(`## ${file}\\n\\`\\`\\`\\n${content.slice(0, 3000)}\\n\\`\\`\\``)\n }\n }\n\n const techStack = await detectTechStack(this.cwd)\n\n const messages: GenerationMessage[] = [\n {\n role: \"system\",\n content: `You are agentx auto-heal. You fix errors in generated code.\n\nProject tech stack: ${formatTechStack(techStack)}\n\nRULES:\n- Fix ONLY the error — do not refactor or add features\n- Output the COMPLETE fixed file content via create_files\n- Be minimal — smallest change that fixes the error\n- This is attempt ${attempt} — ${attempt > 1 ? \"previous fix attempts failed, try a different approach\" : \"analyze carefully\"}`,\n },\n {\n role: \"user\",\n content: `The following code was generated for task: \"${originalTask}\"\n\n## Error\n\\`\\`\\`\n${error}\n\\`\\`\\`\n\n## Affected Files\n${fileContents.join(\"\\n\\n\")}\n\nFix the error. Use create_files with the corrected file content.`,\n },\n ]\n\n const result = await provider.generate(messages, {\n model: this.config.model,\n maxTokens: 8192,\n })\n\n return {\n files: result.files,\n explanation: result.content,\n }\n }\n}\n\nfunction truncateError(error: string): string {\n const lines = error.split(\"\\n\").filter((l) => l.trim())\n if (lines.length > 30) {\n return lines.slice(0, 30).join(\"\\n\") + \"\\n... (truncated)\"\n }\n return lines.join(\"\\n\")\n}\n"],"mappings":"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","names":["existsSync","fs","path","execa","DEFAULT_HEAL_CONFIG","HealEngine","cwd","config","memory","Memory","generatedFiles","originalTask","entryId","error","attempts","currentError","allChangedFiles","fix","file","filePath","path","dir","fs","newError","commands","detected","cmd","bin","args","execa","stderr","stdout","message","truncateError","pkgPath","existsSync","scripts","affectedFiles","attempt","provider","createProvider","fileContents","content","techStack","detectTechStack","messages","formatTechStack","result","lines","l"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{existsSync as i,mkdirSync as d,readdirSync as g,readFileSync as f,unlinkSync as m,writeFileSync as y}from"fs";import{resolve as a}from"path";import{z as n}from"zod";var A=n.object({channel:n.enum(["telegram","whatsapp","slack","discord","email"]),handle:n.string().min(1),preferredForTasks:n.boolean().optional()}),u=n.object({id:n.string().regex(/^actor:[a-z0-9][a-z0-9_-]*$/,"actor id must be 'actor:<slug>'"),name:n.string().min(1),email:n.string().email().optional(),channels:n.array(A).min(1,"actor must have at least one channel handle"),timezone:n.string().optional(),created:n.string().optional(),updated:n.string().optional()}),b=n.union([n.object({actor:n.string()}),n.object({role:n.string()})]),R=n.enum(["first-available","round-robin","all","manager-of"]),h=n.object({id:n.string().regex(/^role:[a-z0-9][a-z0-9_-]*$/,"role id must be 'role:<slug>'"),name:n.string().min(1),members:n.array(b).default([]),assignmentStrategy:R.default("first-available"),rotationCursor:n.number().int().default(0),created:n.string().optional(),updated:n.string().optional()});function D(c){return c.startsWith("actor:")?{kind:"actor",id:c}:c.startsWith("role:")?{kind:"role",id:c}:null}var S=class{actorsDir;rolesDir;constructor(t={}){let e=t.baseDir??a(process.cwd(),".agentx");this.actorsDir=a(e,"actors"),this.rolesDir=a(e,"roles"),d(this.actorsDir,{recursive:!0}),d(this.rolesDir,{recursive:!0})}actorPath(t){let e=t.replace(/^actor:/,"");return a(this.actorsDir,`${e}.json`)}listActors(){if(!i(this.actorsDir))return[];let t=[];for(let e of g(this.actorsDir,{withFileTypes:!0}))if(!(!e.isFile()||!e.name.endsWith(".json")))try{let r=JSON.parse(f(a(this.actorsDir,e.name),"utf-8")),s=u.safeParse(r);s.success&&t.push(s.data)}catch{}return t.sort((e,r)=>e.id.localeCompare(r.id))}getActor(t){let e=this.actorPath(t);if(!i(e))return null;try{let r=JSON.parse(f(e,"utf-8")),s=u.safeParse(r);return s.success?s.data:null}catch{return null}}saveActor(t){let e=u.parse({...t,created:t.created??new Date().toISOString(),updated:new Date().toISOString()});return y(this.actorPath(e.id),JSON.stringify(e,null,2)),e}deleteActor(t){let e=this.actorPath(t);return i(e)?(m(e),!0):!1}rolePath(t){let e=t.replace(/^role:/,"");return a(this.rolesDir,`${e}.json`)}listRoles(){if(!i(this.rolesDir))return[];let t=[];for(let e of g(this.rolesDir,{withFileTypes:!0}))if(!(!e.isFile()||!e.name.endsWith(".json")))try{let r=JSON.parse(f(a(this.rolesDir,e.name),"utf-8")),s=h.safeParse(r);s.success&&t.push(s.data)}catch{}return t.sort((e,r)=>e.id.localeCompare(r.id))}getRole(t){let e=this.rolePath(t);if(!i(e))return null;try{let r=JSON.parse(f(e,"utf-8")),s=h.safeParse(r);return s.success?s.data:null}catch{return null}}saveRole(t){let e=h.parse({...t,created:t.created??new Date().toISOString(),updated:new Date().toISOString()});return y(this.rolePath(e.id),JSON.stringify(e,null,2)),e}deleteRole(t){let e=this.rolePath(t);return i(e)?(m(e),!0):!1}resolveMembers(t){if(t.kind==="actor")return[t.id];let e=new Set,r=[],s=o=>{if(e.has(o))return;e.add(o);let p=this.getRole(o);if(p)for(let l of p.members)"actor"in l?r.includes(l.actor)||r.push(l.actor):s(l.role)};return s(t.id),r}pickAssignees(t){let e=this.resolveMembers(t);if(e.length===0)return[];if(t.kind==="actor")return e;let r=this.getRole(t.id);if(!r)return e.slice(0,1);switch(r.assignmentStrategy){case"all":return e;case"round-robin":{let s=r.rotationCursor%e.length,o=e[s];return this.saveRole({...r,rotationCursor:(r.rotationCursor+1)%Math.max(e.length,1)}),[o]}case"first-available":case"manager-of":default:return[e[0]]}}channelFor(t,e){let r=this.getActor(t);return r?r.channels.find(o=>o.channel===e)?.handle??null:null}preferredChannel(t){let e=this.getActor(t);return!e||e.channels.length===0?null:e.channels.find(r=>r.preferredForTasks)??e.channels[0]}};export{D as a,S as b};
|
|
2
|
+
//# sourceMappingURL=chunk-EW27SCCC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/actors/store.ts","../src/actors/types.ts"],"sourcesContent":["import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from \"fs\"\nimport { resolve } from \"path\"\nimport {\n actorSchema,\n roleSchema,\n type Actor,\n type ActorChannel,\n type Role,\n type AssigneeRef,\n} from \"./types\"\n\n// --- Actor / Role filesystem store ---\n//\n// Two sibling directories under .agentx/:\n// actors/<actor-id>.json\n// roles/<role-id>.json\n//\n// The store exposes CRUD + two resolution helpers needed by the workflow\n// engine:\n// resolveMembers(assignee) — role or actor ref → flat actor id list\n// (handles nested roles, strategy picks who sees\n// the task)\n// channelFor(actorId, ch) — actor id + preferred channel → handle string\n\nexport interface ActorStoreOptions {\n baseDir?: string\n}\n\nexport class ActorStore {\n readonly actorsDir: string\n readonly rolesDir: string\n\n constructor(opts: ActorStoreOptions = {}) {\n const root = opts.baseDir ?? resolve(process.cwd(), \".agentx\")\n this.actorsDir = resolve(root, \"actors\")\n this.rolesDir = resolve(root, \"roles\")\n mkdirSync(this.actorsDir, { recursive: true })\n mkdirSync(this.rolesDir, { recursive: true })\n }\n\n // --- Actors ---\n\n private actorPath(id: string): string {\n const safe = id.replace(/^actor:/, \"\")\n return resolve(this.actorsDir, `${safe}.json`)\n }\n\n listActors(): Actor[] {\n if (!existsSync(this.actorsDir)) return []\n const out: Actor[] = []\n for (const entry of readdirSync(this.actorsDir, { withFileTypes: true })) {\n if (!entry.isFile() || !entry.name.endsWith(\".json\")) continue\n try {\n const raw = JSON.parse(readFileSync(resolve(this.actorsDir, entry.name), \"utf-8\"))\n const parsed = actorSchema.safeParse(raw)\n if (parsed.success) out.push(parsed.data)\n } catch {\n // skip malformed\n }\n }\n return out.sort((a, b) => a.id.localeCompare(b.id))\n }\n\n getActor(id: string): Actor | null {\n const full = this.actorPath(id)\n if (!existsSync(full)) return null\n try {\n const raw = JSON.parse(readFileSync(full, \"utf-8\"))\n const parsed = actorSchema.safeParse(raw)\n return parsed.success ? parsed.data : null\n } catch {\n return null\n }\n }\n\n saveActor(actor: Actor): Actor {\n const parsed = actorSchema.parse({\n ...actor,\n created: actor.created ?? new Date().toISOString(),\n updated: new Date().toISOString(),\n })\n writeFileSync(this.actorPath(parsed.id), JSON.stringify(parsed, null, 2))\n return parsed\n }\n\n deleteActor(id: string): boolean {\n const full = this.actorPath(id)\n if (!existsSync(full)) return false\n unlinkSync(full)\n return true\n }\n\n // --- Roles ---\n\n private rolePath(id: string): string {\n const safe = id.replace(/^role:/, \"\")\n return resolve(this.rolesDir, `${safe}.json`)\n }\n\n listRoles(): Role[] {\n if (!existsSync(this.rolesDir)) return []\n const out: Role[] = []\n for (const entry of readdirSync(this.rolesDir, { withFileTypes: true })) {\n if (!entry.isFile() || !entry.name.endsWith(\".json\")) continue\n try {\n const raw = JSON.parse(readFileSync(resolve(this.rolesDir, entry.name), \"utf-8\"))\n const parsed = roleSchema.safeParse(raw)\n if (parsed.success) out.push(parsed.data)\n } catch {\n // skip\n }\n }\n return out.sort((a, b) => a.id.localeCompare(b.id))\n }\n\n getRole(id: string): Role | null {\n const full = this.rolePath(id)\n if (!existsSync(full)) return null\n try {\n const raw = JSON.parse(readFileSync(full, \"utf-8\"))\n const parsed = roleSchema.safeParse(raw)\n return parsed.success ? parsed.data : null\n } catch {\n return null\n }\n }\n\n saveRole(role: Role): Role {\n const parsed = roleSchema.parse({\n ...role,\n created: role.created ?? new Date().toISOString(),\n updated: new Date().toISOString(),\n })\n writeFileSync(this.rolePath(parsed.id), JSON.stringify(parsed, null, 2))\n return parsed\n }\n\n deleteRole(id: string): boolean {\n const full = this.rolePath(id)\n if (!existsSync(full)) return false\n unlinkSync(full)\n return true\n }\n\n // --- Resolution ---\n\n /** Flatten a role (or actor) reference to a concrete list of actor ids.\n * Handles nested roles (role containing role). Cycles are guarded. */\n resolveMembers(assignee: AssigneeRef): string[] {\n if (assignee.kind === \"actor\") return [assignee.id]\n const seen = new Set<string>()\n const out: string[] = []\n const walk = (roleId: string) => {\n if (seen.has(roleId)) return\n seen.add(roleId)\n const role = this.getRole(roleId)\n if (!role) return\n for (const member of role.members) {\n if (\"actor\" in member) {\n if (!out.includes(member.actor)) out.push(member.actor)\n } else {\n walk(member.role)\n }\n }\n }\n walk(assignee.id)\n return out\n }\n\n /** Pick which members of an assignee actually see a task right now,\n * obeying the role's assignment strategy. Returns one or more actor ids. */\n pickAssignees(assignee: AssigneeRef): string[] {\n const members = this.resolveMembers(assignee)\n if (members.length === 0) return []\n if (assignee.kind === \"actor\") return members\n const role = this.getRole(assignee.id)\n if (!role) return members.slice(0, 1)\n switch (role.assignmentStrategy) {\n case \"all\":\n return members\n case \"round-robin\": {\n const idx = role.rotationCursor % members.length\n const picked = members[idx]\n this.saveRole({ ...role, rotationCursor: (role.rotationCursor + 1) % Math.max(members.length, 1) })\n return [picked]\n }\n case \"first-available\":\n case \"manager-of\":\n default:\n return [members[0]]\n }\n }\n\n /** Look up an actor's handle for a given channel. Returns null if the\n * actor has no handle on that channel. */\n channelFor(actorId: string, channel: ActorChannel[\"channel\"]): string | null {\n const actor = this.getActor(actorId)\n if (!actor) return null\n const match = actor.channels.find((c) => c.channel === channel)\n return match?.handle ?? null\n }\n\n /** Pick the delivery channel for user-task notifications. Prefers the\n * channel marked preferredForTasks, else the first channel. */\n preferredChannel(actorId: string): ActorChannel | null {\n const actor = this.getActor(actorId)\n if (!actor || actor.channels.length === 0) return null\n return actor.channels.find((c) => c.preferredForTasks) ?? actor.channels[0]\n }\n}\n","import { z } from \"zod\"\n\n// --- Actor + Role primitives ---\n//\n// An Actor is a human participant in a business activity. They carry one or\n// more channel handles (a Telegram user id, a WhatsApp phone, a Slack member\n// id, an email address) so the workflow engine can deliver user tasks to\n// wherever they actually read messages.\n//\n// A Role is a named group of Actors (or nested Roles). User tasks may be\n// assigned to a Role instead of a specific Actor — the engine uses the Role's\n// assignmentStrategy to pick which member(s) actually see the task.\n//\n// Both are stored as JSON under .agentx/actors/ and .agentx/roles/.\n\nexport const actorChannelSchema = z.object({\n channel: z.enum([\"telegram\", \"whatsapp\", \"slack\", \"discord\", \"email\"]),\n /** Channel-native handle: telegram user id, phone number (E.164), slack\n * member id, discord user id, email address. Stable per channel. */\n handle: z.string().min(1),\n /** If true, user-task notifications for this actor land on this channel\n * first. At most one channel should be marked preferred; ties break in\n * array order. */\n preferredForTasks: z.boolean().optional(),\n})\nexport type ActorChannel = z.infer<typeof actorChannelSchema>\n\nexport const actorSchema = z.object({\n id: z.string().regex(/^actor:[a-z0-9][a-z0-9_-]*$/, \"actor id must be 'actor:<slug>'\"),\n name: z.string().min(1),\n email: z.string().email().optional(),\n channels: z.array(actorChannelSchema).min(1, \"actor must have at least one channel handle\"),\n timezone: z.string().optional(),\n created: z.string().optional(),\n updated: z.string().optional(),\n})\nexport type Actor = z.infer<typeof actorSchema>\n\nexport const roleMemberSchema = z.union([\n z.object({ actor: z.string() }),\n z.object({ role: z.string() }),\n])\nexport type RoleMember = z.infer<typeof roleMemberSchema>\n\nexport const assignmentStrategySchema = z.enum([\n \"first-available\", // first member in order\n \"round-robin\", // rotate across members (state persisted on the role)\n \"all\", // assign to every member simultaneously\n \"manager-of\", // resolve via actor.managerOf relationship (future)\n])\nexport type AssignmentStrategy = z.infer<typeof assignmentStrategySchema>\n\nexport const roleSchema = z.object({\n id: z.string().regex(/^role:[a-z0-9][a-z0-9_-]*$/, \"role id must be 'role:<slug>'\"),\n name: z.string().min(1),\n members: z.array(roleMemberSchema).default([]),\n assignmentStrategy: assignmentStrategySchema.default(\"first-available\"),\n /** Mutable rotation cursor for round-robin. Incremented on assign. */\n rotationCursor: z.number().int().default(0),\n created: z.string().optional(),\n updated: z.string().optional(),\n})\nexport type Role = z.infer<typeof roleSchema>\n\n// Reference shape used in workflow node configs: either an actor id or a role\n// id. Encoded as a string with the typed prefix so templating stays simple.\nexport type ActorRef = { kind: \"actor\"; id: string }\nexport type RoleRef = { kind: \"role\"; id: string }\nexport type AssigneeRef = ActorRef | RoleRef\n\nexport function parseAssigneeRef(raw: string): AssigneeRef | null {\n if (raw.startsWith(\"actor:\")) return { kind: \"actor\", id: raw }\n if (raw.startsWith(\"role:\")) return { kind: \"role\", id: raw }\n return null\n}\n"],"mappings":"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","names":["existsSync","mkdirSync","readdirSync","readFileSync","unlinkSync","writeFileSync","resolve","z","actorChannelSchema","actorSchema","roleMemberSchema","assignmentStrategySchema","roleSchema","parseAssigneeRef","raw","ActorStore","opts","root","resolve","mkdirSync","id","safe","existsSync","out","entry","readdirSync","raw","readFileSync","parsed","actorSchema","a","b","full","actor","writeFileSync","unlinkSync","roleSchema","role","assignee","seen","walk","roleId","member","members","idx","picked","actorId","channel","c"]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import{existsSync as Y,readFileSync as X,writeFileSync as te,mkdirSync as ne}from"fs";import P from"path";import Q from"os";import U from"chalk";import j from"prompts";import q from"chalk";var C={error(...r){console.log(q.red(...r))},warn(...r){console.log(q.yellow(...r))},info(...r){console.log(q.cyan(...r))},success(...r){console.log(q.green(...r))},break(){console.log("")}};var H=P.join(Q.homedir(),".agentx"),J=P.join(H,"auth.json");function R(){if(!Y(J))return null;try{let r=JSON.parse(X(J,"utf8"));return r.provider&&r.authType&&r.token&&r.model?r:null}catch{return null}}function oe(r){Y(H)||ne(H,{recursive:!0}),te(J,JSON.stringify(r,null,2),"utf8")}function N(r){if(r?.trim()){let y=r.startsWith("sk-ant-oat")?"oauth":"api-key";return{token:r.trim(),authType:y}}let e=process.env.ANTHROPIC_OAUTH_TOKEN;if(e?.trim())return{token:e.trim(),authType:"oauth"};let t=process.env.CLAUDE_CODE_OAUTH_TOKEN;if(t?.trim())return{token:t.trim(),authType:"oauth"};let n=process.env.ANTHROPIC_API_KEY;if(n?.trim())return{token:n.trim(),authType:"api-key"};let o=R();if(o)return{token:o.token,authType:o.authType};let s=Q.homedir(),u=[P.join(s,".openclaw","agents","main","agent","auth-profiles.json"),P.join(s,".openclaw","auth-profiles.json"),P.join(s,".openclaw","credentials","oauth.json"),P.join(s,".claude","oauth.json"),P.join(s,".config","claude","oauth.json"),P.join(s,".config","anthropic","oauth.json")];for(let y of u){let g=re(y);if(g)return{token:g,authType:"oauth"}}return null}function re(r){if(!Y(r))return"";try{let e=JSON.parse(X(r,"utf8"));if(e.profiles){let t="";for(let[n,o]of Object.entries(e.profiles))if(n.startsWith("anthropic")){if(o.type==="oauth"&&o.access){if(o.expires&&Date.now()>o.expires)continue;return o.access}o.type==="token"&&o.token&&(t=o.token)}if(t)return t}return e.anthropic?.access?e.anthropic.access:e.anthropic?.token?e.anthropic.token:""}catch{return""}}var B=[{id:"claude-sonnet-4-20250514",label:"anthropic/claude-sonnet-4",hint:"Claude Sonnet 4 \xB7 ctx 200k \xB7 recommended"},{id:"claude-opus-4-20250514",label:"anthropic/claude-opus-4-5",hint:"Claude Opus 4.5 \xB7 ctx 200k \xB7 reasoning"},{id:"claude-haiku-4-20250514",label:"anthropic/claude-haiku-4",hint:"Claude Haiku 4 \xB7 ctx 200k \xB7 fast"}];async function se(){let r=process.env.ANTHROPIC_API_KEY;if(r?.trim()){let n=r.slice(0,10)+"..."+r.slice(-4),{useExisting:o}=await j({type:"confirm",name:"useExisting",message:`Use existing ANTHROPIC_API_KEY (env, ${n})?`,initial:!0});if(o)return{token:r.trim(),authType:"api-key"}}let e=R();if(e?.authType==="api-key"&&e.token){let n=e.token.slice(0,10)+"..."+e.token.slice(-4),{useExisting:o}=await j({type:"confirm",name:"useExisting",message:`Use stored API key (${n})?`,initial:!0});if(o)return{token:e.token,authType:"api-key"}}let{apiKey:t}=await j({type:"password",name:"apiKey",message:"Enter Anthropic API key",validate:n=>n.startsWith("sk-ant-api")?n.length<40?"API key seems too short":!0:"API key must start with sk-ant-api"});return t?{token:t,authType:"api-key"}:null}async function ie(){C.break();let r=46,e="Run `claude setup-token` in your terminal.",t="Then paste the generated token below.";console.log(` ${U.cyan("Anthropic setup-token")} ${"\u2500".repeat(r-21-1)}\u256E`),console.log(` ${" ".repeat(r)}\u2502`),console.log(` ${e}${" ".repeat(r-e.length)}\u2502`),console.log(` ${t}${" ".repeat(r-t.length)}\u2502`),console.log(` ${" ".repeat(r)}\u2502`),console.log(` ${"\u2500".repeat(r)}\u256F`),C.break();let{token:n}=await j({type:"password",name:"token",message:"Paste Anthropic setup-token",validate:s=>s.trim()?s.length<40?"Token seems too short":!0:"Token is required"});if(!n)return null;let{tokenName:o}=await j({type:"text",name:"tokenName",message:"Token name (blank = default)"});return{token:n,authType:"oauth"}}async function ae(){for(;;){let{provider:r}=await j({type:"select",name:"provider",message:"Model/auth provider",choices:[{title:"Anthropic",value:"anthropic",description:"setup-token + API key"}]});if(r===void 0)return null;let{method:e}=await j({type:"select",name:"method",message:"Anthropic auth method",choices:[{title:"Anthropic token (paste setup-token)",value:"setup-token",description:"run `claude setup-token` elsewhere, then paste the token here"},{title:"Anthropic API key",value:"api-key"},{title:U.dim("Back"),value:"back"}]});if(e===void 0)return null;if(e!=="back")return e==="api-key"?se():e==="setup-token"?ie():null}}async function le(){let r=R(),e=B.map(n=>({title:n.label,value:n.id,description:n.hint+(r?.model===n.id?" \xB7 current":"")}));if(r?.model){let n=B.find(s=>s.id===r.model),o=n?n.label:r.model;e.unshift({title:`Keep current (${o})`,value:r.model,description:"no change"})}let{modelId:t}=await j({type:"select",name:"modelId",message:"Default model",choices:e});return t||null}async function ce(){let r=await ae();if(!r)return null;let e=await le();if(!e)return null;let t={provider:r.authType==="oauth"?"claude-code":"claude",authType:r.authType,token:r.token,model:e};return oe(t),C.break(),C.success("Configuration saved to ~/.agentx/auth.json"),console.log(` Provider: ${U.bold("anthropic")} (${t.authType})`),console.log(` Model: ${U.bold(B.find(n=>n.id===t.model)?.label||t.model)}`),t}async function _e(r){return N(r)?!0:(C.warn("No AI credentials configured."),C.break(),await ce()!==null)}var L=[{name:"create_files",description:"Create one or more files as output. Use this when you need to generate code, documents, configs, or any file-based output.",input_schema:{type:"object",properties:{files:{type:"array",items:{type:"object",properties:{path:{type:"string",description:"Relative file path from project root (e.g., src/components/Button.tsx)"},content:{type:"string",description:"The full content of the file"},language:{type:"string",description:"Programming language or file type"},description:{type:"string",description:"Brief description of what this file does"}},required:["path","content"]}},summary:{type:"string",description:"Brief summary of all generated files"}},required:["files"]},permission:"file-write"},{name:"ask_user",description:"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.",input_schema:{type:"object",properties:{question:{type:"string",description:"The question to ask the user"},options:{type:"array",items:{type:"string"},description:"Optional list of choices for the user"}},required:["question"]},permission:"none"},{name:"read_file",description:"Read the contents of a file. Use this to inspect existing code, understand patterns, check implementations, or gather context before generating code.",input_schema:{type:"object",properties:{path:{type:"string",description:"Relative file path from project root"},max_lines:{type:"number",description:"Maximum number of lines to read. Defaults to 500. Use for large files."}},required:["path"]},permission:"file-read"},{name:"search_files",description:"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.",input_schema:{type:"object",properties:{pattern:{type:"string",description:"Glob pattern to match files (e.g., 'src/**/*.ts', '*.json')"},content_regex:{type:"string",description:"Optional regex to search within matched files. Returns matching lines."},max_results:{type:"number",description:"Maximum number of results to return. Default: 50."}},required:["pattern"]},permission:"none"},{name:"list_directory",description:"List files and directories at a given path. Use this to explore project structure.",input_schema:{type:"object",properties:{path:{type:"string",description:"Relative directory path from project root. Defaults to '.' (root)."},recursive:{type:"boolean",description:"List recursively. Default: false."},max_depth:{type:"number",description:"Maximum depth for recursive listing. Default: 3."}}},permission:"none"},{name:"run_command",description:"Execute a shell command. Use this to run build tools, test commands, linters, or inspect the environment. Commands run in the project root directory.",input_schema:{type:"object",properties:{command:{type:"string",description:"The shell command to execute"},timeout:{type:"number",description:"Timeout in milliseconds. Default: 30000 (30 seconds)."}},required:["command"]},permission:"command"},{name:"edit_file",description:"Apply search-and-replace edits to an existing file. Use this for targeted modifications to existing code rather than rewriting entire files.",input_schema:{type:"object",properties:{path:{type:"string",description:"Relative file path from project root"},edits:{type:"array",items:{type:"object",properties:{old_text:{type:"string",description:"The exact text to find in the file"},new_text:{type:"string",description:"The replacement text"}},required:["old_text","new_text"]},description:"List of search/replace pairs to apply in order"}},required:["path","edits"]},permission:"file-write"},{name:"spawn_agent",description:"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.",input_schema:{type:"object",properties:{agent_id:{type:"string",description:"The ID of the agent to spawn (must be a registered agent)"},prompt:{type:"string",description:"The task description for the sub-agent"},timeout_seconds:{type:"number",description:"Maximum time to wait for completion in seconds. Default: 300 (5 minutes)."}},required:["agent_id","prompt"]},permission:"none"}];function pe(r){return(r?L.filter(t=>r.includes(t.name)):L).map(({name:t,description:n,input_schema:o})=>({name:t,description:n,input_schema:o}))}function I(){return pe(["create_files","ask_user"])}function xe(){let r=L.filter(t=>t.name!=="create_files"&&t.name!=="ask_user");if(r.length===0)return"";let e=["# Available Capabilities","In addition to generating files, you have the following capabilities:",""];for(let t of r)e.push(`## ${t.name}`),e.push(t.description),e.push("");return e.join(`
|
|
2
|
+
`)}var Ae=L.map(r=>r.name);var V="claude-sonnet-4-20250514",W=8192,M=class{name="claude";apiKey;authType="api-key";constructor(e){if(this.apiKey=e||process.env.ANTHROPIC_API_KEY||"",!this.apiKey){let t=N();t&&(this.apiKey=t.token,this.authType=t.authType)}if(!this.apiKey)throw new Error("Anthropic API key required. Run `agentx model` to configure, set ANTHROPIC_API_KEY, or pass --api-key.")}async generate(e,t){let n=t?.model||V,o=t?.maxTokens||W,s=e.find(c=>c.role==="system"),u=e.filter(c=>c.role!=="system").map(c=>({role:c.role,content:c.content})),y={model:n,max_tokens:o,messages:u,tools:I()};s&&(y.system=s.content),t?.temperature!==void 0&&(y.temperature=t.temperature);let g=await this.callApi(y);return this.parseResponse(g)}async generateRaw(e,t,n,o){let s=o?.model||V,u=o?.maxTokens||W,y={model:s,max_tokens:u,system:t,messages:e,tools:n};o?.temperature!==void 0&&(y.temperature=o.temperature);let g=await this.callApi(y);return{content:g.content.map(d=>d.type==="text"?{type:"text",text:d.text||""}:d.type==="tool_use"?{type:"tool_use",id:d.id||"",name:d.name||"",input:d.input||{}}:{type:"text",text:""}),stop_reason:g.stop_reason,usage:g.usage}}async*stream(e,t){let n=t?.model||V,o=t?.maxTokens||W,s=e.find(f=>f.role==="system"),u=e.filter(f=>f.role!=="system").map(f=>({role:f.role,content:f.content})),y={model:n,max_tokens:o,messages:u,stream:!0,tools:I()};s&&(y.system=s.content);let g=this.buildHeaders(),c=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:g,body:JSON.stringify(y)});if(!c.ok){let f=await c.text();yield{type:"error",error:`Anthropic API error (${c.status}): ${f}`};return}let d=c.body?.getReader();if(!d){yield{type:"error",error:"No response body"};return}let m=new TextDecoder,l="",_=[],v="",w,A=0,i=null,k=f=>{let O=(f||"").trim();if(!O||O.includes("```"))return;let T=O.replace(/\r\n/g,`
|
|
3
|
+
`).split(`
|
|
4
|
+
`),x=T.slice(Math.max(0,T.length-20)),p=x.join(`
|
|
5
|
+
`);if(/\b(plan|proposal)\b/i.test(p)&&/(ready for your review|review (the )?plan|requesting plan approval|awaiting approval|waiting for (your )?approval|approve (the )?plan|approval to proceed)/i.test(p))return`The provider is requesting plan approval.
|
|
6
|
+
Reply with:
|
|
7
|
+
- approve
|
|
8
|
+
- revise: <what to change>
|
|
9
|
+
- cancel`;let h=b=>/^(question|clarification|clarify|i need|need more|before i proceed|to proceed|please (confirm|clarify)|which|what|where|when|how|do you)/i.test(b.trim()),D=-1;for(let b=x.length-1;b>=0;b--){let E=x[b].trim();if(E&&(h(E)||E.includes("?"))){D=b;break}}if(D===-1)return;let S=[];for(let b=D;b<x.length&&S.length<8;b++){let E=x[b],K=E.trim();if(S.length>0&&!K||S.length>0&&!/^(options?:|[-*]\s|\d+[\).]\s)/i.test(K)&&!K.includes("?"))break;S.push(E.trimEnd())}let $=S.join(`
|
|
10
|
+
`).trim();if(!($.length<5))return $.length>800?$.slice(0,800).trimEnd():$};try{for(;;){let{done:f,value:O}=await d.read();if(f)break;l+=m.decode(O,{stream:!0});let T=l.split(`
|
|
11
|
+
`);l=T.pop()||"";for(let x of T){if(!x.startsWith("data: "))continue;let p=x.slice(6).trim();if(p!=="[DONE]")try{let a=JSON.parse(p);if(a.type==="content_block_start"&&a.content_block?.type==="tool_use"){if(i){yield{type:"tool_use_end",name:i.name};try{let h=JSON.parse(i.json||"{}");i.name==="create_files"&&h&&(Array.isArray(h.files)&&_.push(...h.files),typeof h.summary=="string"&&h.summary.trim()&&(v+=`
|
|
12
|
+
${h.summary}`)),i.name==="ask_user"&&h&&typeof h.question=="string"&&h.question.trim()&&(w=h.question,Array.isArray(h.options)&&h.options.length&&(w+=`
|
|
13
|
+
Options: ${h.options.join(", ")}`))}catch{}finally{i=null}}i={name:a.content_block.name,id:a.content_block.id,json:""},yield{type:"tool_use_start",name:a.content_block.name,id:a.content_block.id}}if(a.type==="content_block_delta"&&(a.delta?.type==="text_delta"&&(v+=a.delta.text,yield{type:"text_delta",text:a.delta.text}),a.delta?.type==="input_json_delta"&&(i&&(i.json+=a.delta.partial_json||""),yield{type:"tool_use_delta",json:a.delta.partial_json})),a.type==="content_block_stop"&&i){yield{type:"tool_use_end",name:i.name};try{let h=JSON.parse(i.json||"{}");i.name==="create_files"&&h&&(Array.isArray(h.files)&&_.push(...h.files),typeof h.summary=="string"&&h.summary.trim()&&(v+=`
|
|
14
|
+
${h.summary}`)),i.name==="ask_user"&&h&&typeof h.question=="string"&&h.question.trim()&&(w=h.question,Array.isArray(h.options)&&h.options.length&&(w+=`
|
|
15
|
+
Options: ${h.options.join(", ")}`))}catch{}finally{i=null}}a.type==="message_delta"&&a.usage&&(A=(a.usage.input_tokens||0)+(a.usage.output_tokens||0)),a.type}catch{}}}}finally{d.releaseLock()}if(i){yield{type:"tool_use_end",name:i.name};try{let f=JSON.parse(i.json||"{}");i.name==="create_files"&&f&&(Array.isArray(f.files)&&_.push(...f.files),typeof f.summary=="string"&&f.summary.trim()&&(v+=`
|
|
16
|
+
${f.summary}`)),i.name==="ask_user"&&f&&typeof f.question=="string"&&f.question.trim()&&(w=f.question,Array.isArray(f.options)&&f.options.length&&(w+=`
|
|
17
|
+
Options: ${f.options.join(", ")}`))}catch{}i=null}!w&&_.length===0&&(w=k(v)),yield{type:"done",result:{content:v,files:_,followUp:w,tokensUsed:A}}}buildHeaders(){let e={"Content-Type":"application/json","anthropic-version":"2023-06-01"};return this.authType==="oauth"?(e.Authorization=`Bearer ${this.apiKey}`,e["anthropic-beta"]="claude-code-20250219,oauth-2025-04-20",e["user-agent"]="claude-cli/2.1.2 (external, cli)",e["x-app"]="cli",e["anthropic-dangerous-direct-browser-access"]="true"):e["x-api-key"]=this.apiKey,e}async callApi(e){let t=this.buildHeaders(),n=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:t,body:JSON.stringify(e)});if(!n.ok){let o=await n.text();throw new Error(`Anthropic API error (${n.status}): ${o}`)}return await n.json()}parseResponse(e){let t=[],n="",o;for(let s of e.content)if(s.type==="text"&&(n+=s.text||""),s.type==="tool_use"){if(s.name==="create_files"&&s.input){let u=s.input;t.push(...u.files||[]),u.summary&&(n+=`
|
|
18
|
+
${u.summary}`)}if(s.name==="ask_user"&&s.input){let u=s.input;o=u.question,u.options?.length&&(o+=`
|
|
19
|
+
Options: ${u.options.join(", ")}`)}}return{content:n,files:t,followUp:o,tokensUsed:e.usage.input_tokens+e.usage.output_tokens}}};import{execa as Z}from"execa";var F="claude-sonnet-4-20250514",z=8192,ee={"claude-sonnet-4-20250514":"sonnet","claude-opus-4-20250514":"opus","claude-haiku-4-20250514":"haiku"},G=class{name="claude-code";credential;constructor(){let e=R();if(e){this.credential={type:e.authType,token:e.token};return}let t=N();if(!t)throw new Error(`No Claude credentials found.
|
|
20
|
+
Options:
|
|
21
|
+
1. Run \`agentx model\` to configure credentials
|
|
22
|
+
2. Set ANTHROPIC_API_KEY environment variable
|
|
23
|
+
3. Set ANTHROPIC_OAUTH_TOKEN environment variable`);this.credential={type:t.authType,token:t.token}}async generate(e,t){return this.credential.type==="oauth"?this.generateViaCli(e,t):this.generateViaApi(e,t)}async generateRaw(e,t,n,o){if(this.credential.type==="oauth")throw new Error("generateRaw() not available for OAuth/CLI mode");let s=o?.model||F,u=o?.maxTokens||z,y={model:s,max_tokens:u,system:t,messages:e,tools:n};o?.temperature!==void 0&&(y.temperature=o.temperature);let g={"Content-Type":"application/json","anthropic-version":"2023-06-01","x-api-key":this.credential.token},c=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:g,body:JSON.stringify(y)});if(!c.ok){let l=await c.text();throw new Error(`Anthropic API error (${c.status}): ${l}`)}let d=await c.json();return{content:d.content.map(l=>l.type==="text"?{type:"text",text:l.text||""}:l.type==="tool_use"?{type:"tool_use",id:l.id||"",name:l.name||"",input:l.input||{}}:{type:"text",text:""}),stop_reason:d.stop_reason,usage:d.usage}}get supportsAgenticLoop(){return this.credential.type==="api-key"}async generateViaCli(e,t){let n=t?.model||R()?.model||F,o=ee[n]||n,s=e.find(l=>l.role==="system"),y=e.filter(l=>l.role==="user").map(l=>l.content).join(`
|
|
24
|
+
|
|
25
|
+
`),g=["-p","--output-format","json","--model",o,"--dangerously-skip-permissions"];s&&g.push("--append-system-prompt",s.content),g.push(y);let c={...process.env};delete c.ANTHROPIC_API_KEY,delete c.ANTHROPIC_API_KEY_OLD;let d=await Z("claude",g,{env:c,extendEnv:!1,reject:!1,timeout:3e5,stdin:"ignore"});if(d.exitCode!==0){let l=d.stderr||d.stdout||"Claude CLI failed";throw new Error(`Claude CLI error: ${l}`)}let m=d.stdout.trim();try{let l=JSON.parse(m);if(l.is_error&&l.result)throw new Error(l.result)}catch(l){if(l.message&&!l.message.includes("JSON"))throw l}return this.parseCliOutput(m)}async generateViaApi(e,t){let n=t?.model||F,o=t?.maxTokens||z,s=e.find(m=>m.role==="system"),u=e.filter(m=>m.role!=="system").map(m=>({role:m.role,content:m.content})),y={model:n,max_tokens:o,messages:u,tools:I()};s&&(y.system=s.content),t?.temperature!==void 0&&(y.temperature=t.temperature);let g={"Content-Type":"application/json","anthropic-version":"2023-06-01","x-api-key":this.credential.token},c=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:g,body:JSON.stringify(y)});if(!c.ok){let m=await c.text();throw new Error(`Anthropic API error (${c.status}): ${m}`)}let d=await c.json();return this.parseApiResponse(d)}async*stream(e,t){this.credential.type==="oauth"?yield*this.streamViaCli(e,t):yield*this.streamViaApi(e,t)}async*streamViaCli(e,t){let n=t?.model||R()?.model||F,o=ee[n]||n,s=e.find(i=>i.role==="system"),y=e.filter(i=>i.role==="user").map(i=>i.content).join(`
|
|
26
|
+
|
|
27
|
+
`),g=["-p","--output-format","stream-json","--model",o,"--dangerously-skip-permissions"];s&&g.push("--append-system-prompt",s.content),g.push(y);let c={...process.env};delete c.ANTHROPIC_API_KEY,delete c.ANTHROPIC_API_KEY_OLD;let d=Z("claude",g,{env:c,extendEnv:!1,reject:!1,timeout:3e5,stdin:"ignore"}),m="",l="",_;if(d.stderr&&d.stderr.on("data",i=>{l+=Buffer.isBuffer(i)?i.toString("utf8"):String(i)}),d.stdout){let i=new TextDecoder,k=d.stdout;for await(let f of k){let T=(typeof f=="string"?f:i.decode(f,{stream:!0})).split(`
|
|
28
|
+
`).filter(Boolean);for(let x of T)try{let p=JSON.parse(x);if(p.type==="error"){_=String(p.error||p.message||"Claude CLI error");continue}if(p.is_error&&(p.result||p.message)){_=String(p.result||p.message);continue}p.type==="assistant"&&p.message?(m+=p.message,yield{type:"text_delta",text:p.message}):p.type==="result"&&(m=p.result||m)}catch{m+=x,yield{type:"text_delta",text:x}}}}let v=await d;if(v.exitCode!==0&&!_&&(_=(l||v.stderr||v.stdout||"Claude CLI failed").toString().trim()),_){yield{type:"error",error:`Claude CLI error: ${_}`};return}let w=this.extractFilesFromText(m),A=w.length===0?this.inferFollowUpFromText(m):void 0;yield{type:"done",result:{content:m,files:w,followUp:A,tokensUsed:0}}}async*streamViaApi(e,t){let n=t?.model||F,o=t?.maxTokens||z,s=e.find(k=>k.role==="system"),u=e.filter(k=>k.role!=="system").map(k=>({role:k.role,content:k.content})),y={model:n,max_tokens:o,messages:u,tools:I(),stream:!0};s&&(y.system=s.content);let g={"Content-Type":"application/json","anthropic-version":"2023-06-01","x-api-key":this.credential.token},c=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:g,body:JSON.stringify(y)});if(!c.ok){let k=await c.text();yield{type:"error",error:`Anthropic API error (${c.status}): ${k}`};return}let d=c.body?.getReader();if(!d){yield{type:"error",error:"No response body"};return}let m=new TextDecoder,l="",_="",v=0,w=[],A,i=null;try{for(;;){let{done:k,value:f}=await d.read();if(k)break;l+=m.decode(f,{stream:!0});let O=l.split(`
|
|
29
|
+
`);l=O.pop()||"";for(let T of O){if(!T.startsWith("data: "))continue;let x=T.slice(6).trim();if(x!=="[DONE]")try{let p=JSON.parse(x);if(p.type==="content_block_start"&&p.content_block?.type==="tool_use"){if(i){yield{type:"tool_use_end",name:i.name};try{let a=JSON.parse(i.json||"{}");i.name==="create_files"&&a&&(Array.isArray(a.files)&&w.push(...a.files),typeof a.summary=="string"&&a.summary.trim()&&(_+=`
|
|
30
|
+
${a.summary}`)),i.name==="ask_user"&&a&&typeof a.question=="string"&&a.question.trim()&&(A=a.question,Array.isArray(a.options)&&a.options.length&&(A+=`
|
|
31
|
+
Options: ${a.options.join(", ")}`))}catch{}finally{i=null}}i={name:p.content_block.name,id:p.content_block.id,json:""},yield{type:"tool_use_start",name:i.name,id:i.id}}if(p.type==="content_block_delta"&&p.delta?.type==="text_delta"&&(_+=p.delta.text,yield{type:"text_delta",text:p.delta.text}),p.type==="content_block_delta"&&p.delta?.type==="input_json_delta"&&(i&&(i.json+=p.delta.partial_json||""),yield{type:"tool_use_delta",json:p.delta.partial_json}),p.type==="content_block_stop"&&i){yield{type:"tool_use_end",name:i.name};try{let a=JSON.parse(i.json||"{}");i.name==="create_files"&&a&&(Array.isArray(a.files)&&w.push(...a.files),typeof a.summary=="string"&&a.summary.trim()&&(_+=`
|
|
32
|
+
${a.summary}`)),i.name==="ask_user"&&a&&typeof a.question=="string"&&a.question.trim()&&(A=a.question,Array.isArray(a.options)&&a.options.length&&(A+=`
|
|
33
|
+
Options: ${a.options.join(", ")}`))}catch{}finally{i=null}}p.type==="message_delta"&&p.usage&&(v=(p.usage.input_tokens||0)+(p.usage.output_tokens||0))}catch{}}}}finally{d.releaseLock()}if(i){yield{type:"tool_use_end",name:i.name};try{let k=JSON.parse(i.json||"{}");i.name==="create_files"&&k&&(Array.isArray(k.files)&&w.push(...k.files),typeof k.summary=="string"&&k.summary.trim()&&(_+=`
|
|
34
|
+
${k.summary}`)),i.name==="ask_user"&&k&&typeof k.question=="string"&&k.question.trim()&&(A=k.question,Array.isArray(k.options)&&k.options.length&&(A+=`
|
|
35
|
+
Options: ${k.options.join(", ")}`))}catch{}i=null}w.length===0&&w.push(...this.extractFilesFromText(_)),!A&&w.length===0&&(A=this.inferFollowUpFromText(_)),yield{type:"done",result:{content:_,files:w,followUp:A,tokensUsed:v}}}parseCliOutput(e){let t;try{t=JSON.parse(e)}catch{let u=this.inferFollowUpFromText(e.trim());return{content:e.trim(),files:[],followUp:u,tokensUsed:0}}let n=t.result||t.text||t.content||"",o=this.extractFilesFromText(n),s=o.length===0?this.inferFollowUpFromText(n):void 0;return{content:n,files:o,followUp:s,tokensUsed:t.usage?(t.usage.input_tokens||0)+(t.usage.output_tokens||0):0}}extractFilesFromText(e){let t=[],n=/```[\w]*\s*([\w/._-]+\.\w+)\n([\s\S]*?)```/g,o;for(;(o=n.exec(e))!==null;){let s=o[1],u=o[2];s&&u&&t.push({path:s,content:u.trimEnd()})}return t}inferFollowUpFromText(e){let t=(e||"").trim();if(!t||t.includes("```"))return;let n=t.replace(/\r\n/g,`
|
|
36
|
+
`).split(`
|
|
37
|
+
`),o=n.slice(Math.max(0,n.length-20)),s=o.join(`
|
|
38
|
+
`);if(/\b(plan|proposal)\b/i.test(s)&&/(ready for your review|review (the )?plan|requesting plan approval|awaiting approval|waiting for (your )?approval|approve (the )?plan|approval to proceed)/i.test(s))return`The provider is requesting plan approval.
|
|
39
|
+
Reply with:
|
|
40
|
+
- approve
|
|
41
|
+
- revise: <what to change>
|
|
42
|
+
- cancel`;let y=m=>/^(question|clarification|clarify|i need|need more|before i proceed|to proceed|please (confirm|clarify)|which|what|where|when|how|do you)/i.test(m.trim()),g=-1;for(let m=o.length-1;m>=0;m--){let l=o[m].trim();if(l&&(y(l)||l.includes("?"))){g=m;break}}if(g===-1)return;let c=[];for(let m=g;m<o.length&&c.length<8;m++){let l=o[m],_=l.trim();if(c.length>0&&!_||c.length>0&&!/^(options?:|[-*]\s|\d+[\).]\s)/i.test(_)&&!_.includes("?"))break;c.push(l.trimEnd())}let d=c.join(`
|
|
43
|
+
`).trim();if(!(d.length<5))return d.length>800?d.slice(0,800).trimEnd():d}parseApiResponse(e){let t=[],n="",o;for(let s of e.content)if(s.type==="text"&&(n+=s.text||""),s.type==="tool_use"){if(s.name==="create_files"&&s.input){let u=s.input;t.push(...u.files||[]),u.summary&&(n+=`
|
|
44
|
+
${u.summary}`)}if(s.name==="ask_user"&&s.input){let u=s.input;o=u.question,u.options?.length&&(o+=`
|
|
45
|
+
Options: ${u.options.join(", ")}`)}}return{content:n,files:t,followUp:o,tokensUsed:e.usage.input_tokens+e.usage.output_tokens}}};function Ne(r="claude-code",e){let t=r;if(r==="claude-code"&&!e){let n=R();n&&(t=n.provider)}switch(t){case"claude-code":return new G;case"claude":return new M(e);case"openai":throw new Error("OpenAI provider coming soon. Set provider to 'claude-code' or contribute at github.com/anis-marrouchi/agentx");case"ollama":throw new Error("Ollama provider coming soon. Set provider to 'claude-code' or contribute at github.com/anis-marrouchi/agentx");default:throw new Error(`Unknown provider: ${t}. Supported: claude-code, claude`)}}export{C as a,R as b,oe as c,N as d,ce as e,_e as f,pe as g,I as h,xe as i,Ae as j,M as k,G as l,Ne as m};
|
|
46
|
+
//# sourceMappingURL=chunk-HCFSHGK4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/auth-store.ts","../src/utils/logger.ts","../src/agent/tools/definitions.ts","../src/agent/providers/claude.ts","../src/agent/providers/claude-code.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","import chalk from \"chalk\"\n\nexport const logger = {\n error(...args: unknown[]) {\n console.log(chalk.red(...args))\n },\n warn(...args: unknown[]) {\n console.log(chalk.yellow(...args))\n },\n info(...args: unknown[]) {\n console.log(chalk.cyan(...args))\n },\n success(...args: unknown[]) {\n console.log(chalk.green(...args))\n },\n break() {\n console.log(\"\")\n },\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 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\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 = systemMsg.content\n }\n\n if (options?.temperature !== undefined) {\n body.temperature = options.temperature\n }\n\n const response = await this.callApi(body)\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: systemPrompt,\n messages,\n tools,\n }\n\n if (options?.temperature !== undefined) {\n body.temperature = options.temperature\n }\n\n const response = await this.callApi(body)\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 = 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 })\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 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(body: Record<string, unknown>): Promise<AnthropicResponse> {\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 })\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 { AgentProvider } from \"./types\"\nimport { ClaudeProvider } from \"./claude\"\nimport { ClaudeCodeProvider } from \"./claude-code\"\nimport { loadAuthConfig } from \"@/utils/auth-store\"\n\nexport type ProviderName = \"claude-code\" | \"claude\" | \"openai\" | \"ollama\" | \"custom\"\n\nexport function createProvider(\n name: ProviderName = \"claude-code\",\n apiKey?: string\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 throw new Error(\n \"OpenAI provider coming soon. Set provider to 'claude-code' or contribute at github.com/anis-marrouchi/agentx\"\n )\n case \"ollama\":\n throw new Error(\n \"Ollama provider coming soon. Set provider to 'claude-code' or contribute at github.com/anis-marrouchi/agentx\"\n )\n default:\n throw new Error(`Unknown provider: ${resolvedName}. Supported: claude-code, claude`)\n }\n}\n\nexport { ClaudeProvider } from \"./claude\"\nexport { ClaudeCodeProvider } from \"./claude-code\"\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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d,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,CACF,EACA,SAAU,CAAC,SAAS,CACtB,EACA,WAAY,SACd,EACA,CACE,KAAM,YACN,YACE,+IACF,aAAc,CACZ,KAAM,SACN,WAAY,CACV,KAAM,CACJ,KAAM,SACN,YAAa,sCACf,EACA,MAAO,CACL,KAAM,QACN,MAAO,CACL,KAAM,SACN,WAAY,CACV,SAAU,CACR,KAAM,SACN,YAAa,oCACf,EACA,SAAU,CACR,KAAM,SACN,YAAa,sBACf,CACF,EACA,SAAU,CAAC,WAAY,UAAU,CACnC,EACA,YAAa,gDACf,CACF,EACA,SAAU,CAAC,OAAQ,OAAO,CAC5B,EACA,WAAY,YACd,EACA,CACE,KAAM,cACN,YACE,kNACF,aAAc,CACZ,KAAM,SACN,WAAY,CACV,SAAU,CACR,KAAM,SACN,YAAa,2DACf,EACA,OAAQ,CACN,KAAM,SACN,YAAa,wCACf,EACA,gBAAiB,CACf,KAAM,SACN,YAAa,2EACf,CACF,EACA,SAAU,CAAC,WAAY,QAAQ,CACjC,EACA,WAAY,MACd,CACF,EAMO,SAASC,GAAkBC,EAI/B,CAKD,OAJaA,EACTF,EAAiB,OAAQ,GAAME,EAAa,SAAS,EAAE,IAAI,CAAC,EAC5DF,GAEQ,IAAI,CAAC,CAAE,KAAAG,EAAM,YAAAC,EAAa,aAAAC,CAAa,KAAO,CACxD,KAAAF,EACA,YAAAC,EACA,aAAAC,CACF,EAAE,CACJ,CAKO,SAASC,GAIb,CACD,OAAOL,GAAkB,CAAC,eAAgB,UAAU,CAAC,CACvD,CAQO,SAASM,IAAqC,CACnD,IAAMC,EAAeR,EAAiB,OACnC,GAAM,EAAE,OAAS,gBAAkB,EAAE,OAAS,UACjD,EAEA,GAAIQ,EAAa,SAAW,EAAG,MAAO,GAEtC,IAAMC,EAAQ,CACZ,2BACA,wEACA,EACF,EAEA,QAAWC,KAAQF,EACjBC,EAAM,KAAK,MAAMC,EAAK,MAAM,EAC5BD,EAAM,KAAKC,EAAK,WAAW,EAC3BD,EAAM,KAAK,EAAE,EAGf,OAAOA,EAAM,KAAK;AAAA,CAAI,CACxB,CAEO,IAAME,GAAiBX,EAAiB,IAAKY,GAAMA,EAAE,IAAI,EC5QhE,IAAMC,EAAgB,2BAChBC,EAAqB,KAkBdC,EAAN,KAA8C,CACnD,KAAO,SACC,OACA,SAAgC,UAExC,YAAYC,EAAiB,CAE3B,GADA,KAAK,OAASA,GAAU,QAAQ,IAAI,mBAAqB,GACrD,CAAC,KAAK,OAAQ,CAEhB,IAAMC,EAAWC,EAAa,EAC1BD,IACF,KAAK,OAASA,EAAS,MACvB,KAAK,SAAWA,EAAS,UAG7B,GAAI,CAAC,KAAK,OACR,MAAM,IAAI,MACR,wGACF,CAEJ,CAEA,MAAM,SACJE,EACAC,EAC2B,CAC3B,IAAMC,EAAQD,GAAS,OAASP,EAC1BS,EAAYF,GAAS,WAAaN,EAGlCS,EAAYJ,EAAS,KAAMK,GAAMA,EAAE,OAAS,QAAQ,EACpDC,EAAmBN,EACtB,OAAQK,GAAMA,EAAE,OAAS,QAAQ,EACjC,IAAKA,IAAO,CACX,KAAMA,EAAE,KACR,QAASA,EAAE,OACb,EAAE,EAEEE,EAAgC,CACpC,MAAAL,EACA,WAAYC,EACZ,SAAUG,EACV,MAAOE,EAAe,CACxB,EAEIJ,IACFG,EAAK,OAASH,EAAU,SAGtBH,GAAS,cAAgB,SAC3BM,EAAK,YAAcN,EAAQ,aAG7B,IAAMQ,EAAW,MAAM,KAAK,QAAQF,CAAI,EAExC,OAAO,KAAK,cAAcE,CAAQ,CACpC,CAEA,MAAM,YACJT,EACAU,EACAC,EACAV,EAC8B,CAC9B,IAAMC,EAAQD,GAAS,OAASP,EAC1BS,EAAYF,GAAS,WAAaN,EAElCY,EAAgC,CACpC,MAAAL,EACA,WAAYC,EACZ,OAAQO,EACR,SAAAV,EACA,MAAAW,CACF,EAEIV,GAAS,cAAgB,SAC3BM,EAAK,YAAcN,EAAQ,aAG7B,IAAMQ,EAAW,MAAM,KAAK,QAAQF,CAAI,EAkBxC,MAAO,CACL,QAhB8BE,EAAS,QAAQ,IAAKG,GAChDA,EAAM,OAAS,OACV,CAAE,KAAM,OAAiB,KAAMA,EAAM,MAAQ,EAAG,EAErDA,EAAM,OAAS,WACV,CACL,KAAM,WACN,GAAIA,EAAM,IAAM,GAChB,KAAMA,EAAM,MAAQ,GACpB,MAAOA,EAAM,OAAS,CAAC,CACzB,EAEK,CAAE,KAAM,OAAiB,KAAM,EAAG,CAC1C,EAIC,YAAaH,EAAS,YACtB,MAAOA,EAAS,KAClB,CACF,CAEA,MAAO,OACLT,EACAC,EAC4B,CAC5B,IAAMC,EAAQD,GAAS,OAASP,EAC1BS,EAAYF,GAAS,WAAaN,EAElCS,EAAYJ,EAAS,KAAMK,GAAMA,EAAE,OAAS,QAAQ,EACpDC,EAAmBN,EACtB,OAAQK,GAAMA,EAAE,OAAS,QAAQ,EACjC,IAAKA,IAAO,CACX,KAAMA,EAAE,KACR,QAASA,EAAE,OACb,EAAE,EAEEE,EAAgC,CACpC,MAAAL,EACA,WAAYC,EACZ,SAAUG,EACV,OAAQ,GACR,MAAOE,EAAe,CACxB,EAEIJ,IACFG,EAAK,OAASH,EAAU,SAG1B,IAAMS,EAAU,KAAK,aAAa,EAE5BC,EAAM,MAAM,MAAM,wCAAyC,CAC/D,OAAQ,OACR,QAAAD,EACA,KAAM,KAAK,UAAUN,CAAI,CAC3B,CAAC,EAED,GAAI,CAACO,EAAI,GAAI,CACX,IAAMC,EAAY,MAAMD,EAAI,KAAK,EACjC,KAAM,CAAE,KAAM,QAAS,MAAO,wBAAwBA,EAAI,YAAYC,GAAY,EAClF,OAGF,IAAMC,EAASF,EAAI,MAAM,UAAU,EACnC,GAAI,CAACE,EAAQ,CACX,KAAM,CAAE,KAAM,QAAS,MAAO,kBAAmB,EACjD,OAGF,IAAMC,EAAU,IAAI,YAChBC,EAAS,GACPC,EAAyB,CAAC,EAC5BC,EAAU,GACVC,EACAC,EAAa,EACbC,EAAgE,KAE9DC,EAAyBC,GAAqC,CAClE,IAAMC,GAAWD,GAAQ,IAAI,KAAK,EAElC,GADI,CAACC,GACDA,EAAQ,SAAS,KAAK,EAAG,OAE7B,IAAMC,EAAQD,EAAQ,QAAQ,QAAS;AAAA,CAAI,EAAE,MAAM;AAAA,CAAI,EACjDE,EAAOD,EAAM,MAAM,KAAK,IAAI,EAAGA,EAAM,OAAS,EAAE,CAAC,EACjDE,EAAWD,EAAK,KAAK;AAAA,CAAI,EAQ/B,GALE,uBAAuB,KAAKC,CAAQ,GACpC,8JAA8J,KAC5JA,CACF,EAGA,MACE;AAAA;AAAA;AAAA;AAAA,UAQJ,IAAMC,EAAWC,GACf,4IAA4I,KAC1IA,EAAE,KAAK,CACT,EAEEC,EAAW,GACf,QAASC,EAAIL,EAAK,OAAS,EAAGK,GAAK,EAAGA,IAAK,CACzC,IAAMF,EAAIH,EAAKK,CAAC,EAAE,KAAK,EACvB,GAAKF,IACDD,EAAQC,CAAC,GAAKA,EAAE,SAAS,GAAG,GAAG,CACjCC,EAAWC,EACX,OAGJ,GAAID,IAAa,GAAI,OAErB,IAAME,EAAgB,CAAC,EACvB,QAASD,EAAID,EAAUC,EAAIL,EAAK,QAAUM,EAAI,OAAS,EAAGD,IAAK,CAC7D,IAAMF,EAAIH,EAAKK,CAAC,EACVE,EAAIJ,EAAE,KAAK,EAEjB,GADIG,EAAI,OAAS,GAAK,CAACC,GAErBD,EAAI,OAAS,GACb,CAAC,kCAAkC,KAAKC,CAAC,GACzC,CAACA,EAAE,SAAS,GAAG,EAEf,MAEFD,EAAI,KAAKH,EAAE,QAAQ,CAAC,EAGtB,IAAMK,EAAYF,EAAI,KAAK;AAAA,CAAI,EAAE,KAAK,EACtC,GAAI,EAAAE,EAAU,OAAS,GACvB,OAAIA,EAAU,OAAS,IAAYA,EAAU,MAAM,EAAG,GAAG,EAAE,QAAQ,EAC5DA,CACT,EAEA,GAAI,CACF,OAAa,CACX,GAAM,CAAE,KAAAC,EAAM,MAAAC,CAAM,EAAI,MAAMtB,EAAO,KAAK,EAC1C,GAAIqB,EAAM,MAEVnB,GAAUD,EAAQ,OAAOqB,EAAO,CAAE,OAAQ,EAAK,CAAC,EAChD,IAAMX,EAAQT,EAAO,MAAM;AAAA,CAAI,EAC/BA,EAASS,EAAM,IAAI,GAAK,GAExB,QAAWY,KAAQZ,EAAO,CACxB,GAAI,CAACY,EAAK,WAAW,QAAQ,EAAG,SAChC,IAAMC,EAAOD,EAAK,MAAM,CAAC,EAAE,KAAK,EAChC,GAAIC,IAAS,SAEb,GAAI,CACF,IAAMC,EAAQ,KAAK,MAAMD,CAAI,EAE7B,GAAIC,EAAM,OAAS,uBACbA,EAAM,eAAe,OAAS,WAAY,CAE5C,GAAIlB,EAAY,CACd,KAAM,CAAE,KAAM,eAAgB,KAAMA,EAAW,IAAK,EACpD,GAAI,CACF,IAAMmB,EAAQ,KAAK,MAAMnB,EAAW,MAAQ,IAAI,EAC5CA,EAAW,OAAS,gBAAkBmB,IACpC,MAAM,QAAQA,EAAM,KAAK,GAC3BvB,EAAM,KAAK,GAAIuB,EAAM,KAAyB,EAE5C,OAAOA,EAAM,SAAY,UAAYA,EAAM,QAAQ,KAAK,IAC1DtB,GAAW;AAAA,EAAKsB,EAAM,YAGtBnB,EAAW,OAAS,YAAcmB,GAChC,OAAOA,EAAM,UAAa,UAAYA,EAAM,SAAS,KAAK,IAC5DrB,EAAWqB,EAAM,SACb,MAAM,QAAQA,EAAM,OAAO,GAAKA,EAAM,QAAQ,SAChDrB,GAAY;AAAA,WAAcqB,EAAM,QAAQ,KAAK,IAAI,KAIzD,MAAE,CAEF,QAAE,CACAnB,EAAa,IACf,EAGFA,EAAa,CACX,KAAMkB,EAAM,cAAc,KAC1B,GAAIA,EAAM,cAAc,GACxB,KAAM,EACR,EACA,KAAM,CACJ,KAAM,iBACN,KAAMA,EAAM,cAAc,KAC1B,GAAIA,EAAM,cAAc,EAC1B,EAeJ,GAXIA,EAAM,OAAS,wBACbA,EAAM,OAAO,OAAS,eACxBrB,GAAWqB,EAAM,MAAM,KACvB,KAAM,CAAE,KAAM,aAAc,KAAMA,EAAM,MAAM,IAAK,GAEjDA,EAAM,OAAO,OAAS,qBACpBlB,IAAYA,EAAW,MAAQkB,EAAM,MAAM,cAAgB,IAC/D,KAAM,CAAE,KAAM,iBAAkB,KAAMA,EAAM,MAAM,YAAa,IAI/DA,EAAM,OAAS,sBAEblB,EAAY,CAEd,KAAM,CAAE,KAAM,eAAgB,KAAMA,EAAW,IAAK,EACpD,GAAI,CACF,IAAMmB,EAAQ,KAAK,MAAMnB,EAAW,MAAQ,IAAI,EAC5CA,EAAW,OAAS,gBAAkBmB,IACpC,MAAM,QAAQA,EAAM,KAAK,GAC3BvB,EAAM,KAAK,GAAIuB,EAAM,KAAyB,EAE5C,OAAOA,EAAM,SAAY,UAAYA,EAAM,QAAQ,KAAK,IAC1DtB,GAAW;AAAA,EAAKsB,EAAM,YAGtBnB,EAAW,OAAS,YAAcmB,GAChC,OAAOA,EAAM,UAAa,UAAYA,EAAM,SAAS,KAAK,IAC5DrB,EAAWqB,EAAM,SACb,MAAM,QAAQA,EAAM,OAAO,GAAKA,EAAM,QAAQ,SAChDrB,GAAY;AAAA,WAAcqB,EAAM,QAAQ,KAAK,IAAI,KAIzD,MAAE,CAEF,QAAE,CACAnB,EAAa,IACf,EAIAkB,EAAM,OAAS,iBACbA,EAAM,QACRnB,GAAcmB,EAAM,MAAM,cAAgB,IAAMA,EAAM,MAAM,eAAiB,IAI7EA,EAAM,IAGZ,MAAE,CAEF,GAGN,QAAE,CACAzB,EAAO,YAAY,CACrB,CAGA,GAAIO,EAAY,CACd,KAAM,CAAE,KAAM,eAAgB,KAAMA,EAAW,IAAK,EACpD,GAAI,CACF,IAAMmB,EAAQ,KAAK,MAAMnB,EAAW,MAAQ,IAAI,EAC5CA,EAAW,OAAS,gBAAkBmB,IACpC,MAAM,QAAQA,EAAM,KAAK,GAC3BvB,EAAM,KAAK,GAAIuB,EAAM,KAAyB,EAE5C,OAAOA,EAAM,SAAY,UAAYA,EAAM,QAAQ,KAAK,IAC1DtB,GAAW;AAAA,EAAKsB,EAAM,YAGtBnB,EAAW,OAAS,YAAcmB,GAChC,OAAOA,EAAM,UAAa,UAAYA,EAAM,SAAS,KAAK,IAC5DrB,EAAWqB,EAAM,SACb,MAAM,QAAQA,EAAM,OAAO,GAAKA,EAAM,QAAQ,SAChDrB,GAAY;AAAA,WAAcqB,EAAM,QAAQ,KAAK,IAAI,KAIzD,MAAE,CAEF,CACAnB,EAAa,KAGX,CAACF,GAAYF,EAAM,SAAW,IAChCE,EAAWG,EAAsBJ,CAAO,GAG1C,KAAM,CACJ,KAAM,OACN,OAAQ,CAAE,QAAAA,EAAS,MAAAD,EAAO,SAAAE,EAAU,WAAAC,CAAW,CACjD,CACF,CAEQ,cAAuC,CAC7C,IAAMT,EAAkC,CACtC,eAAgB,mBAChB,oBAAqB,YACvB,EAEA,OAAI,KAAK,WAAa,SACpBA,EAAQ,cAAmB,UAAU,KAAK,SAC1CA,EAAQ,gBAAgB,EAAI,wCAC5BA,EAAQ,YAAY,EAAI,mCACxBA,EAAQ,OAAO,EAAI,MACnBA,EAAQ,2CAA2C,EAAI,QAEvDA,EAAQ,WAAW,EAAI,KAAK,OAGvBA,CACT,CAEA,MAAc,QAAQN,EAA2D,CAC/E,IAAMM,EAAU,KAAK,aAAa,EAE5BC,EAAM,MAAM,MAAM,wCAAyC,CAC/D,OAAQ,OACR,QAAAD,EACA,KAAM,KAAK,UAAUN,CAAI,CAC3B,CAAC,EAED,GAAI,CAACO,EAAI,GAAI,CACX,IAAMC,EAAY,MAAMD,EAAI,KAAK,EACjC,MAAM,IAAI,MAAM,wBAAwBA,EAAI,YAAYC,GAAW,EAGrE,OAAQ,MAAMD,EAAI,KAAK,CACzB,CAEQ,cAAcL,EAA+C,CACnE,IAAMU,EAAyB,CAAC,EAC5BC,EAAU,GACVC,EAEJ,QAAWT,KAASH,EAAS,QAK3B,GAJIG,EAAM,OAAS,SACjBQ,GAAWR,EAAM,MAAQ,IAGvBA,EAAM,OAAS,WAAY,CAC7B,GAAIA,EAAM,OAAS,gBAAkBA,EAAM,MAAO,CAChD,IAAM8B,EAAQ9B,EAAM,MAIpBO,EAAM,KAAK,GAAIuB,EAAM,OAAS,CAAC,CAAE,EAC7BA,EAAM,UACRtB,GAAW;AAAA,EAAKsB,EAAM,WAI1B,GAAI9B,EAAM,OAAS,YAAcA,EAAM,MAAO,CAC5C,IAAM8B,EAAQ9B,EAAM,MACpBS,EAAWqB,EAAM,SACbA,EAAM,SAAS,SACjBrB,GAAY;AAAA,WAAcqB,EAAM,QAAQ,KAAK,IAAI,MAMzD,MAAO,CACL,QAAAtB,EACA,MAAAD,EACA,SAAAE,EACA,WAAYZ,EAAS,MAAM,aAAeA,EAAS,MAAM,aAC3D,CACF,CACF,EC5dA,OAAS,SAAAkC,MAAa,QAKtB,IAAMC,EAAgB,2BAChBC,EAAqB,KAGrBC,GAA4C,CAChD,2BAA4B,SAC5B,yBAA0B,OAC1B,0BAA2B,OAC7B,EAkBaC,EAAN,KAAkD,CACvD,KAAO,cACC,WAER,aAAc,CAGZ,IAAMC,EAASC,EAAe,EAC9B,GAAID,EAAQ,CACV,KAAK,WAAa,CAAE,KAAMA,EAAO,SAAU,MAAOA,EAAO,KAAM,EAC/D,OAGF,IAAME,EAAWC,EAAa,EAC9B,GAAI,CAACD,EACH,MAAM,IAAI,MACR;AAAA;AAAA;AAAA;AAAA,oDAKF,EAEF,KAAK,WAAa,CAAE,KAAMA,EAAS,SAAU,MAAOA,EAAS,KAAM,CACrE,CAEA,MAAM,SACJE,EACAC,EAC2B,CAG3B,OAAI,KAAK,WAAW,OAAS,QACpB,KAAK,eAAeD,EAAUC,CAAO,EAEvC,KAAK,eAAeD,EAAUC,CAAO,CAC9C,CAMA,MAAM,YACJD,EACAE,EACAC,EACAF,EAC8B,CAE9B,GAAI,KAAK,WAAW,OAAS,QAC3B,MAAM,IAAI,MAAM,gDAAgD,EAGlE,IAAMG,EAAQH,GAAS,OAAST,EAC1Ba,EAAYJ,GAAS,WAAaR,EAElCa,EAAgC,CACpC,MAAAF,EACA,WAAYC,EACZ,OAAQH,EACR,SAAAF,EACA,MAAAG,CACF,EAEIF,GAAS,cAAgB,SAC3BK,EAAK,YAAcL,EAAQ,aAG7B,IAAMM,EAAkC,CACtC,eAAgB,mBAChB,oBAAqB,aACrB,YAAa,KAAK,WAAW,KAC/B,EAEMC,EAAM,MAAM,MAAM,wCAAyC,CAC/D,OAAQ,OACR,QAAAD,EACA,KAAM,KAAK,UAAUD,CAAI,CAC3B,CAAC,EAED,GAAI,CAACE,EAAI,GAAI,CACX,IAAMC,EAAY,MAAMD,EAAI,KAAK,EACjC,MAAM,IAAI,MAAM,wBAAwBA,EAAI,YAAYC,GAAW,EAGrE,IAAMC,EAAY,MAAMF,EAAI,KAAK,EAiBjC,MAAO,CACL,QAhB8BE,EAAS,QAAQ,IAAKC,GAChDA,EAAM,OAAS,OACV,CAAE,KAAM,OAAiB,KAAMA,EAAM,MAAQ,EAAG,EAErDA,EAAM,OAAS,WACV,CACL,KAAM,WACN,GAAIA,EAAM,IAAM,GAChB,KAAMA,EAAM,MAAQ,GACpB,MAAOA,EAAM,OAAS,CAAC,CACzB,EAEK,CAAE,KAAM,OAAiB,KAAM,EAAG,CAC1C,EAIC,YAAaD,EAAS,YACtB,MAAOA,EAAS,KAClB,CACF,CAMA,IAAI,qBAA+B,CACjC,OAAO,KAAK,WAAW,OAAS,SAClC,CAMA,MAAc,eACZV,EACAC,EAC2B,CAC3B,IAAMG,EAAQH,GAAS,OAASJ,EAAe,GAAG,OAASL,EACrDoB,EAAWlB,GAAkBU,CAAK,GAAKA,EAEvCS,EAAYb,EAAS,KAAMc,GAAMA,EAAE,OAAS,QAAQ,EAEpDC,EADWf,EAAS,OAAQc,GAAMA,EAAE,OAAS,MAAM,EACjC,IAAKA,GAAMA,EAAE,OAAO,EAAE,KAAK;AAAA;AAAA,CAAM,EAEnDE,EAAO,CACX,KACA,kBAAmB,OACnB,UAAWJ,EACX,gCACF,EAEIC,GACFG,EAAK,KAAK,yBAA0BH,EAAU,OAAO,EAIvDG,EAAK,KAAKD,CAAM,EAGhB,IAAME,EAAM,CAAE,GAAG,QAAQ,GAAI,EAC7B,OAAOA,EAAI,kBACX,OAAOA,EAAI,sBAEX,IAAMC,EAAS,MAAMC,EAAM,SAAUH,EAAM,CACzC,IAAAC,EACA,UAAW,GACX,OAAQ,GACR,QAAS,IACT,MAAO,QACT,CAAC,EAED,GAAIC,EAAO,WAAa,EAAG,CACzB,IAAME,EAAMF,EAAO,QAAUA,EAAO,QAAU,oBAC9C,MAAM,IAAI,MAAM,qBAAqBE,GAAK,EAI5C,IAAMC,EAASH,EAAO,OAAO,KAAK,EAClC,GAAI,CACF,IAAMI,EAAO,KAAK,MAAMD,CAAM,EAC9B,GAAIC,EAAK,UAAYA,EAAK,OACxB,MAAM,IAAI,MAAMA,EAAK,MAAM,CAE/B,OAASC,EAAP,CACA,GAAIA,EAAE,SAAW,CAACA,EAAE,QAAQ,SAAS,MAAM,EAAG,MAAMA,CAEtD,CAEA,OAAO,KAAK,eAAeF,CAAM,CACnC,CAKA,MAAc,eACZrB,EACAC,EAC2B,CAC3B,IAAMG,EAAQH,GAAS,OAAST,EAC1Ba,EAAYJ,GAAS,WAAaR,EAElCoB,EAAYb,EAAS,KAAM,GAAM,EAAE,OAAS,QAAQ,EACpDwB,EAAmBxB,EACtB,OAAQ,GAAM,EAAE,OAAS,QAAQ,EACjC,IAAK,IAAO,CACX,KAAM,EAAE,KACR,QAAS,EAAE,OACb,EAAE,EAEEM,EAAgC,CACpC,MAAAF,EACA,WAAYC,EACZ,SAAUmB,EACV,MAAOC,EAAe,CACxB,EAEIZ,IACFP,EAAK,OAASO,EAAU,SAGtBZ,GAAS,cAAgB,SAC3BK,EAAK,YAAcL,EAAQ,aAG7B,IAAMM,EAAkC,CACtC,eAAgB,mBAChB,oBAAqB,aACrB,YAAa,KAAK,WAAW,KAC/B,EAEMC,EAAM,MAAM,MAAM,wCAAyC,CAC/D,OAAQ,OACR,QAAAD,EACA,KAAM,KAAK,UAAUD,CAAI,CAC3B,CAAC,EAED,GAAI,CAACE,EAAI,GAAI,CACX,IAAMC,EAAY,MAAMD,EAAI,KAAK,EACjC,MAAM,IAAI,MAAM,wBAAwBA,EAAI,YAAYC,GAAW,EAGrE,IAAMC,EAAY,MAAMF,EAAI,KAAK,EACjC,OAAO,KAAK,iBAAiBE,CAAQ,CACvC,CAMA,MAAO,OACLV,EACAC,EAC4B,CACxB,KAAK,WAAW,OAAS,QAE3B,MAAO,KAAK,aAAaD,EAAUC,CAAO,EAG1C,MAAO,KAAK,aAAaD,EAAUC,CAAO,CAE9C,CAEA,MAAe,aACbD,EACAC,EAC4B,CAC5B,IAAMG,EAAQH,GAAS,OAASJ,EAAe,GAAG,OAASL,EACrDoB,EAAWlB,GAAkBU,CAAK,GAAKA,EAEvCS,EAAYb,EAAS,KAAMc,GAAMA,EAAE,OAAS,QAAQ,EAEpDC,EADWf,EAAS,OAAQc,GAAMA,EAAE,OAAS,MAAM,EACjC,IAAKA,GAAMA,EAAE,OAAO,EAAE,KAAK;AAAA;AAAA,CAAM,EAEnDE,EAAO,CACX,KACA,kBAAmB,cACnB,UAAWJ,EACX,gCACF,EAEIC,GACFG,EAAK,KAAK,yBAA0BH,EAAU,OAAO,EAGvDG,EAAK,KAAKD,CAAM,EAEhB,IAAME,EAAM,CAAE,GAAG,QAAQ,GAAI,EAC7B,OAAOA,EAAI,kBACX,OAAOA,EAAI,sBAEX,IAAMS,EAAQP,EAAM,SAAUH,EAAM,CAClC,IAAAC,EACA,UAAW,GACX,OAAQ,GACR,QAAS,IACT,MAAO,QACT,CAAC,EAEGU,EAAc,GACdC,EAAS,GACTC,EAQJ,GANIH,EAAM,QACRA,EAAM,OAAO,GAAG,OAASI,GAAe,CACtCF,GAAU,OAAO,SAASE,CAAK,EAAIA,EAAM,SAAS,MAAM,EAAI,OAAOA,CAAK,CAC1E,CAAC,EAGCJ,EAAM,OAAQ,CAChB,IAAMK,EAAU,IAAI,YACdC,EAAWN,EAAM,OAEvB,cAAiBI,KAASE,EAAU,CAGlC,IAAMC,GAFO,OAAOH,GAAU,SAAWA,EAAQC,EAAQ,OAAOD,EAAO,CAAE,OAAQ,EAAK,CAAC,GAEpE,MAAM;AAAA,CAAI,EAAE,OAAO,OAAO,EAC7C,QAAWI,KAAQD,EACjB,GAAI,CACF,IAAME,EAAQ,KAAK,MAAMD,CAAI,EAE7B,GAAIC,EAAM,OAAS,QAAS,CAC1BN,EAAa,OAAOM,EAAM,OAASA,EAAM,SAAW,kBAAkB,EACtE,SAEF,GAAIA,EAAM,WAAaA,EAAM,QAAUA,EAAM,SAAU,CACrDN,EAAa,OAAOM,EAAM,QAAUA,EAAM,OAAO,EACjD,SAEEA,EAAM,OAAS,aAAeA,EAAM,SACtCR,GAAeQ,EAAM,QACrB,KAAM,CAAE,KAAM,aAAc,KAAMA,EAAM,OAAQ,GACvCA,EAAM,OAAS,WACxBR,EAAcQ,EAAM,QAAUR,EAElC,MAAE,CAEAA,GAAeO,EACf,KAAM,CAAE,KAAM,aAAc,KAAMA,CAAK,CACzC,GAKN,IAAM1B,EAAM,MAAMkB,EAKlB,GAJIlB,EAAI,WAAa,GAAK,CAACqB,IACzBA,GAAcD,GAAUpB,EAAI,QAAUA,EAAI,QAAU,qBAAqB,SAAS,EAAE,KAAK,GAGvFqB,EAAY,CACd,KAAM,CAAE,KAAM,QAAS,MAAO,qBAAqBA,GAAa,EAChE,OAGF,IAAMO,EAAQ,KAAK,qBAAqBT,CAAW,EAC7CU,EACJD,EAAM,SAAW,EAAI,KAAK,sBAAsBT,CAAW,EAAI,OACjE,KAAM,CACJ,KAAM,OACN,OAAQ,CAAE,QAASA,EAAa,MAAAS,EAAO,SAAAC,EAAU,WAAY,CAAE,CACjE,CACF,CAEA,MAAe,aACbrC,EACAC,EAC4B,CAC5B,IAAMG,EAAQH,GAAS,OAAST,EAC1Ba,EAAYJ,GAAS,WAAaR,EAElCoB,EAAYb,EAAS,KAAMc,GAAMA,EAAE,OAAS,QAAQ,EACpDU,EAAmBxB,EACtB,OAAQc,GAAMA,EAAE,OAAS,QAAQ,EACjC,IAAKA,IAAO,CAAE,KAAMA,EAAE,KAA8B,QAASA,EAAE,OAAQ,EAAE,EAEtER,EAAgC,CACpC,MAAAF,EACA,WAAYC,EACZ,SAAUmB,EACV,MAAOC,EAAe,EACtB,OAAQ,EACV,EAEIZ,IAAWP,EAAK,OAASO,EAAU,SAEvC,IAAMN,EAAkC,CACtC,eAAgB,mBAChB,oBAAqB,aACrB,YAAa,KAAK,WAAW,KAC/B,EAEMC,EAAM,MAAM,MAAM,wCAAyC,CAC/D,OAAQ,OACR,QAAAD,EACA,KAAM,KAAK,UAAUD,CAAI,CAC3B,CAAC,EAED,GAAI,CAACE,EAAI,GAAI,CACX,IAAMC,EAAY,MAAMD,EAAI,KAAK,EACjC,KAAM,CAAE,KAAM,QAAS,MAAO,wBAAwBA,EAAI,YAAYC,GAAY,EAClF,OAGF,IAAM6B,EAAS9B,EAAI,MAAM,UAAU,EACnC,GAAI,CAAC8B,EAAQ,CACX,KAAM,CAAE,KAAM,QAAS,MAAO,kBAAmB,EACjD,OAGF,IAAMP,EAAU,IAAI,YAChBQ,EAAS,GACTC,EAAU,GACVC,EAAa,EACXL,EAAyB,CAAC,EAC5BC,EACAK,EAAgE,KAEpE,GAAI,CACF,OAAa,CACX,GAAM,CAAE,KAAAC,EAAM,MAAAC,CAAM,EAAI,MAAMN,EAAO,KAAK,EAC1C,GAAIK,EAAM,MAEVJ,GAAUR,EAAQ,OAAOa,EAAO,CAAE,OAAQ,EAAK,CAAC,EAChD,IAAMX,EAAQM,EAAO,MAAM;AAAA,CAAI,EAC/BA,EAASN,EAAM,IAAI,GAAK,GAExB,QAAWC,KAAQD,EAAO,CACxB,GAAI,CAACC,EAAK,WAAW,QAAQ,EAAG,SAChC,IAAMW,EAAOX,EAAK,MAAM,CAAC,EAAE,KAAK,EAChC,GAAIW,IAAS,SAEb,GAAI,CACF,IAAMV,EAAQ,KAAK,MAAMU,CAAI,EAC7B,GAAIV,EAAM,OAAS,uBACbA,EAAM,eAAe,OAAS,WAAY,CAE5C,GAAIO,EAAY,CACd,KAAM,CAAE,KAAM,eAAgB,KAAMA,EAAW,IAAK,EACpD,GAAI,CACF,IAAMI,EAAQ,KAAK,MAAMJ,EAAW,MAAQ,IAAI,EAC5CA,EAAW,OAAS,gBAAkBI,IACpC,MAAM,QAAQA,EAAM,KAAK,GAC3BV,EAAM,KAAK,GAAIU,EAAM,KAAyB,EAE5C,OAAOA,EAAM,SAAY,UAAYA,EAAM,QAAQ,KAAK,IAC1DN,GAAW;AAAA,EAAKM,EAAM,YAGtBJ,EAAW,OAAS,YAAcI,GAChC,OAAOA,EAAM,UAAa,UAAYA,EAAM,SAAS,KAAK,IAC5DT,EAAWS,EAAM,SACb,MAAM,QAAQA,EAAM,OAAO,GAAKA,EAAM,QAAQ,SAChDT,GAAY;AAAA,WAAcS,EAAM,QAAQ,KAAK,IAAI,KAIzD,MAAE,CAEF,QAAE,CACAJ,EAAa,IACf,EAGFA,EAAa,CACX,KAAMP,EAAM,cAAc,KAC1B,GAAIA,EAAM,cAAc,GACxB,KAAM,EACR,EACA,KAAM,CAAE,KAAM,iBAAkB,KAAMO,EAAW,KAAM,GAAIA,EAAW,EAAG,EAY7E,GARIP,EAAM,OAAS,uBAAyBA,EAAM,OAAO,OAAS,eAChEK,GAAWL,EAAM,MAAM,KACvB,KAAM,CAAE,KAAM,aAAc,KAAMA,EAAM,MAAM,IAAK,GAEjDA,EAAM,OAAS,uBAAyBA,EAAM,OAAO,OAAS,qBAC5DO,IAAYA,EAAW,MAAQP,EAAM,MAAM,cAAgB,IAC/D,KAAM,CAAE,KAAM,iBAAkB,KAAMA,EAAM,MAAM,YAAa,GAE7DA,EAAM,OAAS,sBACbO,EAAY,CACd,KAAM,CAAE,KAAM,eAAgB,KAAMA,EAAW,IAAK,EACpD,GAAI,CACF,IAAMI,EAAQ,KAAK,MAAMJ,EAAW,MAAQ,IAAI,EAC5CA,EAAW,OAAS,gBAAkBI,IACpC,MAAM,QAAQA,EAAM,KAAK,GAC3BV,EAAM,KAAK,GAAIU,EAAM,KAAyB,EAE5C,OAAOA,EAAM,SAAY,UAAYA,EAAM,QAAQ,KAAK,IAC1DN,GAAW;AAAA,EAAKM,EAAM,YAGtBJ,EAAW,OAAS,YAAcI,GAChC,OAAOA,EAAM,UAAa,UAAYA,EAAM,SAAS,KAAK,IAC5DT,EAAWS,EAAM,SACb,MAAM,QAAQA,EAAM,OAAO,GAAKA,EAAM,QAAQ,SAChDT,GAAY;AAAA,WAAcS,EAAM,QAAQ,KAAK,IAAI,KAIzD,MAAE,CAEF,QAAE,CACAJ,EAAa,IACf,EAGAP,EAAM,OAAS,iBAAmBA,EAAM,QAC1CM,GAAcN,EAAM,MAAM,cAAgB,IAAMA,EAAM,MAAM,eAAiB,GAEjF,MAAE,CAEF,GAGN,QAAE,CACAG,EAAO,YAAY,CACrB,CAGA,GAAII,EAAY,CACd,KAAM,CAAE,KAAM,eAAgB,KAAMA,EAAW,IAAK,EACpD,GAAI,CACF,IAAMI,EAAQ,KAAK,MAAMJ,EAAW,MAAQ,IAAI,EAC5CA,EAAW,OAAS,gBAAkBI,IACpC,MAAM,QAAQA,EAAM,KAAK,GAC3BV,EAAM,KAAK,GAAIU,EAAM,KAAyB,EAE5C,OAAOA,EAAM,SAAY,UAAYA,EAAM,QAAQ,KAAK,IAC1DN,GAAW;AAAA,EAAKM,EAAM,YAGtBJ,EAAW,OAAS,YAAcI,GAChC,OAAOA,EAAM,UAAa,UAAYA,EAAM,SAAS,KAAK,IAC5DT,EAAWS,EAAM,SACb,MAAM,QAAQA,EAAM,OAAO,GAAKA,EAAM,QAAQ,SAChDT,GAAY;AAAA,WAAcS,EAAM,QAAQ,KAAK,IAAI,KAIzD,MAAE,CAEF,CACAJ,EAAa,KAIXN,EAAM,SAAW,GACnBA,EAAM,KAAK,GAAG,KAAK,qBAAqBI,CAAO,CAAC,EAE9C,CAACH,GAAYD,EAAM,SAAW,IAChCC,EAAW,KAAK,sBAAsBG,CAAO,GAE/C,KAAM,CACJ,KAAM,OACN,OAAQ,CAAE,QAAAA,EAAS,MAAAJ,EAAO,SAAAC,EAAU,WAAAI,CAAW,CACjD,CACF,CAKQ,eAAeM,EAAkC,CACvD,IAAIC,EACJ,GAAI,CACFA,EAAS,KAAK,MAAMD,CAAM,CAC5B,MAAE,CAEA,IAAME,EAAW,KAAK,sBAAsBF,EAAO,KAAK,CAAC,EACzD,MAAO,CACL,QAASA,EAAO,KAAK,EACrB,MAAO,CAAC,EACR,SAAUE,EACV,WAAY,CACd,CACF,CAGA,IAAMC,EAAOF,EAAO,QAAUA,EAAO,MAAQA,EAAO,SAAW,GAGzDZ,EAAQ,KAAK,qBAAqBc,CAAI,EACtCb,EAAWD,EAAM,SAAW,EAAI,KAAK,sBAAsBc,CAAI,EAAI,OAEzE,MAAO,CACL,QAASA,EACT,MAAAd,EACA,SAAAC,EACA,WAAYW,EAAO,OACdA,EAAO,MAAM,cAAgB,IAAMA,EAAO,MAAM,eAAiB,GAClE,CACN,CACF,CAMQ,qBAAqBE,EAA+B,CAC1D,IAAMd,EAAyB,CAAC,EAE1Be,EAAU,8CACZC,EACJ,MAAQA,EAAQD,EAAQ,KAAKD,CAAI,KAAO,MAAM,CAC5C,IAAMG,EAAWD,EAAM,CAAC,EAClBZ,EAAUY,EAAM,CAAC,EACnBC,GAAYb,GACdJ,EAAM,KAAK,CAAE,KAAMiB,EAAU,QAASb,EAAQ,QAAQ,CAAE,CAAC,EAG7D,OAAOJ,CACT,CAMQ,sBAAsBc,EAAkC,CAC9D,IAAMI,GAAWJ,GAAQ,IAAI,KAAK,EAIlC,GAHI,CAACI,GAGDA,EAAQ,SAAS,KAAK,EAAG,OAE7B,IAAMrB,EAAQqB,EAAQ,QAAQ,QAAS;AAAA,CAAI,EAAE,MAAM;AAAA,CAAI,EACjDC,EAAOtB,EAAM,MAAM,KAAK,IAAI,EAAGA,EAAM,OAAS,EAAE,CAAC,EAEjDuB,EAAWD,EAAK,KAAK;AAAA,CAAI,EAO/B,GALE,uBAAuB,KAAKC,CAAQ,GACpC,8JAA8J,KAC5JA,CACF,EAGA,MACE;AAAA;AAAA;AAAA;AAAA,UAQJ,IAAMC,EAAWC,GACf,4IAA4I,KAC1IA,EAAE,KAAK,CACT,EAGEC,EAAW,GACf,QAASC,EAAIL,EAAK,OAAS,EAAGK,GAAK,EAAGA,IAAK,CACzC,IAAM,EAAIL,EAAKK,CAAC,EAAE,KAAK,EACvB,GAAK,IACDH,EAAQ,CAAC,GAAK,EAAE,SAAS,GAAG,GAAG,CACjCE,EAAWC,EACX,OAGJ,GAAID,IAAa,GAAI,OAGrB,IAAME,EAAgB,CAAC,EACvB,QAASD,EAAID,EAAUC,EAAIL,EAAK,QAAUM,EAAI,OAAS,EAAGD,IAAK,CAC7D,IAAM,EAAIL,EAAKK,CAAC,EACVE,EAAI,EAAE,KAAK,EAGjB,GAFID,EAAI,OAAS,GAAK,CAACC,GAGrBD,EAAI,OAAS,GACb,CAAC,kCAAkC,KAAKC,CAAC,GACzC,CAACA,EAAE,SAAS,GAAG,EAEf,MAGFD,EAAI,KAAK,EAAE,QAAQ,CAAC,EAGtB,IAAME,EAAYF,EAAI,KAAK;AAAA,CAAI,EAAE,KAAK,EACtC,GAAI,EAAAE,EAAU,OAAS,GACvB,OAAIA,EAAU,OAAS,IAAYA,EAAU,MAAM,EAAG,GAAG,EAAE,QAAQ,EAC5DA,CACT,CAKQ,iBAAiBrD,EAA+C,CACtE,IAAM0B,EAAyB,CAAC,EAC5BI,EAAU,GACVH,EAEJ,QAAW1B,KAASD,EAAS,QAK3B,GAJIC,EAAM,OAAS,SACjB6B,GAAW7B,EAAM,MAAQ,IAGvBA,EAAM,OAAS,WAAY,CAC7B,GAAIA,EAAM,OAAS,gBAAkBA,EAAM,MAAO,CAChD,IAAMmC,EAAQnC,EAAM,MAIpByB,EAAM,KAAK,GAAIU,EAAM,OAAS,CAAC,CAAE,EAC7BA,EAAM,UACRN,GAAW;AAAA,EAAKM,EAAM,WAI1B,GAAInC,EAAM,OAAS,YAAcA,EAAM,MAAO,CAC5C,IAAMmC,EAAQnC,EAAM,MACpB0B,EAAWS,EAAM,SACbA,EAAM,SAAS,SACjBT,GAAY;AAAA,WAAcS,EAAM,QAAQ,KAAK,IAAI,MAMzD,MAAO,CACL,QAAAN,EACA,MAAAJ,EACA,SAAAC,EACA,WAAY3B,EAAS,MAAM,aAAeA,EAAS,MAAM,aAC3D,CACF,CACF,EC/uBO,SAASsD,GACdC,EAAqB,cACrBC,EACe,CAEf,IAAIC,EAAeF,EACnB,GAAIA,IAAS,eAAiB,CAACC,EAAQ,CACrC,IAAME,EAASC,EAAe,EAC1BD,IACFD,EAAeC,EAAO,UAI1B,OAAQD,EAAc,CACpB,IAAK,cACH,OAAO,IAAIG,EACb,IAAK,SACH,OAAO,IAAIC,EAAeL,CAAM,EAClC,IAAK,SACH,MAAM,IAAI,MACR,8GACF,EACF,IAAK,SACH,MAAM,IAAI,MACR,8GACF,EACF,QACE,MAAM,IAAI,MAAM,qBAAqBC,mCAA8C,CACvF,CACF","names":["existsSync","readFileSync","writeFileSync","mkdirSync","path","os","chalk","prompts","chalk","logger","args","AUTH_DIR","path","os","AUTH_FILE","loadAuthConfig","existsSync","data","readFileSync","saveAuthConfig","config","mkdirSync","writeFileSync","resolveToken","explicitKey","type","envOAuth","envClaudeCodeOAuth","envApiKey","stored","homeDir","storePaths","p","t","readTokenFromAuthProfiles","filePath","fallbackToken","id","cred","ANTHROPIC_MODELS","handleApiKey","envKey","preview","useExisting","prompts","apiKey","v","handleSetupToken","logger","boxWidth","line1","line2","chalk","token","tokenName","selectAuthMethod","provider","method","selectModel","choices","m","current","label","modelId","runModelSetup","auth","ensureCredentials","TOOL_DEFINITIONS","getAnthropicTools","enabledTools","name","description","input_schema","getLegacyTools","formatToolsForSystemPrompt","agenticTools","lines","tool","ALL_TOOL_NAMES","t","DEFAULT_MODEL","DEFAULT_MAX_TOKENS","ClaudeProvider","apiKey","resolved","resolveToken","messages","options","model","maxTokens","systemMsg","m","conversationMsgs","body","getLegacyTools","response","systemPrompt","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","execa","DEFAULT_MODEL","DEFAULT_MAX_TOKENS","CLI_MODEL_ALIASES","ClaudeCodeProvider","stored","loadAuthConfig","resolved","resolveToken","messages","options","systemPrompt","tools","model","maxTokens","body","headers","res","errorText","response","block","cliModel","systemMsg","m","prompt","args","env","result","execa","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","createProvider","name","apiKey","resolvedName","stored","loadAuthConfig","ClaudeCodeProvider","ClaudeProvider"]}
|