@argentic-ai/client 0.1.14 → 0.2.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/README.md CHANGED
@@ -8,11 +8,9 @@ Each tool call the agent makes comes back to your code to carry out. If the proc
8
8
 
9
9
  ## Try it
10
10
 
11
- ```
12
- npx @argentic-ai/client demo
13
- ```
11
+ Install it below and run the example under Use. To compare Argentic with a plain loop on a scripted scenario, run `npx @argentic-ai/check` (see the [repo README](../../README.md#try-it)).
14
12
 
15
- Set `ARGENTIC_API_KEY` and `MODEL_KEY` first. Press Ctrl-C while it works, run it again, and it skips what already finished. Details in the [repo README](../../README.md#run-the-demo-2-minutes).
13
+ Client 0.2.0 removed `npx @argentic-ai/client demo` and the `argentic` command with it, and renamed `runDurable` to `run` and `DurableTool` to `Tool`. The old names still work as deprecated aliases (Python: `run_durable` is now `run`, with the old name kept and a `DeprecationWarning`). Nothing else in the library changed.
16
14
 
17
15
  ## Install
18
16
 
@@ -20,7 +18,7 @@ Set `ARGENTIC_API_KEY` and `MODEL_KEY` first. Press Ctrl-C while it works, run i
20
18
  npm i @argentic-ai/client
21
19
  ```
22
20
 
23
- - `@argentic-ai/client`: the client and `runDurable`, no dependencies, Node 22+.
21
+ - `@argentic-ai/client`: the client and `run`, no dependencies, Node 22+.
24
22
  - `@argentic-ai/client/ai-sdk`: `fromAiSdkTools`, needs `ai` v5, v6 or v7.
25
23
  - `@argentic-ai/client/tools`: `toolPack`, retry-safe tools for Stripe, Resend, SMTP, Slack, GitHub and a generic HTTP POST. See [TOOLS.md](TOOLS.md).
26
24
  - `@argentic-ai/client/mastra`: `fromMastraTools`, takes the `createTool()` tools you would pass to an Agent (the key is the tool name). Needs `@mastra/core` 1.x for the tools; the adapter itself imports nothing.
@@ -32,7 +30,7 @@ To vendor it instead: clone this repo and copy `argentic.ts`, `ai-sdk.ts` and `m
32
30
  ```ts
33
31
  import { tool } from 'ai'
34
32
  import { z } from 'zod'
35
- import { Argentic, runDurable } from '@argentic-ai/client'
33
+ import { Argentic, run } from '@argentic-ai/client'
36
34
  import { fromAiSdkTools } from '@argentic-ai/client/ai-sdk'
37
35
 
38
36
  const tools = { refund_order: tool({
@@ -43,7 +41,7 @@ const tools = { refund_order: tool({
43
41
 
44
42
  // apiKey defaults to ARGENTIC_API_KEY and modelKey to MODEL_KEY
45
43
  const client = new Argentic()
46
- const out = await runDurable({
44
+ const out = await run({
47
45
  client, agent: 'my-agent', runId: 'order-1182', ledgerPath: '.argentic-ledger.json',
48
46
  task: 'Refund order 1182.', system: 'Call one tool per turn.',
49
47
  tools: await fromAiSdkTools(tools),
@@ -51,11 +49,11 @@ const out = await runDurable({
51
49
  console.log(out.reply)
52
50
  ```
53
51
 
54
- Keep `agent` and `runId` the same across restarts. A request that takes longer than `timeoutMs` (default 300000, 0 for no limit) counts as a network failure and is retried with the same key. If the ledger file is not valid JSON, `runDurable` warns on stderr, sets the file aside as `<path>.corrupt` and starts empty, so finished actions may run again. `client.replay(agent, wakeId)` re-renders a wake's prompts from the journal and compares them to what the model saw; it calls no model and spends nothing.
52
+ Keep `agent` and `runId` the same across restarts. A request that takes longer than `timeoutMs` (default 300000, 0 for no limit) counts as a network failure and is retried with the same key. If the ledger file is not valid JSON, `run` warns on stderr, sets the file aside as `<path>.corrupt` and starts empty, so finished actions may run again. `client.replay(agent, wakeId)` re-renders a wake's prompts from the journal and compares them to what the model saw; it calls no model and spends nothing.
55
53
 
56
54
  ## Once per operation (`once_per`, 0.1.7)
57
55
 
58
- A tool can declare `once_per`, a key such as `refund:{order_id}` filled in from the call's arguments. Argentic then refuses a second call with the same key, in this wake or any later one. Set it on a `DurableTool` directly, or pass `{ oncePer: { refund_order: 'refund:{order_id}' } }` as the second argument to `fromAiSdkTools` or `fromMastraTools`. The template is checked before the request is sent. See [TOOLS.md](TOOLS.md#declaring-a-tool-once_per). 0.1.6 and earlier dropped the field.
56
+ A tool can declare `once_per`, a key such as `refund:{order_id}` filled in from the call's arguments. Argentic then refuses a second call with the same key, in this wake or any later one. Set it on a `Tool` directly, or pass `{ oncePer: { refund_order: 'refund:{order_id}' } }` as the second argument to `fromAiSdkTools` or `fromMastraTools`. The template is checked before the request is sent. See [TOOLS.md](TOOLS.md#declaring-a-tool-once_per). 0.1.6 and earlier dropped the field.
59
57
 
60
58
  ## Tools, context and deferred loading (0.1.10)
61
59
 
@@ -68,7 +66,7 @@ const out = await client.wake('support-bot', { message: 'Book Tuesday.', model:
68
66
 
69
67
  ## Structured tool results (0.1.11)
70
68
 
71
- `runDurable` answers the tools an agent asked for with a text message the model reads, and now also sends the same results as the `results` field of the wake: `[{ seq, result }]`, where `seq` is the action's `seq` and `result` is exactly what the tool returned (or the `{ error }` object when it threw). The API journals each as the real result of that call. The first wake of a run has none, and a result the ledger already held is sent again like the others. Pass `structuredResults: false` to send the text only. If the API you talk to does not know the field yet, it answers 400 and names `results`; the client sends that wake again once without it, under the same key, and leaves it out from then on. Calling `client.wake()` yourself, add `results` to the body next to the `message`, never with `fire_due`, at most 100 entries.
69
+ `run` answers the tools an agent asked for with a text message the model reads, and now also sends the same results as the `results` field of the wake: `[{ seq, result }]`, where `seq` is the action's `seq` and `result` is exactly what the tool returned (or the `{ error }` object when it threw). The API journals each as the real result of that call. The first wake of a run has none, and a result the ledger already held is sent again like the others. Pass `structuredResults: false` to send the text only. If the API you talk to does not know the field yet, it answers 400 and names `results`; the client sends that wake again once without it, under the same key, and leaves it out from then on. Calling `client.wake()` yourself, add `results` to the body next to the `message`, never with `fire_due`, at most 100 entries.
72
70
 
73
71
  ## Waking on a schedule (handleDue)
74
72
 
package/dist/ai-sdk.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { type ToolSet } from 'ai';
2
- import { type DurableTool } from './argentic.ts';
2
+ import { type Tool } from './argentic.ts';
3
3
  /**
4
4
  * The tool name is the ToolSet key. Async because a JSON schema may be a promise.
5
5
  * `oncePer` maps a tool name to its `once_per` template, such as `{ refund_order: 'refund:{order_id}' }`.
6
6
  */
7
7
  export declare function fromAiSdkTools(tools: ToolSet, opts?: {
8
8
  oncePer?: Record<string, string>;
9
- }): Promise<DurableTool[]>;
9
+ }): Promise<Tool[]>;
@@ -306,7 +306,7 @@ export declare class Ledger {
306
306
  }
307
307
  /**
308
308
  * Throw this from a tool when the outcome is not known yet and the same call is safe to try again:
309
- * a network drop, a timeout, a provider that is down. runDurable does not write it to the ledger,
309
+ * a network drop, a timeout, a provider that is down. run does not write it to the ledger,
310
310
  * so it stops the run. Run it again and the action is asked for again with the same key.
311
311
  */
312
312
  export declare class RetryLater extends Error {
@@ -318,9 +318,11 @@ export interface ToolContext {
318
318
  /** `${agent}:${seq}`. Use it to make the side effect safe to repeat. */
319
319
  idempotencyKey: string;
320
320
  }
321
- export interface DurableTool extends ToolDecl {
321
+ export interface Tool extends ToolDecl {
322
322
  execute: (args: any, ctx: ToolContext) => unknown | Promise<unknown>;
323
323
  }
324
+ /** @deprecated Renamed to `Tool` in 0.2.0. Same type; this name stays so existing code keeps compiling. */
325
+ export type DurableTool = Tool;
324
326
  export interface ActionInfo {
325
327
  tool: string;
326
328
  seq: number;
@@ -334,7 +336,7 @@ export interface RunOptions {
334
336
  runId: string;
335
337
  task: string;
336
338
  system?: string;
337
- tools: DurableTool[];
339
+ tools: Tool[];
338
340
  ledgerPath: string;
339
341
  model?: ModelSpec;
340
342
  maxSteps?: number;
@@ -373,4 +375,6 @@ export interface RunResult {
373
375
  status: WakeStatus | 'max_steps';
374
376
  spentUsd: number;
375
377
  }
376
- export declare function runDurable(opts: RunOptions): Promise<RunResult>;
378
+ export declare function run(opts: RunOptions): Promise<RunResult>;
379
+ /** @deprecated Renamed to `run` in 0.2.0. Same function; this name stays so existing code keeps working. */
380
+ export declare const runDurable: typeof run;
package/dist/argentic.js CHANGED
@@ -1,4 +1,4 @@
1
- // Argentic core: a small client and a durable tool loop. No dependencies, Node 22+.
1
+ // Argentic core: a small client and a tool loop. No dependencies, Node 22+.
2
2
  // Argentic runs the agent loop; your code carries out the tool calls it asks for.
3
3
  import { createHmac, timingSafeEqual } from 'node:crypto';
4
4
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
@@ -453,10 +453,10 @@ export class Ledger {
453
453
  }
454
454
  }
455
455
  // ---------------------------------------------------------------------------
456
- // runDurable
456
+ // run
457
457
  /**
458
458
  * Throw this from a tool when the outcome is not known yet and the same call is safe to try again:
459
- * a network drop, a timeout, a provider that is down. runDurable does not write it to the ledger,
459
+ * a network drop, a timeout, a provider that is down. run does not write it to the ledger,
460
460
  * so it stops the run. Run it again and the action is asked for again with the same key.
461
461
  */
462
462
  export class RetryLater extends Error {
@@ -473,7 +473,7 @@ const safeKey = (s) => s.replace(/[^A-Za-z0-9\-_.:]/g, '-').slice(0, 128);
473
473
  * wake sends their results and the agent carries on from there with them, instead of the same key resuming it blind.
474
474
  */
475
475
  const carriesOn = (s) => s === 'turn_cap' || s === 'time_cap';
476
- export async function runDurable(opts) {
476
+ export async function run(opts) {
477
477
  const { client, agent, tools } = opts;
478
478
  const log = opts.log ?? ((line) => console.log(line));
479
479
  const maxSteps = opts.maxSteps ?? 8;
@@ -562,3 +562,5 @@ export async function runDurable(opts) {
562
562
  }
563
563
  return { reply, wakes, steps, status, spentUsd };
564
564
  }
565
+ /** @deprecated Renamed to `run` in 0.2.0. Same function; this name stays so existing code keeps working. */
566
+ export const runDurable = run;
package/dist/mastra.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type DurableTool } from './argentic.ts';
1
+ import { type Tool } from './argentic.ts';
2
2
  interface MastraToolLike {
3
3
  description?: string;
4
4
  inputSchema?: unknown;
@@ -10,5 +10,5 @@ interface MastraToolLike {
10
10
  */
11
11
  export declare function fromMastraTools(tools: Record<string, MastraToolLike>, opts?: {
12
12
  oncePer?: Record<string, string>;
13
- }): DurableTool[];
13
+ }): Tool[];
14
14
  export {};
package/dist/tools.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type DurableTool } from './argentic.ts';
1
+ import { type Tool } from './argentic.ts';
2
2
  /** What to do with a Slack, GitHub or SMTP action that may or may not have landed and cannot be checked. */
3
3
  export type Ambiguous = 'hold' | 'retry';
4
4
  export interface StripeOptions {
@@ -68,8 +68,8 @@ export interface ToolPackOptions {
68
68
  /** A key a provider accepts (Stripe allows 255 characters, Resend 256). */
69
69
  export declare function providerKey(key: string): string;
70
70
  /**
71
- * Tools for runDurable. Pass only the services you use; each tool is built from your options and your
71
+ * Tools for run. Pass only the services you use; each tool is built from your options and your
72
72
  * secrets, never from anything the model sends. With Resend and SMTP both set, Resend is `send_email`
73
73
  * and SMTP is `send_email_smtp`.
74
74
  */
75
- export declare function toolPack(opts: ToolPackOptions): DurableTool[];
75
+ export declare function toolPack(opts: ToolPackOptions): Tool[];
package/dist/tools.js CHANGED
@@ -547,7 +547,7 @@ function httpTools(rt, o) {
547
547
  }
548
548
  // ---------------------------------------------------------------------------
549
549
  /**
550
- * Tools for runDurable. Pass only the services you use; each tool is built from your options and your
550
+ * Tools for run. Pass only the services you use; each tool is built from your options and your
551
551
  * secrets, never from anything the model sends. With Resend and SMTP both set, Resend is `send_email`
552
552
  * and SMTP is `send_email_smtp`.
553
553
  */
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "@argentic-ai/client",
3
- "version": "0.1.14",
3
+ "version": "0.2.0",
4
4
  "description": "Argentic client for TypeScript. The engine runs your agent for hours, rebuilding each prompt from a journal (state blocks plus a recent tail of about 40K tokens) instead of an ever-growing transcript. Adapters for the Vercel AI SDK and Mastra.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "engines": { "node": ">=22" },
8
8
  "files": ["dist", "README.md", "LICENSE"],
9
9
  "main": "./dist/argentic.js",
10
- "bin": { "argentic": "./dist/cli.js" },
11
10
  "types": "./dist/argentic.d.ts",
12
11
  "exports": {
13
12
  ".": { "types": "./dist/argentic.d.ts", "default": "./dist/argentic.js" },
package/dist/cli.d.ts DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
package/dist/cli.js DELETED
@@ -1,197 +0,0 @@
1
- #!/usr/bin/env node
2
- // `npx @argentic-ai/client demo`: kill an agent mid-run, run it again, and watch it skip what already finished.
3
- // Uses only the published client. State lives in ./.argentic-demo/ so a rerun from the same folder picks up the same run.
4
- import { appendFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
5
- import { join } from 'node:path';
6
- import { Argentic, freeModel, hintFor, runDurable } from "./argentic.js";
7
- const color = process.stdout.isTTY && !process.env.NO_COLOR;
8
- const paint = (code) => (s) => (color ? `\x1b[${code}m${s}\x1b[0m` : s);
9
- const dim = paint('2');
10
- const bold = paint('1');
11
- const green = paint('32');
12
- const yellow = paint('33');
13
- const out = (s = '') => process.stdout.write(s + '\n');
14
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
15
- const system = 'Tool calls are requests: their outcomes arrive in the next message. ' +
16
- 'Call one tool per turn. When everything is done, reply with a one-line summary and no tool call.';
17
- const task = 'Order 1182 (ticket T-77, customer dana@example.com) arrived broken. Refund the $42 order, ' +
18
- 'email the customer that the refund is on its way, then close the ticket. Do one step at a time.';
19
- const usage = `Usage: npx @argentic-ai/client demo [--fresh] [--kill-after N]
20
-
21
- --fresh start a new run (forget the one in ./.argentic-demo/)
22
- --kill-after N kill the process yourself after N actions, instead of pressing Ctrl-C
23
-
24
- Needs ARGENTIC_API_KEY (from https://getargentic.com/platform) and MODEL_KEY
25
- (your model provider's key; the default model is a free NVIDIA one, key from https://build.nvidia.com).`;
26
- /** The side effects land in outbox.jsonl, keyed by the action's stable key, so a repeat is visible and harmless. */
27
- function makeTools(outbox, workMs) {
28
- const sent = (key) => existsSync(outbox) && readFileSync(outbox, 'utf8').split('\n').some((l) => l && JSON.parse(l).key === key);
29
- const act = async (key, seq, tool, what, data, result) => {
30
- process.stdout.write(` ${bold('▸')} #${seq} ${tool.padEnd(14)} ${what} ${dim('…')}`);
31
- await sleep(workMs);
32
- if (sent(key)) {
33
- out(` ${yellow('already in the outbox under this key, not sent again')}`);
34
- }
35
- else {
36
- appendFileSync(outbox, JSON.stringify({ key, tool, ...data, at: new Date().toISOString() }) + '\n');
37
- out(` ${green('done')}`);
38
- }
39
- return result;
40
- };
41
- const str = { type: 'string' };
42
- return [
43
- {
44
- name: 'refund_order',
45
- description: 'Refund an order.',
46
- parameters: { type: 'object', properties: { order_id: str, amount_usd: { type: 'number' } }, required: ['order_id', 'amount_usd'] },
47
- execute: (a, { idempotencyKey, seq }) => act(idempotencyKey, seq, 'refund_order', `$${a.amount_usd} on order ${a.order_id}`, a, { refunded: true, order_id: a.order_id, amount_usd: a.amount_usd }),
48
- },
49
- {
50
- name: 'send_email',
51
- description: 'Send an email to a customer.',
52
- parameters: { type: 'object', properties: { to: str, subject: str, body: str }, required: ['to', 'subject', 'body'] },
53
- execute: (a, { idempotencyKey, seq }) => act(idempotencyKey, seq, 'send_email', `to ${a.to}`, a, { sent: true, to: a.to }),
54
- },
55
- {
56
- name: 'close_ticket',
57
- description: 'Close a support ticket with a note.',
58
- parameters: { type: 'object', properties: { ticket_id: str, note: str }, required: ['ticket_id', 'note'] },
59
- execute: (a, { idempotencyKey, seq }) => act(idempotencyKey, seq, 'close_ticket', `ticket ${a.ticket_id}`, a, { closed: true, ticket_id: a.ticket_id }),
60
- },
61
- ];
62
- }
63
- function modelFromEnv() {
64
- const e = process.env;
65
- if (!e.ARGENTIC_MODEL_ID)
66
- return freeModel;
67
- return {
68
- provider: e.ARGENTIC_MODEL_PROVIDER ?? freeModel.provider,
69
- id: e.ARGENTIC_MODEL_ID,
70
- prices: { in: Number(e.ARGENTIC_PRICE_IN ?? 0), out: Number(e.ARGENTIC_PRICE_OUT ?? 0) },
71
- };
72
- }
73
- async function demo(argv) {
74
- const apiKey = process.env.ARGENTIC_API_KEY;
75
- const modelKey = process.env.MODEL_KEY;
76
- if (!apiKey || !modelKey) {
77
- console.error(`Set ARGENTIC_API_KEY and MODEL_KEY first.\n\n${usage}`);
78
- process.exit(1);
79
- }
80
- const at = argv.indexOf('--kill-after');
81
- const killAfter = at >= 0 ? Number(argv[at + 1]) || undefined : undefined;
82
- const dir = '.argentic-demo';
83
- const statePath = join(dir, 'run.json');
84
- const ledgerPath = join(dir, 'ledger.json');
85
- const outbox = join(dir, 'outbox.jsonl');
86
- if (argv.includes('--fresh'))
87
- rmSync(dir, { recursive: true, force: true });
88
- mkdirSync(dir, { recursive: true });
89
- const resuming = existsSync(statePath);
90
- if (!resuming) {
91
- const id = Math.random().toString(36).slice(2, 8);
92
- writeFileSync(statePath, JSON.stringify({ agent: `kill-demo-${id}`, runId: `run-${id}` }));
93
- }
94
- const { agent, runId } = JSON.parse(readFileSync(statePath, 'utf8'));
95
- const finishedBefore = existsSync(ledgerPath) ? Object.keys(JSON.parse(readFileSync(ledgerPath, 'utf8')).entries ?? {}).length : 0;
96
- out(bold('Argentic: kill it mid-run'));
97
- out(dim(`agent ${agent}, run ${runId}, state in ./${dir}/`));
98
- out();
99
- if (!resuming) {
100
- out('A support agent works one ticket in three actions: refund the order, email the customer, close the ticket.');
101
- out(`Press ${bold('Ctrl-C')} while it works, then run the same command again.`);
102
- }
103
- else {
104
- out(`Picking up ${runId}: ${finishedBefore} action${finishedBefore === 1 ? '' : 's'} finished before it stopped.`);
105
- out('Same run, same keys. Finished actions are skipped, the rest carry on.');
106
- }
107
- out();
108
- process.on('SIGINT', () => {
109
- out('\n');
110
- out(yellow('Killed mid-run.') + ' Run the same command again and it picks up where it stopped.');
111
- process.exit(130);
112
- });
113
- const client = new Argentic({ apiKey, modelKey });
114
- let ran = 0;
115
- let hinted = resuming || killAfter !== undefined;
116
- const res = await runDurable({
117
- client,
118
- agent,
119
- runId,
120
- task,
121
- system,
122
- tools: makeTools(outbox, Number(process.env.ARGENTIC_DEMO_WORK_MS ?? 1500)),
123
- ledgerPath,
124
- model: modelFromEnv(),
125
- maxSteps: 10,
126
- crashAfterActions: killAfter,
127
- log: () => { },
128
- onWakeStart: ({ step }) => {
129
- if (step === 0 && !resuming)
130
- out(dim(' The first answer can take 15 seconds and nothing runs before it, so wait for the first action before pressing Ctrl-C.'));
131
- process.stdout.write(dim(` model turn ${step + 1}: asking the model… `));
132
- },
133
- onWake: ({ replayed }) => {
134
- if (replayed)
135
- out(dim('answer already in the journal, sent back without calling the model again'));
136
- else
137
- out(dim('answered'));
138
- },
139
- onSkip: ({ tool, seq }) => out(` ${dim('↷')} #${seq} ${tool.padEnd(14)} ${dim('finished on an earlier run, skipped')}`),
140
- onAction: ({ executed }) => {
141
- ran = executed;
142
- if (killAfter && executed >= killAfter)
143
- out(yellow(`\nKilling the process now (--kill-after ${killAfter}). Run the same command without it.`));
144
- else if (!hinted) {
145
- hinted = true;
146
- out(yellow(' Now is a good time to press Ctrl-C.'));
147
- }
148
- },
149
- });
150
- out();
151
- out(`${bold('Reply:')} ${res.reply || dim('(none)')}`);
152
- out(dim(`status ${res.status}, ${res.steps} model turns, ${ran} action${ran === 1 ? '' : 's'} run in this process, spent $${res.spentUsd.toFixed(4)}`));
153
- const counts = { refund_order: 0, send_email: 0, close_ticket: 0 };
154
- if (existsSync(outbox))
155
- for (const l of readFileSync(outbox, 'utf8').split('\n'))
156
- if (l)
157
- counts[JSON.parse(l).tool] = (counts[JSON.parse(l).tool] ?? 0) + 1;
158
- out();
159
- out(bold('What happened, from the outbox') + dim(` (./${outbox})`));
160
- for (const [tool, n] of Object.entries(counts))
161
- out(` ${tool.padEnd(14)} ${n === 1 ? green('1 time') : yellow(`${n} times`)}`);
162
- out();
163
- out(bold('Proof of what ran') + dim(' (re-renders each prompt from the journal and compares it with what the model was sent; free)'));
164
- let clean = true;
165
- for (const wake of res.wakes) {
166
- const r = await client.replay(agent, wake);
167
- const ok = r.mismatched === 0 && r.unverifiable === 0;
168
- clean &&= ok;
169
- out(` ${wake} ${ok ? green('all match') : yellow(`${r.mismatched} differ, ${r.unverifiable} not checkable`)}`);
170
- }
171
- if (res.status !== 'done')
172
- out(yellow(`\nThe run stopped with status ${res.status}. Run the same command again to carry on.`));
173
- const dash = (process.env.ARGENTIC_DASHBOARD_URL ?? 'https://getargentic.com').replace(/\/+$/, '');
174
- out();
175
- out(`See it in the dashboard: ${dash}/platform/ ${dim('->')} Runs ${dim('->')} ${agent}`);
176
- out(dim('Open the oldest run (the one you killed) and press Check under Proof of what ran.'));
177
- if (resuming && res.status === 'done') {
178
- out();
179
- out(`${bold('Worth showing someone?')} On that run page, press Share proof for a read-only link.`);
180
- out(dim('It shows what ran and each action’s key. Messages and tool data stay private, and you can stop sharing any time.'));
181
- }
182
- out(dim('Start over with --fresh.'));
183
- if (!clean)
184
- process.exitCode = 1;
185
- }
186
- const [cmd, ...rest] = process.argv.slice(2);
187
- if (cmd !== 'demo') {
188
- out(usage);
189
- process.exit(cmd === undefined || cmd === '--help' || cmd === '-h' ? 0 : 1);
190
- }
191
- await demo(rest).catch((e) => {
192
- const hint = hintFor(e);
193
- if (!hint)
194
- throw e;
195
- console.error(`\n${e instanceof Error ? e.message : String(e)}\n${hint}`);
196
- process.exit(1);
197
- });