@piagent/platform 1.25.0 → 1.26.1

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.
Files changed (84) hide show
  1. package/CHANGELOG.md +39 -2
  2. package/README.md +1 -1
  3. package/README.vi.md +1 -1
  4. package/docs/command-reference-vietnamese.md +2 -2
  5. package/docs/company-dev-commands.md +68 -0
  6. package/docs/distribution-standard.md +6 -6
  7. package/docs/harness-conformance.md +154 -0
  8. package/docs/languages.json +2 -2
  9. package/docs/managed-local-acceptance.md +38 -0
  10. package/docs/operator-manual-vietnamese.md +7 -7
  11. package/docs/publishing-for-teams.md +2 -0
  12. package/docs/release-install-policy.md +2 -2
  13. package/docs/team-onboarding.md +2 -2
  14. package/docs-site/architecture.html +1 -1
  15. package/docs-site/benchmark.html +1 -1
  16. package/docs-site/commands.html +1 -1
  17. package/docs-site/content/en/index.html +4 -4
  18. package/docs-site/content/en/whats-new.html +12 -0
  19. package/docs-site/content/index.html +4 -4
  20. package/docs-site/content/whats-new.html +12 -0
  21. package/docs-site/context-engine.html +1 -1
  22. package/docs-site/ecosystem.html +1 -1
  23. package/docs-site/en/architecture.html +1 -1
  24. package/docs-site/en/benchmark.html +1 -1
  25. package/docs-site/en/commands.html +1 -1
  26. package/docs-site/en/context-engine.html +1 -1
  27. package/docs-site/en/ecosystem.html +1 -1
  28. package/docs-site/en/index.html +5 -5
  29. package/docs-site/en/mcp-approval.html +1 -1
  30. package/docs-site/en/mcp-auth.html +1 -1
  31. package/docs-site/en/mcp-servers.html +1 -1
  32. package/docs-site/en/mcp.html +1 -1
  33. package/docs-site/en/permissions.html +1 -1
  34. package/docs-site/en/profiles.html +1 -1
  35. package/docs-site/en/quickstart.html +1 -1
  36. package/docs-site/en/runtime.html +1 -1
  37. package/docs-site/en/scope.html +1 -1
  38. package/docs-site/en/security.html +1 -1
  39. package/docs-site/en/team.html +1 -1
  40. package/docs-site/en/whats-new.html +13 -1
  41. package/docs-site/en/windows.html +1 -1
  42. package/docs-site/en/workflows.html +1 -1
  43. package/docs-site/index.html +5 -5
  44. package/docs-site/mcp-approval.html +1 -1
  45. package/docs-site/mcp-auth.html +1 -1
  46. package/docs-site/mcp-servers.html +1 -1
  47. package/docs-site/mcp.html +1 -1
  48. package/docs-site/permissions.html +1 -1
  49. package/docs-site/profiles.html +1 -1
  50. package/docs-site/quickstart.html +1 -1
  51. package/docs-site/runtime.html +1 -1
  52. package/docs-site/scope.html +1 -1
  53. package/docs-site/security.html +1 -1
  54. package/docs-site/team.html +1 -1
  55. package/docs-site/whats-new.html +13 -1
  56. package/docs-site/windows.html +1 -1
  57. package/docs-site/workflows.html +1 -1
  58. package/evals/private-holdout-v1/public-exposure.v2.json +2 -2
  59. package/package.json +1 -1
  60. package/packages/piagent-core/managed/background-process.mjs +101 -0
  61. package/packages/piagent-core/managed/checkpoints.mjs +69 -0
  62. package/packages/piagent-core/managed/command-tools.mjs +5 -2
  63. package/packages/piagent-core/managed/continuation.mjs +8 -0
  64. package/packages/piagent-core/managed/doctor.mjs +4 -4
  65. package/packages/piagent-core/managed/pi-host.mjs +19 -0
  66. package/packages/piagent-core/managed/sandbox-diagnostic.mjs +4 -2
  67. package/packages/piagent-core/managed/session.mjs +3 -3
  68. package/packages/piagent-core/managed/tool-boundary.mjs +45 -18
  69. package/packages/piagent-core/managed/tool-worker.mjs +3 -1
  70. package/packages/piagent-core/managed/ts-diagnostics-server.mjs +102 -0
  71. package/packages/piagent-core/managed/ts-diagnostics.mjs +59 -0
  72. package/packages/piagent-core/managed/workflow.mjs +9 -1
  73. package/packages/piagent-core/package.json +1 -1
  74. package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-B8CsDh3B.js → MarkdownMessage-BpNzOP6f.js} +1 -1
  75. package/packages/piagent-webui/dist/client/assets/index-DWkb1mpZ.js +232 -0
  76. package/packages/piagent-webui/dist/client/index.html +1 -1
  77. package/packages/piagent-webui/gateway/managed-gateway.mjs +2 -1
  78. package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +4 -4
  79. package/packages/piagent-webui/server/transcript-projection.ts +9 -3
  80. package/packages/piagent-webui/shared/process-copy.ts +10 -0
  81. package/schemas/piagent-webui/transcript-v1.schema.json +13 -1
  82. package/scripts/company-terminal.mjs +12 -0
  83. package/scripts/piagent-studio.mjs +20 -7
  84. package/packages/piagent-webui/dist/client/assets/index-CiLLJu4R.js +0 -231
@@ -7,7 +7,7 @@
7
7
  <meta name="csp-nonce" content="__PIAGENT_CSP_NONCE__" />
8
8
  <meta name="piagent-webui-mode" content="__PIAGENT_WEBUI_MODE__" />
9
9
  <title>Piagent WebUI</title>
10
- <script type="module" crossorigin src="/assets/index-CiLLJu4R.js"></script>
10
+ <script type="module" crossorigin src="/assets/index-DWkb1mpZ.js"></script>
11
11
  <link rel="stylesheet" crossorigin href="/assets/index-B5XOKQy-.css">
12
12
  </head>
13
13
  <body>
@@ -2,6 +2,7 @@ import { managedThinkingLevels, nearestLevel } from '../../piagent-core/managed/
2
2
  import { ManagedBrokerClient } from '../../piagent-core/managed/broker-client.mjs';
3
3
  import { ManagedSession } from '../../piagent-core/managed/session.mjs';
4
4
  import { fastAllowed } from '../../piagent-core/managed/fast-mode.mjs';
5
+ import { PI_HOST_VERSION } from '../../piagent-core/managed/pi-host.mjs';
5
6
  import fs from 'node:fs';
6
7
 
7
8
  // A company model as the new chat page sees it: the thinking levels its
@@ -43,7 +44,7 @@ export async function startManagedGateway({ config, configPath, cwd, agentDir, p
43
44
  return {session:runtime.session,dispose:()=>runtime.dispose()};
44
45
  } catch(error) { if(runtime) await runtime.dispose(); else await broker.dispose(); throw error; }
45
46
  };
46
- const gateway=await startPiagentGateway({packageRoot,agentDir,expectedPiVersion:'0.87.1',managed:{host,models,runtimeFactory:factory,project:registerProject?cwd:null}});
47
+ const gateway=await startPiagentGateway({packageRoot,agentDir,expectedPiVersion:PI_HOST_VERSION,managed:{host,models,runtimeFactory:factory,project:registerProject?cwd:null}});
47
48
  return gateway;
48
49
  } finally { if(probe) await probe.dispose(); else await probeBroker.dispose(); }
49
50
  }
@@ -218,8 +218,8 @@ export class SessionRuntimeSupervisor {
218
218
  expectedOperationRef: string | null; messageRequestId?: string; images?: unknown[] }, sessionRevision: string, options: { deferDispatch?: boolean } = {}):
219
219
  Promise<SessionSendResult> {
220
220
  await this.acquire(sessionRef);
221
- const active = this.#active.get(sessionRef), session = active?.runtime.session;
222
- if (!active || !session) throw new Error("session-runtime-unavailable");
221
+ const active = this.#active.get(sessionRef), session = active?.runtime.session; if (!active || !session) throw new Error("session-runtime-unavailable");
222
+ if (payload.delivery === "new-operation" && payload.expectedOperationRef === null && active.operationRef && !active.settling && session.isStreaming && !active.runtime.beginWireOperation) payload = { ...payload, delivery: "follow-up", expectedOperationRef: active.operationRef }; // a message from another tab waits for the running turn, as in the Terminal
223
223
  if (payload.delivery !== "new-operation") {
224
224
  if (active.runtime.beginWireOperation) throw new Error("provider-wire-queued-input-unsupported");
225
225
  if (!active.operationRef || active.operationRef !== payload.expectedOperationRef || active.settling || !session.isStreaming) throw new Error("session-operation-conflict");
@@ -405,11 +405,11 @@ export class SessionRuntimeSupervisor {
405
405
  await this.acquire(sessionRef);
406
406
  const active = this.#active.get(sessionRef), session = active?.runtime.session;
407
407
  if (!active || !session?.sessionManager) throw new Error("session-runtime-unavailable");
408
- if (active.operationRef || !session.isIdle) throw new Error("session-runtime-busy");
408
+ // One appended entry the turn neither reads nor waits on, so no busy check: renamed mid-turn, as Pi's own /name is.
409
409
  const current = String(session.sessionManager.getSessionName?.() ?? "").trim();
410
410
  if (current === title.trim()) return "no-change";
411
411
  session.sessionManager.appendSessionInfo(title);
412
- this.#touchCreated(sessionRef);
412
+ const created = this.#created.get(sessionRef); if (created) created.name = title; this.#touchCreated(sessionRef); // listed from memory until its first message
413
413
  return "renamed";
414
414
  }
415
415
  async fork(sessionRef: string, entryRef: string | null, title: string | null): Promise<string> {
@@ -37,9 +37,10 @@ type TranscriptItem = { messageRef: string; parentMessageRef: string | null; rol
37
37
  summary?: ToolSummary; change?: ToolChange; result?: ToolResult }>; usage?: TurnUsage; model?: string; process?: HarnessProcess; turnEnd?: TurnEnd };
38
38
  type HarnessFinding = { severity: "blocking" | "major" | "minor"; file: string; line: number | null; issue: string };
39
39
  type HarnessIssue = { kind: "wrong_premise" | "conflicts_with_request" | "ambiguous" | "out_of_scope"; detail: string };
40
- type HarnessProcess = { phase: "plan" | "verify" | "review" | "final" | "objection" | "answer" | "claims" | "rejudge" | "dispute" | "continue"; outcome?: string; loop?: number; maxLoops?: number;
40
+ type HarnessProcess = { phase: "plan" | "verify" | "review" | "final" | "objection" | "answer" | "claims" | "rejudge" | "dispute" | "continue" | "undo"; outcome?: string; loop?: number; maxLoops?: number;
41
41
  verified?: boolean; reviewed?: boolean; blockingOpen?: number; planOpen?: number; planDone?: number; planSteps?: number; round?: number; maxRounds?: number; idle?: number; planSkipped?: true; verifyPolicy?: string; reviewPolicy?: string; reviewUnavailable?: string;
42
- findings?: HarnessFinding[]; role?: "scout" | "research" | "verify" | "review"; by?: "main" | "harness"; disputes?: number; unchanged?: true; issues?: HarnessIssue[]; claims?: { claim: string }[] };
42
+ findings?: HarnessFinding[]; role?: "scout" | "research" | "verify" | "review"; by?: "main" | "harness"; disputes?: number; unchanged?: true; issues?: HarnessIssue[]; claims?: { claim: string }[];
43
+ undo?: { result: "done" | "refused" | "busy" | "none" | "failed"; restored: number; removed: number; files: string[]; more?: boolean } };
43
44
  export type TranscriptDocument = { schemaVersion: 1; version: "piagent-webui-transcript-v1"; generatedAt: string; identity: TranscriptIdentity;
44
45
  revision: TranscriptRevision; eventCursor: string; state: "ready" | "unavailable"; items: TranscriptItem[];
45
46
  page: { beforeCursor: string | null; nextBeforeCursor: string | null; hasOlder: boolean; limit: number; truncated: boolean }; reasonCode: string | null };
@@ -197,7 +198,8 @@ function toolCalls(message: any, sessionRef: string, role: TranscriptItem["role"
197
198
  const PROCESS_OUTCOMES = ["no_change", "interrupted", "disputed", "blocking_open", "unverified", "review_unavailable", "unreviewed", "clean"];
198
199
  // A helper's objection to its brief, a failed claim, a disagreement handed to the member.
199
200
  // "continue": the harness sent the agent back to a checklist with open steps.
200
- const PROCESS_PHASES = ["plan", "verify", "review", "final", "objection", "answer", "claims", "rejudge", "dispute", "continue"];
201
+ // "undo": the member's /undo put the last turn's files back (checkpoints.mjs).
202
+ const PROCESS_PHASES = ["plan", "verify", "review", "final", "objection", "answer", "claims", "rejudge", "dispute", "continue", "undo"];
201
203
  const ISSUE_KINDS = ["wrong_premise", "conflicts_with_request", "ambiguous", "out_of_scope"], HELPER_ROLES = ["scout", "research", "verify", "review"];
202
204
  const POLICY_MODES = ["off", "suggest", "require"];
203
205
  function harnessProcess(details: any): HarnessProcess | null {
@@ -233,6 +235,10 @@ function harnessProcess(details: any): HarnessProcess | null {
233
235
  .map((i: any) => ({ kind: i.kind, detail: safeText(i.detail).full.slice(0, 400) }));
234
236
  if (Array.isArray(details.claims)) out.claims = details.claims.filter((c: any) => typeof c?.claim === "string" && c.claim.trim()).slice(0, 10)
235
237
  .map((c: any) => ({ claim: safeText(c.claim).full.slice(0, 500) }));
238
+ const count = (n: unknown) => Number.isInteger(n) && (n as number) >= 0 ? Math.min(n as number, 100000) : 0;
239
+ if (details.phase === "undo" && ["done", "refused", "busy", "none", "failed"].includes(details.undo?.result)) out.undo = { result: details.undo.result,
240
+ restored: count(details.undo.restored), removed: count(details.undo.removed), ...(details.undo.more === true ? { more: true } : {}),
241
+ files: (Array.isArray(details.undo.files) ? details.undo.files : []).filter((f: unknown) => typeof f === "string" && f).slice(0, 5).map((f: string) => safeText(f).full.slice(0, 300)) };
236
242
  return out;
237
243
  }
238
244
  function role(message: any): TranscriptItem["role"] | null {
@@ -8,6 +8,7 @@ export type ProcessCopyInput = {
8
8
  disputes?: number; unchanged?: boolean;
9
9
  findings?: Array<{ severity: string; file?: string; line?: number | null; issue: string }>;
10
10
  claims?: Array<{ claim: string }>; issues?: Array<{ kind: string; detail: string }>;
11
+ undo?: { result: string; restored: number; removed: number; files: string[]; more?: boolean };
11
12
  };
12
13
  const pick = (locale: CopyLocale, vi: string, en: string) => locale === "vi" ? vi : en;
13
14
  const SEVERITY: Record<string, [string, string]> = { blocking: ["blocking", "blocking"], major: ["major", "major"], minor: ["minor", "minor"] };
@@ -51,6 +52,15 @@ export function headlineText(process: ProcessCopyInput, locale: CopyLocale): str
51
52
  case "claims": return pick(locale, `Harness: ${who} đánh fail ${count} claim, gửi lại main agent`, `Harness: ${who} marked ${count} claim(s) as failed; sent back to the main agent`);
52
53
  case "rejudge": return process.role === "verify" ? pick(locale, `Harness: ${who} check lại các claim cùng câu trả lời của main agent`, `Harness: ${who} checks the claims again with the main agent's answer`)
53
54
  : pick(locale, `Harness: main agent giải thích thay vì fix, ${who} xem xét lại lời giải thích`, `Harness: the main agent answered instead of fixing; ${who} judges the answer`);
55
+ case "undo": {
56
+ const u = process.undo, files = u?.files?.length ? ` (${u.files.join(", ")})` : "";
57
+ if (u?.result === "done") return pick(locale, `Đã hoàn tác lượt trước: ${u.restored} file trả về như trước, ${u.removed} file lượt đó tạo đã xoá${files}.${u.more ? " Gửi /undo lần nữa để hoàn tác lượt trước nữa." : ""} Commit trong lượt đó vẫn giữ.`,
58
+ `Undid the last turn: ${u.restored} file(s) restored, ${u.removed} file(s) it created removed${files}.${u.more ? " Send /undo again to undo the turn before." : ""} Commits made in that turn stay.`);
59
+ if (u?.result === "refused") return pick(locale, `Không hoàn tác: file lượt trước đã sửa lại bị đổi tiếp sau đó${files}; hoàn tác sẽ ghi đè thay đổi đó.`, `Not undone: a file the last turn changed was changed again since${files}; undoing would overwrite that change.`);
60
+ if (u?.result === "busy") return pick(locale, "Đang có một lượt chạy: chờ nó xong (hoặc dừng nó) rồi /undo.", "A turn is running: wait for it (or stop it), then /undo.");
61
+ if (u?.result === "none") return pick(locale, "Không có lượt nào để hoàn tác (chỉ áp dụng cho project là git repository, từ khi mở cuộc trò chuyện).", "There is no turn to undo (git repositories only, since the conversation opened).");
62
+ return pick(locale, "Không đọc được trạng thái project; không hoàn tác.", "The project's state could not be read; nothing was undone.");
63
+ }
54
64
  case "dispute": return pick(locale, `Main agent và ${who} vẫn conflict sau 2 lượt, bạn quyết định`, `The main agent and the ${who} still disagree after two rounds; your call`);
55
65
  default: return "";
56
66
  }
@@ -157,7 +157,19 @@
157
157
  "additionalProperties": false,
158
158
  "required": ["phase"],
159
159
  "properties": {
160
- "phase": { "enum": ["plan", "verify", "review", "final", "objection", "answer", "claims", "rejudge", "dispute", "continue"] },
160
+ "phase": { "enum": ["plan", "verify", "review", "final", "objection", "answer", "claims", "rejudge", "dispute", "continue", "undo"] },
161
+ "undo": {
162
+ "type": "object",
163
+ "additionalProperties": false,
164
+ "required": ["result", "restored", "removed", "files"],
165
+ "properties": {
166
+ "result": { "enum": ["done", "refused", "busy", "none", "failed"] },
167
+ "restored": { "type": "integer", "minimum": 0, "maximum": 100000 },
168
+ "removed": { "type": "integer", "minimum": 0, "maximum": 100000 },
169
+ "files": { "type": "array", "maxItems": 5, "items": { "type": "string", "maxLength": 300 } },
170
+ "more": { "type": "boolean" }
171
+ }
172
+ },
161
173
  "outcome": { "enum": ["no_change", "interrupted", "disputed", "blocking_open", "unverified", "review_unavailable", "unreviewed", "clean"] },
162
174
  "role": { "enum": ["scout", "research", "verify", "review"] },
163
175
  "by": { "enum": ["main", "harness"] },
@@ -9,6 +9,7 @@ import { pathToFileURL } from 'node:url';
9
9
 
10
10
  import { registerTerminalUpdateOffer } from '../packages/piagent-core/runtime/update/terminal-update-offer.mjs';
11
11
  import { parseFailure } from '../packages/piagent-core/runtime/managed-failure.mjs';
12
+ import { checkpointsOf } from '../packages/piagent-core/managed/checkpoints.mjs';
12
13
  import { companyFailureText } from '../packages/piagent-webui/shared/company-copy.ts';
13
14
  import { headlineText, outcomeText, processItems } from '../packages/piagent-webui/shared/process-copy.ts';
14
15
  import { turnEndCopy, turnEndOf } from '../packages/piagent-webui/shared/turn-end.ts';
@@ -83,6 +84,17 @@ export function companyTerminalExtension({ managed, packageRoot, sdkRoot, bindin
83
84
  ctx.ui.notify(PERMISSION_TEXT[session.permission] ?? PERMISSION_TEXT['workspace-write'], 'info');
84
85
  } });
85
86
 
87
+ // /undo: the files the last turn changed go back to how they were before
88
+ // it, unless one changed again since (checkpoints.mjs).
89
+ pi.registerCommand('undo', {
90
+ description: 'Hoàn tác file mà lượt trước của agent đã sửa (project là git repository)',
91
+ handler: async (_args, ctx) => {
92
+ const session = managed();
93
+ if (!session?.boundary) { ctx.ui.notify('Cuộc trò chuyện này chưa sẵn sàng.', 'error'); return; }
94
+ try { ctx.ui.notify((await checkpointsOf(session).undo()).text, 'info'); }
95
+ catch (error) { ctx.ui.notify(`Không hoàn tác được: ${String(error?.message ?? error).slice(0, 300)}`, 'error'); }
96
+ } });
97
+
86
98
  // Fast mode, where the team's Harness allows it; off until the member turns it on.
87
99
  const showFast = (ctx) => ctx.ui?.setStatus?.('agent-watch-fast', managed()?.session?.managedFastState?.() === 'on' ? 'Fast' : undefined);
88
100
  pi.on('session_start', async (_event, ctx) => showFast(ctx));
@@ -14,12 +14,13 @@ const digest = file => createHash('sha256').update(fs.readFileSync(file)).digest
14
14
  process.removeAllListeners('warning');
15
15
  process.on('warning', warning => { if (warning.name !== 'ExperimentalWarning') process.stderr.write(`${warning.name}: ${warning.message}\n`); });
16
16
  const args = process.argv.slice(2), flags = new Map();
17
- if (args.includes('--help')) { console.log('piagent studio [--web [--no-open] | --serve | --check | --doctor | --prompt <text>] [--config <Agent Watch configuration>] [--project <folder>]\nDefaults: the configuration imported into ~/.pi/agent and the current project. Personal sessions remain under pi / piagent dashboard.'); process.exit(0); }
17
+ if (args.includes('--help')) { console.log('piagent studio [--web [--no-open] | --serve | --check | --doctor | --prompt <text> [--json]] [--config <Agent Watch configuration>] [--project <folder>]\nDefaults: the configuration imported into ~/.pi/agent and the current project. Personal sessions remain under pi / piagent dashboard.\n--json with --prompt writes the event stream of `pi --mode json` (JSONL on stdout; failures on stderr, exit 1).'); process.exit(0); }
18
18
  for (let i = 0; i < args.length; i++) {
19
- if (['--check', '--doctor', '--web', '--serve', '--no-open'].includes(args[i])) { flags.set(args[i], true); continue; }
19
+ if (['--check', '--doctor', '--web', '--serve', '--no-open', '--json'].includes(args[i])) { flags.set(args[i], true); continue; }
20
20
  if (!['--config', '--project', '--prompt'].includes(args[i]) || flags.has(args[i]) || !args[i + 1]) throw Error('managed-launch-arguments');
21
21
  flags.set(args[i], args[++i]);
22
22
  }
23
+ if (flags.has('--json') && !flags.has('--prompt')) throw Error('managed-launch-arguments');
23
24
  const home = os.homedir();
24
25
  // Do this before importing the SDK: it must not discover personal keys, Node
25
26
  // preload hooks, proxy overrides or provider endpoints from an ambient shell.
@@ -133,11 +134,23 @@ try {
133
134
  const host = await api.createAgentSessionRuntime(factory, { cwd, agentDir, sessionManager: api.SessionManager.create(cwd, path.join(agentDir, 'sessions')) });
134
135
  if (flags.get('--check')) console.log('agent-watch-auto: connected; native context; isolated tools; no inference sent.');
135
136
  else if (flags.has('--prompt')) {
136
- current.session.subscribe(event => {
137
- if (event.type === 'message_update' && event.assistantMessageEvent.type === 'text_delta') process.stdout.write(event.assistantMessageEvent.delta);
138
- if (event.type === 'message_end' && ['agent-watch-helper-receipt','agent-watch-review-status'].includes(event.message?.customType)) process.stderr.write(String(event.message.content) + '\n');
139
- });
140
- await current.session.prompt(flags.get('--prompt')); process.stdout.write('\n');
137
+ if (flags.has('--json')) {
138
+ // The event stream of `pi --mode json`, from Pi's own serializer: the
139
+ // session header, then every session event as one JSON line. Company
140
+ // events (managed_helpers, the Harness's agent-watch-process notes)
141
+ // come in the same stream.
142
+ const { toJsonEvent } = await import(pathToFileURL(path.join(config.sdk_root, 'dist/modes/json-event.js')).href);
143
+ const header = current.session.sessionManager.getHeader?.();
144
+ if (header) process.stdout.write(JSON.stringify(header) + '\n');
145
+ current.session.subscribe(event => process.stdout.write(JSON.stringify(toJsonEvent(event)) + '\n'));
146
+ await current.session.prompt(flags.get('--prompt'));
147
+ } else {
148
+ current.session.subscribe(event => {
149
+ if (event.type === 'message_update' && event.assistantMessageEvent.type === 'text_delta') process.stdout.write(event.assistantMessageEvent.delta);
150
+ if (event.type === 'message_end' && ['agent-watch-helper-receipt','agent-watch-review-status'].includes(event.message?.customType)) process.stderr.write(String(event.message.content) + '\n');
151
+ });
152
+ await current.session.prompt(flags.get('--prompt')); process.stdout.write('\n');
153
+ }
141
154
  const last=current.session.messages.filter(message=>message.role==='assistant').at(-1);
142
155
  if (last?.stopReason === 'error') {
143
156
  // Already says who failed and why, ending in its stable [code].