@dimi-agent/cli 0.6.7 → 0.6.9

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 (2) hide show
  1. package/dist/main.mjs +2281 -92
  2. package/package.json +5 -5
package/dist/main.mjs CHANGED
@@ -34346,7 +34346,7 @@ var init_telemetry$2 = __esmMin((() => {
34346
34346
  };
34347
34347
  ITelemetryService = createDecorator("agentTelemetryService");
34348
34348
  }));
34349
- var init_events$7 = __esmMin((() => {}));
34349
+ var init_events$8 = __esmMin((() => {}));
34350
34350
  //#endregion
34351
34351
  //#region ../../packages/agent-core-v2/src/app/telemetry/telemetryService.ts
34352
34352
  var TelemetryService, TelemetryContextView;
@@ -99307,6 +99307,11 @@ function loadNative() {
99307
99307
  function coldRebuild(recordsJson) {
99308
99308
  return loadNative().coldRebuild(recordsJson);
99309
99309
  }
99310
+ /** Release an agent's scoped subagent registry (tasks + steering queues).
99311
+ * Call when the owning agent scope is disposed. */
99312
+ function dropTaskRegistry(registryId) {
99313
+ loadNative().dropTaskRegistry(registryId);
99314
+ }
99310
99315
  /** `RustHostProcess.spawn` — the M2 exec spawn socket. */
99311
99316
  async function rustHostProcessSpawn(command, args, options) {
99312
99317
  return loadNative().RustHostProcess.spawn(command, [...args], options);
@@ -99337,7 +99342,7 @@ function rustHostEnvironmentProbe() {
99337
99342
  function rustTerminalSpawn(options) {
99338
99343
  return loadNative().RustTerminal.spawn(options);
99339
99344
  }
99340
- var nodeRequire$4, binding, PLATFORM_SUBPACKAGE, RustAgentTranscript, RustFileSystem;
99345
+ var nodeRequire$4, binding, PLATFORM_SUBPACKAGE, RustAgentTranscript, RustFileSystem, RustTurnSession;
99341
99346
  var init_src$7 = __esmMin((() => {
99342
99347
  nodeRequire$4 = createRequire(import.meta.url);
99343
99348
  PLATFORM_SUBPACKAGE = `@dimi-agent/dimi-native-${process.platform}-${process.arch}`;
@@ -99397,6 +99402,89 @@ var init_src$7 = __esmMin((() => {
99397
99402
  return loadNative().RustFileSystem.realpath(path);
99398
99403
  }
99399
99404
  };
99405
+ RustTurnSession = class {
99406
+ #inner;
99407
+ constructor(inputJson, policyJson, scriptedSegmentsJson, registryId) {
99408
+ const NativeClass = loadNative().RustTurnSession;
99409
+ this.#inner = new NativeClass(inputJson, policyJson, scriptedSegmentsJson ?? null, registryId);
99410
+ }
99411
+ /** Release the agent-scoped subagent registry (tasks + steering queues).
99412
+ * Call when the owning agent scope is disposed. */
99413
+ static dropTaskRegistry(registryId) {
99414
+ dropTaskRegistry(registryId);
99415
+ }
99416
+ /** Register the per-event callback: every engine event emitted by `run()` /
99417
+ * `resume()` is pushed through it as a JSON string, in emission order, as
99418
+ * it happens. Register before the first `run()`. */
99419
+ setOnEvent(callback) {
99420
+ this.#inner.setOnEvent(callback);
99421
+ }
99422
+ async run() {
99423
+ return this.#inner.run();
99424
+ }
99425
+ async resume(decisionJson) {
99426
+ return this.#inner.resume(decisionJson);
99427
+ }
99428
+ /** Record a session-scope approval (P1-6): the engine's policy is re-read
99429
+ * on every run/resume, so a pattern approved for the session mid-turn is
99430
+ * honored by the SAME turn's remaining batch. */
99431
+ addSessionApproval(pattern) {
99432
+ this.#inner.addSessionApproval(pattern);
99433
+ }
99434
+ /** Register the native-tool PreToolUse gate (A2 review): every registry
99435
+ * tool call is announced through `callback` (`{requestId, toolName,
99436
+ * arguments}` JSON); answer with `completeToolGate(requestId,
99437
+ * {decision:'allow'|'block', reason?})`. A block short-circuits the call. */
99438
+ setToolGate(callback) {
99439
+ this.#inner.setToolGate(callback);
99440
+ }
99441
+ /** Answer a pending gate request (see `setToolGate`). */
99442
+ completeToolGate(requestId, verdictJson) {
99443
+ this.#inner.completeToolGate(requestId, verdictJson);
99444
+ }
99445
+ /** Register a TS-side tool; `completeToolCall` finishes each call. The
99446
+ * definition (description + JSON parameters schema) is advertised to the
99447
+ * model from the next request on. */
99448
+ registerExternalTool(name, description, parametersJson, callback) {
99449
+ this.#inner.registerExternalTool(name, description, parametersJson, callback);
99450
+ }
99451
+ /** Advertise the LLM-facing definition (description + JSON parameters
99452
+ * schema) of a Rust-native tool (Agent / AgentOutput / WaitFor) registered
99453
+ * executor-first at construction. The executor stays the same; only the
99454
+ * def is updated, so the engine's request `tools` field carries it from
99455
+ * the next request on. */
99456
+ registerNativeToolDef(name, description, parametersJson) {
99457
+ this.#inner.registerNativeToolDef(name, description, parametersJson);
99458
+ }
99459
+ /** Steer the running turn (drained into its next LLM request). Returns
99460
+ * `false` when the turn has already finished — the caller must start a
99461
+ * new turn instead (the steer is never dropped). */
99462
+ steer(message) {
99463
+ return this.#inner.steer(message);
99464
+ }
99465
+ /** Steer a background subagent spawned by the `Agent` tool. */
99466
+ steerSubagent(agentId, message) {
99467
+ this.#inner.steerSubagent(agentId, message);
99468
+ }
99469
+ /** Cancel the running turn (engine stops at the next boundary). */
99470
+ cancel() {
99471
+ this.#inner.cancel();
99472
+ }
99473
+ /** Cancel a background task (TaskStop parity): the engine kills the
99474
+ * subagent nested turn / backgrounded bash command and settles "killed",
99475
+ * carrying the stop reason on the wire. */
99476
+ cancelTask(taskId, reason) {
99477
+ this.#inner.cancelTask(taskId, reason);
99478
+ }
99479
+ /** Close the session (agent dispose): task events stop forwarding, the
99480
+ * in-flight turn is cancelled. Background workers observe the close. */
99481
+ close() {
99482
+ this.#inner.close();
99483
+ }
99484
+ completeToolCall(requestId, resultJson) {
99485
+ this.#inner.completeToolCall(requestId, resultJson);
99486
+ }
99487
+ };
99400
99488
  }));
99401
99489
  //#endregion
99402
99490
  //#region ../../packages/agent-core-v2/src/os/backends/rust-local/rustHostEnvironmentService.ts
@@ -121936,7 +122024,7 @@ function emptyOutputSnapshot() {
121936
122024
  function agentTaskNotificationChildren(output) {
121937
122025
  if (output.fullOutputAvailable && output.outputPath !== void 0) return [renderOutputFileBlock(output.outputPath, output.outputSizeBytes)];
121938
122026
  if (output.preview.length === 0) return void 0;
121939
- return [renderOutputPreviewBlock(output)];
122027
+ return [renderOutputPreviewBlock$1(output)];
121940
122028
  }
121941
122029
  function renderOutputFileBlock(outputPath, outputSizeBytes) {
121942
122030
  return [
@@ -121945,7 +122033,7 @@ function renderOutputFileBlock(outputPath, outputSizeBytes) {
121945
122033
  "</output-file>"
121946
122034
  ].join("\n");
121947
122035
  }
121948
- function renderOutputPreviewBlock(output) {
122036
+ function renderOutputPreviewBlock$1(output) {
121949
122037
  return [
121950
122038
  `<output-preview bytes="${String(output.previewBytes)}" total_bytes="${String(output.outputSizeBytes)}" truncated="${String(output.truncated)}">`,
121951
122039
  output.truncated ? `Showing the last ${String(output.previewBytes)} bytes. No persisted full output is available.` : "No persisted full output is available; this preview is the currently buffered task output.",
@@ -122027,7 +122115,7 @@ function errorMessage$7(error) {
122027
122115
  if (error instanceof Error) return error.message;
122028
122116
  return String(error);
122029
122117
  }
122030
- var TaskNotificationDeliveryModel, MAX_OUTPUT_BYTES, TERMINAL_OUTPUT_TAIL_BYTES, MAX_TASK_OUTPUT_BYTES, SIGTERM_GRACE_MS, TASK_ID_ALPHABET, SESSION_CLOSED_REASON, NOTIFICATION_FALLBACK_PREVIEW_BYTES, ACTIVE_BACKGROUND_TASK_INJECTION_VARIANT, ACTIVE_BACKGROUND_TASK_GUIDANCE, TaskNotificationStepRequest, taskGhostsKey, taskScheduledNotificationKeysKey, taskDeliveredNotificationKeysKey, taskActiveTaskReminderPendingKey, AgentTaskService;
122118
+ var TaskNotificationDeliveryModel, MAX_OUTPUT_BYTES, TERMINAL_OUTPUT_TAIL_BYTES, MAX_TASK_OUTPUT_BYTES, TASK_ID_ALPHABET, SESSION_CLOSED_REASON, NOTIFICATION_FALLBACK_PREVIEW_BYTES, ACTIVE_BACKGROUND_TASK_INJECTION_VARIANT, ACTIVE_BACKGROUND_TASK_GUIDANCE, TaskNotificationStepRequest, taskGhostsKey, taskScheduledNotificationKeysKey, taskDeliveredNotificationKeysKey, taskActiveTaskReminderPendingKey, AgentTaskService;
122031
122119
  var init_taskService = __esmMin((() => {
122032
122120
  init_dist$5();
122033
122121
  init_scope();
@@ -122075,7 +122163,6 @@ var init_taskService = __esmMin((() => {
122075
122163
  MAX_OUTPUT_BYTES = 1024 * 1024;
122076
122164
  TERMINAL_OUTPUT_TAIL_BYTES = 4 * 1024;
122077
122165
  MAX_TASK_OUTPUT_BYTES = 16 * 1024 * 1024;
122078
- SIGTERM_GRACE_MS = 5e3;
122079
122166
  TASK_ID_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz";
122080
122167
  SESSION_CLOSED_REASON = "Session closed";
122081
122168
  NOTIFICATION_FALLBACK_PREVIEW_BYTES = 3e3;
@@ -122274,7 +122361,7 @@ var init_taskService = __esmMin((() => {
122274
122361
  };
122275
122362
  this.assertCanRegister(detached);
122276
122363
  const entry = {
122277
- taskId: generateTaskId(task.idPrefix),
122364
+ taskId: options.taskId ?? generateTaskId(task.idPrefix),
122278
122365
  task,
122279
122366
  handle: void 0,
122280
122367
  outputChunks: [],
@@ -122584,7 +122671,7 @@ var init_taskService = __esmMin((() => {
122584
122671
  entry.stopReason = options.stopReason;
122585
122672
  if (entry.handle) entry.handle.cancel();
122586
122673
  else entry.abortController.abort(options.abortReason);
122587
- const graceMs = resolveAgentTaskConfig(this.config)?.killGracePeriodMs ?? SIGTERM_GRACE_MS;
122674
+ const graceMs = resolveAgentTaskConfig(this.config)?.killGracePeriodMs ?? 5e3;
122588
122675
  let graceTimer;
122589
122676
  const graceful = await Promise.race([entry.lifecyclePromise.then(() => true, () => true), new Promise((resolve) => {
122590
122677
  graceTimer = setTimeout(() => {
@@ -123262,6 +123349,258 @@ var init_waitForTool = __esmMin((() => {
123262
123349
  });
123263
123350
  }));
123264
123351
  //#endregion
123352
+ //#region ../../packages/agent-core-v2/src/agent/tools/agent-output/agent-output.ts
123353
+ var AgentOutputInputSchema, AGENT_NOT_FOUND_MESSAGE, IAgentOutputTool;
123354
+ var init_agent_output$1 = __esmMin((() => {
123355
+ init_zod$1();
123356
+ init_instantiation();
123357
+ AgentOutputInputSchema = object({
123358
+ agent_id: string$2().describe("Agent ID of the subagent to inspect (returned by the Agent tool)."),
123359
+ tail_lines: number$2().int().min(1).max(500).optional().describe("How many of the most recent renderable records to show. Defaults to 60. The output is a transcript-style view of the subagent's latest activity.")
123360
+ });
123361
+ AGENT_NOT_FOUND_MESSAGE = "Agent instance not found.";
123362
+ IAgentOutputTool = createDecorator("agentOutputTool");
123363
+ }));
123364
+ //#endregion
123365
+ //#region ../../packages/agent-core-v2/src/agent/contextSize/contextSize.ts
123366
+ var IAgentContextSizeService;
123367
+ var init_contextSize = __esmMin((() => {
123368
+ init_instantiation();
123369
+ IAgentContextSizeService = createDecorator("agentContextSizeService");
123370
+ }));
123371
+ //#endregion
123372
+ //#region ../../packages/agent-core-v2/src/agent/tools/agent-output/agent-output.md?raw
123373
+ var agent_output_default;
123374
+ var init_agent_output = __esmMin((() => {
123375
+ agent_output_default = "Read the recent rendered output of a subagent — the same transcript-style view a human sees in the TUI: its latest assistant text, thinking, tool calls, and task progress, in time order.\n\nSubagents run fully asynchronously. Use this tool to check on one:\n\n- While you still have other work, do that work instead — the subagent's result (and completion notification) arrives on its own.\n- When you have nothing else to do and want to see what the subagent is doing, call this tool with the `agent_id` returned by the Agent tool.\n- If it is still working and you want to park until it progresses, call `WaitFor` with a reasonable `timeout_seconds` instead of polling this tool in a loop; the wait wakes you on the completion notification or the timeout, then check again with this tool.\n\nPass `tail_lines` to control how much of the recent activity to show (default 60). The `agent_id` is the Agent tool's `agent_id` parameter value (e.g. \"agent-6\"), NOT the `task_id` from its output.\n";
123376
+ }));
123377
+ //#endregion
123378
+ //#region ../../packages/agent-core-v2/src/agent/tools/agent-output/agentOutputTool.ts
123379
+ /**
123380
+ * `tools` domain (L7) — `AgentOutputTool` implementation (the `AgentOutput`
123381
+ * tool).
123382
+ *
123383
+ * Returns a transcript-style view of a subagent's recent activity by reading
123384
+ * the tail of its `wire.jsonl` journal (`<sessionDir>/agents/<agentId>/`):
123385
+ * assistant text, thinking, tool calls, and task progress in time order —
123386
+ * the same surface a human sees in the TUI. Subagents run fully
123387
+ * asynchronously, so this is the primary progress-check tool for the calling
123388
+ * agent (park with `WaitFor` instead of polling when there is nothing else
123389
+ * to do).
123390
+ *
123391
+ * Registered via the module-level `registerAgentToolService(IAgentOutputTool,
123392
+ * AgentOutputTool)` at the bottom of this file — the same "import = register"
123393
+ * pattern used by every agent tool. Bound at Agent scope.
123394
+ */
123395
+ function toRenderable(line) {
123396
+ const trimmed = line.trim();
123397
+ if (trimmed.length === 0) return void 0;
123398
+ let parsed;
123399
+ try {
123400
+ parsed = JSON.parse(trimmed);
123401
+ } catch {
123402
+ return;
123403
+ }
123404
+ if (typeof parsed !== "object" || parsed === null) return void 0;
123405
+ const record = parsed;
123406
+ const timeMs = typeof record.time === "number" ? normalizeTimestampMs$1(record.time) : void 0;
123407
+ const turnStep = turnStepLabel(record.event);
123408
+ switch (record.type) {
123409
+ case "turn.prompt": return {
123410
+ timeMs: timeMs ?? 0,
123411
+ turnStep: "",
123412
+ lines: [`user: ${clip(textOfPrompt(record.input), PROMPT_MAX_CHARS)}`]
123413
+ };
123414
+ case "context.append_loop_event": {
123415
+ const event = record.event;
123416
+ const eventType = event?.type;
123417
+ if (eventType === "content.part" && typeof event?.part?.type === "string") {
123418
+ const partType = event.part.type;
123419
+ const text = partType === "think" ? typeof event.part.think === "string" ? event.part.think : "" : typeof event.part.text === "string" ? event.part.text : "";
123420
+ return {
123421
+ timeMs: timeMs ?? 0,
123422
+ turnStep,
123423
+ lines: [`${partType === "think" ? "think" : "assistant"}: ${clip(text, LINE_MAX_CHARS)}`]
123424
+ };
123425
+ }
123426
+ if (eventType === "tool.call" && typeof event?.name === "string") return {
123427
+ timeMs: timeMs ?? 0,
123428
+ turnStep,
123429
+ lines: [`tool: ${event.name}${argsLabel(event.args)}`]
123430
+ };
123431
+ if (eventType === "tool.result") {
123432
+ const done = event?.isError === true ? "error" : "done";
123433
+ return {
123434
+ timeMs: timeMs ?? 0,
123435
+ turnStep,
123436
+ lines: [` → ${done}`]
123437
+ };
123438
+ }
123439
+ return;
123440
+ }
123441
+ case "task.started":
123442
+ case "task.terminated": {
123443
+ const info = record.info;
123444
+ if (info === void 0) return void 0;
123445
+ const description = typeof info.description === "string" ? info.description : "";
123446
+ const taskId = typeof info.taskId === "string" ? info.taskId : "";
123447
+ const status = typeof info.status === "string" ? info.status : "";
123448
+ const label = record.type === "task.started" ? `task: ${clip(description, LINE_MAX_CHARS)} (running, ${taskId})` : `task: ${clip(description, LINE_MAX_CHARS)} (${status}, ${taskId})`;
123449
+ return {
123450
+ timeMs: timeMs ?? 0,
123451
+ turnStep: "",
123452
+ lines: [label]
123453
+ };
123454
+ }
123455
+ default: return;
123456
+ }
123457
+ }
123458
+ function turnStepLabel(event) {
123459
+ if (event === void 0) return "";
123460
+ const turn = typeof event.turnId === "string" ? event.turnId : void 0;
123461
+ const step = typeof event.step === "number" ? event.step : void 0;
123462
+ if (turn === void 0 && step === void 0) return "";
123463
+ return `[turn ${turn ?? "?"} step ${step ?? "?"}] `;
123464
+ }
123465
+ function argsLabel(args) {
123466
+ if (typeof args !== "object" || args === null) return "";
123467
+ return `(${clip(JSON.stringify(args), ARGS_MAX_CHARS)})`;
123468
+ }
123469
+ function textOfPrompt(input) {
123470
+ if (!Array.isArray(input)) return "";
123471
+ return input.flatMap((part) => typeof part === "object" && part !== null && part.type === "text" && typeof part.text === "string" ? [part.text] : []).join("").replaceAll(/\s+/g, " ").trim();
123472
+ }
123473
+ function clip(text, max) {
123474
+ const collapsed = text.replaceAll(/\s+/g, " ").trim();
123475
+ if (collapsed.length <= max) return collapsed;
123476
+ return `${collapsed.slice(0, max)}…`;
123477
+ }
123478
+ function normalizeTimestampMs$1(value) {
123479
+ return value > 0xe8d4a51000 ? Math.floor(value) : Math.floor(value * 1e3);
123480
+ }
123481
+ function formatTime(timeMs) {
123482
+ if (timeMs <= 0) return "";
123483
+ const date = new Date(timeMs);
123484
+ const pad = (n) => String(n).padStart(2, "0");
123485
+ return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
123486
+ }
123487
+ function formatTokens$2(size) {
123488
+ if (size < 1024) return `${String(size)} tokens`;
123489
+ return `${(size / 1024).toFixed(1)}k tokens`;
123490
+ }
123491
+ function isMissingPath$3(error) {
123492
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
123493
+ }
123494
+ var WIRE_FILENAME$1, TAIL_READ_BYTES, LINE_MAX_CHARS, ARGS_MAX_CHARS, PROMPT_MAX_CHARS, AgentOutputTool;
123495
+ var init_agentOutputTool = __esmMin((() => {
123496
+ init_dist$5();
123497
+ init_input_schema();
123498
+ init_rule_match();
123499
+ init_toolContribution();
123500
+ init_contextSize();
123501
+ init_loop();
123502
+ init_profile();
123503
+ init_agentLifecycle();
123504
+ init_sessionContext();
123505
+ init_agent_output$1();
123506
+ init_agent_output();
123507
+ init_decorateParam();
123508
+ init_decorate();
123509
+ WIRE_FILENAME$1 = "wire.jsonl";
123510
+ TAIL_READ_BYTES = 256 * 1024;
123511
+ LINE_MAX_CHARS = 240;
123512
+ ARGS_MAX_CHARS = 120;
123513
+ PROMPT_MAX_CHARS = 300;
123514
+ AgentOutputTool = class AgentOutputTool {
123515
+ lifecycle;
123516
+ session;
123517
+ name = "AgentOutput";
123518
+ description = agent_output_default;
123519
+ parameters = toInputJsonSchema(AgentOutputInputSchema);
123520
+ constructor(lifecycle, session) {
123521
+ this.lifecycle = lifecycle;
123522
+ this.session = session;
123523
+ }
123524
+ resolveExecution(args) {
123525
+ return {
123526
+ description: `Reading output of agent ${args.agent_id}`,
123527
+ taskMode: "control",
123528
+ approvalRule: this.name,
123529
+ matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.agent_id),
123530
+ execute: () => this.execute(args)
123531
+ };
123532
+ }
123533
+ async execute(args) {
123534
+ const target = this.lifecycle.get(args.agent_id);
123535
+ if (target === void 0) return {
123536
+ isError: true,
123537
+ output: `${AGENT_NOT_FOUND_MESSAGE} agent_id=${args.agent_id}`
123538
+ };
123539
+ const records = await this.readRecentRecords(args.agent_id, args.tail_lines ?? 60);
123540
+ const profileName = target.accessor.get(IAgentProfileService).data().profileName ?? "agent";
123541
+ const status = target.accessor.get(IAgentLoopService).status();
123542
+ const contextSize = target.accessor.get(IAgentContextSizeService)?.get().size ?? 0;
123543
+ const lines = [[
123544
+ `agent_id: ${args.agent_id}`,
123545
+ `type: ${profileName}`,
123546
+ `status: ${status.state}${status.activeTurnId !== void 0 ? ` · turn ${String(status.activeTurnId)}` : ""}`,
123547
+ `context: ${formatTokens$2(contextSize)}`
123548
+ ].join(" · "), ""];
123549
+ if (records.length === 0) lines.push("(no activity recorded yet)");
123550
+ else for (const record of records) {
123551
+ const stamp = formatTime(record.timeMs);
123552
+ for (const line of record.lines) lines.push(`${stamp} ${record.turnStep}${line}`);
123553
+ }
123554
+ return {
123555
+ output: lines.join("\n"),
123556
+ isError: false
123557
+ };
123558
+ }
123559
+ async readRecentRecords(agentId, tailLines) {
123560
+ const path = join$5(this.session.sessionDir, "agents", agentId, WIRE_FILENAME$1);
123561
+ let file;
123562
+ try {
123563
+ file = await open(path, "r");
123564
+ } catch (error) {
123565
+ if (isMissingPath$3(error)) return [];
123566
+ throw error;
123567
+ }
123568
+ let input;
123569
+ try {
123570
+ const size = (await file.stat()).size;
123571
+ const start = Math.max(0, size - TAIL_READ_BYTES);
123572
+ input = size === 0 ? Readable.from([]) : file.createReadStream({
123573
+ encoding: "utf8",
123574
+ autoClose: false,
123575
+ start,
123576
+ end: size - 1
123577
+ });
123578
+ const lines = createInterface({
123579
+ input,
123580
+ crlfDelay: Infinity
123581
+ });
123582
+ const rendered = [];
123583
+ for await (const line of lines) {
123584
+ const record = toRenderable(line);
123585
+ if (record === void 0) continue;
123586
+ rendered.push(record);
123587
+ if (rendered.length > tailLines) rendered.shift();
123588
+ }
123589
+ return rendered;
123590
+ } finally {
123591
+ input?.destroy();
123592
+ if (input !== void 0) await finished(input, { cleanup: true }).catch(() => {});
123593
+ await file.close();
123594
+ }
123595
+ }
123596
+ };
123597
+ AgentOutputTool = __decorate$1([__decorateParam(0, IAgentLifecycleService), __decorateParam(1, ISessionContext)], AgentOutputTool);
123598
+ registerAgentToolService(IAgentOutputTool, AgentOutputTool, {
123599
+ name: "AgentOutput",
123600
+ domain: "subagent"
123601
+ });
123602
+ }));
123603
+ //#endregion
123265
123604
  //#region ../../packages/agent-core-v2/src/agent/tools/all-done/all-done.md?raw
123266
123605
  var all_done_default;
123267
123606
  var init_all_done = __esmMin((() => {
@@ -139289,13 +139628,6 @@ var init_subagent_task = __esmMin((() => {
139289
139628
  };
139290
139629
  }));
139291
139630
  //#endregion
139292
- //#region ../../packages/agent-core-v2/src/agent/contextSize/contextSize.ts
139293
- var IAgentContextSizeService;
139294
- var init_contextSize = __esmMin((() => {
139295
- init_instantiation();
139296
- IAgentContextSizeService = createDecorator("agentContextSizeService");
139297
- }));
139298
- //#endregion
139299
139631
  //#region ../../packages/agent-core-v2/src/session/subagent/mirrorAgentRun.ts
139300
139632
  function emitAgentRunSpawned(requester, targetAgentId, meta) {
139301
139633
  requester.accessor.get(IEventBus)?.publish({
@@ -208545,7 +208877,7 @@ function turnPromptText(input) {
208545
208877
  const text = input.filter((part) => part.type === "text").map((part) => part.text).join("");
208546
208878
  return text.length > 0 ? text : void 0;
208547
208879
  }
208548
- function isDisplayablePromptOrigin(origin) {
208880
+ function isDisplayablePromptOrigin$1(origin) {
208549
208881
  if (origin.kind === "user") return true;
208550
208882
  return (origin.kind === "skill_activation" || origin.kind === "plugin_command") && origin.trigger === "user-slash";
208551
208883
  }
@@ -209007,7 +209339,7 @@ var init_loopService = __esmMin((() => {
209007
209339
  type: "turn.started",
209008
209340
  turnId: job.turn.id,
209009
209341
  origin,
209010
- prompt: isDisplayablePromptOrigin(origin) ? turnPromptText(job.seed.input) : void 0
209342
+ prompt: isDisplayablePromptOrigin$1(origin) ? turnPromptText(job.seed.input) : void 0
209011
209343
  });
209012
209344
  this.runTurn(job.turn, job.ready).then(job.result.resolve, job.result.reject);
209013
209345
  }
@@ -213601,6 +213933,1506 @@ var init_rpc$2 = __esmMin((() => {
213601
213933
  createDecorator("agentSessionRPCService");
213602
213934
  }));
213603
213935
  //#endregion
213936
+ //#region ../../packages/agent-core-v2/src/agent/loop/engineTaskAdapter.ts
213937
+ /** Extract a human-readable stop reason from an AbortSignal's abort reason:
213938
+ * TaskStop aborts with the normalized reason string; `stopByUser` aborts
213939
+ * with a `UserCancellationError` (its `.message` is the user-facing reason);
213940
+ * anything else (undefined / session close) yields no reason — the engine
213941
+ * falls back to "Stopped by TaskStop". An empty reason (e.g. an Error with
213942
+ * an empty message) counts as "no reason" too, so the wire settle carries
213943
+ * the documented fallback instead of an empty `error` string. */
213944
+ function abortReasonString(reason) {
213945
+ if (typeof reason === "string") return reason === "" ? void 0 : reason;
213946
+ if (reason instanceof Error) return reason.message === "" ? void 0 : reason.message;
213947
+ }
213948
+ var EngineTaskAdapter;
213949
+ var init_engineTaskAdapter = __esmMin((() => {
213950
+ EngineTaskAdapter = class {
213951
+ options;
213952
+ idPrefix;
213953
+ kind;
213954
+ description;
213955
+ sink;
213956
+ pendingOutput = "";
213957
+ pendingSettlement;
213958
+ pendingExitCode;
213959
+ exitCode;
213960
+ settleResolve;
213961
+ settled;
213962
+ removeAbortListener;
213963
+ /** UTF-8 bytes already pushed to the sink (or buffered pre-start). The
213964
+ * engine guarantees its `task.output` deltas concatenate byte-for-byte to
213965
+ * the settle's full output — the bash poller seeds a backgrounded command
213966
+ * with its pre-timeout foreground output as the FIRST delta, and the
213967
+ * subagent worker streams the nested turn's assistant deltas verbatim —
213968
+ * so `complete` appends only the not-yet-streamed tail. */
213969
+ streamedBytes = 0;
213970
+ constructor(options) {
213971
+ this.options = options;
213972
+ this.idPrefix = options.kind === "agent" ? "agent" : "bash";
213973
+ this.kind = options.kind;
213974
+ this.description = options.description;
213975
+ this.settled = new Promise((resolve) => {
213976
+ this.settleResolve = resolve;
213977
+ });
213978
+ }
213979
+ async start(sink) {
213980
+ this.sink = sink;
213981
+ const requestStop = () => {
213982
+ this.options.forceStop(abortReasonString(sink.signal.reason));
213983
+ };
213984
+ if (sink.signal.aborted) requestStop();
213985
+ else {
213986
+ sink.signal.addEventListener("abort", requestStop, { once: true });
213987
+ this.removeAbortListener = () => {
213988
+ sink.signal.removeEventListener("abort", requestStop);
213989
+ };
213990
+ }
213991
+ this.flush();
213992
+ await this.settled;
213993
+ }
213994
+ async forceStop() {
213995
+ this.options.forceStop();
213996
+ }
213997
+ /** Runner-side live output (engine `task.output` events): stream the delta
213998
+ * straight into the service sink so TaskOutput shows partial output while
213999
+ * the task runs. Deltas racing `start()` (settle/stream before the
214000
+ * service invoked start) are buffered and flushed with the settle. */
214001
+ appendOutput(delta) {
214002
+ this.streamedBytes += Buffer.byteLength(delta, "utf-8");
214003
+ if (this.sink !== void 0) this.sink.appendOutput(delta);
214004
+ else this.pendingOutput += delta;
214005
+ }
214006
+ /** Runner-side settle: record the final output/exit code and settle the
214007
+ * registry entry (the runner dispatches the wire `task.terminated` and
214008
+ * delivers the notification itself — the service's `recorded: false`
214009
+ * entry only drives TaskList/TaskStop). The settle output is the engine's
214010
+ * full accumulated output; the deltas streamed during the run already
214011
+ * landed in the sink, so only the not-yet-streamed tail is appended (the
214012
+ * concatenated deltas equal the settle output byte-for-byte). */
214013
+ complete(output, settlement, exitCode) {
214014
+ this.pendingSettlement = settlement;
214015
+ this.pendingExitCode = exitCode ?? null;
214016
+ const fullBytes = Buffer.byteLength(output, "utf-8");
214017
+ const streamed = this.streamedBytes;
214018
+ if (streamed < fullBytes) {
214019
+ const tail = Buffer.from(output, "utf-8").subarray(streamed).toString("utf-8");
214020
+ if (this.sink !== void 0) this.sink.appendOutput(tail);
214021
+ else this.pendingOutput += tail;
214022
+ }
214023
+ this.flush();
214024
+ }
214025
+ toInfo(base) {
214026
+ if (this.kind === "agent") return {
214027
+ ...base,
214028
+ kind: "agent",
214029
+ agentId: this.options.agentId ?? "",
214030
+ subagentType: this.options.subagentType ?? this.options.description,
214031
+ detached: true
214032
+ };
214033
+ return {
214034
+ ...base,
214035
+ kind: "process",
214036
+ command: this.description,
214037
+ pid: this.options.pid ?? 0,
214038
+ exitCode: this.exitCode ?? null,
214039
+ detached: true
214040
+ };
214041
+ }
214042
+ flush() {
214043
+ const sink = this.sink;
214044
+ const settlement = this.pendingSettlement;
214045
+ if (sink === void 0 || settlement === void 0) return;
214046
+ this.exitCode = this.pendingExitCode ?? null;
214047
+ if (this.pendingOutput.length > 0) sink.appendOutput(this.pendingOutput);
214048
+ this.pendingOutput = "";
214049
+ this.pendingSettlement = void 0;
214050
+ this.removeAbortListener?.();
214051
+ this.removeAbortListener = void 0;
214052
+ sink.settle(settlement);
214053
+ this.settleResolve?.();
214054
+ }
214055
+ };
214056
+ }));
214057
+ //#endregion
214058
+ //#region ../../packages/agent-core-v2/src/agent/toolResultTruncation/toolResultTruncation.ts
214059
+ var IAgentToolResultTruncationService;
214060
+ var init_toolResultTruncation = __esmMin((() => {
214061
+ init_instantiation();
214062
+ IAgentToolResultTruncationService = createDecorator("agentToolResultTruncationService");
214063
+ }));
214064
+ //#endregion
214065
+ //#region ../../packages/agent-core-v2/src/agent/loop/rustEngineTurnRunner.ts
214066
+ /**
214067
+ * `rustEngineTurnRunner` — the default turn runner: one turn through the Rust
214068
+ * engine (`RustTurnSession` napi socket) instead of the TS loop.
214069
+ *
214070
+ * Enabled by default; the CLI `--legacy` flag sets `DIMI_LEGACY=1` to keep
214071
+ * the TS loop. The runner:
214072
+ * 1. records `turn.prompt` (turn clock) and appends the user message;
214073
+ * 2. assembles the LLM messages from the context (context assembly stays
214074
+ * on the TS side until slice 3);
214075
+ * 3. runs `RustTurnSession` (aimux-backed LLM in production, scripted
214076
+ * segments under test);
214077
+ * 4. streams the engine events onto the event bus as they happen (the
214078
+ * bridge pushes each event through a per-event callback while the turn
214079
+ * is in flight) — the transcript projection layer (`coreEventMap`)
214080
+ * folds them into wire ops exactly as it does for the TS loop — and
214081
+ * mirrors them into the context (`step.begin` / `content.part` /
214082
+ * `tool.call` / `tool.result` / `step.end` + the assistant message) so
214083
+ * the next turn's history is intact.
214084
+ *
214085
+ * Slice-1 scope: single turn, no queue/cancellation/undo (those land with
214086
+ * later slices). The runner is a parallel path — `loopService` is untouched.
214087
+ */
214088
+ function wrapSystemReminder(text) {
214089
+ const trimmed = text.trim();
214090
+ if (trimmed.startsWith("<system-reminder>") && trimmed.endsWith("</system-reminder>")) return trimmed;
214091
+ return `<system-reminder>\n${trimmed}\n</system-reminder>`;
214092
+ }
214093
+ /**
214094
+ * The Rust engine is the default runtime; the CLI `--legacy` flag sets
214095
+ * `DIMI_LEGACY=1` to keep the TS loop (and the node-local OS backends).
214096
+ */
214097
+ function rustEngineEnabled() {
214098
+ return process.env["DIMI_LEGACY"] !== "1";
214099
+ }
214100
+ /** Render an engine event/tool value as text (strings pass through). */
214101
+ function toText(value) {
214102
+ return typeof value === "string" ? value : JSON.stringify(value) ?? "";
214103
+ }
214104
+ /**
214105
+ * Serialize a TS `PromptOrigin` into the wire `TurnOrigin` JSON shape the
214106
+ * engine deserializes (`{ kind: 'user' }` / `{ kind: 'task', taskId }` /
214107
+ * … — see `dimi-wire` `model.rs`). Unknown kinds fall back to a plain user
214108
+ * origin (the wire default).
214109
+ */
214110
+ function toEngineTurnOrigin(origin) {
214111
+ switch (origin.kind) {
214112
+ case "task": return {
214113
+ kind: "task",
214114
+ taskId: origin.taskId
214115
+ };
214116
+ case "cron_job": return {
214117
+ kind: "cron",
214118
+ taskId: origin.jobId
214119
+ };
214120
+ case "cron_missed": return { kind: "cron" };
214121
+ case "hook_result": return { kind: "hook" };
214122
+ case "compaction_summary": return { kind: "compaction" };
214123
+ default: return { kind: "user" };
214124
+ }
214125
+ }
214126
+ /**
214127
+ * TS `isDisplayablePromptOrigin` parity: only user-origin (and user-slash
214128
+ * skill/plugin) turns expose their prompt on `turn.started`; task/cron/hook
214129
+ * steering text must never leak into the transcript.
214130
+ */
214131
+ function isDisplayablePromptOrigin(origin) {
214132
+ if (origin.kind === "user") return true;
214133
+ return (origin.kind === "skill_activation" || origin.kind === "plugin_command") && origin.trigger === "user-slash";
214134
+ }
214135
+ /**
214136
+ * Build a DimiErrorPayload-shaped error from the engine's `{ message, code }`
214137
+ * payload (TS `toDimiErrorPayload` parity): always carries `name` (the error
214138
+ * class name, mapped from the code when the engine omitted it), `retryable`
214139
+ * (mapped from the code per the TS registry) and `message`.
214140
+ */
214141
+ function buildErrorPayload(rawError) {
214142
+ const error = { ...rawError };
214143
+ if (error["name"] === void 0 || error["name"] === null) error["name"] = error["code"] === "PROVIDER_FILTERED" ? "ProviderFilteredError" : "Error";
214144
+ if (error["retryable"] === void 0) error["retryable"] = typeof error["code"] === "string" && RETRYABLE_ERROR_CODES.has(error["code"]);
214145
+ return error;
214146
+ }
214147
+ /** Optional numeric engine field (pid / exitCode). */
214148
+ function toOptionalNumber(value) {
214149
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
214150
+ }
214151
+ /** Mirrors the task domain's `buildAgentTaskNotificationBody` (minus the
214152
+ * output-file block — the engine carries the tail inline). */
214153
+ function buildTaskNotificationBody(task) {
214154
+ const baseLine = task.status === "timed_out" ? `${task.description} timed out.` : task.status === "killed" && task.error !== void 0 ? `${task.description} was stopped. Reason: ${task.error}` : task.status === "failed" && task.error !== void 0 ? `${task.description} failed. Reason: ${task.error}` : `${task.description} ${task.status}.`;
214155
+ if (task.kind !== "agent" || task.status === "completed") return baseLine;
214156
+ return `${baseLine}${[
214157
+ "",
214158
+ `To recover or continue this subagent, call Agent(resume="${task.agentId}", prompt="Pick up where you left off; redo the last tool call if its result was never observed.").`,
214159
+ `Use agent_id ("${task.agentId}"), NOT source_id / task_id ("${task.taskId}") — the two look alike but only agent_id is accepted by the resume parameter.`,
214160
+ "Add run_in_background=true to keep it backgrounded, or omit it to take the result inline in the current turn.",
214161
+ "The subagent retains its full prior context across the restart, but any in-flight tool call lost its result and may need to be redone."
214162
+ ].join("\n")}`;
214163
+ }
214164
+ /** Mirrors the task domain's `renderOutputPreviewBlock` (tail inline): the
214165
+ * engine's accumulated output is the "currently buffered" output, so the
214166
+ * preview is the last `NOTIFICATION_OUTPUT_PREVIEW_BYTES` bytes with the
214167
+ * same heading / `truncated` / `bytes` semantics the TS service renders. */
214168
+ function renderOutputPreviewBlock(output) {
214169
+ const fullBytes = Buffer.byteLength(output, "utf-8");
214170
+ const previewBytes = Math.min(NOTIFICATION_OUTPUT_PREVIEW_BYTES, fullBytes);
214171
+ const truncated = fullBytes > previewBytes;
214172
+ const preview = previewBytes > 0 ? Buffer.from(output, "utf-8").subarray(fullBytes - previewBytes).toString("utf-8") : "";
214173
+ return [
214174
+ `<output-preview bytes="${String(previewBytes)}" total_bytes="${String(fullBytes)}" truncated="${String(truncated)}">`,
214175
+ truncated ? `Showing the last ${String(previewBytes)} bytes. No persisted full output is available.` : "No persisted full output is available; this preview is the currently buffered task output.",
214176
+ escapeXml$1(preview),
214177
+ "</output-preview>"
214178
+ ].join("\n");
214179
+ }
214180
+ /** Resolve once the signal aborts (used to race an approval wait). */
214181
+ function abortOnSignal(signal) {
214182
+ return new Promise((resolve) => {
214183
+ if (signal.aborted) {
214184
+ resolve();
214185
+ return;
214186
+ }
214187
+ signal.addEventListener("abort", () => {
214188
+ resolve();
214189
+ }, { once: true });
214190
+ });
214191
+ }
214192
+ var WAIT_FOR_NATIVE_DESCRIPTION, ENGINE_NATIVE_TOOLS, WAIT_FOR_NATIVE_PARAMETERS, IRustEngineTurnRunner, RETRYABLE_ERROR_CODES, TERMINAL_OUTPUT_TAIL_CHARS, NOTIFICATION_OUTPUT_PREVIEW_BYTES, RustEngineTurnRunner;
214193
+ var init_rustEngineTurnRunner = __esmMin((() => {
214194
+ init_src$7();
214195
+ init_xml_escape();
214196
+ init_contextMemory();
214197
+ init_permissionMode();
214198
+ init_toolRegistry();
214199
+ init_toolPolicy();
214200
+ init_permissionRules();
214201
+ init_engineTaskAdapter();
214202
+ init_notificationXml();
214203
+ init_taskOps();
214204
+ init_task$5();
214205
+ init_configSection$12();
214206
+ init_turnOps();
214207
+ init_eventBus();
214208
+ init_config$5();
214209
+ init_log();
214210
+ init_canonical_args();
214211
+ init_errors$11();
214212
+ init_externalHooksRunner();
214213
+ init_toolResultTruncation();
214214
+ init_approval$4();
214215
+ init_usage();
214216
+ init_profile();
214217
+ init_scopeContext();
214218
+ init_catalog$1();
214219
+ init_providerRuntime();
214220
+ init_sessionContext();
214221
+ init_sessionMetadata();
214222
+ init_usage$1();
214223
+ init_wire();
214224
+ init_compactionHandoff();
214225
+ init_compactionOps();
214226
+ init_completion();
214227
+ init_decorateParam();
214228
+ init_decorate();
214229
+ init_instantiation();
214230
+ init_scope();
214231
+ WAIT_FOR_NATIVE_DESCRIPTION = [
214232
+ "Wait for a background subagent task to finish (or time out).",
214233
+ "Pass the `agent_id` of a subagent launched by the Agent tool (from its launch output).",
214234
+ "The call blocks until that subagent completes, fails, or the timeout expires, then returns its final status and output.",
214235
+ "This waits on a specific subagent task, NOT on the user: user-wait (waking on user notifications) is not implemented.",
214236
+ "Prefer AgentOutput for a quick status check; use WaitFor when the turn should pause until the subagent finishes."
214237
+ ].join(" ");
214238
+ ENGINE_NATIVE_TOOLS = new Set([
214239
+ "Bash",
214240
+ "Agent",
214241
+ "AgentOutput",
214242
+ "WaitFor"
214243
+ ]);
214244
+ WAIT_FOR_NATIVE_PARAMETERS = {
214245
+ type: "object",
214246
+ properties: {
214247
+ agent_id: {
214248
+ type: "string",
214249
+ description: "Agent id of the running subagent task to wait for, as returned by the Agent tool launch output."
214250
+ },
214251
+ timeout_seconds: {
214252
+ type: "integer",
214253
+ minimum: 1,
214254
+ maximum: 1800,
214255
+ description: "How long to wait for the subagent before giving up. Defaults to 60; maximum 1800."
214256
+ }
214257
+ },
214258
+ required: ["agent_id"],
214259
+ additionalProperties: false
214260
+ };
214261
+ IRustEngineTurnRunner = createDecorator("rustEngineTurnRunner");
214262
+ RETRYABLE_ERROR_CODES = new Set([
214263
+ "provider.rate_limit",
214264
+ "provider.connection_error",
214265
+ "provider.overloaded",
214266
+ "context.overflow"
214267
+ ]);
214268
+ TERMINAL_OUTPUT_TAIL_CHARS = 4 * 1024;
214269
+ NOTIFICATION_OUTPUT_PREVIEW_BYTES = 3e3;
214270
+ RustEngineTurnRunner = class RustEngineTurnRunner {
214271
+ context;
214272
+ eventBus;
214273
+ wire;
214274
+ config;
214275
+ modeService;
214276
+ rulesService;
214277
+ toolRegistry;
214278
+ usageService;
214279
+ profile;
214280
+ scopeContext;
214281
+ sessionContext;
214282
+ sessionMetadata;
214283
+ tasks;
214284
+ modelCatalog;
214285
+ providerRuntime;
214286
+ instantiation;
214287
+ toolPolicy;
214288
+ log;
214289
+ /** The in-flight Rust session, while a turn is running (steer/cancel target). */
214290
+ activeSession;
214291
+ /** FIFO queue of turns waiting behind the running one (TS loop queue
214292
+ * parity): prompts during an active turn wait for it to finish. */
214293
+ queued = [];
214294
+ /** Whether a turn is currently executing (queue gate). */
214295
+ turnRunning = false;
214296
+ /** turnId of the executing turn (cancel validation). */
214297
+ executingTurnId;
214298
+ /** Aborts the in-flight approval wait on cancel. */
214299
+ approvalAbort;
214300
+ /**
214301
+ * Rust-side task registry mirror: taskId → launch facts (description /
214302
+ * startedAt / pid) recorded from `task.started`, used to build the
214303
+ * terminal `task.terminated` info from `task.settled`. Entries are removed
214304
+ * once the settle is handled (a task settles exactly once), so the map
214305
+ * stays bounded by the number of tasks in flight.
214306
+ */
214307
+ taskInfos = /* @__PURE__ */ new Map();
214308
+ /** Notification dedupe (TS `deliveredNotificationKeys` parity): guards a
214309
+ * single settle from being delivered twice. Keys are removed after the
214310
+ * delivery completes — a settled task never settles again — so the set
214311
+ * stays bounded; the mid-turn+idle double-delivery guard is the
214312
+ * single-call structure of `deliverTaskNotification`, not this set. */
214313
+ deliveredNotificationKeys = /* @__PURE__ */ new Set();
214314
+ /** Registry entries for engine background tasks (TaskList/TaskStop parity):
214315
+ * taskId → adapter registered with `IAgentTaskService`. Removed once the
214316
+ * task settles (a task settles exactly once), so the map stays bounded. */
214317
+ engineTaskAdapters = /* @__PURE__ */ new Map();
214318
+ /** Owning RustTurnSession per engine task id (F1): recorded at
214319
+ * `task.started` from the session whose EventSink delivered the event.
214320
+ * Tasks launched from background workers (a subagent spawning a
214321
+ * sub-subagent, or a subagent launching a backgrounded bash) emit their
214322
+ * start AFTER the launching turn ended, when `activeSession` is undefined
214323
+ * or a newer turn's session — and the engine's per-task cancel signal
214324
+ * lives on the OWNING session's task map (`RustTurnSession::new` creates
214325
+ * a fresh map per turn), so TaskStop must cancel through this session.
214326
+ * Removed once the task settles, so the map stays bounded. */
214327
+ taskSessions = /* @__PURE__ */ new Map();
214328
+ /** Every subagent agent id this runner has handed out (the engine's
214329
+ * `agent-<n>` ids across turns): the next-turn seed must continue past
214330
+ * them so ids stay monotonic within the session (TS
214331
+ * `nextAvailableAgentId` parity). */
214332
+ engineAgentIds = /* @__PURE__ */ new Set();
214333
+ /** Every RustTurnSession this runner created. Held until the agent scope
214334
+ * is disposed so a session's EventSink stays open while its background
214335
+ * tasks are still settling (a subagent launched in turn 1 must be able to
214336
+ * notify after turn 1 ends), and so teardown can close them all at once
214337
+ * (TS `taskService.dispose` parity). */
214338
+ sessions = /* @__PURE__ */ new Set();
214339
+ /** Whether the agent scope was disposed: late engine events (background
214340
+ * task settles racing the teardown) are ignored instead of dispatching
214341
+ * wire ops on a disposed wire / appending to a disposed context. */
214342
+ disposed = false;
214343
+ constructor(context, eventBus, wire, config, modeService, rulesService, toolRegistry, usageService, profile, scopeContext, sessionContext, sessionMetadata, tasks, modelCatalog, providerRuntime, instantiation, toolPolicy, log) {
214344
+ this.context = context;
214345
+ this.eventBus = eventBus;
214346
+ this.wire = wire;
214347
+ this.config = config;
214348
+ this.modeService = modeService;
214349
+ this.rulesService = rulesService;
214350
+ this.toolRegistry = toolRegistry;
214351
+ this.usageService = usageService;
214352
+ this.profile = profile;
214353
+ this.scopeContext = scopeContext;
214354
+ this.sessionContext = sessionContext;
214355
+ this.sessionMetadata = sessionMetadata;
214356
+ this.tasks = tasks;
214357
+ this.modelCatalog = modelCatalog;
214358
+ this.providerRuntime = providerRuntime;
214359
+ this.instantiation = instantiation;
214360
+ this.toolPolicy = toolPolicy;
214361
+ this.log = log;
214362
+ this.registryId = randomUUID();
214363
+ }
214364
+ /** Agent-scoped Rust subagent registry id (see constructor). */
214365
+ registryId;
214366
+ /** TS `stopHookContinuationUsed` parity: the Stop hook fires at most once
214367
+ * after a continuation was delivered. */
214368
+ stopHookContinuationUsed = false;
214369
+ static isEnabled() {
214370
+ return rustEngineEnabled();
214371
+ }
214372
+ /**
214373
+ * Steer the running turn. Mirrors the TS steer path (`steerTurn` op +
214374
+ * user message in the context) and forwards the text into the engine's
214375
+ * steer queue, where it is drained into the next LLM request. Returns
214376
+ * `false` when there is no steerable turn: either no session is active
214377
+ * (the caller starts a new turn) or the engine's turn already finished
214378
+ * (a steer racing the teardown — between the engine's final steer check
214379
+ * and this runner clearing `activeSession` — would land in a queue that
214380
+ * is never drained again; the caller's `runTurn` fallback queues it as
214381
+ * the next turn instead, so it is never lost).
214382
+ */
214383
+ steer(payload) {
214384
+ if (this.disposed) return false;
214385
+ const session = this.activeSession;
214386
+ if (session === void 0) return false;
214387
+ const text = payload.input.filter((part) => part.type === "text").map((part) => part.text ?? "").join("");
214388
+ if (text.length === 0) return false;
214389
+ if (!session.steer(text)) return false;
214390
+ this.wire.dispatch(steerTurn({
214391
+ input: [...payload.input],
214392
+ origin: payload.origin
214393
+ }));
214394
+ this.context.append({
214395
+ role: "user",
214396
+ content: [...payload.input],
214397
+ toolCalls: [],
214398
+ origin: payload.origin,
214399
+ id: randomUUID()
214400
+ });
214401
+ return true;
214402
+ }
214403
+ /**
214404
+ * Run one turn through the Rust engine. The turn clock advances at enqueue
214405
+ * time (TS parity); the turn itself either starts immediately (resolves
214406
+ * with its id) or waits behind the running turn (resolves `undefined`,
214407
+ * like TS `state === 'pending'`).
214408
+ */
214409
+ async runTurn(payload) {
214410
+ if (this.disposed) return void 0;
214411
+ this.wire.dispatch(promptTurn({
214412
+ input: [...payload.input],
214413
+ origin: payload.origin
214414
+ }));
214415
+ const turnId = this.wire.getModel(TurnModel).nextTurnId - 1;
214416
+ const userMessage = {
214417
+ role: "user",
214418
+ content: [...payload.input],
214419
+ toolCalls: [],
214420
+ origin: payload.origin,
214421
+ id: randomUUID()
214422
+ };
214423
+ this.context.append(userMessage);
214424
+ if (this.turnRunning) {
214425
+ this.queued.push({
214426
+ turnId,
214427
+ payload,
214428
+ cancelled: false
214429
+ });
214430
+ return;
214431
+ }
214432
+ this.startQueuedTurn({
214433
+ turnId,
214434
+ payload,
214435
+ cancelled: false
214436
+ });
214437
+ return { turnId };
214438
+ }
214439
+ /** Cancel the active turn; with `turnId`, also cancels a queued turn. */
214440
+ cancel(turnId) {
214441
+ const session = this.activeSession;
214442
+ if (session !== void 0 && (turnId === void 0 || turnId === this.executingTurnId)) {
214443
+ this.approvalAbort?.abort();
214444
+ session.cancel();
214445
+ return true;
214446
+ }
214447
+ let cancelled = false;
214448
+ for (const entry of this.queued) if (entry.turnId === turnId) {
214449
+ entry.cancelled = true;
214450
+ this.wire.dispatch(cancelTurn({ turnId }));
214451
+ cancelled = true;
214452
+ }
214453
+ return cancelled;
214454
+ }
214455
+ /**
214456
+ * Agent-scope teardown (TS `taskService.dispose` parity): every Rust
214457
+ * session is closed — its EventSink stops forwarding, the in-flight turn
214458
+ * is cancelled, and background workers/pollers observe the closed flag and
214459
+ * kill their processes — and late engine events are ignored instead of
214460
+ * dispatching wire ops on a disposed wire. The DI container invokes this
214461
+ * when the agent scope is disposed.
214462
+ */
214463
+ dispose() {
214464
+ this.disposed = true;
214465
+ this.approvalAbort?.abort();
214466
+ for (const session of this.sessions) session.close();
214467
+ this.sessions.clear();
214468
+ this.taskSessions.clear();
214469
+ this.engineTaskAdapters.clear();
214470
+ RustTurnSession.dropTaskRegistry(this.registryId);
214471
+ }
214472
+ startQueuedTurn(entry) {
214473
+ this.turnRunning = true;
214474
+ this.executingTurnId = entry.turnId;
214475
+ this.runTurnNow(entry.turnId, entry.payload.origin).catch(() => void 0).finally(() => {
214476
+ this.turnRunning = false;
214477
+ this.executingTurnId = void 0;
214478
+ if (this.disposed) {
214479
+ this.queued.length = 0;
214480
+ return;
214481
+ }
214482
+ const next = this.queued.find((queued) => !queued.cancelled);
214483
+ if (next === void 0) return;
214484
+ this.queued.splice(this.queued.indexOf(next), 1);
214485
+ this.startQueuedTurn(next);
214486
+ });
214487
+ }
214488
+ async runTurnNow(turnId, origin) {
214489
+ if (this.disposed) return { turnId };
214490
+ const messages = [{
214491
+ role: "system",
214492
+ content: this.profile.getSystemPrompt()
214493
+ }, ...this.context.get().map((message) => this.toLlmMessage(message))];
214494
+ const provider = await this.providerConfig();
214495
+ const inputJson = JSON.stringify({
214496
+ turnId,
214497
+ origin: toEngineTurnOrigin(origin),
214498
+ usesWorkerRejectionGuidance: this.scopeContext.agentId !== "main",
214499
+ activeTools: this.effectiveActiveTools() ?? null,
214500
+ messages,
214501
+ tools: [],
214502
+ provider,
214503
+ maxStepsPerTurn: this.maxStepsPerTurn() ?? null,
214504
+ maxRetriesPerStep: this.maxRetriesPerStep() ?? null,
214505
+ maxContextTokens: this.maxContextTokens() ?? null,
214506
+ nextAgentId: await this.computeNextAgentId(),
214507
+ killGraceMs: this.killGracePeriodMs(),
214508
+ completionReview: {
214509
+ minSteps: 10,
214510
+ reminder: completion_review_default
214511
+ },
214512
+ cwd: this.profile.data().cwd ?? process.cwd()
214513
+ });
214514
+ const policyJson = JSON.stringify({
214515
+ mode: this.modeService.mode,
214516
+ rules: this.rulesService.rules,
214517
+ sessionApprovedPatterns: this.rulesService.sessionApprovalRulePatterns
214518
+ });
214519
+ const scripted = process.env["DIMI_RUST_ENGINE_SCRIPTED"];
214520
+ const session = new RustTurnSession(inputJson, policyJson, scripted ?? void 0, this.registryId);
214521
+ this.sessions.add(session);
214522
+ this.activeSession = session;
214523
+ session.setToolGate((payloadJson) => {
214524
+ (async () => {
214525
+ const payload = JSON.parse(payloadJson);
214526
+ let verdict = { decision: "allow" };
214527
+ try {
214528
+ const hooksRunner = this.instantiation.invokeFunction((accessor) => accessor.get(IExternalHooksRunnerService));
214529
+ if (hooksRunner !== void 0 && ENGINE_NATIVE_TOOLS.has(payload.toolName)) {
214530
+ const block = await hooksRunner.triggerBlock("PreToolUse", {
214531
+ matcherValue: payload.toolName,
214532
+ signal: new AbortController().signal,
214533
+ sessionId: this.sessionContext.sessionId,
214534
+ inputData: {
214535
+ toolName: payload.toolName,
214536
+ toolInput: isPlainRecord(payload.arguments) ? payload.arguments : {},
214537
+ toolCallId: `native-${payload.toolName}`
214538
+ }
214539
+ });
214540
+ if (block !== void 0) verdict = {
214541
+ decision: "block",
214542
+ reason: block.reason
214543
+ };
214544
+ }
214545
+ } catch {}
214546
+ session.completeToolGate(payload.requestId, JSON.stringify(verdict));
214547
+ })();
214548
+ });
214549
+ try {
214550
+ await this.runEngineSession(session, turnId, provider["model"], origin);
214551
+ } finally {
214552
+ this.activeSession = void 0;
214553
+ }
214554
+ return { turnId };
214555
+ }
214556
+ /**
214557
+ * Run one engine session to completion: register the TS tool ecosystem,
214558
+ * drive the approval loop, mirror the engine events into the context.
214559
+ */
214560
+ async runEngineSession(session, turnId, providerModel, origin) {
214561
+ const engineNativeTools = ENGINE_NATIVE_TOOLS;
214562
+ const activeSet = this.effectiveActiveTools();
214563
+ const isToolActiveForRunner = (name) => activeSet === void 0 || activeSet.includes(name);
214564
+ for (const info of this.toolRegistry.list()) {
214565
+ if (!isToolActiveForRunner(info.name)) continue;
214566
+ try {
214567
+ if (engineNativeTools.has(info.name)) {
214568
+ if (info.name !== "Bash") if (info.name === "WaitFor") session.registerNativeToolDef(info.name, WAIT_FOR_NATIVE_DESCRIPTION, JSON.stringify(WAIT_FOR_NATIVE_PARAMETERS));
214569
+ else session.registerNativeToolDef(info.name, info.description, JSON.stringify(info.parameters ?? {
214570
+ type: "object",
214571
+ properties: {}
214572
+ }));
214573
+ continue;
214574
+ }
214575
+ const tool = this.toolRegistry.resolve(info.name);
214576
+ if (tool === void 0) continue;
214577
+ session.registerExternalTool(info.name, info.description, JSON.stringify(info.parameters ?? {
214578
+ type: "object",
214579
+ properties: {}
214580
+ }), (payloadJson) => {
214581
+ (async () => {
214582
+ const payload = JSON.parse(payloadJson);
214583
+ try {
214584
+ const toolCalls = (payload.toolCalls ?? []).map((call) => ({
214585
+ type: "function",
214586
+ id: call.id ?? payload.toolCallId ?? payload.requestId,
214587
+ name: call.name,
214588
+ arguments: typeof call.arguments === "string" ? call.arguments : JSON.stringify(call.arguments) ?? "null"
214589
+ }));
214590
+ const execution = await tool.resolveExecution(payload.arguments, { toolCalls });
214591
+ if (execution.isError === true) {
214592
+ session.completeToolCall(payload.requestId, JSON.stringify({
214593
+ toolCallId: payload.toolCallId ?? payload.requestId,
214594
+ toolName: payload.name,
214595
+ output: toText(execution.output),
214596
+ isError: true,
214597
+ stopTurn: execution.stopTurn === true,
214598
+ updates: []
214599
+ }));
214600
+ return;
214601
+ }
214602
+ const signal = new AbortController().signal;
214603
+ const hooksRunner = this.instantiation.invokeFunction((accessor) => accessor.get(IExternalHooksRunnerService));
214604
+ const toolInput = isPlainRecord(payload.arguments) ? payload.arguments : {};
214605
+ const toolCallId = payload.toolCallId ?? payload.requestId;
214606
+ if (hooksRunner !== void 0) {
214607
+ const block = await hooksRunner.triggerBlock("PreToolUse", {
214608
+ matcherValue: payload.name,
214609
+ signal,
214610
+ sessionId: this.sessionContext.sessionId,
214611
+ inputData: {
214612
+ toolName: payload.name,
214613
+ toolInput,
214614
+ toolCallId
214615
+ }
214616
+ });
214617
+ if (block !== void 0) {
214618
+ session.completeToolCall(payload.requestId, JSON.stringify({
214619
+ toolCallId,
214620
+ toolName: payload.name,
214621
+ output: block.reason,
214622
+ isError: true,
214623
+ stopTurn: false,
214624
+ updates: []
214625
+ }));
214626
+ return;
214627
+ }
214628
+ }
214629
+ const result = await execution.execute({
214630
+ turnId: 0,
214631
+ toolCallId,
214632
+ signal,
214633
+ onUpdate: (update) => {
214634
+ if (signal.aborted) return;
214635
+ this.eventBus.publish({
214636
+ type: "tool.progress",
214637
+ turnId,
214638
+ toolCallId,
214639
+ update
214640
+ });
214641
+ }
214642
+ });
214643
+ let finalResult = result;
214644
+ const truncation = this.instantiation.invokeFunction((accessor) => accessor.get(IAgentToolResultTruncationService));
214645
+ if (truncation !== void 0) try {
214646
+ finalResult = await truncation.truncateForModel({
214647
+ toolName: payload.name,
214648
+ toolCallId,
214649
+ result
214650
+ });
214651
+ } catch {}
214652
+ session.completeToolCall(payload.requestId, JSON.stringify({
214653
+ toolCallId,
214654
+ toolName: payload.name,
214655
+ output: toText(finalResult.output),
214656
+ isError: finalResult.isError === true,
214657
+ stopTurn: finalResult.stopTurn === true,
214658
+ updates: []
214659
+ }));
214660
+ if (hooksRunner !== void 0) {
214661
+ const outputText = toText(finalResult.output);
214662
+ const isError = finalResult.isError === true;
214663
+ hooksRunner.fireAndForgetTrigger(isError ? "PostToolUseFailure" : "PostToolUse", {
214664
+ matcherValue: payload.name,
214665
+ signal,
214666
+ sessionId: this.sessionContext.sessionId,
214667
+ inputData: {
214668
+ toolName: payload.name,
214669
+ toolInput,
214670
+ toolCallId,
214671
+ error: isError ? toDimiErrorPayload(outputText) : void 0,
214672
+ toolOutput: isError ? void 0 : outputText.slice(0, 2e3)
214673
+ }
214674
+ });
214675
+ }
214676
+ } catch (error) {
214677
+ session.completeToolCall(payload.requestId, JSON.stringify({
214678
+ toolCallId: payload.toolCallId ?? payload.requestId,
214679
+ toolName: payload.name,
214680
+ output: `Tool "${payload.name}" failed: ${error instanceof Error ? error.message : String(error)}`,
214681
+ isError: true,
214682
+ stopTurn: false,
214683
+ updates: []
214684
+ }));
214685
+ }
214686
+ })();
214687
+ });
214688
+ } catch (error) {
214689
+ if (engineNativeTools.has(info.name) && info.name !== "Bash") {
214690
+ this.log.error("[rustEngineTurnRunner] failed to register native tool def", {
214691
+ name: info.name,
214692
+ error
214693
+ });
214694
+ this.eventBus.publish({
214695
+ type: "turn.ended",
214696
+ turnId,
214697
+ reason: "failed",
214698
+ error: {
214699
+ name: "ToolRegistrationError",
214700
+ message: `Failed to register native tool def "${info.name}"`
214701
+ }
214702
+ });
214703
+ this.eventBus.publish({
214704
+ type: "error",
214705
+ name: "ToolRegistrationError",
214706
+ message: `Failed to register native tool def "${info.name}"`
214707
+ });
214708
+ throw error;
214709
+ }
214710
+ this.log.error("[rustEngineTurnRunner] failed to register external tool", {
214711
+ name: info.name,
214712
+ error
214713
+ });
214714
+ }
214715
+ }
214716
+ let stepUuid;
214717
+ const segments = [];
214718
+ let stepSawToolCall = false;
214719
+ const nativeToolCalls = /* @__PURE__ */ new Map();
214720
+ let usage = emptyUsage();
214721
+ const publish = (event) => {
214722
+ const { type, ...rest } = event;
214723
+ this.eventBus.publish({
214724
+ type,
214725
+ ...rest
214726
+ });
214727
+ };
214728
+ const flushParts = (turnId, step) => {
214729
+ for (const segment of segments.splice(0)) {
214730
+ const part = segment.type === "think" ? {
214731
+ type: "think",
214732
+ think: segment.text
214733
+ } : {
214734
+ type: "text",
214735
+ text: segment.text
214736
+ };
214737
+ this.context.appendLoopEvent({
214738
+ type: "content.part",
214739
+ stepUuid,
214740
+ part,
214741
+ uuid: randomUUID(),
214742
+ turnId: String(turnId),
214743
+ step
214744
+ });
214745
+ }
214746
+ };
214747
+ const handleEngineEvent = (event) => {
214748
+ if (this.disposed) return;
214749
+ if (event["type"] === "task.started") {
214750
+ this.taskSessions.set(toText(event["taskId"]), session);
214751
+ this.handleTaskStarted(event);
214752
+ return;
214753
+ }
214754
+ if (event["type"] === "task.output") {
214755
+ this.handleTaskOutput(event);
214756
+ return;
214757
+ }
214758
+ if (event["type"] === "task.settled") {
214759
+ this.handleTaskSettled(event);
214760
+ return;
214761
+ }
214762
+ if (event["type"] === "completion.review.injected") {
214763
+ this.context.append({
214764
+ role: "user",
214765
+ content: [{
214766
+ type: "text",
214767
+ text: wrapSystemReminder(toText(event["reminder"]))
214768
+ }],
214769
+ toolCalls: [],
214770
+ origin: {
214771
+ kind: "system_trigger",
214772
+ name: "completion_review"
214773
+ },
214774
+ id: randomUUID()
214775
+ });
214776
+ return;
214777
+ }
214778
+ const busEvent = { ...event };
214779
+ if (event["type"] === "turn.step.completed") {
214780
+ const engineUsage = event["usage"];
214781
+ if (engineUsage !== void 0) {
214782
+ const inputCacheRead = engineUsage.cachedTokens ?? 0;
214783
+ busEvent["usage"] = {
214784
+ inputOther: Math.max((engineUsage.inputTokens ?? 0) - inputCacheRead, 0),
214785
+ output: engineUsage.outputTokens ?? 0,
214786
+ inputCacheRead,
214787
+ inputCacheCreation: 0
214788
+ };
214789
+ } else busEvent["usage"] = emptyUsage();
214790
+ } else if (event["type"] === "turn.ended" && event["reason"] === "failed") {
214791
+ const rawError = event["error"];
214792
+ if (rawError !== void 0) busEvent["error"] = buildErrorPayload(rawError);
214793
+ } else if (event["type"] === "turn.started" && !isDisplayablePromptOrigin(origin)) delete busEvent["prompt"];
214794
+ publish(busEvent);
214795
+ switch (event["type"]) {
214796
+ case "turn.step.started": {
214797
+ const stepNumber = Number(event["step"] ?? 1);
214798
+ stepUuid = randomUUID();
214799
+ stepSawToolCall = false;
214800
+ this.context.appendLoopEvent({
214801
+ type: "step.begin",
214802
+ uuid: stepUuid,
214803
+ turnId: String(turnId),
214804
+ step: stepNumber
214805
+ });
214806
+ break;
214807
+ }
214808
+ case "thinking.delta": {
214809
+ const last = segments[segments.length - 1];
214810
+ if (last?.type === "think") last.text += toText(event["delta"]);
214811
+ else segments.push({
214812
+ type: "think",
214813
+ text: toText(event["delta"])
214814
+ });
214815
+ break;
214816
+ }
214817
+ case "assistant.delta": {
214818
+ const last = segments[segments.length - 1];
214819
+ if (last?.type === "text") last.text += toText(event["delta"]);
214820
+ else segments.push({
214821
+ type: "text",
214822
+ text: toText(event["delta"])
214823
+ });
214824
+ break;
214825
+ }
214826
+ case "tool.call.delta": break;
214827
+ case "tool.call.started": {
214828
+ const id = toText(event["toolCallId"]);
214829
+ const name = toText(event["name"]);
214830
+ stepSawToolCall = true;
214831
+ if (ENGINE_NATIVE_TOOLS.has(name)) nativeToolCalls.set(id, {
214832
+ name,
214833
+ input: event["args"]
214834
+ });
214835
+ this.context.appendLoopEvent({
214836
+ type: "tool.call",
214837
+ stepUuid,
214838
+ toolCallId: id,
214839
+ name,
214840
+ args: event["args"],
214841
+ uuid: randomUUID(),
214842
+ turnId: String(turnId),
214843
+ step: Number(event["step"] ?? 1)
214844
+ });
214845
+ break;
214846
+ }
214847
+ case "tool.result": {
214848
+ const id = toText(event["toolCallId"]);
214849
+ const output = toText(event["output"]);
214850
+ const isError = event["isError"] === true;
214851
+ const native = nativeToolCalls.get(id);
214852
+ if (native !== void 0) {
214853
+ nativeToolCalls.delete(id);
214854
+ const hooksRunner = this.instantiation.invokeFunction((accessor) => accessor.get(IExternalHooksRunnerService));
214855
+ if (hooksRunner !== void 0) hooksRunner.fireAndForgetTrigger(isError ? "PostToolUseFailure" : "PostToolUse", {
214856
+ matcherValue: native.name,
214857
+ signal: new AbortController().signal,
214858
+ sessionId: this.sessionContext.sessionId,
214859
+ inputData: {
214860
+ toolName: native.name,
214861
+ toolInput: isPlainRecord(native.input) ? native.input : {},
214862
+ toolCallId: id,
214863
+ error: isError ? toDimiErrorPayload(output) : void 0,
214864
+ toolOutput: isError ? void 0 : output.slice(0, 2e3)
214865
+ }
214866
+ });
214867
+ }
214868
+ this.context.appendLoopEvent({
214869
+ type: "tool.result",
214870
+ toolCallId: id,
214871
+ result: {
214872
+ output,
214873
+ isError
214874
+ },
214875
+ parentUuid: stepUuid
214876
+ });
214877
+ break;
214878
+ }
214879
+ case "context.compacted": {
214880
+ const summary = toText(event["summary"]);
214881
+ const tokensBefore = Number(event["tokensBefore"] ?? 0);
214882
+ const compactedCount = Number(event["compactedCount"] ?? 0);
214883
+ this.wire.dispatch(fullCompactionBegin({ source: "auto" }));
214884
+ const result = this.context.applyCompaction({
214885
+ summary,
214886
+ contextSummary: buildCompactionSummaryText(summary),
214887
+ compactedCount,
214888
+ tokensBefore
214889
+ });
214890
+ this.wire.dispatch(fullCompactionComplete({}));
214891
+ this.eventBus.publish({
214892
+ type: "compaction.completed",
214893
+ result
214894
+ });
214895
+ break;
214896
+ }
214897
+ case "turn.step.interrupted":
214898
+ if (stepSawToolCall) flushParts(turnId, Number(event["step"] ?? 1));
214899
+ break;
214900
+ case "turn.step.completed": {
214901
+ const stepFinish = toText(event["finishReason"] ?? "end_turn");
214902
+ const stepNumber = Number(event["step"] ?? 1);
214903
+ flushParts(turnId, stepNumber);
214904
+ const engineUsage = event["usage"];
214905
+ if (engineUsage !== void 0) {
214906
+ const inputCacheRead = engineUsage.cachedTokens ?? 0;
214907
+ usage = {
214908
+ inputOther: Math.max((engineUsage.inputTokens ?? 0) - inputCacheRead, 0),
214909
+ output: engineUsage.outputTokens ?? 0,
214910
+ inputCacheRead,
214911
+ inputCacheCreation: 0
214912
+ };
214913
+ } else usage = emptyUsage();
214914
+ this.usageService.record(providerModel, usage, {
214915
+ kind: "loop",
214916
+ turnId: String(turnId),
214917
+ step: stepNumber
214918
+ });
214919
+ this.context.appendLoopEvent({
214920
+ type: "step.end",
214921
+ uuid: stepUuid,
214922
+ turnId: String(turnId),
214923
+ step: stepNumber,
214924
+ finishReason: stepFinish,
214925
+ usage
214926
+ });
214927
+ if (stepFinish !== "tool_use" && stepFinish !== "filtered") this.runStopHook(turnId);
214928
+ break;
214929
+ }
214930
+ case "turn.ended":
214931
+ this.stopHookContinuationUsed = false;
214932
+ if (event["reason"] === "failed" && event["error"] !== void 0) this.eventBus.publish({
214933
+ type: "error",
214934
+ ...buildErrorPayload(event["error"])
214935
+ });
214936
+ break;
214937
+ default: break;
214938
+ }
214939
+ };
214940
+ session.setOnEvent((eventJson) => {
214941
+ try {
214942
+ handleEngineEvent(JSON.parse(eventJson));
214943
+ } catch (error) {
214944
+ this.log.error("[rustEngineTurnRunner] failed to process engine event", { error });
214945
+ }
214946
+ });
214947
+ let progress = JSON.parse(await session.run());
214948
+ while (progress.progress.status === "needsApproval") {
214949
+ const approval = progress.progress.approval;
214950
+ const approvalRequest = {
214951
+ sessionId: this.sessionContext.sessionId,
214952
+ agentId: this.scopeContext.agentId,
214953
+ turnId,
214954
+ toolCallId: approval.toolCallId,
214955
+ toolName: approval.toolName,
214956
+ action: `Approve ${approval.toolName}`,
214957
+ display: {
214958
+ kind: "generic",
214959
+ summary: `Approve ${approval.toolName}`,
214960
+ detail: approval.toolInput
214961
+ }
214962
+ };
214963
+ this.eventBus.publish({
214964
+ type: "permission.approval.requested",
214965
+ ...approvalRequest
214966
+ });
214967
+ let response = { decision: "approved" };
214968
+ const approvalController = new AbortController();
214969
+ this.approvalAbort = approvalController;
214970
+ try {
214971
+ const approvalService = this.instantiation.invokeFunction((accessor) => accessor.get(ISessionApprovalService));
214972
+ response = approvalService !== void 0 ? await Promise.race([approvalService.request(approvalRequest), abortOnSignal(approvalController.signal).then(() => ({ decision: "cancelled" }))]) : { decision: "approved" };
214973
+ } catch {
214974
+ response = { decision: "rejected" };
214975
+ } finally {
214976
+ this.approvalAbort = void 0;
214977
+ }
214978
+ this.eventBus.publish({
214979
+ type: "permission.approval.resolved",
214980
+ ...approvalRequest,
214981
+ decision: response.decision
214982
+ });
214983
+ this.rulesService.recordApprovalResult({
214984
+ turnId,
214985
+ toolCallId: approvalRequest.toolCallId,
214986
+ toolName: approvalRequest.toolName,
214987
+ action: approvalRequest.action,
214988
+ sessionApprovalRule: response.decision === "approved" && response.scope === "session" ? approvalRequest.toolName : void 0,
214989
+ result: response
214990
+ });
214991
+ if (response.decision === "approved" && response.scope === "session") session.addSessionApproval(approvalRequest.toolName);
214992
+ progress = JSON.parse(await session.resume(JSON.stringify(response)));
214993
+ }
214994
+ }
214995
+ /**
214996
+ * TS `externalHooksService.runStop` parity (P1-3): after a non-tool step,
214997
+ * fire the Stop hook; a returned reason becomes a continuation message
214998
+ * (origin `system_trigger`/`stop_hook`) that keeps the turn alive —
214999
+ * steered into the running turn, or launched as a fresh turn when the
215000
+ * engine already finished (the task-notification fallback). Fires at most
215001
+ * once after a continuation was used, mirroring `stopHookContinuationUsed`.
215002
+ */
215003
+ runStopHook(turnId) {
215004
+ if (this.stopHookContinuationUsed) return;
215005
+ this.stopHookContinuationUsed = true;
215006
+ const hooksRunner = this.instantiation.invokeFunction((accessor) => accessor.get(IExternalHooksRunnerService));
215007
+ if (hooksRunner === void 0) return;
215008
+ (async () => {
215009
+ try {
215010
+ const reason = (await hooksRunner.triggerBlock("Stop", {
215011
+ signal: new AbortController().signal,
215012
+ sessionId: this.sessionContext.sessionId,
215013
+ inputData: { stopHookActive: false }
215014
+ }))?.reason;
215015
+ if (reason === void 0 || reason.length === 0) return;
215016
+ const origin = {
215017
+ kind: "system_trigger",
215018
+ name: "stop_hook"
215019
+ };
215020
+ if (this.turnRunning && this.activeSession !== void 0 && this.activeSession.steer(reason)) {
215021
+ this.context.append({
215022
+ role: "user",
215023
+ content: [{
215024
+ type: "text",
215025
+ text: reason
215026
+ }],
215027
+ toolCalls: [],
215028
+ origin,
215029
+ id: randomUUID()
215030
+ });
215031
+ this.wire.dispatch(steerTurn({
215032
+ input: [{
215033
+ type: "text",
215034
+ text: reason
215035
+ }],
215036
+ origin
215037
+ }));
215038
+ } else this.runTurn({
215039
+ input: [{
215040
+ type: "text",
215041
+ text: reason
215042
+ }],
215043
+ origin
215044
+ }).catch(() => void 0);
215045
+ } catch {}
215046
+ })();
215047
+ }
215048
+ /**
215049
+ * `task.started` (engine transport) → TS records: the persisted
215050
+ * `task.started` wire op (TaskModel / transcript / TaskList) plus the
215051
+ * `subagent.spawned` session event for subagent launches.
215052
+ */
215053
+ handleTaskStarted(event) {
215054
+ const taskId = toText(event["taskId"]);
215055
+ const agentId = toText(event["agentId"]);
215056
+ const kind = toText(event["kind"]);
215057
+ const description = toText(event["description"]);
215058
+ const startedAt = Date.now();
215059
+ const pid = toOptionalNumber(event["pid"]);
215060
+ this.taskInfos.set(taskId, {
215061
+ description,
215062
+ startedAt,
215063
+ pid
215064
+ });
215065
+ if (kind === "agent" && agentId !== "") this.engineAgentIds.add(agentId);
215066
+ const info = kind === "agent" ? {
215067
+ taskId,
215068
+ kind: "agent",
215069
+ agentId,
215070
+ subagentType: description,
215071
+ description,
215072
+ status: "running",
215073
+ detached: true,
215074
+ startedAt,
215075
+ endedAt: null
215076
+ } : {
215077
+ taskId,
215078
+ kind: "process",
215079
+ command: description,
215080
+ pid: pid ?? 0,
215081
+ exitCode: null,
215082
+ description,
215083
+ status: "running",
215084
+ detached: true,
215085
+ startedAt,
215086
+ endedAt: null
215087
+ };
215088
+ this.wire.dispatch(taskStarted({ info }));
215089
+ this.registerEngineTask(taskId, agentId, kind, description, pid);
215090
+ if (kind === "agent") {
215091
+ this.eventBus.publish({
215092
+ type: "subagent.spawned",
215093
+ subagentId: agentId,
215094
+ subagentName: description,
215095
+ parentToolCallId: toText(event["parentToolCallId"]),
215096
+ parentAgentId: this.scopeContext.agentId,
215097
+ callerAgentId: this.scopeContext.agentId,
215098
+ description,
215099
+ runInBackground: false
215100
+ });
215101
+ this.eventBus.publish({
215102
+ type: "subagent.started",
215103
+ subagentId: agentId
215104
+ });
215105
+ }
215106
+ }
215107
+ /**
215108
+ * `task.output` (engine transport) → the task-service entry's live sink:
215109
+ * append the streamed delta so TaskOutput shows partial output while the
215110
+ * engine task is still running (TS ProcessTask parity — TS streams chunks
215111
+ * as they arrive; the adapter's settle also appends only the not-yet-
215112
+ * streamed tail, so nothing is duplicated). No wire op / bus event: the
215113
+ * live output is the service's retained buffer, the wire `task.terminated`
215114
+ * tail comes from the settle's full output.
215115
+ */
215116
+ handleTaskOutput(event) {
215117
+ const taskId = toText(event["taskId"]);
215118
+ const delta = toText(event["delta"]);
215119
+ if (delta.length === 0) return;
215120
+ const adapter = this.engineTaskAdapters.get(taskId);
215121
+ if (adapter !== void 0) {
215122
+ adapter.appendOutput(delta);
215123
+ return;
215124
+ }
215125
+ this.log.debug("[rustEngineTurnRunner] task.output for unregistered task", { taskId });
215126
+ }
215127
+ /**
215128
+ * Register an engine background task with `IAgentTaskService` (TaskList /
215129
+ * TaskStop / TaskOutput parity). The adapter bridges TaskStop into the
215130
+ * engine's per-task cancel (`session.cancelTask`) — the launching session's
215131
+ * worker/poller kills its work and settles "killed" — and its `start`
215132
+ * streams the settle output into the service's sink so TaskOutput and the
215133
+ * terminal info see it.
215134
+ */
215135
+ registerEngineTask(taskId, agentId, kind, description, pid) {
215136
+ const session = this.taskSessions.get(taskId) ?? this.activeSession;
215137
+ const adapter = new EngineTaskAdapter({
215138
+ taskId,
215139
+ agentId,
215140
+ kind: kind === "agent" ? "agent" : "process",
215141
+ description,
215142
+ pid,
215143
+ subagentType: kind === "agent" ? description : void 0,
215144
+ forceStop: (reason) => {
215145
+ try {
215146
+ session?.cancelTask(taskId, reason);
215147
+ } catch (error) {
215148
+ this.log.error("[rustEngineTurnRunner] failed to cancel engine task", {
215149
+ taskId,
215150
+ error
215151
+ });
215152
+ }
215153
+ }
215154
+ });
215155
+ this.engineTaskAdapters.set(taskId, adapter);
215156
+ try {
215157
+ this.tasks.registerTask(adapter, {
215158
+ taskId,
215159
+ detached: false
215160
+ });
215161
+ } catch (error) {
215162
+ this.log.error("[rustEngineTurnRunner] failed to register engine task", {
215163
+ taskId,
215164
+ error
215165
+ });
215166
+ this.engineTaskAdapters.delete(taskId);
215167
+ }
215168
+ }
215169
+ /**
215170
+ * `task.settled` (engine transport) → TS records: the persisted
215171
+ * `task.terminated` wire op (bounded output tail), the `subagent.completed`
215172
+ * / `subagent.failed` session events, and the completion notification
215173
+ * delivered to the model (context message + `task.notified` + steer/new
215174
+ * turn — TS `activeOrNewTurn` parity).
215175
+ */
215176
+ handleTaskSettled(event) {
215177
+ const taskId = toText(event["taskId"]);
215178
+ const agentId = toText(event["agentId"]);
215179
+ const kind = toText(event["kind"]);
215180
+ const status = toText(event["status"]);
215181
+ const output = toText(event["output"]);
215182
+ const error = event["error"] === void 0 || event["error"] === null ? void 0 : toText(event["error"]);
215183
+ const launch = this.taskInfos.get(taskId);
215184
+ const endedAt = Date.now();
215185
+ const stopReason = status === "failed" || status === "timed_out" || status === "killed" ? error : void 0;
215186
+ const base = {
215187
+ taskId,
215188
+ description: launch?.description ?? "",
215189
+ status,
215190
+ detached: true,
215191
+ startedAt: launch?.startedAt ?? endedAt,
215192
+ endedAt,
215193
+ stopReason
215194
+ };
215195
+ const info = kind === "agent" ? {
215196
+ ...base,
215197
+ kind: "agent",
215198
+ agentId,
215199
+ subagentType: launch?.description ?? ""
215200
+ } : {
215201
+ ...base,
215202
+ kind: "process",
215203
+ command: launch?.description ?? "",
215204
+ pid: launch?.pid ?? 0,
215205
+ exitCode: toOptionalNumber(event["exitCode"]) ?? null
215206
+ };
215207
+ this.wire.dispatch(taskTerminated({
215208
+ info,
215209
+ outputTail: output.slice(-TERMINAL_OUTPUT_TAIL_CHARS)
215210
+ }));
215211
+ if (kind === "agent") {
215212
+ if (status === "completed") this.eventBus.publish({
215213
+ type: "subagent.completed",
215214
+ subagentId: agentId,
215215
+ resultSummary: output
215216
+ });
215217
+ else if (status === "failed") this.eventBus.publish({
215218
+ type: "subagent.failed",
215219
+ subagentId: agentId,
215220
+ error: error ?? status
215221
+ });
215222
+ }
215223
+ const adapter = this.engineTaskAdapters.get(taskId);
215224
+ if (adapter !== void 0) {
215225
+ adapter.complete(output, {
215226
+ status,
215227
+ stopReason
215228
+ }, toOptionalNumber(event["exitCode"]));
215229
+ this.engineTaskAdapters.delete(taskId);
215230
+ }
215231
+ this.taskInfos.delete(taskId);
215232
+ this.taskSessions.delete(taskId);
215233
+ if (this.tasks.getTask(taskId)?.terminalNotificationSuppressed === true) return;
215234
+ this.deliverTaskNotification({
215235
+ taskId,
215236
+ agentId,
215237
+ kind,
215238
+ status,
215239
+ description: launch?.description ?? "",
215240
+ output,
215241
+ error
215242
+ });
215243
+ }
215244
+ /**
215245
+ * Deliver a task completion notification to the model — the Rust-engine
215246
+ * equivalent of the TS task domain's detached-task notification
215247
+ * (`TaskNotificationStepRequest`, `activeOrNewTurn` admission): append the
215248
+ * `<notification>` XML as a task-origin user message, fire the
215249
+ * `task.notified` hook event, then fold it into the running turn's next
215250
+ * step (steer) or launch a notification turn when idle.
215251
+ */
215252
+ deliverTaskNotification(task) {
215253
+ const key = `task:${task.taskId}:${task.status}`;
215254
+ if (this.deliveredNotificationKeys.has(key)) return;
215255
+ this.deliveredNotificationKeys.add(key);
215256
+ const kindLabel = task.kind === "agent" ? "agent" : "process";
215257
+ const notification = {
215258
+ id: key,
215259
+ category: "task",
215260
+ type: `task.${task.status}`,
215261
+ source_kind: "background_task",
215262
+ source_id: task.taskId,
215263
+ agent_id: task.kind === "agent" ? task.agentId : void 0,
215264
+ title: `Background ${kindLabel} ${task.status}`,
215265
+ severity: task.status === "completed" ? "info" : "warning",
215266
+ body: buildTaskNotificationBody(task),
215267
+ children: task.output.length > 0 ? [renderOutputPreviewBlock(task.output)] : void 0
215268
+ };
215269
+ const xml = renderNotificationXml(notification);
215270
+ const origin = {
215271
+ kind: "task",
215272
+ taskId: task.taskId,
215273
+ status: task.status,
215274
+ notificationId: key
215275
+ };
215276
+ try {
215277
+ if (this.turnRunning && this.activeSession !== void 0 && this.activeSession.steer(xml)) {
215278
+ this.context.append({
215279
+ role: "user",
215280
+ content: [{
215281
+ type: "text",
215282
+ text: xml
215283
+ }],
215284
+ toolCalls: [],
215285
+ origin,
215286
+ id: randomUUID()
215287
+ });
215288
+ this.wire.dispatch(steerTurn({
215289
+ input: [{
215290
+ type: "text",
215291
+ text: xml
215292
+ }],
215293
+ origin
215294
+ }));
215295
+ } else this.runTurn({
215296
+ input: [{
215297
+ type: "text",
215298
+ text: xml
215299
+ }],
215300
+ origin
215301
+ }).catch(() => void 0);
215302
+ this.eventBus.publish({
215303
+ type: "task.notified",
215304
+ notificationType: `task.${task.status}`,
215305
+ title: notification.title,
215306
+ body: notification.body,
215307
+ severity: notification.severity,
215308
+ sourceKind: notification.source_kind,
215309
+ sourceId: notification.source_id
215310
+ });
215311
+ } finally {
215312
+ this.deliveredNotificationKeys.delete(key);
215313
+ }
215314
+ }
215315
+ toLlmMessage(message) {
215316
+ const text = message.content.filter((part) => part.type === "text").map((part) => part.text ?? "").join("");
215317
+ const media = message.content.filter((part) => part.type === "image_url" || part.type === "audio_url" || part.type === "video_url").map((part) => {
215318
+ return {
215319
+ type: "media_url",
215320
+ url: part.imageUrl?.url ?? part.audioUrl?.url ?? part.videoUrl?.url
215321
+ };
215322
+ }).filter((part) => part.url !== void 0);
215323
+ const toolCalls = message.toolCalls.map((call) => ({
215324
+ id: call.id,
215325
+ type: "function",
215326
+ function: {
215327
+ name: call.name,
215328
+ arguments: call.arguments
215329
+ }
215330
+ }));
215331
+ return {
215332
+ role: message.role,
215333
+ content: media.length > 0 ? [...media, ...text.length > 0 ? [{
215334
+ type: "text",
215335
+ text
215336
+ }] : []] : text,
215337
+ ...message.role === "assistant" && toolCalls.length > 0 ? { toolCalls } : {},
215338
+ ...message.role === "tool" && message.toolCallId !== void 0 ? { toolCallId: message.toolCallId } : {}
215339
+ };
215340
+ }
215341
+ /**
215342
+ * The next subagent agent-id number to hand to the engine for this turn
215343
+ * (TS `nextAvailableAgentId` parity): the max known `agent-<n>` suffix
215344
+ * among (a) the ids this runner's engine has already handed out across
215345
+ * turns and (b) the session's persisted agents, plus one. The engine seeds
215346
+ * its per-session counter from it, so ids stay monotonic across turns and
215347
+ * server restarts and never collide with TS-assigned ids.
215348
+ */
215349
+ async computeNextAgentId() {
215350
+ let maxSuffix = -1;
215351
+ const consider = (id) => {
215352
+ const match = /^agent-(\d+)$/.exec(id);
215353
+ if (match !== null) maxSuffix = Math.max(maxSuffix, Number(match[1]));
215354
+ };
215355
+ for (const id of this.engineAgentIds) consider(id);
215356
+ try {
215357
+ const persisted = (await this.sessionMetadata.read()).agents ?? {};
215358
+ for (const id of Object.keys(persisted)) consider(id);
215359
+ } catch {
215360
+ this.log.warn("[rustEngineTurnRunner] failed to read session metadata; seeding next agent id from in-session engine ids only");
215361
+ }
215362
+ return maxSuffix + 1;
215363
+ }
215364
+ maxStepsPerTurn() {
215365
+ return this.config.get("loop_control")?.maxStepsPerTurn;
215366
+ }
215367
+ /**
215368
+ * The effective engine-side tool allowlist (TS `isToolActiveComposed`
215369
+ * parity): the runner's `IAgentToolPolicyService.isToolActive` composes
215370
+ * the profile allowlist/denylist, the global `[tools]` config, and the
215371
+ * session denylist — with MCP glob semantics for `mcp__*` tools. AllDone
215372
+ * stays active regardless (the completion-review protocol needs it).
215373
+ * `undefined` = unconstrained (all tools).
215374
+ */
215375
+ effectiveActiveTools() {
215376
+ const all = this.toolRegistry.list().map((info) => info.name);
215377
+ const active = all.filter((name) => {
215378
+ if (name === "AllDone") return true;
215379
+ return this.toolPolicy.isToolActive(name);
215380
+ });
215381
+ return active.length === all.length ? void 0 : active;
215382
+ }
215383
+ maxRetriesPerStep() {
215384
+ return this.config.get("loop_control")?.maxRetriesPerStep;
215385
+ }
215386
+ killGracePeriodMs() {
215387
+ return resolveAgentTaskConfig(this.config)?.killGracePeriodMs ?? 5e3;
215388
+ }
215389
+ maxContextTokens() {
215390
+ const capability = this.profile.data().modelCapabilities;
215391
+ const max = capability.max_input_tokens ?? capability.max_context_tokens;
215392
+ return max > 0 ? max : void 0;
215393
+ }
215394
+ async providerConfig() {
215395
+ const modelAlias = this.profile.data().modelAlias ?? this.profile.getModel();
215396
+ let baseUrl = "";
215397
+ let apiKey = "";
215398
+ let modelId = modelAlias || "gpt-4o";
215399
+ try {
215400
+ const model = this.modelCatalog.get(modelAlias);
215401
+ baseUrl = model.baseUrl;
215402
+ apiKey = (await this.providerRuntime.getAuth(model))?.auth.apiKey ?? "";
215403
+ modelId = model.id;
215404
+ } catch {}
215405
+ return {
215406
+ baseUrl: baseUrl || "https://api.openai.com/v1",
215407
+ apiKey,
215408
+ model: modelId,
215409
+ thinkingEffort: this.profile.getEffectiveThinkingLevel()
215410
+ };
215411
+ }
215412
+ };
215413
+ RustEngineTurnRunner = __decorate$1([
215414
+ __decorateParam(0, IAgentContextMemoryService),
215415
+ __decorateParam(1, IEventBus),
215416
+ __decorateParam(2, IWireService),
215417
+ __decorateParam(3, IConfigService),
215418
+ __decorateParam(4, IAgentPermissionModeService),
215419
+ __decorateParam(5, IAgentPermissionRulesService),
215420
+ __decorateParam(6, IAgentToolRegistryService),
215421
+ __decorateParam(7, IAgentUsageService),
215422
+ __decorateParam(8, IAgentProfileService),
215423
+ __decorateParam(9, IAgentScopeContext),
215424
+ __decorateParam(10, ISessionContext),
215425
+ __decorateParam(11, ISessionMetadata),
215426
+ __decorateParam(12, IAgentTaskService),
215427
+ __decorateParam(13, IModelCatalog),
215428
+ __decorateParam(14, IProviderRuntime),
215429
+ __decorateParam(15, IInstantiationService),
215430
+ __decorateParam(16, IAgentToolPolicyService),
215431
+ __decorateParam(17, ILogService)
215432
+ ], RustEngineTurnRunner);
215433
+ registerScopedService(2, IRustEngineTurnRunner, RustEngineTurnRunner, 0, "rustEngineTurnRunner");
215434
+ }));
215435
+ //#endregion
213604
215436
  //#region ../../packages/agent-core-v2/src/agent/rpc/prompt-metadata.ts
213605
215437
  function promptMetadataTextFromPayload(payload) {
213606
215438
  return promptMetadataTextFromContentParts(payload.input);
@@ -213669,6 +215501,7 @@ var init_rpcService = __esmMin((() => {
213669
215501
  init_telemetry$2();
213670
215502
  init_toolRegistry();
213671
215503
  init_loop();
215504
+ init_rustEngineTurnRunner();
213672
215505
  init_rpc$2();
213673
215506
  init_prompt_metadata();
213674
215507
  init_decorateParam();
@@ -213692,7 +215525,8 @@ var init_rpcService = __esmMin((() => {
213692
215525
  sessionContext;
213693
215526
  scopeContext;
213694
215527
  agentLifecycle;
213695
- constructor(promptService, conversationUndo, loop, toolPolicy, permissionMode, fullCompaction, toolRegistry, context, contextSize, skills, telemetry, eventBus, eventService, plugins, metadata, sessionContext, scopeContext, agentLifecycle) {
215528
+ rustEngineTurnRunner;
215529
+ constructor(promptService, conversationUndo, loop, toolPolicy, permissionMode, fullCompaction, toolRegistry, context, contextSize, skills, telemetry, eventBus, eventService, plugins, metadata, sessionContext, scopeContext, agentLifecycle, rustEngineTurnRunner) {
213696
215530
  this.promptService = promptService;
213697
215531
  this.conversationUndo = conversationUndo;
213698
215532
  this.loop = loop;
@@ -213711,6 +215545,7 @@ var init_rpcService = __esmMin((() => {
213711
215545
  this.sessionContext = sessionContext;
213712
215546
  this.scopeContext = scopeContext;
213713
215547
  this.agentLifecycle = agentLifecycle;
215548
+ this.rustEngineTurnRunner = rustEngineTurnRunner;
213714
215549
  }
213715
215550
  async prompt(payload) {
213716
215551
  if (payload.disabledTools !== void 0) try {
@@ -213720,6 +215555,13 @@ var init_rpcService = __esmMin((() => {
213720
215555
  throw error;
213721
215556
  }
213722
215557
  await this.updatePromptMetadata(promptMetadataTextFromPayload(payload));
215558
+ if (RustEngineTurnRunner.isEnabled()) {
215559
+ const launched = await this.rustEngineTurnRunner.runTurn({
215560
+ input: [...payload.input],
215561
+ origin: { kind: "user" }
215562
+ });
215563
+ return launched === void 0 ? void 0 : { turn_id: launched.turnId };
215564
+ }
213723
215565
  const handle = await this.promptService.enqueue({ message: {
213724
215566
  role: "user",
213725
215567
  content: [...payload.input],
@@ -213732,6 +215574,17 @@ var init_rpcService = __esmMin((() => {
213732
215574
  }
213733
215575
  async steer(payload) {
213734
215576
  this.telemetry.track2("input_steer", { parts: payload.input.length });
215577
+ if (RustEngineTurnRunner.isEnabled()) {
215578
+ if (this.rustEngineTurnRunner.steer({
215579
+ input: [...payload.input],
215580
+ origin: { kind: "user" }
215581
+ })) return { turn_id: 0 };
215582
+ const launched = await this.rustEngineTurnRunner.runTurn({
215583
+ input: [...payload.input],
215584
+ origin: { kind: "user" }
215585
+ });
215586
+ return launched === void 0 ? void 0 : { turn_id: launched.turnId };
215587
+ }
213735
215588
  const submitted = await this.promptService.enqueueOrSteer({ message: {
213736
215589
  role: "user",
213737
215590
  content: [...payload.input],
@@ -213742,6 +215595,10 @@ var init_rpcService = __esmMin((() => {
213742
215595
  return turn === void 0 ? void 0 : { turn_id: turn.id };
213743
215596
  }
213744
215597
  cancel({ turnId }) {
215598
+ if (RustEngineTurnRunner.isEnabled()) {
215599
+ this.rustEngineTurnRunner.cancel(turnId);
215600
+ return;
215601
+ }
213745
215602
  if (this.loop.status().state === "running") this.telemetry.track2("cancel", {
213746
215603
  from: "streaming",
213747
215604
  trace_id: this.loop.status().activeTraceId
@@ -213846,7 +215703,8 @@ var init_rpcService = __esmMin((() => {
213846
215703
  __decorateParam(14, ISessionMetadata),
213847
215704
  __decorateParam(15, ISessionContext),
213848
215705
  __decorateParam(16, IAgentScopeContext),
213849
- __decorateParam(17, IAgentLifecycleService)
215706
+ __decorateParam(17, IAgentLifecycleService),
215707
+ __decorateParam(18, IRustEngineTurnRunner)
213850
215708
  ], AgentRPCService);
213851
215709
  registerScopedService(2, IAgentRPCService, AgentRPCService, 0, "rpc");
213852
215710
  }));
@@ -216423,13 +218281,6 @@ var init_args_validator = __esmMin((() => {
216423
218281
  ]);
216424
218282
  }));
216425
218283
  //#endregion
216426
- //#region ../../packages/agent-core-v2/src/agent/toolResultTruncation/toolResultTruncation.ts
216427
- var IAgentToolResultTruncationService;
216428
- var init_toolResultTruncation = __esmMin((() => {
216429
- init_instantiation();
216430
- IAgentToolResultTruncationService = createDecorator("agentToolResultTruncationService");
216431
- }));
216432
- //#endregion
216433
218284
  //#region ../../packages/agent-core-v2/src/agent/toolExecutor/toolScheduler.ts
216434
218285
  function createControlledPromise() {
216435
218286
  let resolve;
@@ -217493,7 +219344,7 @@ var init_src$6 = __esmMin((() => {
217493
219344
  init_migration();
217494
219345
  init_sessionLogService();
217495
219346
  init_telemetry$2();
217496
- init_events$7();
219347
+ init_events$8();
217497
219348
  init_telemetryService();
217498
219349
  init_agentTelemetryContext();
217499
219350
  init_agentTelemetryContextService();
@@ -217688,6 +219539,8 @@ var init_src$6 = __esmMin((() => {
217688
219539
  init_wait();
217689
219540
  init_waitService();
217690
219541
  init_waitForTool();
219542
+ init_agent_output$1();
219543
+ init_agentOutputTool();
217691
219544
  init_completion();
217692
219545
  init_allDoneTool();
217693
219546
  init_configSection$4();
@@ -218578,7 +220431,7 @@ var modelCatalogItemSchema$1, providerCatalogStatusSchema$1, providerCatalogItem
218578
220431
  provider: string$2().min(1),
218579
220432
  reason: string$2().min(1)
218580
220433
  });
218581
- })), tokenUsageSchema$2, finishReasonSchema$1, usageStatusSchema$2, permissionModeSchema$2, skillSourceSchema$2, userPromptOriginSchema$1, skillActivationOriginSchema$1, pluginCommandOriginSchema$1, injectionOriginSchema$1, shellCommandOriginSchema$1, compactionSummaryOriginSchema$1, systemTriggerOriginSchema$1, taskLifecycleStatusSchema$2, taskOriginSchema$1, cronJobOriginSchema$1, cronMissedOriginSchema$1, hookResultOriginSchema$1, retryOriginSchema$1, promptOriginSchema$1, dimiErrorCodeSchema$1, dimiErrorPayloadSchema$1, dimiErrorPayloadObjectSchema$1, taskInfoBaseSchema$1, processTaskInfoSchema$1, agentTaskInfoSchema$2, questionTaskInfoSchema$1, toolTaskInfoSchema$1, taskInfoSchema$1, compactionResultSchema$1, toolUpdateSchema$1, turnEndReasonSchema$2, agentPhaseSchema$1, agentStatusUpdatedEventSchema$2, sessionMetaUpdatedEventSchema$1, sessionCreatedEventSchema$1, workspaceCreatedEventSchema$1, workspaceUpdatedEventSchema$1, workspaceDeletedEventSchema$1, sessionWorkChangedEventSchema$1, sessionStatusChangedEventSchema$1, modelCatalogChangedEventSchema, skillActivatedEventSchema$1, pluginCommandActivatedEventSchema$1, errorEventSchema$2, warningEventSchema$2, turnStartedEventSchema$2, turnEndedEventSchema$2, turnStepStartedEventSchema$1, turnStepCompletedEventSchema$1, turnStepRetryingEventSchema$1, turnStepInterruptedEventSchema$1, assistantDeltaEventSchema$2, hookResultEventSchema$1, thinkingDeltaEventSchema$2, toolCallDeltaEventSchema$1, toolCallStartedEventSchema$2, toolProgressEventSchema$1, shellOutputEventSchema$1, shellStartedEventSchema$1, shellCompletedEventSchema$1, toolResultEventSchema$2, subagentSpawnedEventSchema$1, subagentStartedEventSchema$1, subagentSuspendedEventSchema$1, subagentCompletedEventSchema$1, subagentFailedEventSchema$1, compactionStartedEventSchema$1, compactionBlockedEventSchema$1, compactionCancelledEventSchema$1, compactionCompletedEventSchema$1, taskStartedEventSchema$1, taskTerminatedEventSchema$1, cronFiredEventSchema$1, promptSubmittedEventSchema$1, promptCompletedEventSchema$2, promptAbortedEventSchema$2, promptSteeredEventSchema$1, toolListUpdatedReasonSchema$1, toolListUpdatedEventSchema$1, mcpServerStatusPayloadSchema$1, mcpServerStatusEventSchema$1, agentEventSchema$1, eventSchema$1, VOLATILE_EVENT_TYPES$1, init_events$6 = __esmMin((() => {
220434
+ })), tokenUsageSchema$2, finishReasonSchema$1, usageStatusSchema$2, permissionModeSchema$2, skillSourceSchema$2, userPromptOriginSchema$1, skillActivationOriginSchema$1, pluginCommandOriginSchema$1, injectionOriginSchema$1, shellCommandOriginSchema$1, compactionSummaryOriginSchema$1, systemTriggerOriginSchema$1, taskLifecycleStatusSchema$2, taskOriginSchema$1, cronJobOriginSchema$1, cronMissedOriginSchema$1, hookResultOriginSchema$1, retryOriginSchema$1, promptOriginSchema$1, dimiErrorCodeSchema$1, dimiErrorPayloadSchema$1, dimiErrorPayloadObjectSchema$1, taskInfoBaseSchema$1, processTaskInfoSchema$1, agentTaskInfoSchema$2, questionTaskInfoSchema$1, toolTaskInfoSchema$1, taskInfoSchema$1, compactionResultSchema$1, toolUpdateSchema$1, turnEndReasonSchema$2, agentPhaseSchema$1, agentStatusUpdatedEventSchema$2, sessionMetaUpdatedEventSchema$1, sessionCreatedEventSchema$1, workspaceCreatedEventSchema$1, workspaceUpdatedEventSchema$1, workspaceDeletedEventSchema$1, sessionWorkChangedEventSchema$1, sessionStatusChangedEventSchema$1, modelCatalogChangedEventSchema, skillActivatedEventSchema$1, pluginCommandActivatedEventSchema$1, errorEventSchema$2, warningEventSchema$2, turnStartedEventSchema$2, turnEndedEventSchema$2, turnStepStartedEventSchema$1, turnStepCompletedEventSchema$1, turnStepRetryingEventSchema$1, turnStepInterruptedEventSchema$1, assistantDeltaEventSchema$2, hookResultEventSchema$1, thinkingDeltaEventSchema$2, toolCallDeltaEventSchema$1, toolCallStartedEventSchema$2, toolProgressEventSchema$1, shellOutputEventSchema$1, shellStartedEventSchema$1, shellCompletedEventSchema$1, toolResultEventSchema$2, subagentSpawnedEventSchema$1, subagentStartedEventSchema$1, subagentSuspendedEventSchema$1, subagentCompletedEventSchema$1, subagentFailedEventSchema$1, compactionStartedEventSchema$1, compactionBlockedEventSchema$1, compactionCancelledEventSchema$1, compactionCompletedEventSchema$1, taskStartedEventSchema$1, taskTerminatedEventSchema$1, cronFiredEventSchema$1, promptSubmittedEventSchema$1, promptCompletedEventSchema$2, promptAbortedEventSchema$2, promptSteeredEventSchema$1, toolListUpdatedReasonSchema$1, toolListUpdatedEventSchema$1, mcpServerStatusPayloadSchema$1, mcpServerStatusEventSchema$1, agentEventSchema$1, eventSchema$1, VOLATILE_EVENT_TYPES$1, init_events$7 = __esmMin((() => {
218582
220435
  init_zod$1();
218583
220436
  init_display$1();
218584
220437
  init_message$1();
@@ -219352,7 +221205,7 @@ var modelCatalogItemSchema$1, providerCatalogStatusSchema$1, providerCatalogItem
219352
221205
  })), sessionCursorSchema$1, cursorsBySessionSchema$1, wsEventEnvelopeSchema$1, wsAckEnvelopeSchema$1, serverHelloPayloadSchema$1, serverHelloMessageSchema$1, agentFilterSchema$1, clientHelloPayloadSchema$1, clientHelloMessageSchema$1, clientHelloAckPayloadSchema$1, clientHelloAckMessageSchema$1, watchFsConfigSchema$1, subscribePayloadSchema$1, subscribeMessageSchema$1, subscribeAckPayloadSchema$1, subscribeAckMessageSchema$1, unsubscribePayloadSchema$1, unsubscribeMessageSchema$1, unsubscribeAckPayloadSchema$1, unsubscribeAckMessageSchema$1, watchFsAddPayloadSchema$1, watchFsAddMessageSchema$1, watchFsRemovePayloadSchema$1, watchFsRemoveMessageSchema$1, watchFsAckPayloadSchema$1, watchFsAckMessageSchema$1, abortPayloadSchema$1, abortMessageSchema$1, abortAckPayloadSchema$1, abortAckMessageSchema$1, terminalAttachPayloadSchema$1, terminalAttachMessageSchema$1, terminalAttachAckPayloadSchema$1, terminalAttachAckMessageSchema$1, terminalDetachPayloadSchema$1, terminalDetachMessageSchema$1, terminalDetachAckPayloadSchema$1, terminalDetachAckMessageSchema$1, terminalInputPayloadSchema$1, terminalInputMessageSchema$1, terminalInputAckPayloadSchema$1, terminalInputAckMessageSchema$1, terminalResizePayloadSchema$1, terminalResizeMessageSchema$1, terminalResizeAckPayloadSchema$1, terminalResizeAckMessageSchema$1, terminalClosePayloadSchema$1, terminalCloseMessageSchema$1, terminalCloseAckPayloadSchema$1, terminalCloseAckMessageSchema$1, pingPayloadSchema$1, pingMessageSchema$1, pongPayloadSchema$1, pongMessageSchema$1, resyncRequiredPayloadSchema$1, resyncRequiredMessageSchema$1, wsErrorPayloadSchema$1, wsErrorMessageSchema$1, terminalOutputPayloadSchema$1, terminalExitPayloadSchema$1, clientControlOperations$1, serverSystemOperations$1;
219353
221206
  var init_ws_control$1 = __esmMin((() => {
219354
221207
  init_zod$1();
219355
- init_events$6();
221208
+ init_events$7();
219356
221209
  init_time();
219357
221210
  sessionCursorSchema$1 = object({
219358
221211
  seq: number$2().int().nonnegative(),
@@ -219897,7 +221750,7 @@ var init_tool$2 = __esmMin((() => {
219897
221750
  var skillDescriptorSchema$1;
219898
221751
  var init_skill$2 = __esmMin((() => {
219899
221752
  init_zod$1();
219900
- init_events$6();
221753
+ init_events$7();
219901
221754
  skillDescriptorSchema$1 = object({
219902
221755
  name: string$2().min(1),
219903
221756
  description: string$2(),
@@ -220630,7 +222483,7 @@ var init_src$5 = __esmMin((() => {
220630
222483
  init_pagination$1();
220631
222484
  init_time();
220632
222485
  init_request_id$2();
220633
- init_events$6();
222486
+ init_events$7();
220634
222487
  init_display$1();
220635
222488
  init_ws_control$1();
220636
222489
  init_asyncapi$1();
@@ -220669,7 +222522,7 @@ var init_src$5 = __esmMin((() => {
220669
222522
  }));
220670
222523
  //#endregion
220671
222524
  //#region ../../packages/node-sdk/src/events.ts
220672
- var init_events$5 = __esmMin((() => {
222525
+ var init_events$6 = __esmMin((() => {
220673
222526
  init_src$5();
220674
222527
  }));
220675
222528
  //#endregion
@@ -220724,7 +222577,7 @@ var MAIN_AGENT_ID$6, Session;
220724
222577
  var init_session$3 = __esmMin((() => {
220725
222578
  init_src$6();
220726
222579
  init_errors$3();
220727
- init_events$5();
222580
+ init_events$6();
220728
222581
  MAIN_AGENT_ID$6 = "main";
220729
222582
  Session = class {
220730
222583
  id;
@@ -221753,7 +223606,7 @@ var init_helpers = __esmMin((() => {
221753
223606
  removed: array$2(string$2()),
221754
223607
  changed: array$2(string$2())
221755
223608
  });
221756
- })), textPartSchema, imageUrlPartSchema, videoUrlPartSchema, promptPartSchema, emptyPayloadSchema, promptPayloadSchema, steerPayloadSchema, promptLaunchResultSchema, cancelPayloadSchema, runShellCommandPayloadSchema, shellCommandResultSchema, setModelResultSchema, permissionModeSchema$1, setPermissionPayloadSchema, tokenUsageSchema$1, usageStatusSchema$1, agentContextDataSchema, planDataSchema, taskLifecycleStatusSchema$1, taskInfoBaseFields, agentTaskInfoSchema$1, agentRpcContract;
223609
+ })), textPartSchema, imageUrlPartSchema, videoUrlPartSchema, promptPartSchema, emptyPayloadSchema, promptPayloadSchema, steerPayloadSchema, promptLaunchResultSchema, cancelPayloadSchema, runShellCommandPayloadSchema, shellCommandResultSchema$1, setModelResultSchema, permissionModeSchema$1, setPermissionPayloadSchema, tokenUsageSchema$1, usageStatusSchema$1, agentContextDataSchema, planDataSchema, taskLifecycleStatusSchema$1, taskInfoBaseFields, agentTaskInfoSchema$1, agentRpcContract;
221757
223610
  var init_rpc$1 = __esmMin((() => {
221758
223611
  init_zod$1();
221759
223612
  init_helpers();
@@ -221792,7 +223645,7 @@ var init_rpc$1 = __esmMin((() => {
221792
223645
  command: string$2(),
221793
223646
  commandId: string$2().optional()
221794
223647
  });
221795
- shellCommandResultSchema = object({
223648
+ shellCommandResultSchema$1 = object({
221796
223649
  stdout: string$2(),
221797
223650
  stderr: string$2(),
221798
223651
  isError: boolean$2().optional(),
@@ -221924,7 +223777,7 @@ var init_services = __esmMin((() => {
221924
223777
  agentShellCommandContract = {
221925
223778
  run: {
221926
223779
  input: tuple([runShellCommandPayloadSchema]),
221927
- output: shellCommandResultSchema
223780
+ output: shellCommandResultSchema$1
221928
223781
  },
221929
223782
  cancel: {
221930
223783
  input: tuple([string$2()]),
@@ -222868,7 +224721,7 @@ var init_contract$1 = __esmMin((() => {
222868
224721
  //#endregion
222869
224722
  //#region ../../packages/klient/src/contract/global/events.ts
222870
224723
  var configChangedSchema, reloadSummarySchema, sessionMetaUpdatedSchema, globalEvents;
222871
- var init_events$4 = __esmMin((() => {
224724
+ var init_events$5 = __esmMin((() => {
222872
224725
  init_zod$1();
222873
224726
  configChangedSchema = object({
222874
224727
  domain: string$2(),
@@ -222932,7 +224785,7 @@ var init_events$4 = __esmMin((() => {
222932
224785
  //#endregion
222933
224786
  //#region ../../packages/klient/src/contract/session/events.ts
222934
224787
  var sessionEvents;
222935
- var init_events$3 = __esmMin((() => {
224788
+ var init_events$4 = __esmMin((() => {
222936
224789
  init_zod$1();
222937
224790
  init_interaction$1();
222938
224791
  init_metadata();
@@ -222958,7 +224811,7 @@ var init_events$3 = __esmMin((() => {
222958
224811
  //#endregion
222959
224812
  //#region ../../packages/klient/src/contract/agent/events.ts
222960
224813
  var turnStartedEventSchema$1, turnEndedEventSchema$1, assistantDeltaEventSchema$1, thinkingDeltaEventSchema$1, toolCallStartedEventSchema$1, toolResultEventSchema$1, promptCompletedEventSchema$1, promptAbortedEventSchema$1, permissionApprovalRequestedEventSchema, permissionApprovalResolvedEventSchema, errorEventSchema$1, warningEventSchema$1, agentStatusUpdatedEventSchema$1, agentEvents;
222961
- var init_events$2 = __esmMin((() => {
224814
+ var init_events$3 = __esmMin((() => {
222962
224815
  init_zod$1();
222963
224816
  turnStartedEventSchema$1 = object({
222964
224817
  type: literal("turn.started"),
@@ -223592,9 +225445,9 @@ function createKlientFromChannel(channel, options = {}) {
223592
225445
  }
223593
225446
  var init_klient = __esmMin((() => {
223594
225447
  init_contract$1();
225448
+ init_events$5();
223595
225449
  init_events$4();
223596
225450
  init_events$3();
223597
- init_events$2();
223598
225451
  init_hub();
223599
225452
  init_global();
223600
225453
  init_session$2();
@@ -226020,7 +227873,7 @@ var init_src$4 = __esmMin((() => {
226020
227873
  init_errors$3();
226021
227874
  init_logging();
226022
227875
  init_src$6();
226023
- init_events$5();
227876
+ init_events$6();
226024
227877
  }));
226025
227878
  //#endregion
226026
227879
  //#region ../../packages/telemetry/src/index.ts
@@ -232272,7 +234125,7 @@ var init_schema$1 = __esmMin((() => {
232272
234125
  seq: transcriptSeqSchema.optional()
232273
234126
  });
232274
234127
  })), transcriptResetEventSchema, transcriptOpsEventSchema;
232275
- var init_events$1 = __esmMin((() => {
234128
+ var init_events$2 = __esmMin((() => {
232276
234129
  init_zod$1();
232277
234130
  init_schema$1();
232278
234131
  transcriptResetEventSchema = transcriptResetPayloadSchema.extend({ type: literal("transcript.reset") });
@@ -232303,7 +234156,7 @@ var init_src$2 = __esmMin((() => {
232303
234156
  init_groupTurns();
232304
234157
  init_foldFacts();
232305
234158
  init_schema$1();
232306
- init_events$1();
234159
+ init_events$2();
232307
234160
  }));
232308
234161
  //#endregion
232309
234162
  //#region ../../packages/agent-core-v2/src/agent/contextMemory/protocolMessage.ts
@@ -259106,7 +260959,7 @@ function isVolatileEventType(type) {
259106
260959
  return volatileEventTypeSet.has(type);
259107
260960
  }
259108
260961
  var VOLATILE_EVENT_TYPES, volatileEventTypeSet;
259109
- var init_events = __esmMin((() => {
260962
+ var init_events$1 = __esmMin((() => {
259110
260963
  VOLATILE_EVENT_TYPES = [
259111
260964
  "assistant.delta",
259112
260965
  "thinking.delta",
@@ -259344,11 +261197,11 @@ function registerApprovalsRoutes(app, core) {
259344
261197
  const listRoute = defineRoute({
259345
261198
  method: "GET",
259346
261199
  path: "/sessions/{session_id}/approvals",
259347
- params: sessionIdParamSchema$9,
261200
+ params: sessionIdParamSchema$10,
259348
261201
  querystring: listPendingApprovalsQuerySchema,
259349
261202
  success: { data: listPendingApprovalsResponseSchema },
259350
261203
  errors: {
259351
- [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$7 },
261204
+ [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$8 },
259352
261205
  [ErrorCode.SESSION_NOT_FOUND]: {}
259353
261206
  },
259354
261207
  description: "List pending approval requests for a session",
@@ -259371,7 +261224,7 @@ function registerApprovalsRoutes(app, core) {
259371
261224
  body: approvalResolveRequestSchema,
259372
261225
  success: { data: approvalResolveResultSchema },
259373
261226
  errors: {
259374
- [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$7 },
261227
+ [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$8 },
259375
261228
  [ErrorCode.SESSION_NOT_FOUND]: {},
259376
261229
  [ErrorCode.APPROVAL_NOT_FOUND]: {},
259377
261230
  [ErrorCode.APPROVAL_ALREADY_RESOLVED]: { dataSchema: approvalAlreadyResolvedDataSchema }
@@ -259434,7 +261287,7 @@ function toWireApproval(interaction, sessionId) {
259434
261287
  expires_at: new Date(interaction.createdAt + APPROVAL_EXPIRY_MS).toISOString()
259435
261288
  };
259436
261289
  }
259437
- var sessionIdParamSchema$9, approvalParamsSchema, detailsSchema$7, APPROVAL_EXPIRY_MS;
261290
+ var sessionIdParamSchema$10, approvalParamsSchema, detailsSchema$8, APPROVAL_EXPIRY_MS;
259438
261291
  var init_approvals = __esmMin((() => {
259439
261292
  init_src$6();
259440
261293
  init_error_codes();
@@ -259443,12 +261296,12 @@ var init_approvals = __esmMin((() => {
259443
261296
  init_envelope();
259444
261297
  init_requestLog();
259445
261298
  init_defineRoute();
259446
- sessionIdParamSchema$9 = object({ session_id: string$2().min(1) });
261299
+ sessionIdParamSchema$10 = object({ session_id: string$2().min(1) });
259447
261300
  approvalParamsSchema = object({
259448
261301
  session_id: string$2().min(1),
259449
261302
  approval_id: string$2().min(1)
259450
261303
  });
259451
- detailsSchema$7 = array$2(object({
261304
+ detailsSchema$8 = array$2(object({
259452
261305
  path: string$2(),
259453
261306
  message: string$2()
259454
261307
  }));
@@ -259507,11 +261360,11 @@ function registerQuestionsRoutes(app, core) {
259507
261360
  const listRoute = defineRoute({
259508
261361
  method: "GET",
259509
261362
  path: "/sessions/{session_id}/questions",
259510
- params: sessionIdParamSchema$8,
261363
+ params: sessionIdParamSchema$9,
259511
261364
  querystring: listPendingQuestionsQuerySchema,
259512
261365
  success: { data: listPendingQuestionsResponseSchema },
259513
261366
  errors: {
259514
- [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$6 },
261367
+ [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$7 },
259515
261368
  [ErrorCode.SESSION_NOT_FOUND]: {}
259516
261369
  },
259517
261370
  description: "List pending question requests for a session",
@@ -259533,7 +261386,7 @@ function registerQuestionsRoutes(app, core) {
259533
261386
  params: tailParamsSchema,
259534
261387
  success: { data: questionResolveResultSchema },
259535
261388
  errors: {
259536
- [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$6 },
261389
+ [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$7 },
259537
261390
  [ErrorCode.SESSION_NOT_FOUND]: {},
259538
261391
  [ErrorCode.QUESTION_NOT_FOUND]: {},
259539
261392
  [ErrorCode.APPROVAL_ALREADY_RESOLVED]: { dataSchema: questionAlreadyResolvedDataSchema },
@@ -259717,7 +261570,7 @@ function toInProcessResponse(resp, request) {
259717
261570
  if (resp.method !== void 0 && resp.method !== "click") out.method = resp.method;
259718
261571
  return out;
259719
261572
  }
259720
- var sessionIdParamSchema$8, tailParamsSchema, detailsSchema$6;
261573
+ var sessionIdParamSchema$9, tailParamsSchema, detailsSchema$7;
259721
261574
  var init_questions = __esmMin((() => {
259722
261575
  init_src$6();
259723
261576
  init_error_codes();
@@ -259728,12 +261581,12 @@ var init_questions = __esmMin((() => {
259728
261581
  init_requestLog();
259729
261582
  init_defineRoute();
259730
261583
  init_action_suffix();
259731
- sessionIdParamSchema$8 = object({ session_id: string$2().min(1) });
261584
+ sessionIdParamSchema$9 = object({ session_id: string$2().min(1) });
259732
261585
  tailParamsSchema = object({
259733
261586
  session_id: string$2().min(1),
259734
261587
  tail: string$2().min(1)
259735
261588
  });
259736
- detailsSchema$6 = array$2(object({
261589
+ detailsSchema$7 = array$2(object({
259737
261590
  path: string$2(),
259738
261591
  message: string$2()
259739
261592
  }));
@@ -260411,7 +262264,7 @@ function sessionCreatedPayload(payload) {
260411
262264
  var GLOBAL_SESSION_ID, TRANSCRIPT_RESET_TAIL_TURNS, SessionEventBroadcaster, volatileSignalTypeSet, TRANSCRIPT_PROJECTED_EVENT_TYPES;
260412
262265
  var init_sessionEventBroadcaster = __esmMin((() => {
260413
262266
  init_src$6();
260414
- init_events();
262267
+ init_events$1();
260415
262268
  init_src$2();
260416
262269
  init_approvals();
260417
262270
  init_questions();
@@ -264961,11 +266814,11 @@ function registerMessagesRoutes(app, core) {
264961
266814
  const listRoute = defineRoute({
264962
266815
  method: "GET",
264963
266816
  path: "/sessions/{session_id}/messages",
264964
- params: sessionIdParamSchema$7,
266817
+ params: sessionIdParamSchema$8,
264965
266818
  querystring: messagesListQueryCoercion,
264966
266819
  success: { data: listMessagesResponseSchema },
264967
266820
  errors: {
264968
- [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$5 },
266821
+ [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$6 },
264969
266822
  [ErrorCode.SESSION_NOT_FOUND]: {}
264970
266823
  },
264971
266824
  description: "List messages for a session",
@@ -264986,7 +266839,7 @@ function registerMessagesRoutes(app, core) {
264986
266839
  params: messageIdParamSchema,
264987
266840
  success: { data: getMessageResponseSchema },
264988
266841
  errors: {
264989
- [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$5 },
266842
+ [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$6 },
264990
266843
  [ErrorCode.SESSION_NOT_FOUND]: {},
264991
266844
  [ErrorCode.MESSAGE_NOT_FOUND]: {}
264992
266845
  },
@@ -265023,7 +266876,7 @@ function sendMappedError$7(reply, req, err) {
265023
266876
  log?.error({ err }, "message request failed");
265024
266877
  reply.send(errEnvelope(ErrorCode.INTERNAL_ERROR, err instanceof Error ? err.message : String(err), requestId, err instanceof Error ? err.stack : void 0));
265025
266878
  }
265026
- var messagesListQueryCoercion, sessionIdParamSchema$7, messageIdParamSchema, detailsSchema$5;
266879
+ var messagesListQueryCoercion, sessionIdParamSchema$8, messageIdParamSchema, detailsSchema$6;
265027
266880
  var init_messages = __esmMin((() => {
265028
266881
  init_src$6();
265029
266882
  init_protocolMessage();
@@ -265047,12 +266900,12 @@ var init_messages = __esmMin((() => {
265047
266900
  params: { code: ErrorCode.VALIDATION_FAILED }
265048
266901
  });
265049
266902
  });
265050
- sessionIdParamSchema$7 = object({ session_id: string$2().min(1) });
266903
+ sessionIdParamSchema$8 = object({ session_id: string$2().min(1) });
265051
266904
  messageIdParamSchema = object({
265052
266905
  session_id: string$2().min(1),
265053
266906
  message_id: string$2().min(1)
265054
266907
  });
265055
- detailsSchema$5 = array$2(object({
266908
+ detailsSchema$6 = array$2(object({
265056
266909
  path: string$2(),
265057
266910
  message: string$2()
265058
266911
  }));
@@ -265405,6 +267258,107 @@ var init_registerDebugRoutes = __esmMin((() => {
265405
267258
  init_serviceDispatcherRoutes();
265406
267259
  }));
265407
267260
  //#endregion
267261
+ //#region ../../packages/kap-server/src/routes/events.ts
267262
+ function registerEventsRoute(app, opts) {
267263
+ app.get("/sessions/:session_id/events", { schema: {
267264
+ params: {
267265
+ type: "object",
267266
+ properties: { session_id: { type: "string" } },
267267
+ required: ["session_id"]
267268
+ },
267269
+ querystring: {
267270
+ type: "object",
267271
+ properties: { event_seq: { type: "string" } },
267272
+ required: []
267273
+ }
267274
+ } }, async (req, reply) => {
267275
+ const { session_id } = req.params;
267276
+ if (!session_id) {
267277
+ await reply.code(400).send({
267278
+ code: 4e4,
267279
+ msg: "session_id required",
267280
+ data: null,
267281
+ request_id: req.id
267282
+ });
267283
+ return;
267284
+ }
267285
+ const eventSeq = parseEventSeq(req.query.event_seq);
267286
+ if (eventSeq !== void 0 && (!Number.isInteger(eventSeq) || eventSeq < 0)) {
267287
+ await reply.code(400).send({
267288
+ code: 4e4,
267289
+ msg: "event_seq must be a non-negative integer",
267290
+ data: null,
267291
+ request_id: req.id
267292
+ });
267293
+ return;
267294
+ }
267295
+ reply.hijack();
267296
+ const raw = reply.raw;
267297
+ raw.writeHead(200, SSE_HEADERS);
267298
+ raw.write(": connected\n\n");
267299
+ let closed = false;
267300
+ const target = { send(envelope) {
267301
+ if (closed) return;
267302
+ raw.write(`event: ${envelope.type}\ndata: ${JSON.stringify(envelope)}\n\n`);
267303
+ } };
267304
+ if (!await opts.broadcaster.subscribe(session_id, target)) {
267305
+ if (await opts.core.accessor.get(ISessionLifecycleService).resume(session_id) === void 0) {
267306
+ closed = true;
267307
+ raw.write("event: error\ndata: {\"code\":40401,\"msg\":\"session not found\"}\n\n");
267308
+ raw.end();
267309
+ return;
267310
+ }
267311
+ if (!await opts.broadcaster.subscribe(session_id, target)) {
267312
+ closed = true;
267313
+ raw.write("event: error\ndata: {\"code\":40401,\"msg\":\"session not found\"}\n\n");
267314
+ raw.end();
267315
+ return;
267316
+ }
267317
+ }
267318
+ if (eventSeq !== void 0) {
267319
+ const result = await opts.broadcaster.getBufferedSince(session_id, { seq: eventSeq });
267320
+ if (result.resyncRequired !== false) raw.write(`event: resync_required\ndata: ${JSON.stringify({
267321
+ type: "resync_required",
267322
+ session_id,
267323
+ reason: result.resyncRequired,
267324
+ current_seq: result.currentSeq,
267325
+ epoch: result.epoch
267326
+ })}\n\n`);
267327
+ else for (const { envelope } of result.events) target.send(envelope);
267328
+ }
267329
+ const heartbeat = setInterval(() => {
267330
+ if (!closed) raw.write(": ping\n\n");
267331
+ }, HEARTBEAT_INTERVAL_MS);
267332
+ req.raw.on("close", () => {
267333
+ closed = true;
267334
+ clearInterval(heartbeat);
267335
+ opts.broadcaster.unsubscribe(session_id, target);
267336
+ });
267337
+ });
267338
+ }
267339
+ /**
267340
+ * Parse the optional `?event_seq=` replay cursor. `undefined` = absent
267341
+ * (live-only stream); `NaN` = present but malformed — the caller rejects it
267342
+ * with a 400. A valid cursor is a non-negative integer (the journal seq
267343
+ * domain, matching `sessionCursorSchema.seq`).
267344
+ */
267345
+ function parseEventSeq(raw) {
267346
+ if (raw === void 0) return void 0;
267347
+ if (typeof raw !== "string" || !/^\d+$/.test(raw)) return NaN;
267348
+ return Number(raw);
267349
+ }
267350
+ var SSE_HEADERS, HEARTBEAT_INTERVAL_MS;
267351
+ var init_events = __esmMin((() => {
267352
+ init_src$6();
267353
+ SSE_HEADERS = {
267354
+ "Content-Type": "text/event-stream",
267355
+ "Cache-Control": "no-cache",
267356
+ Connection: "keep-alive",
267357
+ "X-Accel-Buffering": "no"
267358
+ };
267359
+ HEARTBEAT_INTERVAL_MS = 15e3;
267360
+ }));
267361
+ //#endregion
265408
267362
  //#region ../../packages/kap-server/src/protocol/rest-meta.ts
265409
267363
  var metaCapabilitiesSchema, metaResponseSchema;
265410
267364
  var init_rest_meta = __esmMin((() => {
@@ -266010,7 +267964,7 @@ function registerPromptsRoutes(app, core) {
266010
267964
  const listRoute = defineRoute({
266011
267965
  method: "GET",
266012
267966
  path: "/sessions/{session_id}/prompts",
266013
- params: sessionIdParamSchema$6,
267967
+ params: sessionIdParamSchema$7,
266014
267968
  success: { data: promptListResponseSchema },
266015
267969
  errors: { [ErrorCode.SESSION_NOT_FOUND]: {} },
266016
267970
  description: "List the active prompt and queued prompts for a session",
@@ -266030,7 +267984,7 @@ function registerPromptsRoutes(app, core) {
266030
267984
  method: "POST",
266031
267985
  path: "/sessions/{session_id}/prompts",
266032
267986
  body: promptSubmissionSchema,
266033
- params: sessionIdParamSchema$6,
267987
+ params: sessionIdParamSchema$7,
266034
267988
  success: { data: promptSubmitResultSchema },
266035
267989
  errors: {
266036
267990
  [ErrorCode.VALIDATION_FAILED]: { detailsSchema: validationDetailsSchema },
@@ -266096,7 +268050,7 @@ function registerPromptsRoutes(app, core) {
266096
268050
  method: "POST",
266097
268051
  path: "/sessions/{session_id}/prompts::steer",
266098
268052
  body: promptSteerRequestSchema,
266099
- params: sessionIdParamSchema$6,
268053
+ params: sessionIdParamSchema$7,
266100
268054
  success: { data: promptSteerResultSchema },
266101
268055
  errors: {
266102
268056
  [ErrorCode.VALIDATION_FAILED]: {},
@@ -266588,7 +268542,7 @@ function authModelDetails(err) {
266588
268542
  if (typeof providerId === "string") details.provider_id = providerId;
266589
268543
  return Object.keys(details).length === 0 ? null : details;
266590
268544
  }
266591
- var sessionIdParamSchema$6, validationDetailsSchema, authProviderDetailsSchema, authModelDetailsSchema, VIDEO_EXT_BY_MIME, ATTACHMENT_NAME_MAX;
268545
+ var sessionIdParamSchema$7, validationDetailsSchema, authProviderDetailsSchema, authModelDetailsSchema, VIDEO_EXT_BY_MIME, ATTACHMENT_NAME_MAX;
266592
268546
  var init_prompts$1 = __esmMin((() => {
266593
268547
  init_src$6();
266594
268548
  init_error_codes();
@@ -266599,7 +268553,7 @@ var init_prompts$1 = __esmMin((() => {
266599
268553
  init_defineRoute();
266600
268554
  init_mainAgent();
266601
268555
  init_action_suffix();
266602
- sessionIdParamSchema$6 = object({ session_id: string$2().min(1) });
268556
+ sessionIdParamSchema$7 = object({ session_id: string$2().min(1) });
266603
268557
  validationDetailsSchema = array$2(object({
266604
268558
  path: string$2(),
266605
268559
  message: string$2()
@@ -266881,7 +268835,7 @@ function registerSessionsRoutes(app, core) {
266881
268835
  body: createSessionRequestSchema,
266882
268836
  success: { data: sessionSchema },
266883
268837
  errors: {
266884
- [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$4 },
268838
+ [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$5 },
266885
268839
  [ErrorCode.WORKSPACE_NOT_FOUND]: {},
266886
268840
  [ErrorCode.FS_PATH_NOT_FOUND]: {}
266887
268841
  },
@@ -266919,6 +268873,7 @@ function registerSessionsRoutes(app, core) {
266919
268873
  const touched = await registry.createOrTouch(workDir);
266920
268874
  const handle = await core.accessor.get(ISessionLifecycleService).create({ workDir });
266921
268875
  if (typeof body.title === "string") await handle.accessor.get(ISessionMetadata).setTitle(body.title);
268876
+ if (body.metadata !== void 0 && Object.keys(body.metadata).length > 0) await handle.accessor.get(ISessionMetadata).update({ custom: { ...body.metadata } });
266922
268877
  const session = toWireSession({
266923
268878
  ...await handle.accessor.get(ISessionMetadata).read(),
266924
268879
  workspaceId: touched.id
@@ -266947,7 +268902,7 @@ function registerSessionsRoutes(app, core) {
266947
268902
  querystring: sessionsListQueryCoercion,
266948
268903
  success: { data: pageResponseSchema(sessionSchema) },
266949
268904
  errors: {
266950
- [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$4 },
268905
+ [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$5 },
266951
268906
  [ErrorCode.WORKSPACE_NOT_FOUND]: {}
266952
268907
  },
266953
268908
  description: "List sessions",
@@ -267015,10 +268970,10 @@ function registerSessionsRoutes(app, core) {
267015
268970
  const getRoute = defineRoute({
267016
268971
  method: "GET",
267017
268972
  path: "/sessions/{session_id}",
267018
- params: sessionIdParamSchema$5,
268973
+ params: sessionIdParamSchema$6,
267019
268974
  success: { data: sessionSchema },
267020
268975
  errors: {
267021
- [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$4 },
268976
+ [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$5 },
267022
268977
  [ErrorCode.SESSION_NOT_FOUND]: {}
267023
268978
  },
267024
268979
  description: "Get a session by ID",
@@ -267041,10 +268996,10 @@ function registerSessionsRoutes(app, core) {
267041
268996
  const getProfileRoute = defineRoute({
267042
268997
  method: "GET",
267043
268998
  path: "/sessions/{session_id}/profile",
267044
- params: sessionIdParamSchema$5,
268999
+ params: sessionIdParamSchema$6,
267045
269000
  success: { data: sessionSchema },
267046
269001
  errors: {
267047
- [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$4 },
269002
+ [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$5 },
267048
269003
  [ErrorCode.SESSION_NOT_FOUND]: {}
267049
269004
  },
267050
269005
  description: "Get session profile",
@@ -267067,11 +269022,11 @@ function registerSessionsRoutes(app, core) {
267067
269022
  const updateProfileRoute = defineRoute({
267068
269023
  method: "POST",
267069
269024
  path: "/sessions/{session_id}/profile",
267070
- params: sessionIdParamSchema$5,
269025
+ params: sessionIdParamSchema$6,
267071
269026
  body: updateSessionProfileRequestSchema,
267072
269027
  success: { data: sessionSchema },
267073
269028
  errors: {
267074
- [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$4 },
269029
+ [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$5 },
267075
269030
  [ErrorCode.SESSION_NOT_FOUND]: {}
267076
269031
  },
267077
269032
  description: "Update session profile (title, metadata, agent_config)",
@@ -267113,7 +269068,7 @@ function registerSessionsRoutes(app, core) {
267113
269068
  archiveSessionResponseSchema
267114
269069
  ]) },
267115
269070
  errors: {
267116
- [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$4 },
269071
+ [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$5 },
267117
269072
  [ErrorCode.SESSION_NOT_FOUND]: {},
267118
269073
  [ErrorCode.SESSION_BUSY]: {},
267119
269074
  [ErrorCode.COMPACTION_UNABLE]: {},
@@ -267252,11 +269207,11 @@ function registerSessionsRoutes(app, core) {
267252
269207
  const listChildrenRoute = defineRoute({
267253
269208
  method: "GET",
267254
269209
  path: "/sessions/{session_id}/children",
267255
- params: sessionIdParamSchema$5,
269210
+ params: sessionIdParamSchema$6,
267256
269211
  querystring: sessionChildrenListQueryCoercion,
267257
269212
  success: { data: listSessionChildrenResponseSchema },
267258
269213
  errors: {
267259
- [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$4 },
269214
+ [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$5 },
267260
269215
  [ErrorCode.SESSION_NOT_FOUND]: {}
267261
269216
  },
267262
269217
  description: "List child sessions",
@@ -267290,11 +269245,11 @@ function registerSessionsRoutes(app, core) {
267290
269245
  const createChildRoute = defineRoute({
267291
269246
  method: "POST",
267292
269247
  path: "/sessions/{session_id}/children",
267293
- params: sessionIdParamSchema$5,
269248
+ params: sessionIdParamSchema$6,
267294
269249
  body: createSessionChildRequestSchema,
267295
269250
  success: { data: sessionSchema },
267296
269251
  errors: {
267297
- [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$4 },
269252
+ [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$5 },
267298
269253
  [ErrorCode.SESSION_NOT_FOUND]: {},
267299
269254
  [ErrorCode.SESSION_BUSY]: {}
267300
269255
  },
@@ -267331,10 +269286,10 @@ function registerSessionsRoutes(app, core) {
267331
269286
  const statusRoute = defineRoute({
267332
269287
  method: "GET",
267333
269288
  path: "/sessions/{session_id}/status",
267334
- params: sessionIdParamSchema$5,
269289
+ params: sessionIdParamSchema$6,
267335
269290
  success: { data: sessionStatusResponseSchema },
267336
269291
  errors: {
267337
- [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$4 },
269292
+ [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$5 },
267338
269293
  [ErrorCode.SESSION_NOT_FOUND]: {}
267339
269294
  },
267340
269295
  description: "Get realtime session status (best-effort in this slice)",
@@ -267352,10 +269307,10 @@ function registerSessionsRoutes(app, core) {
267352
269307
  const sessionWarningsRoute = defineRoute({
267353
269308
  method: "GET",
267354
269309
  path: "/sessions/{session_id}/warnings",
267355
- params: sessionIdParamSchema$5,
269310
+ params: sessionIdParamSchema$6,
267356
269311
  success: { data: sessionWarningsResponseSchema },
267357
269312
  errors: {
267358
- [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$4 },
269313
+ [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$5 },
267359
269314
  [ErrorCode.SESSION_NOT_FOUND]: {}
267360
269315
  },
267361
269316
  description: "Get session-level warnings (e.g. oversized AGENTS.md)",
@@ -267511,7 +269466,7 @@ function sendMappedError$4(reply, req, err) {
267511
269466
  log?.error({ err }, "session request failed");
267512
269467
  reply.send(errEnvelope(ErrorCode.INTERNAL_ERROR, err instanceof Error ? err.message : String(err), requestId, err instanceof Error ? err.stack : void 0));
267513
269468
  }
267514
- var booleanQueryParam, DEFAULT_SESSION_LIST_PAGE_SIZE, sessionsListQueryCoercion, sessionIdParamSchema$5, sessionChildrenListQueryCoercion, sessionActionTailParamSchema, sessionActionRequestSchema, detailsSchema$4, DEFAULT_UNDO_MESSAGE_PAGE_SIZE, MAX_UNDO_MESSAGE_PAGE_SIZE;
269469
+ var booleanQueryParam, DEFAULT_SESSION_LIST_PAGE_SIZE, sessionsListQueryCoercion, sessionIdParamSchema$6, sessionChildrenListQueryCoercion, sessionActionTailParamSchema, sessionActionRequestSchema, detailsSchema$5, DEFAULT_UNDO_MESSAGE_PAGE_SIZE, MAX_UNDO_MESSAGE_PAGE_SIZE;
267515
269470
  var init_sessions = __esmMin((() => {
267516
269471
  init_src$6();
267517
269472
  init_error_codes();
@@ -267555,7 +269510,7 @@ var init_sessions = __esmMin((() => {
267555
269510
  params: { code: ErrorCode.VALIDATION_FAILED }
267556
269511
  });
267557
269512
  });
267558
- sessionIdParamSchema$5 = object({ session_id: string$2().min(1) });
269513
+ sessionIdParamSchema$6 = object({ session_id: string$2().min(1) });
267559
269514
  sessionChildrenListQueryCoercion = object({
267560
269515
  before_id: string$2().min(1).optional(),
267561
269516
  after_id: string$2().min(1).optional(),
@@ -267577,7 +269532,7 @@ var init_sessions = __esmMin((() => {
267577
269532
  count: number$2().int().positive().optional(),
267578
269533
  page_size: number$2().int().min(1).max(100).optional()
267579
269534
  }));
267580
- detailsSchema$4 = array$2(object({
269535
+ detailsSchema$5 = array$2(object({
267581
269536
  path: string$2(),
267582
269537
  message: string$2()
267583
269538
  }));
@@ -267585,6 +269540,60 @@ var init_sessions = __esmMin((() => {
267585
269540
  MAX_UNDO_MESSAGE_PAGE_SIZE = 100;
267586
269541
  }));
267587
269542
  //#endregion
269543
+ //#region ../../packages/kap-server/src/routes/shell.ts
269544
+ function registerShellRoute(app, core) {
269545
+ const runShellCommandRoute = defineRoute({
269546
+ method: "POST",
269547
+ path: "/sessions/{session_id}/shell",
269548
+ params: sessionIdParamSchema$5,
269549
+ body: shellCommandRequestSchema,
269550
+ success: { data: shellCommandResultSchema },
269551
+ errors: {
269552
+ [ErrorCode.VALIDATION_FAILED]: { detailsSchema: detailsSchema$4 },
269553
+ [ErrorCode.SESSION_NOT_FOUND]: {}
269554
+ },
269555
+ description: "Run a user-initiated `!` shell command in a session",
269556
+ tags: ["sessions"]
269557
+ }, async (req, reply) => {
269558
+ const { session_id } = req.params;
269559
+ const session = await core.accessor.get(ISessionLifecycleService).resume(session_id);
269560
+ if (session === void 0) {
269561
+ reply.send(errEnvelope(ErrorCode.SESSION_NOT_FOUND, `session ${session_id} does not exist`, req.id));
269562
+ return;
269563
+ }
269564
+ const result = await (await ensureMainAgent(session)).accessor.get(IAgentShellCommandService).run({
269565
+ command: req.body.command,
269566
+ commandId: req.body.commandId
269567
+ });
269568
+ reply.send(okEnvelope(result, req.id));
269569
+ });
269570
+ app.post(runShellCommandRoute.path, runShellCommandRoute.options, runShellCommandRoute.handler);
269571
+ }
269572
+ var sessionIdParamSchema$5, shellCommandRequestSchema, shellCommandResultSchema, detailsSchema$4;
269573
+ var init_shell = __esmMin((() => {
269574
+ init_src$6();
269575
+ init_zod$1();
269576
+ init_envelope();
269577
+ init_defineRoute();
269578
+ init_error_codes();
269579
+ init_mainAgent();
269580
+ sessionIdParamSchema$5 = object({ session_id: string$2().min(1) });
269581
+ shellCommandRequestSchema = object({
269582
+ command: string$2(),
269583
+ commandId: string$2().optional()
269584
+ });
269585
+ shellCommandResultSchema = object({
269586
+ stdout: string$2(),
269587
+ stderr: string$2(),
269588
+ isError: boolean$2().optional(),
269589
+ backgrounded: boolean$2().optional()
269590
+ });
269591
+ detailsSchema$4 = array$2(object({
269592
+ path: string$2(),
269593
+ message: string$2()
269594
+ }));
269595
+ }));
269596
+ //#endregion
267588
269597
  //#region ../../packages/kap-server/src/routes/shutdown.ts
267589
269598
  function registerShutdownRoutes(app, opts) {
267590
269599
  const route = defineRoute({
@@ -269820,6 +271829,11 @@ async function registerApiV1Routes(app, core, opts) {
269820
271829
  registerConfigRoutes(apiV1, core);
269821
271830
  registerModelCatalogRoutes(apiV1, core);
269822
271831
  registerSessionsRoutes(apiV1, core);
271832
+ registerShellRoute(apiV1, core);
271833
+ registerEventsRoute(apiV1, {
271834
+ broadcaster: opts.broadcaster,
271835
+ core
271836
+ });
269823
271837
  registerSessionExportRoute(apiV1, core, { serverVersion: opts.serverVersion });
269824
271838
  registerSkillsRoutes(apiV1, core);
269825
271839
  registerMessagesRoutes(apiV1, core);
@@ -269878,6 +271892,7 @@ var init_registerApiV1Routes = __esmMin((() => {
269878
271892
  init_guiStore();
269879
271893
  init_messages();
269880
271894
  init_registerDebugRoutes();
271895
+ init_events();
269881
271896
  init_meta();
269882
271897
  init_modelCatalog();
269883
271898
  init_oauth();
@@ -269885,6 +271900,7 @@ var init_registerApiV1Routes = __esmMin((() => {
269885
271900
  init_questions();
269886
271901
  init_sessionExport();
269887
271902
  init_sessions();
271903
+ init_shell();
269888
271904
  init_shutdown();
269889
271905
  init_snapshot();
269890
271906
  init_skills$1();
@@ -273846,7 +275862,9 @@ var DEFAULT_MAX_BUFFER_SIZE, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_MAX_BATCH_SIZE,
273846
275862
  var init_wsConnectionV1 = __esmMin((() => {
273847
275863
  init_ws_control();
273848
275864
  init_src$2();
275865
+ init_src$6();
273849
275866
  init_node$3();
275867
+ init_error_codes();
273850
275868
  init_protocol();
273851
275869
  init_sessionEventBroadcaster();
273852
275870
  init_fsWatchBridge();
@@ -273864,6 +275882,7 @@ var init_wsConnectionV1 = __esmMin((() => {
273864
275882
  socket;
273865
275883
  broadcaster;
273866
275884
  fsWatchBridge;
275885
+ core;
273867
275886
  validateCredential;
273868
275887
  maxBufferSize;
273869
275888
  flushIntervalMs;
@@ -273875,6 +275894,12 @@ var init_wsConnectionV1 = __esmMin((() => {
273875
275894
  /** Per-session subscription state: legacy agent allowlist + opt-in transcript grades. */
273876
275895
  subscriptions = /* @__PURE__ */ new Map();
273877
275896
  /**
275897
+ * Terminal services this connection has attached to (one per session,
275898
+ * resolved lazily). Tracked so teardown can detach every terminal sink
275899
+ * the connection owns.
275900
+ */
275901
+ terminalServices = /* @__PURE__ */ new Set();
275902
+ /**
273878
275903
  * Serializes control-frame handling in receive order. Frames arrive
273879
275904
  * back-to-back (e.g. `client_hello` immediately followed by
273880
275905
  * `subscribe_v2`), and a later handler reads subscription state the
@@ -273896,6 +275921,7 @@ var init_wsConnectionV1 = __esmMin((() => {
273896
275921
  this.socket = opts.socket;
273897
275922
  this.broadcaster = opts.broadcaster;
273898
275923
  this.fsWatchBridge = opts.fsWatchBridge;
275924
+ this.core = opts.core;
273899
275925
  this.validateCredential = opts.validateCredential;
273900
275926
  this.logger = opts.logger;
273901
275927
  this.maxBufferSize = opts.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE;
@@ -273959,6 +275985,21 @@ var init_wsConnectionV1 = __esmMin((() => {
273959
275985
  case "watch_fs_remove":
273960
275986
  this.enqueueControl(() => this.onWatchFs(frame, false));
273961
275987
  return;
275988
+ case "terminal_attach":
275989
+ this.enqueueControl(() => this.onTerminalAttach(frame));
275990
+ return;
275991
+ case "terminal_input":
275992
+ this.enqueueControl(() => this.onTerminalInput(frame));
275993
+ return;
275994
+ case "terminal_resize":
275995
+ this.enqueueControl(() => this.onTerminalResize(frame));
275996
+ return;
275997
+ case "terminal_close":
275998
+ this.enqueueControl(() => this.onTerminalClose(frame));
275999
+ return;
276000
+ case "terminal_detach":
276001
+ this.enqueueControl(() => this.onTerminalDetach(frame));
276002
+ return;
273962
276003
  default: return;
273963
276004
  }
273964
276005
  }
@@ -274104,6 +276145,120 @@ var init_wsConnectionV1 = __esmMin((() => {
274104
276145
  }));
274105
276146
  }
274106
276147
  /**
276148
+ * `terminal_attach` — attach this connection's sink to a session terminal
276149
+ * stream. The sink's frames (`terminal_output` / `terminal_exit`) are
276150
+ * delivered over the same subscription buffer as session events (coalesced
276151
+ * only when mergeable — terminal frames never merge, they just share the
276152
+ * flush window). `since_seq` replays buffered frames past the cursor, like
276153
+ * the REST/WS attach contract.
276154
+ */
276155
+ async onTerminalAttach(frame) {
276156
+ const parsed = terminalAttachMessageSchema.safeParse(frame);
276157
+ if (!parsed.success) {
276158
+ this.sendImmediateFrame(buildAck(frame.id ?? "", 1, "invalid terminal_attach payload", {}));
276159
+ return;
276160
+ }
276161
+ const { session_id, terminal_id, since_seq } = parsed.data.payload;
276162
+ try {
276163
+ const terminals = await this.resolveTerminalService(session_id);
276164
+ const sink = {
276165
+ id: this.id,
276166
+ send: (terminalFrame) => this.sendSubscribedFrame(terminalFrame)
276167
+ };
276168
+ const { replayed } = await terminals.attach(terminal_id, sink, { sinceSeq: since_seq });
276169
+ this.sendImmediateFrame(buildAck(frame.id ?? "", 0, "success", {
276170
+ attached: true,
276171
+ replayed
276172
+ }));
276173
+ } catch (error) {
276174
+ this.sendTerminalErrorAck(frame.id, error);
276175
+ }
276176
+ }
276177
+ /** `terminal_input` — write bytes to the attached terminal's pty. */
276178
+ async onTerminalInput(frame) {
276179
+ const parsed = terminalInputMessageSchema.safeParse(frame);
276180
+ if (!parsed.success) {
276181
+ this.sendImmediateFrame(buildAck(frame.id ?? "", 1, "invalid terminal_input payload", {}));
276182
+ return;
276183
+ }
276184
+ const { session_id, terminal_id, data } = parsed.data.payload;
276185
+ try {
276186
+ await (await this.resolveTerminalService(session_id)).write(terminal_id, data);
276187
+ this.sendImmediateFrame(buildAck(frame.id ?? "", 0, "success", { accepted: true }));
276188
+ } catch (error) {
276189
+ this.sendTerminalErrorAck(frame.id, error);
276190
+ }
276191
+ }
276192
+ /** `terminal_resize` — resize the attached terminal's pty. */
276193
+ async onTerminalResize(frame) {
276194
+ const parsed = terminalResizeMessageSchema.safeParse(frame);
276195
+ if (!parsed.success) {
276196
+ this.sendImmediateFrame(buildAck(frame.id ?? "", 1, "invalid terminal_resize payload", {}));
276197
+ return;
276198
+ }
276199
+ const { session_id, terminal_id, cols, rows } = parsed.data.payload;
276200
+ try {
276201
+ await (await this.resolveTerminalService(session_id)).resize(terminal_id, cols, rows);
276202
+ this.sendImmediateFrame(buildAck(frame.id ?? "", 0, "success", { resized: true }));
276203
+ } catch (error) {
276204
+ this.sendTerminalErrorAck(frame.id, error);
276205
+ }
276206
+ }
276207
+ /** `terminal_close` — close the terminal's pty (idempotent). */
276208
+ async onTerminalClose(frame) {
276209
+ const parsed = terminalCloseMessageSchema.safeParse(frame);
276210
+ if (!parsed.success) {
276211
+ this.sendImmediateFrame(buildAck(frame.id ?? "", 1, "invalid terminal_close payload", {}));
276212
+ return;
276213
+ }
276214
+ const { session_id, terminal_id } = parsed.data.payload;
276215
+ try {
276216
+ await (await this.resolveTerminalService(session_id)).close(terminal_id);
276217
+ this.sendImmediateFrame(buildAck(frame.id ?? "", 0, "success", { closed: true }));
276218
+ } catch (error) {
276219
+ this.sendTerminalErrorAck(frame.id, error);
276220
+ }
276221
+ }
276222
+ /** `terminal_detach` — detach this connection's sink from a terminal stream. */
276223
+ async onTerminalDetach(frame) {
276224
+ const parsed = terminalDetachMessageSchema.safeParse(frame);
276225
+ if (!parsed.success) {
276226
+ this.sendImmediateFrame(buildAck(frame.id ?? "", 1, "invalid terminal_detach payload", {}));
276227
+ return;
276228
+ }
276229
+ const { session_id, terminal_id } = parsed.data.payload;
276230
+ try {
276231
+ (await this.resolveTerminalService(session_id)).detach(terminal_id, this.id);
276232
+ this.sendImmediateFrame(buildAck(frame.id ?? "", 0, "success", { detached: true }));
276233
+ } catch (error) {
276234
+ this.sendTerminalErrorAck(frame.id, error);
276235
+ }
276236
+ }
276237
+ /**
276238
+ * Resolve a session's `ISessionTerminalService` (cold-loading a
276239
+ * persisted-but-not-live session, matching the REST terminal route).
276240
+ * The resolved service is remembered so `onClose` can detach every sink
276241
+ * this connection owns.
276242
+ */
276243
+ async resolveTerminalService(sessionId) {
276244
+ const session = await this.core.accessor.get(ISessionLifecycleService).resume(sessionId);
276245
+ if (session === void 0) throw new Error2(ErrorCodes.SESSION_NOT_FOUND, `session ${sessionId} does not exist`);
276246
+ const terminals = session.accessor.get(ISessionTerminalService);
276247
+ this.terminalServices.add(terminals);
276248
+ return terminals;
276249
+ }
276250
+ /** Ack a terminal control failure with the wire error code when known. */
276251
+ sendTerminalErrorAck(id, error) {
276252
+ let code = 1;
276253
+ let msg = "internal error";
276254
+ if (isError2(error)) {
276255
+ if (error.code === ErrorCodes.SESSION_NOT_FOUND) code = ErrorCode.SESSION_NOT_FOUND;
276256
+ else if (error.code === ErrorCodes.TERMINAL_NOT_FOUND) code = ErrorCode.TERMINAL_NOT_FOUND;
276257
+ msg = error.message;
276258
+ } else if (error instanceof Error) msg = error.message;
276259
+ this.sendImmediateFrame(buildAck(id ?? "", code, msg, {}));
276260
+ }
276261
+ /**
274107
276262
  * Shared attach path behind `client_hello` (legacy inline subscriptions)
274108
276263
  * and `subscribe`. Subscribes the connection via the broadcaster, then
274109
276264
  * either replays durable events since the client's cursor (with the
@@ -274248,6 +276403,7 @@ var init_wsConnectionV1 = __esmMin((() => {
274248
276403
  this.outbound = [];
274249
276404
  this.broadcaster.removeGlobalTarget(this);
274250
276405
  for (const sid of this.subscriptions.keys()) this.broadcaster.unsubscribe(sid, this);
276406
+ for (const terminals of this.terminalServices) terminals.detachAllForSink(this.id);
274251
276407
  this.fsWatchBridge?.detachConnection(this);
274252
276408
  }
274253
276409
  };
@@ -274266,6 +276422,7 @@ function registerWsV1(core, opts) {
274266
276422
  broadcaster,
274267
276423
  fsWatchBridge: opts.fsWatchBridge,
274268
276424
  connectionRegistry: registry,
276425
+ core,
274269
276426
  validateCredential: opts.validateCredential,
274270
276427
  remoteAddress: req.socket.remoteAddress ?? null,
274271
276428
  userAgent: req.headers["user-agent"] ?? null,
@@ -296567,6 +298724,15 @@ function stringifyToolOutput(output) {
296567
298724
  if (typeof output === "string") return output;
296568
298725
  return JSON.stringify(output) ?? String(output);
296569
298726
  }
298727
+ function stringifyToolArgs(args) {
298728
+ if (typeof args === "string") return args;
298729
+ return JSON.stringify(args) ?? String(args);
298730
+ }
298731
+ function truncateChars(text, max) {
298732
+ if (text.length <= max) return text;
298733
+ const suffix = `… (${text.length - max} more chars)`;
298734
+ return `${text.slice(0, max - suffix.length)}${suffix}`;
298735
+ }
296570
298736
  function writeVersion(version, outputFormat, stdout, stderr) {
296571
298737
  if (outputFormat === "stream-json") {
296572
298738
  const message = {
@@ -296595,16 +298761,23 @@ function writeResumeHint(sessionId, outputFormat, stdout, stderr) {
296595
298761
  }
296596
298762
  stderr.write(`${content}\n`);
296597
298763
  }
296598
- var PROMPT_BLOCK_BULLET, PROMPT_BLOCK_INDENT, PromptTranscriptWriter, PromptJsonWriter, PromptBlockWriter;
298764
+ var PROMPT_BLOCK_BULLET, PROMPT_BLOCK_INDENT, TOOL_CALL_MARK, TOOL_RESULT_MARK, RETRY_MARK, MAX_TOOL_CALL_ARGS_CHARS, MAX_TOOL_RESULT_CHARS, PromptTranscriptWriter, PromptJsonWriter, PromptBlockWriter;
296599
298765
  var init_prompt_render = __esmMin((() => {
296600
298766
  PROMPT_BLOCK_BULLET = "• ";
296601
298767
  PROMPT_BLOCK_INDENT = " ";
298768
+ TOOL_CALL_MARK = "⚒ ";
298769
+ TOOL_RESULT_MARK = "⚒ result: ";
298770
+ RETRY_MARK = "↻ retry";
298771
+ MAX_TOOL_CALL_ARGS_CHARS = 500;
298772
+ MAX_TOOL_RESULT_CHARS = 2e3;
296602
298773
  PromptTranscriptWriter = class {
296603
298774
  assistantWriter;
296604
298775
  thinkingWriter;
298776
+ toolWriter;
296605
298777
  constructor(stdout, stderr) {
296606
298778
  this.assistantWriter = new PromptBlockWriter(stdout);
296607
298779
  this.thinkingWriter = new PromptBlockWriter(stderr);
298780
+ this.toolWriter = new PromptBlockWriter(stderr);
296608
298781
  }
296609
298782
  writeAssistantDelta(delta) {
296610
298783
  this.thinkingWriter.finish();
@@ -296619,10 +298792,25 @@ var init_prompt_render = __esmMin((() => {
296619
298792
  writeThinkingDelta(delta) {
296620
298793
  this.thinkingWriter.write(delta);
296621
298794
  }
296622
- writeToolCall() {}
298795
+ writeToolCall(toolCallId, name, args) {
298796
+ this.thinkingWriter.finish();
298797
+ this.assistantWriter.finish();
298798
+ this.toolWriter.write(`${TOOL_CALL_MARK}${name}(${truncateChars(stringifyToolArgs(args), MAX_TOOL_CALL_ARGS_CHARS)})`);
298799
+ this.toolWriter.finish();
298800
+ }
296623
298801
  writeToolCallDelta() {}
296624
- writeToolResult() {}
296625
- writeRetrying() {}
298802
+ writeToolResult(toolCallId, output) {
298803
+ this.toolWriter.finish();
298804
+ this.toolWriter.write(`${TOOL_RESULT_MARK}${truncateChars(stringifyToolOutput(output), MAX_TOOL_RESULT_CHARS)}`);
298805
+ this.toolWriter.finish();
298806
+ }
298807
+ writeRetrying(event) {
298808
+ this.thinkingWriter.finish();
298809
+ this.assistantWriter.finish();
298810
+ const error = [event.errorName, event.errorMessage].filter(Boolean).join(": ");
298811
+ this.toolWriter.write(`${RETRY_MARK} ${event.failedAttempt}/${event.maxAttempts} (${error}) — ${event.delayMs}ms`);
298812
+ this.toolWriter.finish();
298813
+ }
296626
298814
  flushAssistant() {
296627
298815
  this.assistantWriter.finish();
296628
298816
  }
@@ -296630,6 +298818,7 @@ var init_prompt_render = __esmMin((() => {
296630
298818
  finish() {
296631
298819
  this.thinkingWriter.finish();
296632
298820
  this.assistantWriter.finish();
298821
+ this.toolWriter.finish();
296633
298822
  }
296634
298823
  };
296635
298824
  PromptJsonWriter = class {