@xingular/harness 1.3.1-unstable.4 → 1.3.1-unstable.5

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 (91) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/cli.js +775 -354
  3. package/dist/sdk/commands/extensionOwners.d.ts +4 -0
  4. package/dist/sdk/commands/extensionOwners.js +17 -0
  5. package/dist/sdk/commands/list.js +15 -2
  6. package/dist/sdk/commands/promptLoader.d.ts +5 -0
  7. package/dist/sdk/commands/promptLoader.js +76 -0
  8. package/dist/sdk/commands/promptRoutes.d.ts +13 -0
  9. package/dist/sdk/commands/promptRoutes.js +46 -0
  10. package/dist/sdk/commands/runtimeRouter.d.ts +4 -0
  11. package/dist/sdk/commands/runtimeRouter.js +48 -0
  12. package/dist/sdk/computer/boundary.d.ts +40 -0
  13. package/dist/sdk/computer/boundary.js +79 -0
  14. package/dist/sdk/computer/contracts.d.ts +127 -0
  15. package/dist/sdk/computer/contracts.js +13 -0
  16. package/dist/sdk/computer/control.d.ts +34 -0
  17. package/dist/sdk/computer/control.js +112 -0
  18. package/dist/sdk/computer/terminalCapture.d.ts +26 -0
  19. package/dist/sdk/computer/terminalCapture.js +64 -0
  20. package/dist/sdk/computer/volatileImages.d.ts +9 -0
  21. package/dist/sdk/computer/volatileImages.js +50 -0
  22. package/dist/sdk/context/runDetails.d.ts +16 -0
  23. package/dist/sdk/context/runDetails.js +22 -0
  24. package/dist/sdk/doctor/checks.js +3 -0
  25. package/dist/sdk/doctor/probe.js +8 -6
  26. package/dist/sdk/history/trajectory.js +2 -0
  27. package/dist/sdk/hub/schema.d.ts +2 -2
  28. package/dist/sdk/plugins/manifest.d.ts +2 -0
  29. package/dist/sdk/plugins/manifest.js +1 -0
  30. package/dist/sdk/prompt/compile.d.ts +2 -0
  31. package/dist/sdk/prompt/compile.js +4 -2
  32. package/dist/sdk/refine/trajectory.js +7 -2
  33. package/dist/sdk/remote/webClient.js +7 -3
  34. package/dist/sdk/server/methods.js +2 -0
  35. package/dist/sdk/sessions/contracts.d.ts +124 -2
  36. package/dist/sdk/sessions/contracts.js +114 -3
  37. package/dist/sdk/sessions/registry.d.ts +6 -2
  38. package/dist/sdk/sessions/registry.js +16 -13
  39. package/dist/sdk/sessions/runStore.d.ts +3 -1
  40. package/dist/sdk/sessions/runStore.js +25 -8
  41. package/dist/sdk/sessions/runner.d.ts +5 -5
  42. package/dist/sdk/sessions/runner.js +78 -15
  43. package/dist/sdk/sessions/sessions.js +28 -21
  44. package/dist/sdk/sessions/workSlice.d.ts +2 -0
  45. package/dist/sdk/sessions/workSlice.js +4 -3
  46. package/dist/sdk/terminal/client.d.ts +3 -0
  47. package/dist/sdk/terminal/client.js +13 -0
  48. package/dist/sdk/terminal/controller.js +11 -6
  49. package/dist/sdk/terminal/piExtension.js +73 -13
  50. package/dist/sdk/terminal/terminals.d.ts +1 -1
  51. package/dist/sdk/terminal/terminals.js +20 -5
  52. package/dist/sdk/terminal/trajectory.js +2 -2
  53. package/dist/sdk/terminal/tui/commandPages.d.ts +1 -1
  54. package/dist/sdk/terminal/tui/commandPages.js +4 -2
  55. package/dist/sdk/terminal/tui/commands.js +9 -65
  56. package/dist/sdk/terminal/tui/pages/contextPage.d.ts +30 -1
  57. package/dist/sdk/terminal/tui/pages/contextPage.js +109 -8
  58. package/dist/sdk/terminal/tui/pages/trajectoryPage.d.ts +1 -0
  59. package/dist/sdk/terminal/tui/pages/trajectoryPage.js +12 -3
  60. package/dist/sdk/terminal/tui/runtime.js +15 -2
  61. package/dist/sdk/terminal/tui/sideQuestion.d.ts +3 -0
  62. package/dist/sdk/terminal/tui/sideQuestion.js +7 -1
  63. package/dist/sdk/terminal/tui/status.d.ts +0 -6
  64. package/dist/sdk/terminal/tui/status.js +1 -15
  65. package/dist/sdk/tools/definitions.js +1 -1
  66. package/dist/sdk/tools/dispatch.d.ts +1 -1
  67. package/dist/sdk/tools/dispatch.js +17 -3
  68. package/dist/sdk/world/actions.js +1 -1
  69. package/dist/terminal-opentui.js +984 -361
  70. package/dist/terminal-pi.js +145 -18
  71. package/docs/architecture/02-sessions-and-runs.md +5 -2
  72. package/docs/architecture/04-prompt.md +5 -3
  73. package/docs/architecture/06-plugins-and-capabilities.md +13 -2
  74. package/docs/architecture/07-tools-and-gate.md +14 -0
  75. package/docs/architecture/08-memory-and-history.md +7 -3
  76. package/docs/architecture/10-server-protocol.md +15 -3
  77. package/docs/architecture/11-terminal.md +28 -14
  78. package/docs/architecture/13-world-tools.md +19 -0
  79. package/docs/architecture/14-surfaces-and-parity.md +3 -3
  80. package/docs/architecture/README.md +1 -0
  81. package/docs/architecture/proposals/PROP-0049-context-attribution.md +7 -1
  82. package/docs/architecture/proposals/PROP-0050-run-stop-codes-and-refusals.md +30 -0
  83. package/docs/architecture/proposals/README.md +1 -0
  84. package/docs/decisions/ADR-0059-run-diagnostic-evidence.md +35 -0
  85. package/docs/decisions/ADR-0060-approvals-are-controls.md +40 -0
  86. package/docs/decisions/ADR-0061-strict-plugin-command-namespaces.md +41 -0
  87. package/docs/doctor.md +2 -0
  88. package/docs/parity.md +16 -5
  89. package/docs/releases/1.3.1-unstable.5.md +53 -0
  90. package/docs/terminal-tui.md +16 -10
  91. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -161,7 +161,7 @@ function runSetup(input) {
161
161
  if (!input.trust.trusted(input.repoRoot, script.hash)) return Promise.resolve({ state: "untrusted", hash: script.hash, log: null });
162
162
  mkdirSync20(dirname17(input.logFile), { recursive: true });
163
163
  const log2 = openSync4(input.logFile, "w");
164
- return new Promise((resolve19) => {
164
+ return new Promise((resolve23) => {
165
165
  const child = spawn3("/bin/sh", [script.path], {
166
166
  cwd: input.checkout,
167
167
  env: { ...process.env, HARNESS_WORKTREE_PATH: input.checkout, HARNESS_WORKTREE_SOURCE: input.source, HARNESS_WORKTREE_NAME: input.name },
@@ -174,7 +174,7 @@ function runSetup(input) {
174
174
  settled3 = true;
175
175
  clearTimeout(timer);
176
176
  closeSync4(log2);
177
- resolve19({ state: exitCode2 === 0 ? "ran" : "failed", hash: script.hash, log: input.logFile, exitCode: exitCode2 });
177
+ resolve23({ state: exitCode2 === 0 ? "ran" : "failed", hash: script.hash, log: input.logFile, exitCode: exitCode2 });
178
178
  };
179
179
  child.once("error", () => finish(null));
180
180
  child.once("close", (code) => finish(code));
@@ -209,12 +209,12 @@ var init_setup = __esm({
209
209
  });
210
210
 
211
211
  // src/cli.ts
212
- import { resolve as resolve18 } from "node:path";
212
+ import { resolve as resolve22 } from "node:path";
213
213
 
214
214
  // package.json
215
215
  var package_default = {
216
216
  name: "@xingular/harness",
217
- version: "1.3.1-unstable.4",
217
+ version: "1.3.1-unstable.5",
218
218
  type: "module",
219
219
  description: "Xingular Harness \u2014 a Pi agent harness built on recursive harnessing (Harness\xB2): versioned eight-component variants, refinement from evidence, multi-account sessions, a compiled prompt, project notes, tools, and MCP",
220
220
  repository: {
@@ -457,6 +457,7 @@ var runEventTypeSchema = z.enum([
457
457
  "message.steered",
458
458
  "tool.started",
459
459
  "tool.completed",
460
+ "tool.refused",
460
461
  "retry.started",
461
462
  "retry.ended",
462
463
  "compaction.started",
@@ -517,7 +518,38 @@ var toolPayload = z.object({
517
518
  });
518
519
  var retryStartedPayload = z.object({ attempt: z.number(), maxAttempts: z.number(), delayMs: z.number(), error: z.string().max(1e3).optional() });
519
520
  var retryEndedPayload = z.object({ attempt: z.number(), success: z.boolean(), error: z.string().max(1e3).optional() });
520
- var compactionStartedPayload = z.object({ reason: z.string() });
521
+ var refusalCodeSchema = z.enum([
522
+ /** Plan mode allows only research tools and read-only Bash. */
523
+ "plan_mode",
524
+ /** A file write or command outside the active project workspace, or a workspace that could not be verified. */
525
+ "workspace",
526
+ /** The host's work scope does not cover the call. */
527
+ "scope",
528
+ /** The host's tool policy disabled the tool. */
529
+ "policy",
530
+ /** The tool is outside the run's capability set. */
531
+ "capability",
532
+ /** A person was asked and declined. */
533
+ "approval_denied",
534
+ /** The call needs a person and the run has nobody to ask: refinement, reflection, diagnostics. */
535
+ "unattended",
536
+ /** The reply reached its work-slice budget. */
537
+ "work_slice",
538
+ /** The terminal controller could not be reached to decide. */
539
+ "controller_unavailable"
540
+ ]);
541
+ var toolRefusedPayload = z.object({
542
+ toolCallId: z.string(),
543
+ name: z.string(),
544
+ code: refusalCodeSchema,
545
+ reason: z.string().max(2e3)
546
+ });
547
+ var compactionStartedPayload = z.object({
548
+ reason: z.string(),
549
+ /** The latest request's size and the model's limit when compaction began, when known. */
550
+ contextTokens: z.number().optional(),
551
+ contextWindow: z.number().optional()
552
+ });
521
553
  var compactionEndedPayload = z.object({
522
554
  reason: z.string(),
523
555
  aborted: z.boolean(),
@@ -565,6 +597,34 @@ var questionOptionSchema = z.object({
565
597
  recommended: z.boolean().optional(),
566
598
  sketch: questionSketchSchema.optional()
567
599
  });
600
+ function questionAnswerProblem(question, answer) {
601
+ const name = question.summary?.trim() ? `Question "${question.summary.trim()}"` : "This question";
602
+ const selected = answer.selected;
603
+ if (question.multiSelect !== true && selected.length > 1) {
604
+ return { field: "selection", message: `${name} takes one choice, but ${selected.length} were selected.` };
605
+ }
606
+ if (new Set(selected).size !== selected.length) {
607
+ return { field: "selection", message: `${name} lists the same choice more than once.` };
608
+ }
609
+ const unknown = selected.find((label) => !question.options.some((option) => option.label === label));
610
+ if (unknown !== void 0) {
611
+ return { field: "selection", message: `${name} does not offer "${unknown}".` };
612
+ }
613
+ if (answer.text?.trim() && question.allowText === false) {
614
+ return { field: "text", message: `${name} does not take a typed answer; choose one of its options.` };
615
+ }
616
+ for (const [label, note] of Object.entries(answer.notes ?? {})) {
617
+ const normalized = typeof note === "string" ? note.trim() : "";
618
+ if (!normalized) continue;
619
+ if (!selected.includes(label)) {
620
+ return { field: "notes", message: `${name}: a note is attached to "${label}", which is not selected.` };
621
+ }
622
+ if (normalized.length > 1e4) {
623
+ return { field: "notes", message: `${name}: the note for "${label}" is longer than 10,000 characters.` };
624
+ }
625
+ }
626
+ return null;
627
+ }
568
628
  var questionBatchFields = {
569
629
  batchId: z.string().min(1).optional(),
570
630
  batchIndex: z.number().int().min(1).max(5).optional(),
@@ -720,7 +780,37 @@ var accountStatusPayload = z.object({
720
780
  plan: z.string().optional(),
721
781
  limits: z.array(accountLimitSchema).optional()
722
782
  });
723
- var statusPayload = z.object({ status: runStatusSchema, reason: z.string().optional() });
783
+ var runStopCodeSchema = z.enum([
784
+ "completed",
785
+ /** A person stopped the run. */
786
+ "user_abort",
787
+ /** The reply reached its work-slice budget and handed off. */
788
+ "slice_budget",
789
+ /** Three compactions in one reply ended it. */
790
+ "compaction_thrash",
791
+ /** The model request failed (auth, quota, rate limit, network, unsupported model). */
792
+ "provider_error",
793
+ /** The run stopped reporting progress or heartbeats. */
794
+ "stalled",
795
+ /** The owning process went away without settling the run. */
796
+ "owner_gone",
797
+ /** The harness started and found the run unsettled. */
798
+ "harness_restart",
799
+ /** The terminal needs attention: a startup error or a lost decision callback. */
800
+ "terminal_attention",
801
+ /** The terminal closed while the run was live. */
802
+ "terminal_closed",
803
+ /** An ancestor run stopped, so this one did too. */
804
+ "parent_stopped",
805
+ /** Any other failure. */
806
+ "failed"
807
+ ]);
808
+ var stopCodeOf = (code) => {
809
+ const parsed = runStopCodeSchema.safeParse(code);
810
+ if (parsed.success) return parsed.data;
811
+ return code.startsWith("provider_") ? "provider_error" : "failed";
812
+ };
813
+ var statusPayload = z.object({ status: runStatusSchema, reason: z.string().optional(), code: runStopCodeSchema.optional() });
724
814
  var failedPayload = z.object({ code: z.string(), message: z.string() });
725
815
  var emptyPayload = z.object({}).passthrough();
726
816
  var runEventPayloadSchemas = {
@@ -731,6 +821,7 @@ var runEventPayloadSchemas = {
731
821
  "message.steered": messageSteeredPayload,
732
822
  "tool.started": toolPayload,
733
823
  "tool.completed": toolPayload,
824
+ "tool.refused": toolRefusedPayload,
734
825
  "retry.started": retryStartedPayload,
735
826
  "retry.ended": retryEndedPayload,
736
827
  "compaction.started": compactionStartedPayload,
@@ -777,9 +868,11 @@ var runRecordSchema = z.object({
777
868
  parentRunId: text,
778
869
  relation: text,
779
870
  terminalReason: text,
871
+ terminalCode: runStopCodeSchema.nullish().transform((value) => value ?? null),
780
872
  id: z.string(),
781
873
  projectId: text,
782
874
  mode: z.string(),
875
+ effectiveMode: z.enum(["plan", "normal"]).optional(),
783
876
  harness: z.string(),
784
877
  sessionType: z.string(),
785
878
  sessionId: z.string(),
@@ -802,7 +895,9 @@ var runRecordSchema = z.object({
802
895
  profile: text.optional(),
803
896
  title: text.optional(),
804
897
  model: text.optional(),
805
- effort: text.optional()
898
+ effort: text.optional(),
899
+ loadedSkills: z.array(z.string()).optional(),
900
+ activatedPlugins: z.array(z.string()).optional()
806
901
  });
807
902
  function upgradeRunRecord(raw) {
808
903
  if (!raw || typeof raw !== "object") return raw;
@@ -2434,6 +2529,7 @@ var REQUIRED_SKILLS_POINTER = "Some required skill text was omitted to fit. Load
2434
2529
  var REFERENCES_BUDGET = 12e3;
2435
2530
  var VARIANT_BUDGET = 3e4;
2436
2531
  var NOTES_LISTED = 25;
2532
+ var APPROVAL_GUIDANCE = "Approvals: when a call needs a person, the Harness pauses that exact call and shows them Allow and Deny. Never ask for permission in a message, never end your turn to wait for an answer, and never call a task blocked on approval before trying: a denial arrives as the tool result, and you continue from it.";
2437
2533
  var SECTION_ORDER = { harnessIdentity: 5, system: 25, sessionState: 30, guardrails: 40, memory: 45, references: 50, requiredSkills: 52, subagents: 64, scripts: 66, wiki: 80, tools: 90, reporting: 95 };
2438
2534
  var REPORTING_GUIDANCE = `When you finish work, write the final reply for a person who did not watch you work.
2439
2535
 
@@ -2540,7 +2636,7 @@ function toolGroups(context, launch, dataRoot) {
2540
2636
  const native = NATIVE_GROUP.filter((name) => enabled.includes(name));
2541
2637
  const wikiTools = WIKI_GROUP.filter((name) => name !== "wiki_write" || enabled.includes(name));
2542
2638
  const readGuidance = `Read-only file tools accept absolute paths and paths beginning with ~ anywhere this process has operating-system read access. The working folder is a default location, not a read boundary.${dataRoot ? ` The Harness data root is ${dataRoot}.` : ""}`;
2543
- const writeGuidance = "Keep native file writes and Bash filesystem effects inside the active project workspace. Outside file targets require one-call approval; remembered tool grants do not satisfy it. Bash, including an initial ! command, may read as before but writes are sandboxed to the workspace unless the explicit Full Permission switch is on. Keep task outputs in the project, following an existing artifacts/ or outputs/ convention when present.";
2639
+ const writeGuidance = "Keep native file writes and Bash filesystem effects inside the active project workspace, where they run without asking. An outside file target is allowed too: call the tool and the Harness pauses that one call until the person clicks Allow or Deny, so nothing outside changes first. Remembered tool grants do not answer an outside target; only the explicit Full Permission switch answers it for the run. Bash, including an initial ! command, may read as before but writes are sandboxed to the workspace unless that switch is on. Keep task outputs in the project, following an existing artifacts/ or outputs/ convention when present.";
2544
2640
  const nativeGuidance = `Files and shell: ${readGuidance} ${writeGuidance} Edits and commands still pass through the Harness gate.`;
2545
2641
  const groups = [
2546
2642
  {
@@ -2589,7 +2685,9 @@ function toolGroups(context, launch, dataRoot) {
2589
2685
  return present2.map((group, index) => ({ group, index })).sort((left, right) => left.group.order - right.group.order || left.index - right.index).map(({ group }) => group);
2590
2686
  }
2591
2687
  function tools2(groups, guidance, pointer) {
2592
- let text11 = "";
2688
+ let text11 = `${APPROVAL_GUIDANCE}
2689
+
2690
+ `;
2593
2691
  for (const group of groups) if (group.guidance) text11 += `${group.guidance}
2594
2692
 
2595
2693
  `;
@@ -3205,9 +3303,11 @@ function ordered(record3) {
3205
3303
  parentRunId: record3.parentRunId,
3206
3304
  relation: record3.relation,
3207
3305
  terminalReason: record3.terminalReason,
3306
+ ...record3.terminalCode !== void 0 ? { terminalCode: record3.terminalCode } : {},
3208
3307
  id: record3.id,
3209
3308
  projectId: record3.projectId,
3210
3309
  mode: record3.mode,
3310
+ ...record3.effectiveMode !== void 0 ? { effectiveMode: record3.effectiveMode } : {},
3211
3311
  harness: record3.harness,
3212
3312
  sessionType: record3.sessionType,
3213
3313
  sessionId: record3.sessionId,
@@ -3230,7 +3330,9 @@ function ordered(record3) {
3230
3330
  ...record3.profile ? { profile: record3.profile } : {},
3231
3331
  ...record3.title ? { title: record3.title } : {},
3232
3332
  ...record3.model ? { model: record3.model } : {},
3233
- ...record3.effort ? { effort: record3.effort } : {}
3333
+ ...record3.effort ? { effort: record3.effort } : {},
3334
+ ...record3.loadedSkills !== void 0 ? { loadedSkills: record3.loadedSkills } : {},
3335
+ ...record3.activatedPlugins !== void 0 ? { activatedPlugins: record3.activatedPlugins } : {}
3234
3336
  };
3235
3337
  }
3236
3338
  function write2(root, record3) {
@@ -3287,9 +3389,9 @@ function recordedSubagents(root, parentRunId) {
3287
3389
  function updateRun(root, runId, update3) {
3288
3390
  const record3 = readRecord(recordPath(root, runId));
3289
3391
  if (!record3) return;
3290
- const { status, endedAt, terminalReason } = record3;
3392
+ const { status, endedAt, terminalReason, terminalCode } = record3;
3291
3393
  update3(record3);
3292
- if (!isActiveStatus(status)) Object.assign(record3, { status, endedAt, terminalReason });
3394
+ if (!isActiveStatus(status)) Object.assign(record3, { status, endedAt, terminalReason, terminalCode });
3293
3395
  write2(root, record3);
3294
3396
  }
3295
3397
  function terminalStatus(event) {
@@ -3304,6 +3406,12 @@ function reasonOf(event) {
3304
3406
  const reason = payload2?.reason ?? payload2?.message;
3305
3407
  return typeof reason === "string" ? reason : null;
3306
3408
  }
3409
+ function stopCodeOfEvent(event) {
3410
+ const payload2 = event.payload;
3411
+ if (event.type === "completed") return "completed";
3412
+ if (event.type === "failed") return stopCodeOf(typeof payload2?.code === "string" ? payload2.code : "failed");
3413
+ return typeof payload2?.code === "string" ? stopCodeOf(payload2.code) : null;
3414
+ }
3307
3415
  function appendEvent(root, event) {
3308
3416
  const directory = runDir(root, event.runId);
3309
3417
  mkdirSync5(directory, { recursive: true });
@@ -3347,6 +3455,7 @@ function persistEvent(root, event) {
3347
3455
  record3.status = outcome;
3348
3456
  record3.endedAt = event.timestamp;
3349
3457
  record3.terminalReason = reasonOf(event);
3458
+ record3.terminalCode = stopCodeOfEvent(event);
3350
3459
  changed2 = true;
3351
3460
  } else if (event.type === "status.changed") {
3352
3461
  const status = event.payload.status;
@@ -3373,10 +3482,11 @@ function markInterruptedRuns(root, reason = STOPPED) {
3373
3482
  if (settled3) {
3374
3483
  const status = terminalStatus(settled3);
3375
3484
  const settledReason = reasonOf(settled3);
3376
- if (record3.status !== status || record3.endedAt !== settled3.timestamp || record3.terminalReason !== settledReason) {
3485
+ const settledCode = stopCodeOfEvent(settled3);
3486
+ if (record3.status !== status || record3.endedAt !== settled3.timestamp || record3.terminalReason !== settledReason || settledCode !== null && record3.terminalCode !== settledCode) {
3377
3487
  const backup2 = `${path4}.before-outcome-repair`;
3378
3488
  if (!existsSync9(backup2)) copyFileSync2(path4, backup2);
3379
- Object.assign(record3, { status, endedAt: settled3.timestamp, terminalReason: settledReason });
3489
+ Object.assign(record3, { status, endedAt: settled3.timestamp, terminalReason: settledReason, ...settledCode ? { terminalCode: settledCode } : {} });
3380
3490
  write2(root, record3);
3381
3491
  }
3382
3492
  }
@@ -3384,9 +3494,10 @@ function markInterruptedRuns(root, reason = STOPPED) {
3384
3494
  if (ownedElsewhere(root, record3.id)) continue;
3385
3495
  record3.status = "interrupted";
3386
3496
  record3.terminalReason = reason;
3497
+ record3.terminalCode = "harness_restart";
3387
3498
  record3.endedAt ??= now();
3388
3499
  const last = events2.reduce((highest, event) => Math.max(highest, event.sequence), -1);
3389
- appendEvent(root, { id: newId(), runId: record3.id, sessionId: record3.sessionId, sequence: last + 1, timestamp: now(), type: "status.changed", payload: { status: "interrupted", reason } });
3500
+ appendEvent(root, { id: newId(), runId: record3.id, sessionId: record3.sessionId, sequence: last + 1, timestamp: now(), type: "status.changed", payload: { status: "interrupted", reason, code: "harness_restart" } });
3390
3501
  write2(root, record3);
3391
3502
  interrupted.push(record3.id);
3392
3503
  }
@@ -3408,9 +3519,10 @@ function reconcileStaleRuns(root, at = Date.now(), options2 = {}) {
3408
3519
  const reason = lastHeartbeat ? "stalled" : "owner_gone";
3409
3520
  record3.status = "interrupted";
3410
3521
  record3.terminalReason = reason;
3522
+ record3.terminalCode = reason;
3411
3523
  record3.endedAt = now();
3412
3524
  const sequence = events2.reduce((highest, event) => Math.max(highest, event.sequence), -1) + 1;
3413
- appendEvent(root, { id: newId(), runId: record3.id, sessionId: record3.sessionId, sequence, timestamp: now(), type: "status.changed", payload: { status: "interrupted", reason } });
3525
+ appendEvent(root, { id: newId(), runId: record3.id, sessionId: record3.sessionId, sequence, timestamp: now(), type: "status.changed", payload: { status: "interrupted", reason, code: reason } });
3414
3526
  write2(root, record3);
3415
3527
  stalled.push(record3.id);
3416
3528
  }
@@ -3648,10 +3760,10 @@ function until(promise, signal) {
3648
3760
  });
3649
3761
  return Promise.reject(new HarnessError("run_not_active", "The parent run stopped."));
3650
3762
  }
3651
- return new Promise((resolve19, reject2) => {
3763
+ return new Promise((resolve23, reject2) => {
3652
3764
  const abort = () => reject2(new HarnessError("run_not_active", "The parent run stopped."));
3653
3765
  signal.addEventListener("abort", abort, { once: true });
3654
- promise.then(resolve19, reject2).finally(() => signal.removeEventListener("abort", abort));
3766
+ promise.then(resolve23, reject2).finally(() => signal.removeEventListener("abort", abort));
3655
3767
  });
3656
3768
  }
3657
3769
  var Subagents = class {
@@ -3692,8 +3804,8 @@ var Subagents = class {
3692
3804
  contributions: capabilities.contributions.filter((id2) => frozen.contributions.includes(id2))
3693
3805
  };
3694
3806
  let started;
3695
- const ready = new Promise((resolve19) => {
3696
- started = resolve19;
3807
+ const ready = new Promise((resolve23) => {
3808
+ started = resolve23;
3697
3809
  });
3698
3810
  if (!deferUntilPrompt) started();
3699
3811
  this.parents.set(input.run.id, { ready, started, input: structuredClone({ ...input, text: "", images: void 0, requiresImageInput: false, preamble: null, resume: null }), accountId, capabilities: ceiling, stopped: new AbortController(), recovered: /* @__PURE__ */ new Set(), launching: /* @__PURE__ */ new Map() });
@@ -4113,7 +4225,7 @@ It stopped before finishing. ${continueHint(result.runId)}` : ""}`;
4113
4225
 
4114
4226
  // src/harness.ts
4115
4227
  import { mkdirSync as mkdirSync26 } from "node:fs";
4116
- import { join as join69 } from "node:path";
4228
+ import { join as join70 } from "node:path";
4117
4229
 
4118
4230
  // src/accounts/accounts.ts
4119
4231
  import { chmodSync as chmodSync3, existsSync as existsSync10, mkdirSync as mkdirSync9, readdirSync as readdirSync11, rmSync as rmSync7 } from "node:fs";
@@ -4480,7 +4592,7 @@ async function withCredentialLock(dir2, key, work2, options2 = {}) {
4480
4592
  const blocked = participants().some((entry2) => entry2.id !== id2 && (entry2.ticket === null || entry2.ticket < ticket || entry2.ticket === ticket && entry2.id < id2));
4481
4593
  if (!blocked) return await work2();
4482
4594
  if (performance.now() - started >= timeout) throw new Error("Timed out waiting for credential modification; no credentials were changed.");
4483
- await new Promise((resolve19) => setTimeout(resolve19, Math.min(poll, Math.max(1, timeout - (performance.now() - started)))));
4595
+ await new Promise((resolve23) => setTimeout(resolve23, Math.min(poll, Math.max(1, timeout - (performance.now() - started)))));
4484
4596
  }
4485
4597
  } finally {
4486
4598
  rmSync6(own, { recursive: true, force: true });
@@ -5156,9 +5268,9 @@ var Accounts = class {
5156
5268
  const interaction = {
5157
5269
  signal: controller.signal,
5158
5270
  notify: (notification) => event(notification),
5159
- prompt: (prompt) => new Promise((resolve19, reject2) => {
5271
+ prompt: (prompt) => new Promise((resolve23, reject2) => {
5160
5272
  const promptId = randomUUID4();
5161
- login.prompts.set(promptId, { resolve: resolve19, reject: reject2 });
5273
+ login.prompts.set(promptId, { resolve: resolve23, reject: reject2 });
5162
5274
  prompt.signal?.addEventListener("abort", () => {
5163
5275
  login.prompts.delete(promptId);
5164
5276
  event({ type: "promptClosed", promptId });
@@ -5667,6 +5779,7 @@ function packageManifest(packageRoot) {
5667
5779
  version: text3(manifest.version) ?? text3(plugin.version),
5668
5780
  extends: (Array.isArray(own.extends) ? own.extends : typeof own.extends === "string" ? [own.extends] : []).filter((id2) => typeof id2 === "string" && !!id2),
5669
5781
  commandPrefix: typeof own.commandPrefix === "string" && /^[a-z][a-z0-9-]*$/i.test(own.commandPrefix) && !["plugin", "skill"].includes(own.commandPrefix.toLowerCase()) ? own.commandPrefix.toLowerCase() : null,
5782
+ defaultPrompt: typeof own.defaultPrompt === "string" && /^[a-z][a-z0-9-]*$/.test(own.defaultPrompt) ? own.defaultPrompt : null,
5670
5783
  hooks: inside2(own.hooks),
5671
5784
  agents: inside2(own.agents) ?? "agents",
5672
5785
  connectorTemplates: (Array.isArray(own.connectors) ? own.connectors : []).flatMap((value) => {
@@ -5725,14 +5838,14 @@ var piResolver = async ({ cwd, agentDir, install: install3, packages }) => {
5725
5838
  const settings = pi.SettingsManager.create(cwd, agentDir);
5726
5839
  const shared = new Set((settings.getGlobalSettings().packages ?? []).map((entry2) => typeof entry2 === "string" ? entry2 : entry2.source));
5727
5840
  const manager2 = () => new pi.DefaultPackageManager({ cwd, agentDir, settingsManager: settings, builtinExtensions: [...PI_BUILTINS] });
5728
- const resolve19 = (installing) => manager2().resolve(async (source) => installing && shared.has(source) ? "install" : "skip");
5841
+ const resolve23 = (installing) => manager2().resolve(async (source) => installing && shared.has(source) ? "install" : "skip");
5729
5842
  let resolved2;
5730
5843
  try {
5731
- resolved2 = install3 ? await withoutInstallScripts(() => resolve19(true)) : await resolve19(false);
5844
+ resolved2 = install3 ? await withoutInstallScripts(() => resolve23(true)) : await resolve23(false);
5732
5845
  } catch (error) {
5733
5846
  if (!install3) throw error;
5734
5847
  log("a plugin package could not be installed", error instanceof Error ? error.message : String(error));
5735
- resolved2 = await resolve19(false);
5848
+ resolved2 = await resolve23(false);
5736
5849
  }
5737
5850
  const found = [];
5738
5851
  const add = (paths2) => {
@@ -7222,7 +7335,7 @@ var TRAJECTORY_TOOLS = [
7222
7335
  name: "trajectory_search",
7223
7336
  call: "trajectory.search",
7224
7337
  label: "Search trajectories",
7225
- description: "List recorded runs across this {product} data root, including active runs. Filter by agent name or id, project name or slug, mode, status, or start time. Results are newest first; use the returned cursor to continue. Then use trajectory_read for captured prompts, context snapshots, model and tool events, decisions, retries, compaction, and usage. Hidden agents excluded by the host stay hidden.",
7338
+ description: "List recorded runs across this {product} data root, including active runs. Filter by agent name or id, project name or slug, mode, status, or start time. Results include terminalReason and terminalCode (null when unavailable) and are newest first; use the returned cursor to continue. Then use trajectory_read for captured prompts, context snapshots, model and tool events, decisions, retries, compaction, and usage. Hidden agents excluded by the host stay hidden.",
7226
7339
  parameters: {
7227
7340
  type: "object",
7228
7341
  properties: {
@@ -7419,13 +7532,17 @@ function workSliceGuard(options2) {
7419
7532
  });
7420
7533
  pi.on("tool_call", (event) => {
7421
7534
  const name = event.toolName;
7535
+ const refuse = (result) => {
7536
+ options2.onRefuse?.(event, result.reason);
7537
+ return result;
7538
+ };
7422
7539
  state.steps += 1;
7423
7540
  state.lastTool = name;
7424
7541
  const canCheckpoint = checkpointAvailable();
7425
7542
  const handoff = canCheckpoint && HANDOFF_TOOLS.has(name);
7426
7543
  if (state.endedBy) {
7427
7544
  const reason = state.checkpointWritten ? "This reply is over. Its checkpoint is recorded; nothing else runs." : "This reply is over. The Harness will record an evidence-derived checkpoint; nothing else runs.";
7428
- return handoff ? void 0 : { block: true, terminate: true, reason };
7545
+ return handoff ? void 0 : refuse({ block: true, terminate: true, reason });
7429
7546
  }
7430
7547
  if (state.compactions >= 3) {
7431
7548
  if (handoff) {
@@ -7433,14 +7550,14 @@ function workSliceGuard(options2) {
7433
7550
  state.endedByLimit = "three compactions";
7434
7551
  return void 0;
7435
7552
  }
7436
- return end("compaction-thrash", "three compactions");
7553
+ return refuse(end("compaction-thrash", "three compactions"));
7437
7554
  }
7438
7555
  const limit2 = sliceLimitReached(policy, state);
7439
7556
  if (limit2) {
7440
7557
  state.endedBy = "budget";
7441
7558
  state.endedByLimit = limit2;
7442
7559
  state.refusedAt ??= state.steps;
7443
- return handoff ? void 0 : { block: true, reason: handoffRequest(limit2, state, canCheckpoint) };
7560
+ return handoff ? void 0 : refuse({ block: true, reason: handoffRequest(limit2, state, canCheckpoint) });
7444
7561
  }
7445
7562
  });
7446
7563
  };
@@ -7642,14 +7759,14 @@ var safeRequest = async (start, options2) => {
7642
7759
  }
7643
7760
  };
7644
7761
  function readOnce(url, options2) {
7645
- return new Promise((resolve19, reject2) => {
7762
+ return new Promise((resolve23, reject2) => {
7646
7763
  const start = url.protocol === "https:" ? httpsRequest : httpRequest;
7647
7764
  let settled3 = false;
7648
7765
  const finish = (value) => {
7649
7766
  if (settled3) return;
7650
7767
  settled3 = true;
7651
7768
  clearTimeout(total);
7652
- resolve19(value);
7769
+ resolve23(value);
7653
7770
  };
7654
7771
  const fail3 = (error) => {
7655
7772
  if (settled3) return;
@@ -8105,9 +8222,9 @@ import { isIP as isIP2 } from "node:net";
8105
8222
  var OPEN_SCHEMES = /* @__PURE__ */ new Set(["data:", "blob:", "about:"]);
8106
8223
  var CACHE_MS2 = 6e4;
8107
8224
  var NetworkPolicy = class {
8108
- constructor(config, resolve19 = async (host) => (await lookup(host, { all: true, verbatim: true })).map((entry2) => entry2.address)) {
8225
+ constructor(config, resolve23 = async (host) => (await lookup(host, { all: true, verbatim: true })).map((entry2) => entry2.address)) {
8109
8226
  this.config = config;
8110
- this.resolve = resolve19;
8227
+ this.resolve = resolve23;
8111
8228
  }
8112
8229
  config;
8113
8230
  resolve;
@@ -8364,7 +8481,7 @@ async function askForAction(ask) {
8364
8481
  rememberable: false,
8365
8482
  standing: "none"
8366
8483
  });
8367
- return decision === "approved" ? null : "The user denied this action. Ask how they would like to proceed.";
8484
+ return decision === "approved" ? null : "The user denied this action. Do not repeat it or ask for permission in a message; choose another approach or report what is blocked.";
8368
8485
  }
8369
8486
 
8370
8487
  // src/browser/contracts.ts
@@ -12196,6 +12313,8 @@ function summary2(env, run2, projects) {
12196
12313
  project: project ? { id: project.id, name: project.name, slug: project.slug } : null,
12197
12314
  mode: run2.mode,
12198
12315
  status: run2.status,
12316
+ terminalReason: run2.terminalReason ?? null,
12317
+ terminalCode: run2.terminalCode ?? null,
12199
12318
  goal: goal.length > 500 ? `${goal.slice(0, 497)}\u2026` : goal || null,
12200
12319
  startedAt: run2.startedAt,
12201
12320
  endedAt: run2.endedAt,
@@ -12485,12 +12604,19 @@ ${tail}` : ""}`;
12485
12604
  return `Proposed ${proposal.name} (${proposal.id}). A person can review it in the plugin manager; it is not installed or enabled.`;
12486
12605
  }
12487
12606
  case "skill.load":
12488
- return loadSkill(options2.variantDir ?? null, str(params, "name"));
12607
+ return loadSkill(options2.variantDir ?? null, str(params, "name"), (name) => {
12608
+ if (!context.runId) return;
12609
+ const run2 = findRun(env.root, context.runId);
12610
+ if (!run2 || !isActiveStatus(run2.status) || !run2.loadedSkills) return;
12611
+ updateRun(env.root, context.runId, (record3) => {
12612
+ if (record3.loadedSkills && !record3.loadedSkills.includes(name)) record3.loadedSkills.push(name);
12613
+ });
12614
+ });
12489
12615
  default:
12490
12616
  throw new HarnessError("unknown_method", `Unknown harness call ${call}.`);
12491
12617
  }
12492
12618
  }
12493
- function loadSkill(variantDir, name) {
12619
+ function loadSkill(variantDir, name, onLoad) {
12494
12620
  const skills = variantDir && existsSync17(join40(variantDir, "skills")) ? readdirSync20(join40(variantDir, "skills"), { withFileTypes: true }).filter((entry2) => entry2.isDirectory() && existsSync17(join40(variantDir, "skills", entry2.name, "SKILL.md"))).map((entry2) => entry2.name).sort() : [];
12495
12621
  if (!skills.includes("plugin-authoring") && defaultSkillPaths(variantDir).length) skills.push("plugin-authoring");
12496
12622
  skills.sort();
@@ -12503,7 +12629,9 @@ function loadSkill(variantDir, name) {
12503
12629
  const helperNote = skill.helpers.length ? `
12504
12630
 
12505
12631
  (This skill's helpers are in ${join40(skill.baseDir, "scripts")}: ${skill.helpers.join(", ")}.)` : "";
12506
- return `${readFileSync20(skill.path, "utf8")}${helperNote}`;
12632
+ const text11 = readFileSync20(skill.path, "utf8");
12633
+ onLoad?.(wanted);
12634
+ return `${text11}${helperNote}`;
12507
12635
  }
12508
12636
 
12509
12637
  // src/tools/harnessTools.ts
@@ -14182,8 +14310,8 @@ var cancelActive = null;
14182
14310
  function listenForCallback(expectedState, clientName) {
14183
14311
  return new Promise((ready, failed) => {
14184
14312
  let settle;
14185
- const result = new Promise((resolve19, reject2) => {
14186
- settle = { resolve: resolve19, reject: reject2 };
14313
+ const result = new Promise((resolve23, reject2) => {
14314
+ settle = { resolve: resolve23, reject: reject2 };
14187
14315
  });
14188
14316
  result.catch(() => void 0);
14189
14317
  const server = createServer((request2, response) => {
@@ -14207,7 +14335,7 @@ function listenForCallback(expectedState, clientName) {
14207
14335
  clearTimeout(timer);
14208
14336
  failed(new HarnessError("invalid_request", error.code === "EADDRINUSE" ? `Port ${MCP_OAUTH_PORT} is in use. Finish or cancel the other sign-in, or quit the program using that port, then try again.` : "The sign-in listener could not start."));
14209
14337
  });
14210
- const closed = new Promise((resolve19) => server.once("close", resolve19));
14338
+ const closed = new Promise((resolve23) => server.once("close", resolve23));
14211
14339
  const close = () => {
14212
14340
  clearTimeout(timer);
14213
14341
  if (cancelActive === cancel) cancelActive = null;
@@ -14887,6 +15015,9 @@ ${clip4(payload2.text, options2.obsTrunc)}`);
14887
15015
  ${clip4(text11, options2.obsTrunc)}${payload2.outputTruncated ? "\n(the tool output was already bounded when it was recorded)" : ""}`);
14888
15016
  break;
14889
15017
  }
15018
+ case "tool.refused":
15019
+ out.push(`[ERROR] ${String(payload2.name ?? "tool")} refused${typeof payload2.code === "string" ? ` (${payload2.code})` : ""}${typeof payload2.reason === "string" && payload2.reason ? `: ${clip4(payload2.reason, options2.obsTrunc)}` : ""}`);
15020
+ break;
14890
15021
  case "retry.started":
14891
15022
  out.push(`[ERROR] model request failed, retrying (${String(payload2.attempt)}/${String(payload2.maxAttempts)}): ${String(payload2.error ?? "")}`);
14892
15023
  break;
@@ -14903,7 +15034,10 @@ ${clip4(text11, options2.obsTrunc)}${payload2.outputTruncated ? "\n(the tool out
14903
15034
  out.push(`[ERROR] run failed: ${String(payload2.message ?? payload2.code ?? "")}`);
14904
15035
  break;
14905
15036
  case "status.changed":
14906
- if (payload2.status === "interrupted") out.push("[ERROR] run interrupted");
15037
+ if (payload2.status === "interrupted") {
15038
+ const reason = typeof payload2.reason === "string" && payload2.reason ? payload2.reason : typeof payload2.code === "string" ? payload2.code : "";
15039
+ out.push(`[ERROR] run interrupted${reason ? `: ${clip4(reason, options2.obsTrunc)}` : ""}`);
15040
+ }
14907
15041
  break;
14908
15042
  default:
14909
15043
  break;
@@ -14931,7 +15065,7 @@ var PROBE_TRIGGER = "probe";
14931
15065
  var IMPROVER_TRIGGER = "improver";
14932
15066
  var IMPROVER_IDENTITY = "You improve the harness an autonomous agent runs with, from evidence of how it ran. Work only inside the current folder: the evidence is under `context/`. Follow the mission in the user message exactly.";
14933
15067
  function settled(runtime, runId, signal, timeoutMs = 60 * 6e4) {
14934
- return new Promise((resolve19) => {
15068
+ return new Promise((resolve23) => {
14935
15069
  let done = false;
14936
15070
  const finish = (status) => {
14937
15071
  if (done) return;
@@ -14939,7 +15073,7 @@ function settled(runtime, runId, signal, timeoutMs = 60 * 6e4) {
14939
15073
  stop();
14940
15074
  clearTimeout(timer);
14941
15075
  signal?.removeEventListener("abort", abort);
14942
- resolve19(status);
15076
+ resolve23(status);
14943
15077
  };
14944
15078
  const stop = runtime.runs.listen((event) => {
14945
15079
  if (event.runId !== runId) return;
@@ -15924,8 +16058,8 @@ function shellPath() {
15924
16058
  if (!shell) throw new Error("The workspace write-boundary sandbox requires Bash, but no Bash executable was found.");
15925
16059
  return shell;
15926
16060
  }
15927
- function requireShell(resolve19) {
15928
- const shell = resolve19();
16061
+ function requireShell(resolve23) {
16062
+ const shell = resolve23();
15929
16063
  if (!shell) {
15930
16064
  throw new Error(process.platform === "win32" ? "No shell was found: neither Git Bash nor PowerShell is available. Bash was not run." : "No Bash executable was found. Bash was not run.");
15931
16065
  }
@@ -16064,7 +16198,7 @@ function killProcess(child) {
16064
16198
  }
16065
16199
  function runProcess(spec, cwd, options2, marker) {
16066
16200
  if (options2.signal?.aborted) return Promise.reject(new Error("aborted"));
16067
- return new Promise((resolve19, reject2) => {
16201
+ return new Promise((resolve23, reject2) => {
16068
16202
  const child = spawn2(spec.executable, spec.args, {
16069
16203
  cwd,
16070
16204
  detached: process.platform !== "win32",
@@ -16107,7 +16241,7 @@ function runProcess(spec, cwd, options2, marker) {
16107
16241
  if (!started && pending2) options2.onData(Buffer.from(pending2));
16108
16242
  if (options2.signal?.aborted) reject2(new Error("aborted"));
16109
16243
  else if (timedOut) reject2(new Error(`timeout:${options2.timeout}`));
16110
- else resolve19({ exitCode: code });
16244
+ else resolve23({ exitCode: code });
16111
16245
  };
16112
16246
  const onAbort = () => killProcess(child);
16113
16247
  if (options2.timeout !== void 0 && options2.timeout > 0) {
@@ -16220,6 +16354,28 @@ function createWorkspaceBashOperations(workspaceRoot, options2 = {}) {
16220
16354
  };
16221
16355
  }
16222
16356
 
16357
+ // src/context/runDetails.ts
16358
+ function runDetails(root, runId) {
16359
+ const run2 = loadRun2(root, runId);
16360
+ const capabilities = loadRun(root, runId);
16361
+ const snapshot2 = readEvents(root, runId).filter((event) => event.type === "context.snapshot").at(-1);
16362
+ const payload2 = snapshot2?.payload;
16363
+ return {
16364
+ runId: run2.id,
16365
+ status: run2.status ?? null,
16366
+ mode: run2.effectiveMode ?? null,
16367
+ model: run2.model ?? null,
16368
+ effort: run2.effort ?? null,
16369
+ variantScope: run2.variantScope ?? null,
16370
+ variantVersion: run2.variantVersion ?? null,
16371
+ workspaceRoot: run2.workspaceRoot ?? null,
16372
+ tools: payload2 && !payload2.toolsTruncated && Array.isArray(payload2.tools) ? payload2.tools.map((tool) => tool.name) : null,
16373
+ connectors: capabilities?.connectors ?? null,
16374
+ skills: run2.loadedSkills ?? null,
16375
+ plugins: run2.activatedPlugins ?? null
16376
+ };
16377
+ }
16378
+
16223
16379
  // src/terminal/terminals.ts
16224
16380
  import { chmodSync as chmodSync11, existsSync as existsSync26, lstatSync as lstatSync13, mkdirSync as mkdirSync22, readFileSync as readFileSync30, unlinkSync as unlinkSync5, writeFileSync as writeFileSync11 } from "node:fs";
16225
16381
  import { basename as basename12, dirname as dirname20, join as join59 } from "node:path";
@@ -16644,7 +16800,7 @@ function request(connection, body2, what, timeoutMs, signal) {
16644
16800
  var StaleConnection = class extends Error {
16645
16801
  };
16646
16802
  function attempt(connection, body2, what, timeoutMs, reused, signal) {
16647
- return new Promise((resolve19, reject2) => {
16803
+ return new Promise((resolve23, reject2) => {
16648
16804
  const stream = reused ?? createConnection(connection.socket);
16649
16805
  let buffer = "";
16650
16806
  let replied = false;
@@ -16662,7 +16818,7 @@ function attempt(connection, body2, what, timeoutMs, reused, signal) {
16662
16818
  if (reply2.error) {
16663
16819
  const error = typeof reply2.error === "string" ? { message: reply2.error } : reply2.error;
16664
16820
  finish(() => reject2(new SocketError(error.code ?? "error", error.message ?? `${what} failed.`)), !rest);
16665
- } else finish(() => resolve19(reply2.result), !rest);
16821
+ } else finish(() => resolve23(reply2.result), !rest);
16666
16822
  } catch {
16667
16823
  finish(() => reject2(new SocketError("invalid_reply", "The harness sent an unreadable reply.")));
16668
16824
  }
@@ -17029,11 +17185,11 @@ var TerminalController = class {
17029
17185
  }
17030
17186
  async waitPending(revision) {
17031
17187
  if (revision === this.pendingRevision && !this.disposed) {
17032
- await new Promise((resolve19) => {
17188
+ await new Promise((resolve23) => {
17033
17189
  const wake = () => {
17034
17190
  clearTimeout(timer);
17035
17191
  this.pendingWaiters.delete(wake);
17036
- resolve19();
17192
+ resolve23();
17037
17193
  };
17038
17194
  const timer = setTimeout(wake, 2e4);
17039
17195
  this.pendingWaiters.add(wake);
@@ -17076,9 +17232,9 @@ var TerminalController = class {
17076
17232
  this.delivery = null;
17077
17233
  return run2;
17078
17234
  }
17079
- finish(outcome, message4, reason) {
17235
+ finish(outcome, message4, reason, code) {
17080
17236
  this.stopStallWatch();
17081
- this.run?.finish(outcome, message4 ? { code: "terminal_attention", message: message4 } : void 0, reason);
17237
+ this.run?.finish(outcome, message4 ? { code: code === "provider_error" ? "provider_error" : "terminal_attention", message: message4 } : void 0, reason, code);
17082
17238
  this.run = null;
17083
17239
  this.seen.clear();
17084
17240
  if (this.pending.size) {
@@ -17118,15 +17274,15 @@ var TerminalController = class {
17118
17274
  if (this.disposed) throw new HarnessError("terminal_not_ready", "Reopen the terminal before sending instructions.");
17119
17275
  if (this.activeRun || this.delivery) throw new HarnessError("session_busy", "This terminal has an active task or delivery.");
17120
17276
  const deadline = Date.now() + READY_TIMEOUT_MS;
17121
- while (!this.ready && !this.startupError && !this.disposed && Date.now() < deadline) await new Promise((resolve19) => setTimeout(resolve19, 50));
17277
+ while (!this.ready && !this.startupError && !this.disposed && Date.now() < deadline) await new Promise((resolve23) => setTimeout(resolve23, 50));
17122
17278
  if (this.disposed) throw new HarnessError("terminal_not_ready", "The terminal stopped during initialization.");
17123
17279
  if (this.startupError) throw new HarnessError("terminal_not_ready", this.startupError);
17124
17280
  if (!this.ready) throw new HarnessError("terminal_not_ready", `The native telemetry handshake is missing (${this.startupPhase}). Inspect the terminal sign-in or trust screen.`);
17125
17281
  if (this.dirty) throw new HarnessError("terminal_has_draft", "The terminal contains user input. Submit or clear it before handing back control.");
17126
17282
  let timer;
17127
- const ack = new Promise((resolve19, reject2) => {
17283
+ const ack = new Promise((resolve23, reject2) => {
17128
17284
  timer = setTimeout(() => reject2(new HarnessError("delivery_uncertain", "The native prompt acknowledgement was not received. Inspect the terminal; this instruction will not be resent.")), ACK_TIMEOUT_MS);
17129
- this.delivery = { runId, text: text11, resolve: resolve19, reject: reject2 };
17285
+ this.delivery = { runId, text: text11, resolve: resolve23, reject: reject2 };
17130
17286
  });
17131
17287
  void ack.catch(() => {
17132
17288
  });
@@ -17346,14 +17502,15 @@ var TerminalController = class {
17346
17502
  }
17347
17503
  if (event === "shutdown") {
17348
17504
  this.ready = false;
17349
- this.finish("interrupted");
17505
+ this.finish("interrupted", void 0, void 0, "terminal_closed");
17350
17506
  return {};
17351
17507
  }
17352
17508
  if (event === "settled") {
17353
17509
  const stalled = this.stallRequested !== null && this.stallRequested === this.run?.runId;
17354
- const reason = stalled ? "stalled" : typeof data.reason === "string" ? data.reason : void 0;
17510
+ const reason = data.error ? void 0 : stalled ? "stalled" : typeof data.reason === "string" ? data.reason : void 0;
17355
17511
  const outcome = data.error ? "failed" : data.aborted || reason ? "interrupted" : "completed";
17356
- this.finish(outcome, data.error ? String(data.error) : void 0, reason);
17512
+ const code = data.error ? data.errorCode === "provider_error" ? "provider_error" : void 0 : stalled ? "stalled" : data.code === "slice_budget" || data.code === "compaction_thrash" ? data.code : data.aborted ? "user_abort" : void 0;
17513
+ this.finish(outcome, data.error ? String(data.error) : void 0, reason, code);
17357
17514
  return {};
17358
17515
  }
17359
17516
  const run2 = this.run;
@@ -17392,6 +17549,8 @@ var TerminalController = class {
17392
17549
  }
17393
17550
  }
17394
17551
  if (event === "usage") run2.emit("usage.updated", data);
17552
+ else if (event === "refusal") run2.emit("tool.refused", data);
17553
+ else if (event === "compaction.started" || event === "compaction.ended") run2.emit(event, data);
17395
17554
  else if (event === "heartbeat") run2.emit("heartbeat", data);
17396
17555
  else if (event === "todo" || event === "checklist") run2.emit("todo.updated", { items: data.items });
17397
17556
  else if (event === "checkpoint") run2.emit("checkpoint", data);
@@ -17416,7 +17575,7 @@ var TerminalController = class {
17416
17575
  this.ready = false;
17417
17576
  this.delivery?.reject(new Error("The terminal stopped before prompt acknowledgement."));
17418
17577
  this.delivery = null;
17419
- this.finish("interrupted");
17578
+ this.finish("interrupted", void 0, void 0, "terminal_closed");
17420
17579
  this.subagents.clear();
17421
17580
  this.subagentParentRunId = null;
17422
17581
  }
@@ -17651,7 +17810,7 @@ var SessionSocket = class {
17651
17810
  });
17652
17811
  }
17653
17812
  bind(path4) {
17654
- return new Promise((resolve19, reject2) => {
17813
+ return new Promise((resolve23, reject2) => {
17655
17814
  const server = createServer2((client) => this.serve(client));
17656
17815
  server.once("error", reject2);
17657
17816
  server.listen(path4, () => {
@@ -17663,7 +17822,7 @@ var SessionSocket = class {
17663
17822
  }
17664
17823
  }
17665
17824
  this.server = server;
17666
- resolve19();
17825
+ resolve23();
17667
17826
  });
17668
17827
  });
17669
17828
  }
@@ -17699,7 +17858,7 @@ var SessionSocket = class {
17699
17858
  this.server = null;
17700
17859
  this.listening = null;
17701
17860
  for (const client of this.clients) client.destroy();
17702
- if (server) await new Promise((resolve19) => server.close(() => resolve19()));
17861
+ if (server) await new Promise((resolve23) => server.close(() => resolve23()));
17703
17862
  if (!isPipePath(this.path)) rmSync22(this.path, { force: true });
17704
17863
  }
17705
17864
  };
@@ -17751,7 +17910,7 @@ function summary3(run2) {
17751
17910
  }
17752
17911
  function listTerminalTrajectoryRuns(root, sessionId, nativeSessionId) {
17753
17912
  if (!nativeSessionId) return [];
17754
- return listRuns(root, { sessionId }).filter((run2) => run2.nativeSessionId === nativeSessionId || run2.trigger === "side-question" && run2.sessionId === sessionId).map(summary3);
17913
+ return listRuns(root, { sessionId }).filter((run2) => run2.nativeSessionId === nativeSessionId).map(summary3);
17755
17914
  }
17756
17915
  function latestTerminalTodo(root, sessionId, nativeSessionId) {
17757
17916
  if (!nativeSessionId) return { runId: "", todo: [] };
@@ -17761,7 +17920,7 @@ function latestTerminalTodo(root, sessionId, nativeSessionId) {
17761
17920
  function readTerminalTrajectory(root, sessionId, nativeSessionId, runId, afterSequence) {
17762
17921
  if (!nativeSessionId || !Number.isSafeInteger(afterSequence ?? 0) || (afterSequence ?? 0) < 0) throw notFound("Run");
17763
17922
  const run2 = findRun(root, runId);
17764
- if (!run2 || run2.sessionId !== sessionId || run2.nativeSessionId !== nativeSessionId && run2.trigger !== "side-question") throw notFound("Run");
17923
+ if (!run2 || run2.sessionId !== sessionId || run2.nativeSessionId !== nativeSessionId) throw notFound("Run");
17765
17924
  const events2 = readEvents(root, runId).filter((event) => afterSequence === void 0 || event.sequence > afterSequence);
17766
17925
  return { run: summary3(run2), events: events2 };
17767
17926
  }
@@ -18202,13 +18361,13 @@ var Pairing = class {
18202
18361
  if (this.pending) throw new HarnessError("remote_busy", "Another device is waiting for approval in the terminal.");
18203
18362
  if (this.devices.size >= MAX_DEVICES) throw new HarnessError("remote_full", `At most ${MAX_DEVICES} devices can control a session. Press n in /remote-control to start over.`);
18204
18363
  const label = deviceLabel(body2.userAgent);
18205
- const allowed3 = await new Promise((resolve19) => {
18364
+ const allowed3 = await new Promise((resolve23) => {
18206
18365
  const timer = setTimeout(() => decide2(false), confirmMs);
18207
18366
  timer.unref?.();
18208
18367
  const decide2 = (allow) => {
18209
18368
  clearTimeout(timer);
18210
18369
  if (this.pending?.decide === decide2) this.pending = null;
18211
- resolve19(allow);
18370
+ resolve23(allow);
18212
18371
  this.changed();
18213
18372
  };
18214
18373
  this.pending = { id: id(), label, at, decide: decide2 };
@@ -18357,11 +18516,11 @@ async function listenRemote(host, port, routes) {
18357
18516
  sockets.add(socket);
18358
18517
  socket.on("close", () => sockets.delete(socket));
18359
18518
  });
18360
- await new Promise((resolve19, reject2) => {
18519
+ await new Promise((resolve23, reject2) => {
18361
18520
  server.once("error", reject2);
18362
18521
  server.listen({ host, port, exclusive: true }, () => {
18363
18522
  server.off("error", reject2);
18364
- resolve19();
18523
+ resolve23();
18365
18524
  });
18366
18525
  });
18367
18526
  const bound = server.address().port;
@@ -18371,8 +18530,8 @@ async function listenRemote(host, port, routes) {
18371
18530
  port: bound,
18372
18531
  close: async () => {
18373
18532
  for (const stream of [...streams]) stream.end();
18374
- await new Promise((resolve19) => {
18375
- server.close(() => resolve19());
18533
+ await new Promise((resolve23) => {
18534
+ server.close(() => resolve23());
18376
18535
  for (const socket of sockets) socket.destroy();
18377
18536
  });
18378
18537
  }
@@ -18685,11 +18844,15 @@ var PAGE_JS = String.raw`
18685
18844
  inputs.push(input)
18686
18845
  void index
18687
18846
  })
18688
- var other = document.createElement('input'); other.type = 'text'; other.placeholder = 'Or type an answer'
18689
- card.appendChild(other)
18847
+ var allowsText = request.allowText !== false
18848
+ var other = null
18849
+ if (allowsText) {
18850
+ other = document.createElement('input'); other.type = 'text'; other.placeholder = 'Or type an answer'
18851
+ card.appendChild(other)
18852
+ }
18690
18853
  return function () {
18691
18854
  var selected = inputs.filter(function (i) { return i.checked }).map(function (i) { return i.value })
18692
- var typed = other.value.trim()
18855
+ var typed = allowsText && other ? other.value.trim() : ''
18693
18856
  if (!selected.length && !typed) throw new Error('Choose an answer first.')
18694
18857
  return typed ? { selected: selected, text: typed } : { selected: selected }
18695
18858
  }
@@ -18927,11 +19090,11 @@ var RemoteControl = class {
18927
19090
  /** Returns at once for a changed revision, otherwise after a change or 20 seconds. */
18928
19091
  async waitStatus(revision) {
18929
19092
  if (revision === this.revision && !this.ended) {
18930
- await new Promise((resolve19) => {
19093
+ await new Promise((resolve23) => {
18931
19094
  const wake = () => {
18932
19095
  clearTimeout(timer);
18933
19096
  this.waiters.delete(wake);
18934
- resolve19();
19097
+ resolve23();
18935
19098
  };
18936
19099
  const timer = setTimeout(wake, 2e4);
18937
19100
  timer.unref?.();
@@ -19065,7 +19228,7 @@ var RemoteControl = class {
19065
19228
  if (changed2) this.broadcast({ type: "pending", requests: snapshot2.requests.map(projectRequest) });
19066
19229
  } catch {
19067
19230
  if (loop.signal.aborted) return;
19068
- await new Promise((resolve19) => setTimeout(resolve19, 1e3).unref?.());
19231
+ await new Promise((resolve23) => setTimeout(resolve23, 1e3).unref?.());
19069
19232
  revision = void 0;
19070
19233
  }
19071
19234
  }
@@ -19123,6 +19286,7 @@ function resourceIdentity(loading) {
19123
19286
  builtins: loading.builtins,
19124
19287
  skills: loading.skillPaths,
19125
19288
  prompts: loading.promptPaths,
19289
+ resourceOwners: loading.found.filter((item) => (item.kind === "prompt" || item.kind === "extension") && item.packageRoot),
19126
19290
  themes: loading.themePaths,
19127
19291
  profiles: loading.profilePaths,
19128
19292
  detached: loading.detached,
@@ -19304,7 +19468,8 @@ var Terminals = class {
19304
19468
  ownNames = /* @__PURE__ */ new Map();
19305
19469
  async ownTools(live, listing) {
19306
19470
  const sessionId = live.caller.sessionId;
19307
- const all3 = harnessTools(this.options.env, live.caller, { variantDir: this.variantDirs.get(sessionId) ?? null }).filter((tool) => DEFAULT_TOOL_NAMES.includes(tool.name));
19471
+ const caller = { ...live.caller, runId: live.controller.activeRun?.runId ?? null };
19472
+ const all3 = harnessTools(this.options.env, caller, { variantDir: this.variantDirs.get(sessionId) ?? null }).filter((tool) => DEFAULT_TOOL_NAMES.includes(tool.name));
19308
19473
  if (!listing) return all3.filter((tool) => this.ownNames.get(sessionId)?.has(tool.name) ?? false);
19309
19474
  const on = await this.worldOn(live);
19310
19475
  const owner = (tool) => HARNESS_PLUGINS.find((plugin) => plugin.tools?.some((entry2) => entry2.name === tool))?.id ?? null;
@@ -19600,10 +19765,12 @@ var Terminals = class {
19600
19765
  throw new HarnessError("unknown_method", `Unknown call ${call}.`);
19601
19766
  }
19602
19767
  }
19603
- /** `/context`: what the session's latest request was made of, and one row's text on request. */
19768
+ /** `/context`: the selected run's request, and one row's text on request. */
19604
19769
  contextCall(live, params) {
19605
- const runId = live.controller.lastRun;
19770
+ if (params.runId !== void 0 && (typeof params.runId !== "string" || !params.runId)) throw invalid("A valid run id is required.");
19771
+ const runId = typeof params.runId === "string" ? params.runId : live.controller.lastRun;
19606
19772
  if (!runId) return { attribution: null, text: null };
19773
+ if (params.runId !== void 0) readTerminalTrajectory(this.options.env.root, live.input.sessionId, live.controller.nativeSessionId, runId);
19607
19774
  const text11 = typeof params.section === "string" && params.section ? rowText(this.options.env, runId, params.section) : null;
19608
19775
  return { attribution: attribution(this.options.env, runId), text: text11 };
19609
19776
  }
@@ -19786,6 +19953,7 @@ var Terminals = class {
19786
19953
  startedAt: now(),
19787
19954
  endedAt: null,
19788
19955
  status: "queued",
19956
+ loadedSkills: [],
19789
19957
  exitCode: null,
19790
19958
  handoffId: null,
19791
19959
  accountId: live.account.id,
@@ -19951,6 +20119,9 @@ ${formatBashCommandForApproval(command)}`,
19951
20119
  }, packaged).body
19952
20120
  };
19953
20121
  freezeRun(this.options.env.root, run2.runId, caller, capabilities);
20122
+ updateRun(this.options.env.root, run2.runId, (record3) => {
20123
+ record3.effectiveMode = params.plan === true ? "plan" : "normal";
20124
+ });
19954
20125
  if (!service || live.input.subagents === null) return { runId: run2.runId, connectors: capabilities.connectors, subagentProfiles };
19955
20126
  service.register({
19956
20127
  run: { id: run2.runId, sessionId: live.input.sessionId, mode: live.input.run?.mode ?? "code", projectId: live.input.projectId, worktreeId: live.input.run?.worktreeId },
@@ -20027,6 +20198,11 @@ ${formatBashCommandForApproval(command)}`,
20027
20198
  if (call === "terminal.trajectory.list") {
20028
20199
  return { runs: listTerminalTrajectoryRuns(this.options.env.root, live.input.sessionId, live.controller.nativeSessionId) };
20029
20200
  }
20201
+ if (call === "terminal.trajectory.details") {
20202
+ if (typeof params.runId !== "string" || !params.runId) throw invalid("A run id is required.");
20203
+ readTerminalTrajectory(this.options.env.root, live.input.sessionId, live.controller.nativeSessionId, params.runId);
20204
+ return runDetails(this.options.env.root, params.runId);
20205
+ }
20030
20206
  if (call === "terminal.trajectory.events") {
20031
20207
  if (typeof params.runId !== "string" || !params.runId) throw invalid("A run id is required.");
20032
20208
  const afterSequence = params.afterSequence;
@@ -20266,7 +20442,8 @@ ${formatBashCommandForApproval(command)}`,
20266
20442
  if (resourceIdentity(current2) !== resourceIdentity(loading) || variant && (currentVariant?.fingerprint !== variant.fingerprint || JSON.stringify(currentVariant.extensionPaths) !== JSON.stringify(variant.extensionPaths))) {
20267
20443
  throw new HarnessError("terminal_resources_changed", "Trusted resources changed since this terminal was prepared. Review /plugins and reopen the terminal to load the new selection.");
20268
20444
  }
20269
- return {};
20445
+ const resourceOwners = (current2?.found ?? []).filter((item) => item.packageRoot && (item.kind === "prompt" && current2?.promptPaths.includes(item.path) || item.kind === "extension" && current2?.extensionPaths.includes(item.path)));
20446
+ return resourceOwners.length ? { resourceOwners } : {};
20270
20447
  } }
20271
20448
  });
20272
20449
  live.token = token;
@@ -20502,6 +20679,7 @@ async function builtinChecks(harness, context, secrets, assets = defaultAssets()
20502
20679
  }
20503
20680
  }
20504
20681
  },
20682
+ check2("computer.capture", "tools", "Application window capture", async () => context.tools.includes("computer_screenshot") ? { status: "unverified", evidence: "A computer screenshot tool is listed for this session; its backend and window isolation were not tested.", remedy: "Check the provider and approve an application window before capture." } : { status: "blocked", evidence: "No computer screenshot tool is listed for this session. An application window cannot be read through this capability.", remedy: "Use a host with an approved-window capture backend." }),
20505
20683
  check2("runtime.pi", "runtime", "Pi runtime", async () => {
20506
20684
  await loadPi();
20507
20685
  return pass(`Pi ${PINNED_PI_VERSION} loaded with its required dispatcher.`);
@@ -20702,7 +20880,7 @@ async function diagnosticProbe(harness, context, inputSignal, action) {
20702
20880
  const run2 = harness.runs.start(id2, id2);
20703
20881
  run2.onInterrupt(() => interrupt.abort());
20704
20882
  const abort = () => {
20705
- run2.finish("interrupted");
20883
+ run2.finish("interrupted", void 0, void 0, "user_abort");
20706
20884
  void harness.world.release(id2);
20707
20885
  };
20708
20886
  signal.addEventListener("abort", abort, { once: true });
@@ -20716,10 +20894,13 @@ async function diagnosticProbe(harness, context, inputSignal, action) {
20716
20894
  call: async (name, input) => {
20717
20895
  signal.throwIfAborted();
20718
20896
  if (run2.finished || !capabilities.tools.includes(name)) throw new HarnessError("doctor_blocked", "This tool is outside the diagnostic capability set.");
20719
- const refusal = await gate.check(name, input);
20720
- if (refusal) throw new HarnessError("doctor_blocked", "The tool policy refused this diagnostic call.");
20721
- signal.throwIfAborted();
20722
20897
  const callId = randomUUID15();
20898
+ const refusal = await gate.checkDetailed?.(name, input);
20899
+ if (refusal) {
20900
+ run2.emit("tool.refused", { toolCallId: callId, name, code: refusal.code, reason: "The tool policy refused this diagnostic call." });
20901
+ throw new HarnessError("doctor_blocked", "The tool policy refused this diagnostic call.");
20902
+ }
20903
+ signal.throwIfAborted();
20723
20904
  run2.emit("tool.started", { toolCallId: callId, name, summary: "Synthetic diagnostic input" });
20724
20905
  try {
20725
20906
  let result2;
@@ -20746,11 +20927,11 @@ async function diagnosticProbe(harness, context, inputSignal, action) {
20746
20927
  });
20747
20928
  return result;
20748
20929
  } catch (error) {
20749
- run2.finish(signal.aborted ? "interrupted" : "failed", { code: "doctor_failed", message: "The diagnostic probe failed." });
20930
+ run2.finish(signal.aborted ? "interrupted" : "failed", { code: "doctor_failed", message: "The diagnostic probe failed." }, void 0, signal.aborted ? "user_abort" : void 0);
20750
20931
  throw error;
20751
20932
  } finally {
20752
20933
  signal.removeEventListener("abort", abort);
20753
- run2.finish(signal.aborted ? "interrupted" : "completed");
20934
+ run2.finish(signal.aborted ? "interrupted" : "completed", void 0, void 0, signal.aborted ? "user_abort" : void 0);
20754
20935
  harness.apps.revokeRun(id2, id2);
20755
20936
  await harness.world.release(id2);
20756
20937
  }
@@ -21054,7 +21235,7 @@ var RunRegistry = class {
21054
21235
  if (finished || finishing || this.nativeCleared.delete(questionId)) return Promise.resolve({ selected: [] });
21055
21236
  validateQuestionRecommendations(input);
21056
21237
  if (this.questions.has(questionId)) throw new HarnessError("invalid_request", "This question id is already pending.");
21057
- return new Promise((resolve19) => {
21238
+ return new Promise((resolve23) => {
21058
21239
  this.questions.set(questionId, {
21059
21240
  runId,
21060
21241
  input,
@@ -21063,7 +21244,7 @@ var RunRegistry = class {
21063
21244
  this.questions.delete(questionId);
21064
21245
  emit("question.answered", { questionId, ...answer, ...batch ?? {} });
21065
21246
  emit("status.changed", { status: waitingStatus() });
21066
- resolve19(answer);
21247
+ resolve23(answer);
21067
21248
  }
21068
21249
  });
21069
21250
  emit("question.requested", { questionId, ...payload2, ...batch ?? {} });
@@ -21106,7 +21287,7 @@ var RunRegistry = class {
21106
21287
  }
21107
21288
  emit("approval.requested", { approvalId, ...payload2 });
21108
21289
  emit("status.changed", { status: "waiting_for_approval" });
21109
- return new Promise((resolve19) => {
21290
+ return new Promise((resolve23) => {
21110
21291
  const timer = timeoutMs ? setTimeout(() => this.approvals.get(approvalId)?.resolve("denied"), timeoutMs) : null;
21111
21292
  this.approvals.set(approvalId, {
21112
21293
  runId,
@@ -21115,7 +21296,7 @@ var RunRegistry = class {
21115
21296
  this.approvals.delete(approvalId);
21116
21297
  emit("approval.resolved", { approvalId, decision });
21117
21298
  emit("status.changed", { status: waitingStatus() });
21118
- resolve19(decision);
21299
+ resolve23(decision);
21119
21300
  }
21120
21301
  });
21121
21302
  });
@@ -21136,17 +21317,17 @@ var RunRegistry = class {
21136
21317
  emit("status.changed", { status: "waiting_for_input" });
21137
21318
  return Promise.all(answers);
21138
21319
  },
21139
- finish: (outcome, failure3, reason) => {
21320
+ finish: (outcome, failure3, reason, code) => {
21140
21321
  if (finished || finishing) return;
21141
21322
  finishing = true;
21142
21323
  for (const [, approval] of [...this.approvals]) if (approval.runId === runId) approval.resolve("denied");
21143
21324
  for (const [, question] of [...this.questions]) if (question.runId === runId) question.resolve({ selected: [] });
21144
21325
  if (outcome === "failed") emit("failed", failure3 ?? { code: "failed", message: "The run failed." });
21145
- else if (outcome === "interrupted") emit("status.changed", { status: "interrupted", ...reason ? { reason } : {} });
21326
+ else if (outcome === "interrupted") emit("status.changed", { status: "interrupted", ...reason ? { reason } : {}, ...code ? { code } : {} });
21146
21327
  else emit("completed", {});
21147
21328
  finished = true;
21148
21329
  this.runs.delete(runId);
21149
- for (const resolve19 of [...this.waiters.get(runId) ?? []]) resolve19();
21330
+ for (const resolve23 of [...this.waiters.get(runId) ?? []]) resolve23();
21150
21331
  this.waiters.delete(runId);
21151
21332
  for (const [id2, owner] of this.nativeCleared) if (owner === runId) this.nativeCleared.delete(id2);
21152
21333
  try {
@@ -21159,7 +21340,7 @@ var RunRegistry = class {
21159
21340
  return finished;
21160
21341
  },
21161
21342
  interrupt: async () => {
21162
- run2.finish("interrupted");
21343
+ run2.finish("interrupted", void 0, void 0, "user_abort");
21163
21344
  for (const handler of interruptHandlers) {
21164
21345
  try {
21165
21346
  await handler();
@@ -21173,7 +21354,7 @@ var RunRegistry = class {
21173
21354
  emit("started", {});
21174
21355
  emit("status.changed", { status: "running" });
21175
21356
  const record3 = findRun(this.options.root(), runId);
21176
- if (record3?.parentRunId && this.hasStoppedAncestor(record3.parentRunId)) run2.finish("interrupted");
21357
+ if (record3?.parentRunId && this.hasStoppedAncestor(record3.parentRunId)) run2.finish("interrupted", void 0, void 0, "parent_stopped");
21177
21358
  return run2;
21178
21359
  }
21179
21360
  hasStoppedAncestor(runId) {
@@ -21196,7 +21377,7 @@ var RunRegistry = class {
21196
21377
  if (!isActiveStatus(record3.status)) return Promise.resolve();
21197
21378
  if (!this.runs.has(runId)) throw new HarnessError("run_not_active", "This run is not active in this process.");
21198
21379
  if (signal?.aborted) return Promise.reject(new HarnessError("run_not_active", "The wait was interrupted."));
21199
- return new Promise((resolve19, reject2) => {
21380
+ return new Promise((resolve23, reject2) => {
21200
21381
  const cleanup = () => {
21201
21382
  signal?.removeEventListener("abort", abort);
21202
21383
  const pending3 = this.waiters.get(runId);
@@ -21206,7 +21387,7 @@ var RunRegistry = class {
21206
21387
  };
21207
21388
  const done = () => {
21208
21389
  cleanup();
21209
- resolve19();
21390
+ resolve23();
21210
21391
  };
21211
21392
  const abort = () => {
21212
21393
  cleanup();
@@ -21248,7 +21429,7 @@ var RunRegistry = class {
21248
21429
  }
21249
21430
  /** Interrupts every live run, giving each a moment to stop cleanly. */
21250
21431
  async interruptAll(timeoutMs = 3e3) {
21251
- await Promise.race([Promise.allSettled(this.activeRunIds().map((id2) => this.interrupt(id2))), new Promise((resolve19) => setTimeout(resolve19, timeoutMs))]);
21432
+ await Promise.race([Promise.allSettled(this.activeRunIds().map((id2) => this.interrupt(id2))), new Promise((resolve23) => setTimeout(resolve23, timeoutMs))]);
21252
21433
  }
21253
21434
  resolveApproval(runId, approvalId, decision) {
21254
21435
  if (decision !== "approved" && decision !== "denied" && decision !== "native") throw new HarnessError("invalid_request", "Unknown decision.");
@@ -21277,15 +21458,22 @@ var RunRegistry = class {
21277
21458
  }
21278
21459
  validateAnswer(input, answer) {
21279
21460
  const selected = answer.selected;
21280
- const selectedLabels = new Set(selected);
21281
21461
  const notes = {};
21282
21462
  for (const [label, note] of Object.entries(answer.notes ?? {})) {
21283
21463
  const normalized = typeof note === "string" ? note.trim() : "";
21284
21464
  if (normalized) notes[label] = normalized;
21285
21465
  }
21286
21466
  const text11 = answer.text?.trim();
21287
- if (!input.multiSelect && selected.length > 1 || selectedLabels.size !== selected.length || selected.some((label) => !input.options.some((option) => option.label === label)) || text11 && !input.allowText || Object.keys(notes).some((label) => !selectedLabels.has(label)) || Object.values(notes).some((note) => note.length > 1e4)) {
21288
- throw new HarnessError("invalid_answer", "Choose the offered options or an allowed text answer, and attach notes only to selected choices.");
21467
+ const problem = questionAnswerProblem(input, { selected, ...text11 ? { text: text11 } : {}, notes });
21468
+ if (problem) {
21469
+ log("a question answer was refused", {
21470
+ summary: input.summary ?? null,
21471
+ field: problem.field,
21472
+ selected: selected.length,
21473
+ notes: Object.keys(notes).length,
21474
+ textLength: text11?.length ?? 0
21475
+ });
21476
+ throw new HarnessError("invalid_answer", problem.message);
21289
21477
  }
21290
21478
  return { selected, ...text11 ? { text: text11 } : {}, ...Object.keys(notes).length ? { notes } : {} };
21291
21479
  }
@@ -21306,7 +21494,7 @@ var RunRegistry = class {
21306
21494
  // src/sessions/sessions.ts
21307
21495
  import { randomUUID as randomUUID17 } from "node:crypto";
21308
21496
  import { existsSync as existsSync28, mkdirSync as mkdirSync23 } from "node:fs";
21309
- import { isAbsolute as isAbsolute9, join as join63 } from "node:path";
21497
+ import { isAbsolute as isAbsolute9, join as join64 } from "node:path";
21310
21498
  init_errors();
21311
21499
  init_fs();
21312
21500
 
@@ -21372,7 +21560,7 @@ ${text11}`);
21372
21560
  async function waitForChildren(root, runId, signal) {
21373
21561
  while (!signal?.aborted) {
21374
21562
  if (settled2(root, runId)) return true;
21375
- await new Promise((resolve19) => setTimeout(resolve19, 250));
21563
+ await new Promise((resolve23) => setTimeout(resolve23, 250));
21376
21564
  }
21377
21565
  return false;
21378
21566
  }
@@ -21921,11 +22109,11 @@ var MAX_TEXT2 = 4e3;
21921
22109
  var clip5 = (text11) => text11.length > MAX_TEXT2 ? `${text11.slice(0, MAX_TEXT2)}\u2026` : text11;
21922
22110
  function dismissable(answer, fallback, options2) {
21923
22111
  if (!options2?.signal && !options2?.timeout) return answer;
21924
- return new Promise((resolve19) => {
22112
+ return new Promise((resolve23) => {
21925
22113
  let timer;
21926
22114
  const done = (value) => {
21927
22115
  if (timer) clearTimeout(timer);
21928
- resolve19(value);
22116
+ resolve23(value);
21929
22117
  };
21930
22118
  if (options2.signal?.aborted) return done(fallback);
21931
22119
  options2.signal?.addEventListener("abort", () => done(fallback), { once: true });
@@ -22006,6 +22194,217 @@ function parseInitialBashCommand(input) {
22006
22194
  return outside ? { command: input.slice(outside[0].length).trim(), workspaceWriteScope: "outside" } : { command: input, workspaceWriteScope: "workspace" };
22007
22195
  }
22008
22196
 
22197
+ // src/commands/promptLoader.ts
22198
+ import { readdirSync as readdirSync29, statSync as statSync22 } from "node:fs";
22199
+ import { dirname as dirname23, join as join63, resolve as resolve12 } from "node:path";
22200
+
22201
+ // src/commands/promptRoutes.ts
22202
+ import { resolve as resolve11, sep as sep10 } from "node:path";
22203
+
22204
+ // src/plugins/commandPrefix.ts
22205
+ import { basename as basename14, dirname as dirname22, extname as extname3 } from "node:path";
22206
+ var slug3 = (value) => {
22207
+ const normalized = value.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
22208
+ const prefix = /^[a-z]/.test(normalized) ? normalized : `plugin-${normalized || "command"}`;
22209
+ return prefix === "plugin" || prefix === "skill" ? `extension-${prefix}` : prefix;
22210
+ };
22211
+ function pluginCommandPrefix(source) {
22212
+ if (source.origin === "package" && source.baseDir) {
22213
+ const manifest = packageManifest(source.baseDir);
22214
+ return manifest.commandPrefix ?? slug3(manifest.name);
22215
+ }
22216
+ if (source.path.startsWith("builtin:")) return slug3(source.path.slice("builtin:".length));
22217
+ const synthetic = /^<[^:>]+:([^>]+)>$/.exec(source.path);
22218
+ if (synthetic) return slug3(synthetic[1]);
22219
+ const file2 = basename14(source.path, extname3(source.path));
22220
+ return slug3(file2 === "index" ? basename14(dirname22(source.path)) : file2);
22221
+ }
22222
+
22223
+ // src/commands/routes.ts
22224
+ function commandRoutes(commands) {
22225
+ const routes = /* @__PURE__ */ new Map();
22226
+ const byName = /* @__PURE__ */ new Map();
22227
+ const ownersByPrefix = /* @__PURE__ */ new Map();
22228
+ const builtinPrefixes = new Map(BUILTIN_PLUGIN_REGISTRY.commands().map((command) => [command.prefix, command.pluginId]));
22229
+ for (const command of commands) {
22230
+ const owners = ownersByPrefix.get(command.prefix) ?? /* @__PURE__ */ new Map();
22231
+ const paths2 = owners.get(command.owner) ?? /* @__PURE__ */ new Set();
22232
+ paths2.add(command.path);
22233
+ owners.set(command.owner, paths2);
22234
+ ownersByPrefix.set(command.prefix, owners);
22235
+ const route = `${command.prefix}:${command.name}`;
22236
+ routes.set(route, [...routes.get(route) ?? [], command]);
22237
+ byName.set(command.name, [...byName.get(command.name) ?? [], command]);
22238
+ }
22239
+ const conflictingPrefixes = /* @__PURE__ */ new Map();
22240
+ for (const [prefix, owners] of ownersByPrefix) {
22241
+ const builtin = builtinPrefixes.get(prefix);
22242
+ if (owners.size > 1 || builtin || prefix === "plugin" || prefix === "skill") {
22243
+ conflictingPrefixes.set(prefix, [
22244
+ ...builtin ? [`built-in plugin ${builtin}`] : [],
22245
+ ...[...owners.values()].flatMap((paths2) => [...paths2])
22246
+ ]);
22247
+ }
22248
+ }
22249
+ return { routes, byName, ownersByPrefix, conflictingPrefixes };
22250
+ }
22251
+ function promptCommandAvailable(name, taken, extensionCommands = /* @__PURE__ */ new Set()) {
22252
+ return !name.includes(":") && !taken.has(name) && !extensionCommands.has(name);
22253
+ }
22254
+
22255
+ // src/commands/promptRoutes.ts
22256
+ function promptOwners(path4, found) {
22257
+ const target = resolve11(path4);
22258
+ return found.filter((item) => item.kind === "prompt" && item.packageRoot && (target === resolve11(item.path) || target.startsWith(`${resolve11(item.path)}${sep10}`))).sort((a, b) => b.path.length - a.path.length);
22259
+ }
22260
+ function promptOwner(path4, found) {
22261
+ return promptOwners(path4, found)[0];
22262
+ }
22263
+ function promptPackageRoot(prompt, found) {
22264
+ return promptOwner(prompt.filePath, found)?.packageRoot ?? (prompt.sourceInfo.origin === "package" ? prompt.sourceInfo.baseDir ?? null : null);
22265
+ }
22266
+ function namespacePrompts(prompts, found, extensions = []) {
22267
+ const loose = [];
22268
+ const routes = [];
22269
+ for (const prompt of prompts) {
22270
+ const owners = new Set(promptOwners(prompt.filePath, found).map((owner) => owner.packageRoot));
22271
+ if (owners.size > 1) continue;
22272
+ const packageRoot = promptPackageRoot(prompt, found);
22273
+ if (!packageRoot) {
22274
+ if (!prompt.name.includes(":")) loose.push(prompt);
22275
+ continue;
22276
+ }
22277
+ const attribution2 = promptOwner(prompt.filePath, found)?.commandOwner;
22278
+ const prefix = attribution2?.prefix ?? pluginCommandPrefix({ path: prompt.filePath, origin: "package", baseDir: packageRoot });
22279
+ const name = !attribution2 && packageManifest(packageRoot).defaultPrompt === prompt.name ? "" : prompt.name;
22280
+ if (name && !/^[a-z][a-z0-9-]*$/.test(name)) continue;
22281
+ routes.push({ prefix, name, owner: attribution2?.owner ?? `package:${packageRoot}`, path: prompt.filePath, prompt });
22282
+ }
22283
+ const index = commandRoutes([...extensions, ...routes]);
22284
+ const selected = routes.flatMap((route) => {
22285
+ const key = `${route.prefix}:${route.name}`;
22286
+ return !index.conflictingPrefixes.has(route.prefix) && index.routes.get(key)?.length === 1 ? [{ ...route.prompt, name: key }] : [];
22287
+ });
22288
+ return [...loose, ...selected];
22289
+ }
22290
+
22291
+ // src/commands/promptLoader.ts
22292
+ var originalGetters = /* @__PURE__ */ new WeakMap();
22293
+ async function wrapPromptLoader(pi, loader, found, paths2, cwd, agentDir, extensions = () => [], bare = /* @__PURE__ */ new Set()) {
22294
+ const scan = async (paths3) => {
22295
+ const files3 = paths3.flatMap((path4) => {
22296
+ try {
22297
+ return statSync22(path4).isDirectory() ? readdirSync29(path4).filter((name) => name.endsWith(".md") && !name.startsWith(".")).sort().map((name) => join63(path4, name)) : [path4];
22298
+ } catch {
22299
+ return [];
22300
+ }
22301
+ });
22302
+ const templates2 = [];
22303
+ for (const path4 of [...new Set(files3)]) {
22304
+ const scanner = new pi.DefaultResourceLoader({ cwd, agentDir, settingsManager: pi.SettingsManager.inMemory({}), noExtensions: true, noSkills: true, noPromptTemplates: true, noThemes: true, noContextFiles: true, additionalPromptTemplatePaths: [path4] });
22305
+ await scanner.reload();
22306
+ templates2.push(...scanner.getPrompts().prompts);
22307
+ }
22308
+ return templates2;
22309
+ };
22310
+ const templates = await scan(paths2);
22311
+ const discovered = /* @__PURE__ */ new Map();
22312
+ for (const extension of loader.getExtensions().extensions) {
22313
+ if (extension.hidden) continue;
22314
+ const handlers = extension.handlers.get("resources_discover");
22315
+ if (!handlers) continue;
22316
+ const source = extension.sourceInfo;
22317
+ extension.handlers.set("resources_discover", handlers.map((handler, index) => async (event, context) => {
22318
+ const result = await handler(event, context);
22319
+ const key = `${extension.resolvedPath}:${index}`;
22320
+ if (!result) discovered.delete(key);
22321
+ else {
22322
+ const paths3 = (result.promptPaths ?? []).map((path4) => resolve12(dirname23(extension.resolvedPath), path4));
22323
+ const isPackage = source.origin === "package" && !!source.baseDir;
22324
+ const root = isPackage ? source.baseDir : extension.resolvedPath;
22325
+ const commandOwner = isPackage ? void 0 : { prefix: pluginCommandPrefix(source), owner: `extension:${source.path}` };
22326
+ const owners = paths3.map((path4) => ({ kind: "prompt", path: path4, origin: isPackage ? "package" : "shared", packageRoot: root, source: source.source, project: source.scope === "project", enabled: true, ...commandOwner ? { commandOwner } : {} }));
22327
+ discovered.set(key, { owners, prompts: await scan(paths3) });
22328
+ }
22329
+ return result;
22330
+ }));
22331
+ }
22332
+ bare.clear();
22333
+ for (const prompt of templates) if (promptPackageRoot(prompt, found)) bare.add(prompt.name);
22334
+ const getPrompts = originalGetters.get(loader) ?? loader.getPrompts.bind(loader);
22335
+ originalGetters.set(loader, getPrompts);
22336
+ loader.getPrompts = () => {
22337
+ const base = getPrompts();
22338
+ const dynamic = [...discovered.values()];
22339
+ const owners = [...found, ...dynamic.flatMap((item) => item.owners)];
22340
+ const byPath = new Map(base.prompts.map((prompt) => [prompt.filePath, prompt]));
22341
+ for (const prompt of [...templates, ...dynamic.flatMap((item) => item.prompts)]) {
22342
+ if (byPath.has(prompt.filePath)) continue;
22343
+ if (promptPackageRoot(prompt, owners) || ![...byPath.values()].some((item) => item.name === prompt.name && !promptPackageRoot(item, owners))) byPath.set(prompt.filePath, prompt);
22344
+ }
22345
+ bare.clear();
22346
+ for (const prompt of byPath.values()) if (promptPackageRoot(prompt, owners)) bare.add(prompt.name);
22347
+ return { ...base, prompts: namespacePrompts([...byPath.values()], owners, extensions()) };
22348
+ };
22349
+ return bare;
22350
+ }
22351
+
22352
+ // src/commands/extensionOwners.ts
22353
+ import { resolve as resolve13, sep as sep11 } from "node:path";
22354
+ function applyExtensionOwners(loader, found) {
22355
+ for (const extension of loader.getExtensions().extensions) {
22356
+ if (extension.hidden) continue;
22357
+ const path4 = resolve13(extension.resolvedPath || extension.path);
22358
+ const owner = found.filter((item) => item.kind === "extension" && item.packageRoot && (path4 === resolve13(item.path) || path4.startsWith(`${resolve13(item.path)}${sep11}`))).sort((a, b) => b.path.length - a.path.length)[0];
22359
+ if (!owner?.packageRoot) continue;
22360
+ const sourceInfo = { ...extension.sourceInfo, origin: "package", baseDir: owner.packageRoot, source: owner.source ?? owner.packageRoot };
22361
+ extension.sourceInfo = sourceInfo;
22362
+ for (const command of extension.commands.values()) command.sourceInfo = sourceInfo;
22363
+ }
22364
+ }
22365
+
22366
+ // src/commands/runtimeRouter.ts
22367
+ init_errors();
22368
+ import { resolve as resolve14, sep as sep12 } from "node:path";
22369
+ function installPluginCommandRouter(session, found, barePrompts) {
22370
+ const registrations = () => session.extensionRunner.getRegisteredCommands().map((command) => {
22371
+ const source = command.sourceInfo;
22372
+ const path4 = resolve14(source.path);
22373
+ const owner = found.filter((item) => item.kind === "extension" && item.packageRoot && (path4 === resolve14(item.path) || path4.startsWith(`${resolve14(item.path)}${sep12}`))).sort((a, b) => b.path.length - a.path.length)[0];
22374
+ const prefix = pluginCommandPrefix(owner?.packageRoot ? { path: source.path, origin: "package", baseDir: owner.packageRoot } : source);
22375
+ return { ...command, prefix, owner: owner?.packageRoot ? `package:${owner.packageRoot}` : source.origin === "package" ? `package:${source.baseDir ?? source.source}` : `extension:${source.path}`, path: source.path };
22376
+ });
22377
+ const originalPrompt = session.prompt.bind(session);
22378
+ session.prompt = async (text11, options2) => {
22379
+ if (options2?.expandPromptTemplates === false) return originalPrompt(text11, options2);
22380
+ const match = /^\/([^\s]+)(?:\s+([\s\S]*))?$/.exec(text11.trim());
22381
+ if (match) {
22382
+ const name = match[1];
22383
+ const index = commandRoutes(registrations());
22384
+ const promptNames = new Set(session.resourceLoader.getPrompts().prompts.map((prompt) => prompt.name));
22385
+ if (name.startsWith("plugin:") || index.byName.has(name) || barePrompts.has(name) && !promptNames.has(name)) {
22386
+ throw new HarnessError("invalid_request", `Plugin command /${name} requires its owner prefix.`);
22387
+ }
22388
+ const separator = name.indexOf(":");
22389
+ const prefix = separator < 0 ? "" : name.slice(0, separator);
22390
+ if (separator > 0 && index.ownersByPrefix.has(prefix)) {
22391
+ if (index.conflictingPrefixes.has(prefix)) throw new HarnessError("invalid_request", `Plugin prefix /${prefix}: is ambiguous.`);
22392
+ const matches = index.routes.get(name) ?? [];
22393
+ if (matches.length > 1 || matches.length === 1 && promptNames.has(name)) throw new HarnessError("invalid_request", `Plugin command /${name} is ambiguous.`);
22394
+ if (matches.length === 1) {
22395
+ const command = session.extensionRunner.getCommand(matches[0].invocationName);
22396
+ if (!command) throw new HarnessError("invalid_request", `Plugin command /${name} is unavailable.`);
22397
+ await command.handler(match[2]?.trim() ?? "", session.extensionRunner.createCommandContext());
22398
+ return;
22399
+ }
22400
+ if (!promptNames.has(name)) throw new HarnessError("invalid_request", `Unknown plugin command /${name}.`);
22401
+ }
22402
+ }
22403
+ await originalPrompt(text11, options2);
22404
+ };
22405
+ return () => [...registrations().map((command) => `${command.prefix}:${command.name}`), ...session.resourceLoader.getPrompts().prompts.map((prompt) => prompt.name)];
22406
+ }
22407
+
22009
22408
  // src/sessions/runner.ts
22010
22409
  function summarize(input, toolName = "") {
22011
22410
  if (input && typeof input === "object") {
@@ -22141,6 +22540,7 @@ async function runTurn(run2, session, params) {
22141
22540
  let aborted = false;
22142
22541
  const usage = { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, costUsd: 0, requests: 0 };
22143
22542
  let usageReported = false;
22543
+ let lastContextTokens;
22144
22544
  let reasoningTokens;
22145
22545
  let requestStartedAt = null;
22146
22546
  let firstOutputAt = null;
@@ -22266,6 +22666,7 @@ async function runTurn(run2, session, params) {
22266
22666
  ...message4.stopReason ? { stopReason: message4.stopReason } : {}
22267
22667
  };
22268
22668
  requestStartedAt = null;
22669
+ if (reported2) lastContextTokens = (reported2.input ?? 0) + (reported2.cacheRead ?? 0) + (reported2.cacheWrite ?? 0);
22269
22670
  const contextWindow = session.model?.contextWindow;
22270
22671
  const { costUsd, ...tokens } = usage;
22271
22672
  run2.emit("usage.updated", {
@@ -22349,16 +22750,18 @@ async function runTurn(run2, session, params) {
22349
22750
  break;
22350
22751
  }
22351
22752
  case "compaction_start": {
22352
- run2.emit("compaction.started", { reason: stringField(event, "reason", "context limit") });
22753
+ run2.emit("compaction.started", { reason: stringField(event, "reason", "context limit"), ...lastContextTokens === void 0 ? {} : { contextTokens: lastContextTokens }, ...session.model?.contextWindow ? { contextWindow: session.model.contextWindow } : {} });
22353
22754
  break;
22354
22755
  }
22355
22756
  case "compaction_end": {
22356
22757
  const compaction2 = event;
22758
+ const result = compaction2.result && typeof compaction2.result === "object" ? compaction2.result : null;
22357
22759
  const summary4 = evidenceText(compaction2.result);
22358
22760
  const boundedSummary = summary4 ? limitedText(summary4) : null;
22359
22761
  run2.emit("compaction.ended", {
22360
22762
  reason: stringField(compaction2, "reason", "context limit"),
22361
22763
  aborted: compaction2.aborted === true,
22764
+ ...typeof result?.tokensBefore === "number" && Number.isFinite(result.tokensBefore) ? { tokensBefore: result.tokensBefore } : {},
22362
22765
  ...typeof compaction2.willRetry === "boolean" ? { willRetry: compaction2.willRetry } : {},
22363
22766
  ...stringField(compaction2, "errorMessage") ? { error: stringField(compaction2, "errorMessage").slice(0, 1e3) } : {},
22364
22767
  ...boundedSummary ? { summary: boundedSummary.text, ...boundedSummary.truncated ? { summaryTruncated: true } : {} } : {}
@@ -22372,7 +22775,7 @@ async function runTurn(run2, session, params) {
22372
22775
  if (params.command) {
22373
22776
  const notice = await runCommand(session, params.command);
22374
22777
  if (notice) run2.emit("extension.notice", { kind: "notify", level: "info", text: notice });
22375
- if (aborted) run2.finish("interrupted");
22778
+ if (aborted) run2.finish("interrupted", void 0, void 0, "user_abort");
22376
22779
  else run2.finish("completed");
22377
22780
  return;
22378
22781
  }
@@ -22381,8 +22784,7 @@ async function runTurn(run2, session, params) {
22381
22784
  await session.sendCustomMessage({ customType: CONTEXT_MESSAGE_TYPE, content: params.update.content, display: false, details: { [CONTEXT_SNAPSHOT_KEY]: params.update.snapshot } }, { triggerTurn: false });
22382
22785
  }
22383
22786
  const command = /^\/(\S+)/.exec(params.text)?.[1];
22384
- const extensionCommand = command && session.extensionCommands?.includes(command);
22385
- if (params.preamble && extensionCommand) {
22787
+ if (params.preamble && command) {
22386
22788
  if (!session.sendCustomMessage) throw new HarnessError("provider_error", "The Pi session cannot receive the command context.");
22387
22789
  await session.sendCustomMessage({ customType: "xingular-preamble", content: params.preamble, display: false, details: {} }, { triggerTurn: false });
22388
22790
  }
@@ -22409,9 +22811,15 @@ ${result}`;
22409
22811
  ...input.truncated ? { inputTruncated: true } : {}
22410
22812
  });
22411
22813
  let refusal = !command2 ? "Enter a Bash command after !outside." : !params.initialBashAvailable ? "Bash is not available in this run's capability snapshot." : !session.executeBash ? "This Pi session cannot execute Bash directly." : !params.gate ? "This run has no Bash policy gate." : null;
22814
+ if (refusal && !params.initialBashAvailable && command2) params.onRefusal?.(toolCallId, "bash", { code: "capability", reason: refusal });
22412
22815
  if (!refusal) {
22413
22816
  try {
22414
- refusal = await params.gate.check("bash", args);
22817
+ const blocked = params.gate.checkDetailed ? await params.gate.checkDetailed("bash", args) : null;
22818
+ if (!params.gate.checkDetailed) refusal = await params.gate.check("bash", args);
22819
+ if (blocked) {
22820
+ params.onRefusal?.(toolCallId, "bash", blocked);
22821
+ refusal = blocked.reason;
22822
+ }
22415
22823
  } catch (error) {
22416
22824
  refusal = error instanceof Error ? error.message : "The Bash policy could not be checked.";
22417
22825
  }
@@ -22445,7 +22853,7 @@ ${result}`;
22445
22853
  await session.sendCustomMessage({ customType: "xingular-initial-bash", content: initialBashInstruction, display: false, details: {} }, { triggerTurn: false });
22446
22854
  initialBashInstruction = void 0;
22447
22855
  }
22448
- const prompt = params.preamble && !extensionCommand ? `${params.preamble}
22856
+ const prompt = params.preamble && !command ? `${params.preamble}
22449
22857
 
22450
22858
  ${params.text}` : params.text;
22451
22859
  const images = (params.images ?? []).map((image) => ({ type: "image", mimeType: image.mediaType, data: image.data }));
@@ -22475,14 +22883,14 @@ ${initialBashInstruction}` : prompt;
22475
22883
  }
22476
22884
  if (slice?.endedBy && !aborted && !lastError) {
22477
22885
  if (!slice.checkpointWritten) run2.emit("checkpoint", derivedCheckpoint(slice, ledger));
22478
- run2.finish("interrupted", void 0, "slice_complete");
22886
+ run2.finish("interrupted", void 0, "slice_complete", slice.endedBy === "budget" ? "slice_budget" : "compaction_thrash");
22479
22887
  return;
22480
22888
  }
22481
- if (aborted) run2.finish("interrupted");
22889
+ if (aborted) run2.finish("interrupted", void 0, void 0, "user_abort");
22482
22890
  else if (lastError) run2.finish("failed", { code: "provider_error", message: lastError });
22483
22891
  else run2.finish("completed");
22484
22892
  } catch (error) {
22485
- if (aborted) run2.finish("interrupted");
22893
+ if (aborted) run2.finish("interrupted", void 0, void 0, "user_abort");
22486
22894
  else run2.finish("failed", { code: "provider_error", message: error instanceof Error ? error.message : String(error) });
22487
22895
  } finally {
22488
22896
  if (heartbeat) clearInterval(heartbeat);
@@ -22509,6 +22917,7 @@ async function runCommand(session, command) {
22509
22917
  }
22510
22918
  }
22511
22919
  }
22920
+ var promptRouting = /* @__PURE__ */ new WeakMap();
22512
22921
  async function loadResources(pi, params, gate, automaticSettings) {
22513
22922
  const loading = params.automatic ? null : await params.plugins?.loading() ?? null;
22514
22923
  const resourceLoader = new pi.DefaultResourceLoader({
@@ -22530,11 +22939,37 @@ async function loadResources(pi, params, gate, automaticSettings) {
22530
22939
  });
22531
22940
  await resourceLoader.reload();
22532
22941
  if (loading) {
22942
+ applyExtensionOwners(resourceLoader, loading.found);
22943
+ const extensions = () => registeredBy(resourceLoader).flatMap((record3) => {
22944
+ const owner = runtimeOwner(loading, record3.path);
22945
+ const prefix = pluginCommandPrefix(owner?.packageRoot ? { path: record3.path, origin: "package", baseDir: owner.packageRoot } : { path: record3.path });
22946
+ return record3.commands.map((name) => ({ prefix, name, owner: owner?.packageRoot ? `package:${owner.packageRoot}` : `extension:${record3.path}`, path: record3.path }));
22947
+ });
22533
22948
  const records = registeredBy(resourceLoader).map((record3) => ({ ...record3, hash: registrationHash(loading, record3.path) }));
22534
22949
  const publish = () => params.plugins?.loaded(records);
22535
22950
  observeResources(pi, resourceLoader, loading, records, publish);
22951
+ const bare = await wrapPromptLoader(pi, resourceLoader, loading.found, loading.promptPaths, params.cwd, params.account.configDir, extensions);
22952
+ promptRouting.set(resourceLoader, { loading, bare });
22536
22953
  publish();
22537
22954
  }
22955
+ if (loading || params.automatic) {
22956
+ const extensions = resourceLoader.getExtensions().extensions;
22957
+ const ids2 = [];
22958
+ let known = true;
22959
+ for (const extension of extensions) {
22960
+ const path4 = extension.resolvedPath || extension.path;
22961
+ if (extension.hidden) {
22962
+ for (const id2 of [OUTPUT_TRIM, BUDGET_GUARD]) if (path4 === `<inline:${id2}>`) ids2.push(id2);
22963
+ } else if (params.variantExtensions?.some((entry2) => path4 === entry2 || path4.startsWith(`${entry2}/`))) {
22964
+ ids2.push("variant");
22965
+ } else {
22966
+ const owner = loading && runtimeOwner(loading, path4);
22967
+ if (owner) ids2.push(pluginIdOf(owner));
22968
+ else known = false;
22969
+ }
22970
+ }
22971
+ if (known) params.activatedPlugins?.([...new Set(ids2)]);
22972
+ }
22538
22973
  return resourceLoader;
22539
22974
  }
22540
22975
  function registeredBy(loader) {
@@ -22581,11 +23016,20 @@ var piSessionFactory = async ({ params, allow, gate, tools: tools4, runtime: loa
22581
23016
  authorized: false
22582
23017
  };
22583
23018
  } catch (error) {
22584
- return { block: true, reason: error instanceof Error ? error.message : "The active project workspace could not be verified." };
23019
+ const reason = error instanceof Error ? error.message : "The active project workspace could not be verified.";
23020
+ params.onRefusal?.(event.toolCallId, event.toolName, { code: "workspace", reason });
23021
+ return { block: true, reason };
22585
23022
  }
22586
23023
  }
22587
- const reason = await gate.check(event.toolName, event.input);
22588
- if (reason) return { block: true, reason };
23024
+ const refusal = gate.checkDetailed ? await gate.checkDetailed(event.toolName, event.input) : null;
23025
+ if (!gate.checkDetailed) {
23026
+ const reason = await gate.check(event.toolName, event.input);
23027
+ if (reason) return { block: true, reason };
23028
+ }
23029
+ if (refusal) {
23030
+ params.onRefusal?.(event.toolCallId, event.toolName, refusal);
23031
+ return { block: true, reason: refusal.reason };
23032
+ }
22589
23033
  if (authorization) {
22590
23034
  authorization.authorized = true;
22591
23035
  writeAuthorizations.set(event.toolCallId, authorization);
@@ -22638,7 +23082,9 @@ var piSessionFactory = async ({ params, allow, gate, tools: tools4, runtime: loa
22638
23082
  });
22639
23083
  }
22640
23084
  const piSession = session;
22641
- piSession.extensionCommands = loaded.flatMap((extension) => extension.commands);
23085
+ const routing = promptRouting.get(resourceLoader);
23086
+ const routes = installPluginCommandRouter(session, routing?.loading.found ?? [], routing?.bare ?? /* @__PURE__ */ new Set());
23087
+ piSession.extensionCommands = routes();
22642
23088
  supportsImageInput = piSession.model?.input.includes("image") ?? false;
22643
23089
  const streamAgent = piSession.agent;
22644
23090
  const originalStream = streamAgent.streamFunction;
@@ -22736,32 +23182,34 @@ var Sessions = class {
22736
23182
  }
22737
23183
  gate(run2, caller, gated, diagnostic = false, automatic = false) {
22738
23184
  const { policy } = this.options;
22739
- return {
23185
+ const gate = {
23186
+ check: async (name, input) => (await gate.checkDetailed(name, input))?.reason ?? null,
22740
23187
  ask: (names2) => {
22741
23188
  for (const name of names2) gated.add(name);
22742
23189
  },
22743
- check: async (tool, args) => {
23190
+ checkDetailed: async (tool, args) => {
22744
23191
  let record3;
22745
23192
  let inspection;
22746
23193
  try {
22747
23194
  record3 = loadRun2(this.env.root, run2.runId);
22748
23195
  inspection = inspectRunWorkspaceWrite(record3, tool, args);
22749
23196
  } catch (error) {
22750
- return error instanceof Error ? error.message : "The active project workspace could not be verified.";
23197
+ return { code: "workspace", reason: error instanceof Error ? error.message : "The active project workspace could not be verified." };
22751
23198
  }
22752
23199
  const call = { run: record3, caller, tool, args: inspection.input };
22753
23200
  const hostScope = await policy.scope?.(call) ?? { allowed: true };
22754
23201
  const scope = combineWorkspaceScope(hostScope, inspection);
22755
- if (!scope.allowed && (scope.automatic || automatic && inspection.outside || !scope.requiresApproval)) return scope.reason ?? "Outside the approved work scope.";
23202
+ if (!scope.allowed && (scope.automatic || automatic && inspection.outside || !scope.requiresApproval)) return { code: inspection.outside ? "workspace" : "scope", reason: scope.reason ?? "Outside the approved work scope." };
22756
23203
  try {
22757
23204
  await policy.authorize?.(call);
22758
23205
  } catch (error) {
22759
- return error instanceof Error ? error.message : "Tool unavailable in the app settings.";
23206
+ return { code: "policy", reason: error instanceof Error ? error.message : "Tool unavailable in the app settings." };
22760
23207
  }
22761
23208
  if (scope.allowed && !scope.humanOnly && (scope.automatic || !gated.has(tool))) return null;
22762
- if (diagnostic) return "This diagnostic call requires a separate per-call approval.";
22763
- if (call.run.trigger === "probe" || call.run.trigger === "improver") return scope.reason ? `Refused in a refinement run: ${scope.reason}` : "Refused in a refinement run: this call needs a person to approve it.";
22764
- if (call.run.trigger === "reflection") return scope.reason ? `Refused in a reflection run: ${scope.reason}` : "Refused in a reflection run: this call needs a person to approve it.";
23209
+ if (diagnostic) return { code: "unattended", reason: "This diagnostic call requires a separate per-call approval." };
23210
+ if (call.run.trigger === "probe" || call.run.trigger === "improver") return { code: "unattended", reason: scope.reason ? `Refused in a refinement run: ${scope.reason}` : "Refused in a refinement run: this call needs a person to approve it." };
23211
+ if (call.run.trigger === "reflection") return { code: "unattended", reason: scope.reason ? `Refused in a reflection run: ${scope.reason}` : "Refused in a reflection run: this call needs a person to approve it." };
23212
+ if (automatic) return { code: "unattended", reason: scope.reason ?? "Refused in an unattended run: this call needs a person to approve it." };
22765
23213
  const boundaryAction = inspection.outside ? `${inspection.reason} ${summarize(call.args, tool)}` : summarize(call.args);
22766
23214
  const decision = await run2.requestApproval({
22767
23215
  target: tool,
@@ -22770,11 +23218,11 @@ var Sessions = class {
22770
23218
  rememberable: scope.allowed && !scope.humanOnly,
22771
23219
  standing: scope.humanOnly ? scope.allowGlobalApproval ? "switch" : "none" : void 0
22772
23220
  });
22773
- if (decision !== "approved") return "The user denied this action. Ask how they would like to proceed.";
23221
+ if (decision !== "approved") return { code: "approval_denied", reason: "The user denied this action. Do not repeat it or ask for permission in a message; choose another approach or report what is blocked." };
22774
23222
  try {
22775
23223
  await policy.authorize?.(call);
22776
23224
  } catch (error) {
22777
- return error instanceof Error ? error.message : "Tool was disabled while waiting.";
23225
+ return { code: "policy", reason: error instanceof Error ? error.message : "Tool was disabled while waiting." };
22778
23226
  }
22779
23227
  return null;
22780
23228
  },
@@ -22831,6 +23279,7 @@ ${formatBashCommandForApproval(operationArgs.command)}`,
22831
23279
  }
22832
23280
  }
22833
23281
  };
23282
+ return gate;
22834
23283
  }
22835
23284
  /** Compatibility alias for the shared native service. */
22836
23285
  spawnSubagent(request2) {
@@ -22985,6 +23434,7 @@ Attached saved file references:`);
22985
23434
  id: runId,
22986
23435
  projectId: input.run.projectId ?? caller.projectId ?? null,
22987
23436
  mode: input.run.mode ?? "chat",
23437
+ effectiveMode: input.plan ? "plan" : "normal",
22988
23438
  harness: input.run.harness ?? "pi",
22989
23439
  sessionType: input.run.sessionType ?? "structured",
22990
23440
  sessionId: input.run.sessionId,
@@ -22997,6 +23447,7 @@ Attached saved file references:`);
22997
23447
  startedAt: now(),
22998
23448
  endedAt: null,
22999
23449
  status: "queued",
23450
+ loadedSkills: [],
23000
23451
  exitCode: null,
23001
23452
  handoffId: input.run.handoffId ?? null,
23002
23453
  accountId: account.id,
@@ -23010,12 +23461,12 @@ Attached saved file references:`);
23010
23461
  ...input.effort ? { effort: input.effort } : {}
23011
23462
  });
23012
23463
  try {
23013
- writeJson(join63(runDir(root, runId), "caller.json"), caller);
23464
+ writeJson(join64(runDir(root, runId), "caller.json"), caller);
23014
23465
  } catch {
23015
23466
  }
23016
23467
  const fail3 = (error, code = "provider_unavailable") => {
23017
23468
  const message4 = error instanceof Error ? error.message : String(error);
23018
- updateRun(root, runId, (record3) => Object.assign(record3, { status: "failed", endedAt: now(), terminalReason: message4 }));
23469
+ updateRun(root, runId, (record3) => Object.assign(record3, { status: "failed", endedAt: now(), terminalReason: message4, terminalCode: stopCodeOf(code) }));
23019
23470
  log(`session ${runId} did not start: ${message4}`, code);
23020
23471
  };
23021
23472
  const { plugins } = this.options;
@@ -23117,20 +23568,19 @@ Attached saved file references:`);
23117
23568
  for (const tool of tools4) if (tool.planModeSafe) planModeSafety.set(tool.name, tool.planModeSafe);
23118
23569
  const gate = (() => {
23119
23570
  const baseGate = this.gate(run2, caller, new Set((input.tools.gated ?? []).filter((name) => capabilities.tools.includes(name))), false, input.automatic === true);
23120
- return {
23121
- ...baseGate,
23122
- check: (name, args) => {
23123
- if (input.run.relation === "subagent" && !capabilities.tools.includes(name)) return Promise.resolve("This tool is outside the child capability snapshot.");
23124
- const refusal = input.plan ? planRefusal(name, readOnly, args, planModeSafety.get(name)) : null;
23125
- return refusal ? Promise.resolve(refusal) : baseGate.check(name, args);
23126
- }
23571
+ const checkDetailed = (name, args) => {
23572
+ if (input.run.relation === "subagent" && !capabilities.tools.includes(name)) return Promise.resolve({ code: "capability", reason: "This tool is outside the child capability snapshot." });
23573
+ const refusal = input.plan ? planRefusal(name, readOnly, args, planModeSafety.get(name)) : null;
23574
+ return refusal ? Promise.resolve({ code: "plan_mode", reason: refusal }) : baseGate.checkDetailed(name, args);
23127
23575
  };
23576
+ return { ...baseGate, checkDetailed, check: async (name, args) => (await checkDetailed(name, args))?.reason ?? null };
23128
23577
  })();
23129
23578
  const params = {
23130
23579
  runId,
23131
23580
  ...input.initialBashCommand !== void 0 && input.initialBashCommand !== null ? { initialBashCommand: input.initialBashCommand, initialBashAvailable: capabilities.tools.includes("bash") } : {},
23132
23581
  ...input.initialBashContext !== void 0 && input.initialBashContext !== null ? { initialBashContext: input.initialBashContext } : {},
23133
23582
  gate,
23583
+ onRefusal: (toolCallId, name, refusal) => run2.emit("tool.refused", { toolCallId, name, ...refusal, reason: refusal.reason.slice(0, 2e3) }),
23134
23584
  restrictTools: input.run.relation === "subagent",
23135
23585
  ...input.plan ? { plan: true } : {},
23136
23586
  ...stateEligible && input.plan ? { repairPlan: true } : {},
@@ -23154,9 +23604,14 @@ Attached saved file references:`);
23154
23604
  this.options.subagents.extension(runId),
23155
23605
  { name: "image-pruning", factory: imagePruning() },
23156
23606
  { name: COMPACTION_CHECKPOINT_EXTENSION, factory: compactionCheckpoint({ initialTodo: continuation?.todo, planInstructions: checkpointPlanInstructions }) },
23157
- ...sliceEnabled ? [{ name: WORK_SLICE_EXTENSION, factory: workSliceGuard({ policy: slicePolicy, state: sliceState, checkpointAvailable }) }] : []
23607
+ ...sliceEnabled ? [{ name: WORK_SLICE_EXTENSION, factory: workSliceGuard({ policy: slicePolicy, state: sliceState, checkpointAvailable, onRefuse: (event, reason) => {
23608
+ if (typeof event.toolCallId === "string") run2.emit("tool.refused", { toolCallId: event.toolCallId, name: event.toolName, code: "work_slice", reason: reason.slice(0, 2e3) });
23609
+ } }) }] : []
23158
23610
  ],
23159
23611
  ...plugins && loadPlugins ? { plugins: { loading: loadPlugins, loaded: (extensions) => plugins.loaded(extensions) } } : {},
23612
+ activatedPlugins: (ids2) => updateRun(root, runId, (record3) => {
23613
+ record3.activatedPlugins = ids2;
23614
+ }),
23160
23615
  system,
23161
23616
  ...sliceEnabled ? { slice: { state: sliceState, maxToolCalls: slicePolicy.maxToolCalls } } : {},
23162
23617
  // A command sends no request of its own, so the context update waits for the next turn.
@@ -23218,7 +23673,7 @@ Attached saved file references:`);
23218
23673
  // src/sessions/sideQuestions.ts
23219
23674
  import { mkdtemp, rm } from "node:fs/promises";
23220
23675
  import { tmpdir as tmpdir8 } from "node:os";
23221
- import { join as join64 } from "node:path";
23676
+ import { join as join65 } from "node:path";
23222
23677
  import { z as z6 } from "zod";
23223
23678
  init_errors();
23224
23679
  init_fs();
@@ -23419,7 +23874,7 @@ ${note.body}`;
23419
23874
  return `${reference.label ?? reference.path}: this selected reference is unavailable.`;
23420
23875
  }
23421
23876
  });
23422
- const sessionDir = await mkdtemp(join64(tmpdir8(), "xh-side-question-"));
23877
+ const sessionDir = await mkdtemp(join65(tmpdir8(), "xh-side-question-"));
23423
23878
  this.sessionDirs.set(runId, sessionDir);
23424
23879
  const caller = { ...input.caller, runId, sessionId };
23425
23880
  try {
@@ -23493,13 +23948,13 @@ init_fs();
23493
23948
  import { createHash as createHash14, randomBytes as randomBytes3 } from "node:crypto";
23494
23949
  import { existsSync as existsSync30, mkdirSync as mkdirSync25, realpathSync as realpathSync7, rmSync as rmSync23 } from "node:fs";
23495
23950
  import { tmpdir as tmpdir9 } from "node:os";
23496
- import { basename as basename14, dirname as dirname23, isAbsolute as isAbsolute10, join as join66, resolve as resolve11 } from "node:path";
23951
+ import { basename as basename15, dirname as dirname25, isAbsolute as isAbsolute10, join as join67, resolve as resolve15 } from "node:path";
23497
23952
 
23498
23953
  // src/worktrees/copy.ts
23499
23954
  init_errors();
23500
23955
  init_fs();
23501
23956
  import { copyFileSync as copyFileSync5, existsSync as existsSync29, lstatSync as lstatSync14, mkdirSync as mkdirSync24 } from "node:fs";
23502
- import { dirname as dirname22, join as join65 } from "node:path";
23957
+ import { dirname as dirname24, join as join66 } from "node:path";
23503
23958
 
23504
23959
  // src/worktrees/git.ts
23505
23960
  init_errors();
@@ -23507,7 +23962,7 @@ import { spawn as spawn5 } from "node:child_process";
23507
23962
  var QUIET_FILTERS = ["-c", "filter.lfs.smudge=", "-c", "filter.lfs.process=", "-c", "filter.lfs.required=false", "-c", "core.hooksPath=/dev/null"];
23508
23963
  var FILTER_KEYS = /^filter\.(.*)\.(?:clean|smudge|process|required)$/i;
23509
23964
  function configuredFilters(cwd, env) {
23510
- return new Promise((resolve19, reject2) => {
23965
+ return new Promise((resolve23, reject2) => {
23511
23966
  const child = spawn5("git", ["config", "--null", "--name-only", "--get-regexp", "^filter\\..*\\.(clean|smudge|process|required)$"], {
23512
23967
  cwd,
23513
23968
  env,
@@ -23530,7 +23985,7 @@ function configuredFilters(cwd, env) {
23530
23985
  const match = FILTER_KEYS.exec(key);
23531
23986
  if (match) filters.add(match[1]);
23532
23987
  }
23533
- resolve19([...filters].flatMap((filter) => [
23988
+ resolve23([...filters].flatMap((filter) => [
23534
23989
  "-c",
23535
23990
  `filter.${filter}.clean=`,
23536
23991
  "-c",
@@ -23546,7 +24001,7 @@ function configuredFilters(cwd, env) {
23546
24001
  async function git(cwd, args, options2 = {}) {
23547
24002
  const env = { ...process.env, GIT_TERMINAL_PROMPT: "0", GIT_LFS_SKIP_SMUDGE: "1", LC_ALL: "C", ...options2.env };
23548
24003
  const filters = await configuredFilters(cwd, env);
23549
- return new Promise((resolve19, reject2) => {
24004
+ return new Promise((resolve23, reject2) => {
23550
24005
  const input = options2.input;
23551
24006
  const hasInput = input !== void 0 && input.length > 0;
23552
24007
  const child = spawn5("git", [...QUIET_FILTERS, ...filters, ...args], {
@@ -23565,7 +24020,7 @@ async function git(cwd, args, options2 = {}) {
23565
24020
  reject2(new HarnessError("git_failed", `git ${args.join(" ")} failed: ${result.stderr.trim() || result.stdout.trim() || `exit ${result.code}`}`));
23566
24021
  return;
23567
24022
  }
23568
- resolve19(result);
24023
+ resolve23(result);
23569
24024
  });
23570
24025
  if (hasInput && child.stdin) {
23571
24026
  child.stdin.on("error", (error) => {
@@ -23598,8 +24053,8 @@ function copyFiles(from, to, paths2) {
23598
24053
  } catch {
23599
24054
  continue;
23600
24055
  }
23601
- const source = join65(from, relative10);
23602
- const target = join65(to, relative10);
24056
+ const source = join66(from, relative10);
24057
+ const target = join66(to, relative10);
23603
24058
  if (existsSync29(target)) {
23604
24059
  report.skipped.push({ path: relative10, reason: "exists" });
23605
24060
  continue;
@@ -23626,7 +24081,7 @@ function copyFiles(from, to, paths2) {
23626
24081
  report.skipped.push({ path: relative10, reason: "over_total" });
23627
24082
  continue;
23628
24083
  }
23629
- mkdirSync24(dirname22(target), { recursive: true });
24084
+ mkdirSync24(dirname24(target), { recursive: true });
23630
24085
  copyFileSync5(source, target);
23631
24086
  total += stat.size;
23632
24087
  report.copied.push(relative10);
@@ -23652,7 +24107,7 @@ async function ignoredFiles(checkout) {
23652
24107
  if (pathBytes > IGNORED_PATH_BYTES_CAP) throw ignoredFilesError("the ignored paths exceed the snapshot limit");
23653
24108
  let stat;
23654
24109
  try {
23655
- stat = lstatSync14(join65(checkout, relative10));
24110
+ stat = lstatSync14(join66(checkout, relative10));
23656
24111
  } catch {
23657
24112
  throw ignoredFilesError(`could not inspect ${relative10}`);
23658
24113
  }
@@ -23667,7 +24122,7 @@ function ignoredFilesError(reason) {
23667
24122
  return new HarnessError("worktree_has_work", `Ignored files cannot be safely preserved: ${reason}.`, "Move or remove those files, then try again.");
23668
24123
  }
23669
24124
  async function includedFiles(checkout) {
23670
- const patterns2 = join65(checkout, INCLUDE_FILE);
24125
+ const patterns2 = join66(checkout, INCLUDE_FILE);
23671
24126
  if (!existsSync29(patterns2)) return [];
23672
24127
  const ignored = new Set(nulList((await git(checkout, ["ls-files", "--others", "--ignored", "--exclude-standard", "-z"])).stdout));
23673
24128
  const matching = nulList((await git(checkout, ["ls-files", "--others", "--ignored", `--exclude-from=${patterns2}`, "-z"])).stdout);
@@ -23691,10 +24146,10 @@ function real(path4) {
23691
24146
  try {
23692
24147
  return realpathSync7(path4);
23693
24148
  } catch {
23694
- return resolve11(path4);
24149
+ return resolve15(path4);
23695
24150
  }
23696
24151
  }
23697
- function slug3(text11) {
24152
+ function slug4(text11) {
23698
24153
  return text11.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "repo";
23699
24154
  }
23700
24155
  function statusPaths(output) {
@@ -23716,11 +24171,11 @@ var Worktrees = class {
23716
24171
  leaseCounts = /* @__PURE__ */ new Map();
23717
24172
  chain = Promise.resolve();
23718
24173
  constructor(stateDir, options2 = {}) {
23719
- this.root = options2.root ?? join66(stateDir, "worktrees");
24174
+ this.root = options2.root ?? join67(stateDir, "worktrees");
23720
24175
  this.branchPrefix = options2.branchPrefix ?? "wt/";
23721
24176
  this.keep = options2.keep ?? 15;
23722
- this.registryFile = join66(stateDir, "worktrees.json");
23723
- this.trust = new SetupTrust(join66(stateDir, "worktree-setup-trust.json"));
24177
+ this.registryFile = join67(stateDir, "worktrees.json");
24178
+ this.trust = new SetupTrust(join67(stateDir, "worktree-setup-trust.json"));
23724
24179
  this.recover(true);
23725
24180
  }
23726
24181
  // ---- Registry -------------------------------------------------------------------------------
@@ -23785,7 +24240,7 @@ var Worktrees = class {
23785
24240
  const top = await git(folder2, ["rev-parse", "--show-toplevel"], { allowFail: true });
23786
24241
  if (top.code !== 0) throw new HarnessError("not_a_repository", `${folder2} is not inside a git repository.`, "Worktrees need a git repository with at least one commit.");
23787
24242
  const gitDir = await gitText(folder2, ["rev-parse", "--absolute-git-dir"]);
23788
- const common = resolve11(top.stdout.trim(), await gitText(folder2, ["rev-parse", "--git-common-dir"]));
24243
+ const common = resolve15(top.stdout.trim(), await gitText(folder2, ["rev-parse", "--git-common-dir"]));
23789
24244
  if (real(gitDir) === real(common)) return real(top.stdout.trim());
23790
24245
  const main2 = (await gitText(folder2, ["worktree", "list", "--porcelain"])).split("\n").find((line2) => line2.startsWith("worktree "));
23791
24246
  return real(main2 ? main2.slice("worktree ".length) : top.stdout.trim());
@@ -23808,19 +24263,19 @@ var Worktrees = class {
23808
24263
  }
23809
24264
  // ---- Create and adopt -----------------------------------------------------------------------
23810
24265
  repoKey(repoRoot) {
23811
- return `${slug3(basename14(repoRoot))}-${createHash14("sha256").update(repoRoot).digest("hex").slice(0, 8)}`;
24266
+ return `${slug4(basename15(repoRoot))}-${createHash14("sha256").update(repoRoot).digest("hex").slice(0, 8)}`;
23812
24267
  }
23813
24268
  uniqueName(repoRoot, wanted) {
23814
24269
  const taken = new Set(this.read().worktrees.filter((record3) => record3.repoRoot === repoRoot && !record3.removed).map((record3) => record3.name));
23815
- const folder2 = join66(this.root, this.repoKey(repoRoot));
24270
+ const folder2 = join67(this.root, this.repoKey(repoRoot));
23816
24271
  if (wanted !== void 0) {
23817
24272
  if (!NAME2.test(wanted)) throw invalid("A worktree name uses lowercase letters, digits and dashes (at most 63).");
23818
- if (taken.has(wanted) || existsSync30(join66(folder2, wanted))) throw new HarnessError("worktree_exists", `A worktree named ${wanted} already exists.`);
24273
+ if (taken.has(wanted) || existsSync30(join67(folder2, wanted))) throw new HarnessError("worktree_exists", `A worktree named ${wanted} already exists.`);
23819
24274
  return wanted;
23820
24275
  }
23821
24276
  for (; ; ) {
23822
24277
  const name = `wt-${randomBytes3(3).toString("hex")}`;
23823
- if (!taken.has(name) && !existsSync30(join66(folder2, name))) return name;
24278
+ if (!taken.has(name) && !existsSync30(join67(folder2, name))) return name;
23824
24279
  }
23825
24280
  }
23826
24281
  async branchName(repoRoot, name) {
@@ -23842,8 +24297,8 @@ var Worktrees = class {
23842
24297
  const baseCommit = (await git(repoRoot, ["rev-parse", "--verify", "-q", `${baseRef}^{commit}`], { allowFail: true })).stdout.trim();
23843
24298
  if (!baseCommit) throw invalid(`${baseRef} does not name a commit.`);
23844
24299
  const branch = input.branch ? await this.branchName(repoRoot, input.branch) : null;
23845
- const path4 = join66(this.root, this.repoKey(repoRoot), name);
23846
- mkdirSync25(dirname23(path4), { recursive: true });
24300
+ const path4 = join67(this.root, this.repoKey(repoRoot), name);
24301
+ mkdirSync25(dirname25(path4), { recursive: true });
23847
24302
  await git(repoRoot, ["worktree", "add", ...branch ? ["-b", branch] : ["--detach"], path4, baseCommit]);
23848
24303
  const at = now();
23849
24304
  const record3 = {
@@ -23872,7 +24327,7 @@ var Worktrees = class {
23872
24327
  carried = copyFiles(repoRoot, path4, await untrackedFiles(repoRoot));
23873
24328
  }
23874
24329
  const included = copyFiles(repoRoot, path4, await includedFiles(repoRoot));
23875
- const setup = input.setup === false ? { state: "none", hash: null, log: null } : await runSetup({ checkout: path4, source: repoRoot, name, repoRoot, logFile: join66(this.root, "logs", `${record3.id}.setup.log`), trust: this.trust });
24330
+ const setup = input.setup === false ? { state: "none", hash: null, log: null } : await runSetup({ checkout: path4, source: repoRoot, name, repoRoot, logFile: join67(this.root, "logs", `${record3.id}.setup.log`), trust: this.trust });
23876
24331
  return { worktree: record3, carried: { ...carried, patched }, included, setup };
23877
24332
  });
23878
24333
  }
@@ -23889,7 +24344,7 @@ var Worktrees = class {
23889
24344
  const branch = (await git(folder2, ["symbolic-ref", "-q", "--short", "HEAD"], { allowFail: true })).stdout.trim() || null;
23890
24345
  const head2 = await gitText(folder2, ["rev-parse", "HEAD"]);
23891
24346
  const taken = new Set(this.read().worktrees.filter((record4) => record4.repoRoot === repoRoot && !record4.removed).map((record4) => record4.name));
23892
- let name = slug3(basename14(folder2)).slice(0, 56) || "adopted";
24347
+ let name = slug4(basename15(folder2)).slice(0, 56) || "adopted";
23893
24348
  if (!/^[a-z0-9]/.test(name)) name = `w-${name}`;
23894
24349
  for (let suffix = 2; taken.has(name); suffix += 1) name = `${name.replace(/-\d+$/, "")}-${suffix}`;
23895
24350
  const at = now();
@@ -23979,7 +24434,7 @@ var Worktrees = class {
23979
24434
  }
23980
24435
  /** Records the worktree's commits and working changes under a snapshot ref. */
23981
24436
  async snapshot(record3, ignored) {
23982
- const index = join66(tmpdir9(), `xharness-index-${record3.id}-${randomBytes3(4).toString("hex")}`);
24437
+ const index = join67(tmpdir9(), `xharness-index-${record3.id}-${randomBytes3(4).toString("hex")}`);
23983
24438
  const env = { ...IDENTITY, GIT_INDEX_FILE: index };
23984
24439
  try {
23985
24440
  await git(record3.path, ["read-tree", "HEAD"], { env });
@@ -24036,7 +24491,7 @@ var Worktrees = class {
24036
24491
  const { snapshot: snapshot2, head: head2 } = record3.removed;
24037
24492
  const start = snapshot2 ? await gitText(record3.repoRoot, ["rev-parse", `${snapshot2}^`]) : head2;
24038
24493
  const onBranch = record3.branch && (await git(record3.repoRoot, ["rev-parse", "--verify", "-q", `refs/heads/${record3.branch}`], { allowFail: true })).stdout.trim() === start;
24039
- mkdirSync25(dirname23(record3.path), { recursive: true });
24494
+ mkdirSync25(dirname25(record3.path), { recursive: true });
24040
24495
  await git(record3.repoRoot, ["worktree", "add", ...onBranch ? [record3.path, record3.branch] : ["--detach", record3.path, start]]);
24041
24496
  if (snapshot2) {
24042
24497
  await git(record3.path, ["read-tree", "-u", "--reset", snapshot2]);
@@ -24094,7 +24549,7 @@ var Worktrees = class {
24094
24549
  if (dirty) throw new HarnessError("handoff_target_dirty", `The ${direction === "toSource" ? "main checkout" : "worktree"} has uncommitted changes.`, "Commit or stash them, then hand off again.");
24095
24550
  const patch = (await git(origin3, ["diff", "--binary", direction === "toSource" ? record3.baseCommit : "HEAD"])).stdout;
24096
24551
  const untracked = await untrackedFiles(origin3);
24097
- const collisions = untracked.filter((path4) => existsSync30(join66(target, path4)));
24552
+ const collisions = untracked.filter((path4) => existsSync30(join67(target, path4)));
24098
24553
  if (collisions.length) throw new HarnessError("handoff_conflict", `${collisions.length} untracked file(s) already exist in the target, for example ${collisions[0]}.`);
24099
24554
  if (patch.trim()) {
24100
24555
  const check2 = await git(target, ["apply", "--check", "--binary", "--whitespace=nowarn"], { input: patch, allowFail: true });
@@ -25246,16 +25701,16 @@ var UsageLimits = class {
25246
25701
  // src/annotations/capture.ts
25247
25702
  import { spawn as spawn6 } from "node:child_process";
25248
25703
  import { randomUUID as randomUUID18 } from "node:crypto";
25249
- import { existsSync as existsSync32, readdirSync as readdirSync29, statSync as statSync22 } from "node:fs";
25704
+ import { existsSync as existsSync32, readdirSync as readdirSync30, statSync as statSync23 } from "node:fs";
25250
25705
  import { createServer as createServer5 } from "node:net";
25251
- import { basename as basename15, join as join68 } from "node:path";
25706
+ import { basename as basename16, join as join69 } from "node:path";
25252
25707
 
25253
25708
  // src/annotations/bridge.ts
25254
25709
  init_fs();
25255
25710
  import { randomBytes as randomBytes4 } from "node:crypto";
25256
25711
  import { chmodSync as chmodSync14, existsSync as existsSync31 } from "node:fs";
25257
25712
  import { createServer as createServer4 } from "node:http";
25258
- import { join as join67 } from "node:path";
25713
+ import { join as join68 } from "node:path";
25259
25714
  var POLL_MS = 2e4;
25260
25715
  var GONE_MS = 45e3;
25261
25716
  var CALL_MS = 1e4;
@@ -25308,7 +25763,7 @@ var NotesBridge = class {
25308
25763
  nextWindow = 1;
25309
25764
  nextCall = 1;
25310
25765
  settingsFile() {
25311
- return join67(this.options.stateDir, "notes-bridge.json");
25766
+ return join68(this.options.stateDir, "notes-bridge.json");
25312
25767
  }
25313
25768
  /** Starts the bridge (once) on the port and token kept from last time, so the app's snippet stays valid. */
25314
25769
  start() {
@@ -25329,13 +25784,13 @@ var NotesBridge = class {
25329
25784
  const token = typeof saved?.token === "string" && /^[a-f0-9]{48}$/.test(saved.token) ? saved.token : randomBytes4(24).toString("hex");
25330
25785
  const wanted = this.options.port ?? (Number.isSafeInteger(saved?.port) ? saved.port : 0);
25331
25786
  const server = createServer4((request2, response) => void this.handle(request2, response, token));
25332
- const port = await new Promise((resolve19, reject2) => {
25787
+ const port = await new Promise((resolve23, reject2) => {
25333
25788
  const attempt2 = (port2, retry) => {
25334
25789
  server.once("error", (error) => {
25335
25790
  if (retry && error.code === "EADDRINUSE") attempt2(0, false);
25336
25791
  else reject2(error);
25337
25792
  });
25338
- server.listen(port2, "127.0.0.1", () => resolve19(server.address().port));
25793
+ server.listen(port2, "127.0.0.1", () => resolve23(server.address().port));
25339
25794
  };
25340
25795
  attempt2(wanted, wanted !== 0);
25341
25796
  });
@@ -25361,12 +25816,12 @@ var NotesBridge = class {
25361
25816
  this.sweep();
25362
25817
  const live = this.windows.get(windowId);
25363
25818
  if (!live) return Promise.reject(new Error("That app window is no longer connected."));
25364
- return new Promise((resolve19, reject2) => {
25819
+ return new Promise((resolve23, reject2) => {
25365
25820
  const id2 = String(this.nextCall++);
25366
25821
  const pending2 = {
25367
25822
  id: id2,
25368
25823
  operation,
25369
- resolve: resolve19,
25824
+ resolve: resolve23,
25370
25825
  reject: reject2,
25371
25826
  timer: setTimeout(() => {
25372
25827
  live.sent.delete(id2);
@@ -25487,7 +25942,7 @@ var NotesBridge = class {
25487
25942
  this.server = null;
25488
25943
  this.info = null;
25489
25944
  if (server) {
25490
- const closed = new Promise((resolve19) => server.close(() => resolve19()));
25945
+ const closed = new Promise((resolve23) => server.close(() => resolve23()));
25491
25946
  server.closeAllConnections();
25492
25947
  await closed;
25493
25948
  }
@@ -25555,24 +26010,24 @@ var spawnApp = (command, args) => {
25555
26010
  child.on("error", () => void 0);
25556
26011
  child.unref();
25557
26012
  };
25558
- var freePort = () => new Promise((resolve19, reject2) => {
26013
+ var freePort = () => new Promise((resolve23, reject2) => {
25559
26014
  const server = createServer5();
25560
26015
  server.unref();
25561
26016
  server.on("error", reject2);
25562
26017
  server.listen(0, "127.0.0.1", () => {
25563
26018
  const address = server.address();
25564
- server.close(() => typeof address === "object" && address ? resolve19(address.port) : reject2(new Error("No free port.")));
26019
+ server.close(() => typeof address === "object" && address ? resolve23(address.port) : reject2(new Error("No free port.")));
25565
26020
  });
25566
26021
  });
25567
26022
  function appExecutable(path4) {
25568
26023
  if (!/\.app\/?$/.test(path4)) return path4;
25569
- const folder2 = join68(path4, "Contents", "MacOS");
26024
+ const folder2 = join69(path4, "Contents", "MacOS");
25570
26025
  if (!existsSync32(folder2)) throw new Error(`${path4} has no Contents/MacOS folder.`);
25571
- const programs = readdirSync29(folder2).filter((name) => !name.startsWith(".") && statSync22(join68(folder2, name)).isFile());
25572
- const named2 = basename15(path4.replace(/\/$/, ""), ".app");
26026
+ const programs = readdirSync30(folder2).filter((name) => !name.startsWith(".") && statSync23(join69(folder2, name)).isFile());
26027
+ const named2 = basename16(path4.replace(/\/$/, ""), ".app");
25573
26028
  const program = programs.find((name) => name === named2) ?? programs[0];
25574
26029
  if (!program) throw new Error(`${path4} has no program in Contents/MacOS.`);
25575
- return join68(folder2, program);
26030
+ return join69(folder2, program);
25576
26031
  }
25577
26032
  function describeApp(port, version2, list7) {
25578
26033
  const agent = typeof version2["User-Agent"] === "string" ? version2["User-Agent"] : "";
@@ -25704,8 +26159,8 @@ var AnnotationCapture = class {
25704
26159
  if (existing instanceof WebTarget) return existing;
25705
26160
  const session = new BrowserSession({
25706
26161
  key: `notes-${key}`,
25707
- profileDir: join68(this.options.stateDir, "browser", "profiles", "person"),
25708
- downloadsDir: join68(this.options.stateDir, "browser", "downloads", fileName(`notes-${key}`)),
26162
+ profileDir: join69(this.options.stateDir, "browser", "profiles", "person"),
26163
+ downloadsDir: join69(this.options.stateDir, "browser", "downloads", fileName(`notes-${key}`)),
25709
26164
  // Always a window the person can see and select in.
25710
26165
  config: () => ({ ...readBrowserConfig(this.options.stateDir), headed: true }),
25711
26166
  network: this.network,
@@ -25790,10 +26245,10 @@ var AnnotationCapture = class {
25790
26245
  const deadline = Date.now() + (this.options.launchTimeoutMs ?? 2e4);
25791
26246
  for (; ; ) {
25792
26247
  if (await probe(`${endpoint}/json/version`, PROBE_MS)) break;
25793
- if (Date.now() > deadline) throw new Error(`${basename15(path4)} did not open a DevTools port. Only Chromium and Electron apps can be inspected this way.`);
25794
- await new Promise((resolve19) => setTimeout(resolve19, 250));
26248
+ if (Date.now() > deadline) throw new Error(`${basename16(path4)} did not open a DevTools port. Only Chromium and Electron apps can be inspected this way.`);
26249
+ await new Promise((resolve23) => setTimeout(resolve23, 250));
25795
26250
  }
25796
- const name = basename15(path4.trim().replace(/\/$/, "")).replace(/\.app$/, "");
26251
+ const name = basename16(path4.trim().replace(/\/$/, "")).replace(/\.app$/, "");
25797
26252
  const attached2 = await this.attach(key, { kind: "cdp", endpoint, name });
25798
26253
  return { kind: "cdp", app: attached2.app, windows: attached2.windows, endpoint };
25799
26254
  }
@@ -25936,8 +26391,8 @@ function compose(notes, prompt) {
25936
26391
 
25937
26392
  // src/annotations/delivery.ts
25938
26393
  var RETRY_DELAYS = [250, 1e3, 3e3, 8e3];
25939
- var wait = (ms) => new Promise((resolve19) => {
25940
- const timer = setTimeout(resolve19, ms);
26394
+ var wait = (ms) => new Promise((resolve23) => {
26395
+ const timer = setTimeout(resolve23, ms);
25941
26396
  timer.unref?.();
25942
26397
  });
25943
26398
  var NoteDeliveries = class {
@@ -26140,7 +26595,7 @@ var Harness = class {
26140
26595
  this.browserEnabled = options2.browser !== false;
26141
26596
  this.stateDir = options2.stateDir;
26142
26597
  mkdirSync26(this.stateDir, { recursive: true });
26143
- isolateProcess(join69(this.stateDir, "accounts"));
26598
+ isolateProcess(join70(this.stateDir, "accounts"));
26144
26599
  this.env = new Env(options2.dataRoot || null, options2.directory ?? EMPTY_DIRECTORY, options2.productName ?? DEFAULT_PRODUCT_NAME, this.bus);
26145
26600
  this.hub = new Hub(this, options2.hub);
26146
26601
  this.policy = options2.policy ?? {};
@@ -26148,8 +26603,8 @@ var Harness = class {
26148
26603
  configureBuiltins(options2.builtins ?? []);
26149
26604
  this.trust = new VariantTrust(this.stateDir);
26150
26605
  this.refineHost = options2.refineHost ?? null;
26151
- const secrets = options2.secrets ?? fileSecretStore(join69(this.stateDir, "secrets.json"));
26152
- this.accounts = new Accounts({ dir: join69(this.stateDir, "accounts"), secrets, bus: this.bus });
26606
+ const secrets = options2.secrets ?? fileSecretStore(join70(this.stateDir, "secrets.json"));
26607
+ this.accounts = new Accounts({ dir: join70(this.stateDir, "accounts"), secrets, bus: this.bus });
26153
26608
  this.approvals = new Approvals(this.stateDir, this.bus);
26154
26609
  this.usageLimits = new UsageLimits({ accounts: this.accounts, root: () => this.env.dataRoot });
26155
26610
  this.accounts.observeModels = (models, accountId) => this.usageLimits.observeModels(models, accountId);
@@ -26164,7 +26619,7 @@ var Harness = class {
26164
26619
  ended: (runId, sessionId) => this.apps.revokeRun(runId, sessionId)
26165
26620
  });
26166
26621
  this.apps = new ConnectedApps({ env: this.env, stateDir: this.stateDir, secrets, bus: this.bus, runs: this.runs, policy: this.policy, oauthClients: options2.oauthClients });
26167
- this.plugins = new Plugins({ stateDir: this.stateDir, bus: this.bus, resolver: options2.resolver, attachments: () => this.env.dataRoot ? load3(this.env.dataRoot) : null, packagesDir: () => this.env.dataRoot ? join69(this.env.dataRoot, "plugins") : null });
26622
+ this.plugins = new Plugins({ stateDir: this.stateDir, bus: this.bus, resolver: options2.resolver, attachments: () => this.env.dataRoot ? load3(this.env.dataRoot) : null, packagesDir: () => this.env.dataRoot ? join70(this.env.dataRoot, "plugins") : null });
26168
26623
  this.hooks = new Hooks({
26169
26624
  stateDir: this.stateDir,
26170
26625
  bus: this.bus,
@@ -26172,9 +26627,9 @@ var Harness = class {
26172
26627
  // The shared folder stands in for an account: packages are the same for all of them.
26173
26628
  wanted: async () => {
26174
26629
  if (!this.env.dataRoot) return [];
26175
- const shared = ensureSharedDir(join69(this.stateDir, "accounts"));
26176
- const named2 = readJsonOr(join69(shared, "settings.json"), {}).packages;
26177
- if (!packageFolders(join69(this.env.dataRoot, "plugins")).length && !(Array.isArray(named2) && named2.length)) return [];
26630
+ const shared = ensureSharedDir(join70(this.stateDir, "accounts"));
26631
+ const named2 = readJsonOr(join70(shared, "settings.json"), {}).packages;
26632
+ if (!packageFolders(join70(this.env.dataRoot, "plugins")).length && !(Array.isArray(named2) && named2.length)) return [];
26178
26633
  return (await this.plugins.loading({ cwd: this.stateDir, agentDir: shared, caller: {} })).hooks;
26179
26634
  },
26180
26635
  call: (connection, tool, args) => this.apps.callUnattended(connection, tool, args ?? {}),
@@ -26259,7 +26714,7 @@ var Harness = class {
26259
26714
  const root = dataRoot;
26260
26715
  this.hooks.watch(wikiDir(root));
26261
26716
  void this.hooks.reload();
26262
- for (const folder2 of ["registry/skills", "registry/prompts", "registry/rules", "config", "runs"]) mkdirSync26(join69(root, folder2), { recursive: true });
26717
+ for (const folder2 of ["registry/skills", "registry/prompts", "registry/rules", "config", "runs"]) mkdirSync26(join70(root, folder2), { recursive: true });
26263
26718
  let migrationError = null;
26264
26719
  try {
26265
26720
  const moved = migrateVariantLayout(root);
@@ -26268,7 +26723,7 @@ var Harness = class {
26268
26723
  migrationError = error instanceof Error ? error.message : String(error);
26269
26724
  log("variant layout", migrationError);
26270
26725
  }
26271
- mkdirSync26(join69(root, "variants"), { recursive: true });
26726
+ mkdirSync26(join70(root, "variants"), { recursive: true });
26272
26727
  let legacyWiki = null;
26273
26728
  try {
26274
26729
  legacyWiki = moveLegacy(root);
@@ -26365,7 +26820,7 @@ function createHarness(options2) {
26365
26820
 
26366
26821
  // src/plugins/cli.ts
26367
26822
  import { homedir as homedir6 } from "node:os";
26368
- import { join as join70, resolve as resolve12 } from "node:path";
26823
+ import { join as join71, resolve as resolve16 } from "node:path";
26369
26824
  import { createInterface } from "node:readline/promises";
26370
26825
  var PLUGINS_USAGE = `Usage: xagent plugins <command> [options]
26371
26826
 
@@ -26431,10 +26886,10 @@ async function runPluginsCli(argv, dependencies = {}) {
26431
26886
  return command ? 0 : 2;
26432
26887
  }
26433
26888
  const base = dependencies.cwd ?? process.cwd();
26434
- const root = join70(dependencies.home ?? homedir6(), ".xingular-harness");
26435
- const options2 = { dataRoot: parsed.values.data ? resolve12(base, parsed.values.data) : join70(root, "data"), stateDir: parsed.values.state ? resolve12(base, parsed.values.state) : join70(root, "state"), productName: DEFAULT_PRODUCT_NAME };
26889
+ const root = join71(dependencies.home ?? homedir6(), ".xingular-harness");
26890
+ const options2 = { dataRoot: parsed.values.data ? resolve16(base, parsed.values.data) : join71(root, "data"), stateDir: parsed.values.state ? resolve16(base, parsed.values.state) : join71(root, "state"), productName: DEFAULT_PRODUCT_NAME };
26436
26891
  const harness = dependencies.createHarness ? dependencies.createHarness(options2) : new Harness(options2);
26437
- const scope = { cwd: parsed.values.cwd ? resolve12(base, parsed.values.cwd) : null };
26892
+ const scope = { cwd: parsed.values.cwd ? resolve16(base, parsed.values.cwd) : null };
26438
26893
  const need = (what) => {
26439
26894
  if (!target) throw new Error(`${command} needs ${what}.`);
26440
26895
  return target;
@@ -26545,7 +27000,7 @@ function terminalAsk() {
26545
27000
 
26546
27001
  // src/hub/cli.ts
26547
27002
  import { homedir as homedir7 } from "node:os";
26548
- import { join as join71, resolve as resolve13 } from "node:path";
27003
+ import { join as join72, resolve as resolve17 } from "node:path";
26549
27004
  var HUB_USAGE = `Usage: xagent hub <list | show | install> [id@version] [options]
26550
27005
 
26551
27006
  list [--refresh] Browse the curated catalog and locally installed versions
@@ -26571,7 +27026,7 @@ async function runHubCli(argv, dependencies = {}) {
26571
27026
  `);
26572
27027
  return 2;
26573
27028
  }
26574
- values2[arg.slice(2)] = resolve13(value);
27029
+ values2[arg.slice(2)] = resolve17(value);
26575
27030
  } else if (arg.startsWith("--")) {
26576
27031
  write4(`Unknown option ${arg}.
26577
27032
  `);
@@ -26587,8 +27042,8 @@ async function runHubCli(argv, dependencies = {}) {
26587
27042
  write4(HUB_USAGE);
26588
27043
  return 2;
26589
27044
  }
26590
- const root = join71(dependencies.home ?? homedir7(), ".xingular-harness");
26591
- const options2 = { dataRoot: values2.data ?? join71(root, "data"), stateDir: values2.state ?? join71(root, "state") };
27045
+ const root = join72(dependencies.home ?? homedir7(), ".xingular-harness");
27046
+ const options2 = { dataRoot: values2.data ?? join72(root, "data"), stateDir: values2.state ?? join72(root, "state") };
26592
27047
  const harness = dependencies.createHarness?.(options2) ?? new Harness(options2);
26593
27048
  try {
26594
27049
  const overview2 = await harness.hub.list(refresh);
@@ -26634,13 +27089,13 @@ Delivery: ${JSON.stringify(delivery)}
26634
27089
  init_fs();
26635
27090
  import { mkdtempSync as mkdtempSync10, readFileSync as readFileSync35 } from "node:fs";
26636
27091
  import { homedir as homedir8, tmpdir as tmpdir11 } from "node:os";
26637
- import { basename as basename16, join as join74, resolve as resolve14 } from "node:path";
27092
+ import { basename as basename17, join as join75, resolve as resolve18 } from "node:path";
26638
27093
 
26639
27094
  // src/refine/demo.ts
26640
27095
  import { spawnSync as spawnSync3 } from "node:child_process";
26641
- import { cpSync as cpSync5, existsSync as existsSync33, mkdirSync as mkdirSync27, mkdtempSync as mkdtempSync9, readdirSync as readdirSync30, readFileSync as readFileSync33, writeFileSync as writeFileSync12 } from "node:fs";
27096
+ import { cpSync as cpSync5, existsSync as existsSync33, mkdirSync as mkdirSync27, mkdtempSync as mkdtempSync9, readdirSync as readdirSync31, readFileSync as readFileSync33, writeFileSync as writeFileSync12 } from "node:fs";
26642
27097
  import { tmpdir as tmpdir10 } from "node:os";
26643
- import { join as join72 } from "node:path";
27098
+ import { join as join73 } from "node:path";
26644
27099
  var DEMO_SCRIPT = `import { readFileSync } from 'node:fs'
26645
27100
 
26646
27101
  const rows = readFileSync(process.argv[2], 'utf8').trim().split('\\n').slice(1)
@@ -26663,11 +27118,11 @@ var DEMO_LEDGERS = {
26663
27118
  };
26664
27119
  function demoTasks(root, scope = "demo") {
26665
27120
  return Object.entries(DEMO_LEDGERS).map(([id2, amounts]) => {
26666
- const sourceDir = join72(root, "sources", id2);
27121
+ const sourceDir = join73(root, "sources", id2);
26667
27122
  mkdirSync27(sourceDir, { recursive: true });
26668
- writeFileSync12(join72(sourceDir, "ledger.csv"), `amount
27123
+ writeFileSync12(join73(sourceDir, "ledger.csv"), `amount
26669
27124
  ${amounts}`);
26670
- writeFileSync12(join72(sourceDir, "reference_answer.txt"), "never shown to a model");
27125
+ writeFileSync12(join73(sourceDir, "reference_answer.txt"), "never shown to a model");
26671
27126
  return {
26672
27127
  id: id2,
26673
27128
  scope,
@@ -26687,24 +27142,24 @@ var DemoExecutor = class {
26687
27142
  executions = 0;
26688
27143
  async execute(task, variantDir, label, runId) {
26689
27144
  this.executions++;
26690
- const dir2 = join72(this.root, "executions", runId);
27145
+ const dir2 = join73(this.root, "executions", runId);
26691
27146
  mkdirSync27(dir2, { recursive: true });
26692
- const script = join72(variantDir ?? "", "scripts", "summarize.mjs");
26693
- const result = spawnSync3(process.execPath, [script, join72(task.sourceDir ?? "", "ledger.csv")], { encoding: "utf8", timeout: 1e4 });
27147
+ const script = join73(variantDir ?? "", "scripts", "summarize.mjs");
27148
+ const result = spawnSync3(process.execPath, [script, join73(task.sourceDir ?? "", "ledger.csv")], { encoding: "utf8", timeout: 1e4 });
26694
27149
  if (result.status !== 0) {
26695
27150
  return { label, runId, dir: dir2, ok: false, delivered: false, costUsd: 0, wallMs: 0, error: result.stderr || "script failed" };
26696
27151
  }
26697
- writeFileSync12(join72(dir2, "answer.txt"), result.stdout);
26698
- writeFileSync12(join72(dir2, "trajectory.md"), `[ACTION] Run scripts/summarize.mjs on ledger.csv
27152
+ writeFileSync12(join73(dir2, "answer.txt"), result.stdout);
27153
+ writeFileSync12(join73(dir2, "trajectory.md"), `[ACTION] Run scripts/summarize.mjs on ledger.csv
26699
27154
  [OBSERVATION]
26700
27155
  ${result.stdout}`);
26701
27156
  return { label, runId, dir: dir2, ok: true, delivered: true, costUsd: 0, wallMs: 0, error: "" };
26702
27157
  }
26703
27158
  trajectory(rollout) {
26704
- return readFileSync33(join72(rollout.dir, "trajectory.md"), "utf8");
27159
+ return readFileSync33(join73(rollout.dir, "trajectory.md"), "utf8");
26705
27160
  }
26706
27161
  deliverableFiles(rollout) {
26707
- const path4 = join72(rollout.dir, "answer.txt");
27162
+ const path4 = join73(rollout.dir, "answer.txt");
26708
27163
  return existsSync33(path4) ? { "answer.txt": readFileSync33(path4, "utf8") } : {};
26709
27164
  }
26710
27165
  validateVariant(_task, variant) {
@@ -26723,15 +27178,15 @@ var DemoImprover = class {
26723
27178
  let workspace = null;
26724
27179
  let text11;
26725
27180
  if (input.label.startsWith("select.")) {
26726
- const expected = `Total: ${signedTotal(readFileSync33(join72(context, "source", "ledger.csv"), "utf8"))}`;
26727
- const ids2 = readdirSync30(join72(context, "candidates")).filter((name) => name.endsWith(".md")).sort();
26728
- const chosen = ids2.find((name) => readFileSync33(join72(context, "candidates", name), "utf8").includes(expected))?.replace(/\.md$/, "") ?? ids2[0].replace(/\.md$/, "");
27181
+ const expected = `Total: ${signedTotal(readFileSync33(join73(context, "source", "ledger.csv"), "utf8"))}`;
27182
+ const ids2 = readdirSync31(join73(context, "candidates")).filter((name) => name.endsWith(".md")).sort();
27183
+ const chosen = ids2.find((name) => readFileSync33(join73(context, "candidates", name), "utf8").includes(expected))?.replace(/\.md$/, "") ?? ids2[0].replace(/\.md$/, "");
26729
27184
  text11 = `<why>The signed total matches the source ledger.</why>
26730
27185
  \`\`\`choice
26731
27186
  ${chosen}
26732
27187
  \`\`\``;
26733
27188
  } else if (input.label.startsWith("experience.")) {
26734
- const changed2 = readFileSync33(join72(context, "harness", "scripts", "summarize.mjs"), "utf8") !== readFileSync33(join72(context, "base_harness", "scripts", "summarize.mjs"), "utf8");
27189
+ const changed2 = readFileSync33(join73(context, "harness", "scripts", "summarize.mjs"), "utf8") !== readFileSync33(join73(context, "base_harness", "scripts", "summarize.mjs"), "utf8");
26735
27190
  text11 = `<edits>
26736
27191
  component: scripts/summarize.mjs
26737
27192
  candidates: inv1
@@ -26742,9 +27197,9 @@ verdict: ${changed2 ? "helped" : "no observed effect"}
26742
27197
 
26743
27198
  ` + (changed2 ? "<experience>\ncue: When a ledger includes credits and debits\nlesson: Include both signs when summing in scripts/summarize.mjs.\nevidence: The scripts/summarize.mjs row -- removing the positive-only filter changed the probe total to include the credits omitted by the base trajectory.\nconfidence: high for this observed arithmetic contrast\n</experience>" : "NO_ENTRY");
26744
27199
  } else {
26745
- workspace = mkdtempSync9(join72(tmpdir10(), "harness-demo-"));
26746
- cpSync5(join72(context, "harness"), workspace, { recursive: true });
26747
- const script = join72(workspace, "scripts", "summarize.mjs");
27200
+ workspace = mkdtempSync9(join73(tmpdir10(), "harness-demo-"));
27201
+ cpSync5(join73(context, "harness"), workspace, { recursive: true });
27202
+ const script = join73(workspace, "scripts", "summarize.mjs");
26748
27203
  writeFileSync12(script, readFileSync33(script, "utf8").replace(".filter((amount) => amount > 0)", ""));
26749
27204
  text11 = "<notes>Keep signed amounts: the base total omitted credits in the source ledger.</notes>";
26750
27205
  }
@@ -26756,10 +27211,10 @@ verdict: ${changed2 ? "helped" : "no observed effect"}
26756
27211
  init_fs();
26757
27212
  import { createHash as createHash15 } from "node:crypto";
26758
27213
  import { existsSync as existsSync34, readFileSync as readFileSync34 } from "node:fs";
26759
- import { dirname as dirname24, join as join73 } from "node:path";
27214
+ import { dirname as dirname26, join as join74 } from "node:path";
26760
27215
  import { fileURLToPath as fileURLToPath3 } from "node:url";
26761
27216
  function matrixDir(root, id2) {
26762
- return join73(root, "refine", safeId(id2));
27217
+ return join74(root, "refine", safeId(id2));
26763
27218
  }
26764
27219
  var packageVersionMemo = null;
26765
27220
  function packageVersion() {
@@ -26768,8 +27223,8 @@ function packageVersion() {
26768
27223
  try {
26769
27224
  let dir2 = fileURLToPath3(import.meta.url);
26770
27225
  for (let up = 0; up < 6; up++) {
26771
- dir2 = dirname24(dir2);
26772
- const manifest = join73(dir2, "package.json");
27226
+ dir2 = dirname26(dir2);
27227
+ const manifest = join74(dir2, "package.json");
26773
27228
  if (existsSync34(manifest)) {
26774
27229
  const json2 = JSON.parse(readFileSync34(manifest, "utf8"));
26775
27230
  if (json2.version) packageVersionMemo = json2.version;
@@ -26797,7 +27252,7 @@ function stage(input) {
26797
27252
  return input.versions.map((entry2) => {
26798
27253
  try {
26799
27254
  assertVariantHasNoGraderVerdicts(entry2.variant);
26800
- return { label: entry2.label, variant: entry2.variant, fingerprint: fingerprint(entry2.variant), dir: writeVariant(PI_SURFACE, entry2.variant, join73(matrixDir(input.root, input.id), "variants", entry2.label)), error: "" };
27255
+ return { label: entry2.label, variant: entry2.variant, fingerprint: fingerprint(entry2.variant), dir: writeVariant(PI_SURFACE, entry2.variant, join74(matrixDir(input.root, input.id), "variants", entry2.label)), error: "" };
26801
27256
  } catch (error) {
26802
27257
  return { label: entry2.label, variant: entry2.variant, fingerprint: fingerprint(entry2.variant), dir: "", error: error instanceof Error ? error.message : String(error) };
26803
27258
  }
@@ -26824,10 +27279,10 @@ async function runMatrix(input) {
26824
27279
  };
26825
27280
  const save3 = () => {
26826
27281
  summary4.usd = Math.round(summary4.rows.reduce((sum, row) => sum + row.costUsd, 0) * 1e6) / 1e6;
26827
- writeJson(join73(matrixDir(root, id2), "matrix.json"), summary4);
27282
+ writeJson(join74(matrixDir(root, id2), "matrix.json"), summary4);
26828
27283
  emit({ type: "summary", summary: structuredClone(summary4) });
26829
27284
  };
26830
- writeJson(join73(matrixDir(root, id2), "suite.json"), { id: id2, versions: staged.map(({ label, fingerprint: fingerprint2 }) => ({ label, fingerprint: fingerprint2 })), tasks });
27285
+ writeJson(join74(matrixDir(root, id2), "suite.json"), { id: id2, versions: staged.map(({ label, fingerprint: fingerprint2 }) => ({ label, fingerprint: fingerprint2 })), tasks });
26831
27286
  save3();
26832
27287
  const pairs = staged.flatMap((version2) => tasks.map((task) => ({ version: version2, task })));
26833
27288
  await mapLimit(pairs, input.rolloutWidth ?? 4, async ({ version: version2, task }) => {
@@ -26851,7 +27306,7 @@ async function runMatrix(input) {
26851
27306
  try {
26852
27307
  rollout = await executor.execute(task, version2.dir, runId, runId);
26853
27308
  } catch (error) {
26854
- rollout = { label: runId, runId, dir: join73(matrixDir(root, id2), "missing", version2.label, task.id), ok: false, delivered: false, costUsd: 0, wallMs: 0, error: error instanceof Error ? error.message : String(error) };
27309
+ rollout = { label: runId, runId, dir: join74(matrixDir(root, id2), "missing", version2.label, task.id), ok: false, delivered: false, costUsd: 0, wallMs: 0, error: error instanceof Error ? error.message : String(error) };
26855
27310
  }
26856
27311
  emit({ type: "execution.finished", version: version2.label, task: task.id, ok: rollout.ok, delivered: rollout.delivered, error: rollout.error });
26857
27312
  row = rowOf(version2, task, runId, executorName, rollout);
@@ -26953,7 +27408,7 @@ async function runRefineCli(argv, io = { write: (text11) => void process.stdout.
26953
27408
  const { command, values: values2 } = parse3(argv);
26954
27409
  try {
26955
27410
  if (command === "demo") {
26956
- const root = values2.out ? resolve14(values2.out) : mkdtempSync10(join74(tmpdir11(), "harness-refine-demo-"));
27411
+ const root = values2.out ? resolve18(values2.out) : mkdtempSync10(join75(tmpdir11(), "harness-refine-demo-"));
26957
27412
  const k = Number(values2.k ?? 1);
26958
27413
  seed(root, "demo", demoBaseVariant(), "demo base harness");
26959
27414
  const executor = new DemoExecutor(root);
@@ -26963,7 +27418,7 @@ async function runRefineCli(argv, io = { write: (text11) => void process.stdout.
26963
27418
  describe3(summary4, io.write);
26964
27419
  }
26965
27420
  io.write(`library: ${new Library(root, "demo", "accounting").entries().join(", ") || "(empty)"}
26966
- records: ${join74(root, "refine")}
27421
+ records: ${join75(root, "refine")}
26967
27422
  `);
26968
27423
  return 0;
26969
27424
  }
@@ -26972,15 +27427,15 @@ records: ${join74(root, "refine")}
26972
27427
  io.write(REFINE_USAGE);
26973
27428
  return 2;
26974
27429
  }
26975
- const base = join74(homedir8(), ".xingular-harness");
26976
- const harness = createHarness({ dataRoot: resolve14(values2.data ?? join74(base, "data")), stateDir: resolve14(values2.state ?? join74(base, "state")) });
27430
+ const base = join75(homedir8(), ".xingular-harness");
27431
+ const harness = createHarness({ dataRoot: resolve18(values2.data ?? join75(base, "data")), stateDir: resolve18(values2.state ?? join75(base, "state")) });
26977
27432
  try {
26978
- const scope = values2.scope ?? basename16(resolve14(values2.variant)).replace(/[^A-Za-z0-9_-]/g, "-");
26979
- const variant = readVariant(PI_SURFACE, resolve14(values2.variant));
27433
+ const scope = values2.scope ?? basename17(resolve18(values2.variant)).replace(/[^A-Za-z0-9_-]/g, "-");
27434
+ const variant = readVariant(PI_SURFACE, resolve18(values2.variant));
26980
27435
  commit(harness.env.root, scope, variant, { kind: "import", note: `from ${values2.variant}` });
26981
- const prompt = readFileSync35(resolve14(values2.task), "utf8");
27436
+ const prompt = readFileSync35(resolve18(values2.task), "utf8");
26982
27437
  const { id: id2 } = harness.refine.start({
26983
- task: { id: basename16(values2.task).replace(/\.md$/, "").replace(/[^A-Za-z0-9_-]/g, "-"), prompt, scope, sourceDir: values2.source ? resolve14(values2.source) : null },
27438
+ task: { id: basename17(values2.task).replace(/\.md$/, "").replace(/[^A-Za-z0-9_-]/g, "-"), prompt, scope, sourceDir: values2.source ? resolve18(values2.source) : null },
26984
27439
  k: Number(values2.k ?? 1),
26985
27440
  mode: mode(values2.mode),
26986
27441
  improver: values2.model ? { model: values2.model } : {},
@@ -27004,7 +27459,7 @@ records: ${join74(root, "refine")}
27004
27459
  }
27005
27460
  }
27006
27461
  if (command === "matrix") {
27007
- const root = resolve14(values2.data ?? join74(homedir8(), ".xingular-harness", "data"));
27462
+ const root = resolve18(values2.data ?? join75(homedir8(), ".xingular-harness", "data"));
27008
27463
  let versions2 = [];
27009
27464
  let tasks;
27010
27465
  if (values2.demo) {
@@ -27016,10 +27471,10 @@ records: ${join74(root, "refine")}
27016
27471
  io.write(REFINE_USAGE);
27017
27472
  return 2;
27018
27473
  }
27019
- const suite = JSON.parse(readFileSync35(resolve14(values2.suite), "utf8"));
27474
+ const suite = JSON.parse(readFileSync35(resolve18(values2.suite), "utf8"));
27020
27475
  if (suite.executor && suite.executor !== "demo") throw new Error(`Executor '${suite.executor}' needs a model; this command runs without one. Use 'demo'.`);
27021
27476
  for (const entry2 of suite.versions) {
27022
- if (entry2.dir) versions2.push({ label: entry2.label, variant: readVariant(PI_SURFACE, resolve14(entry2.dir)) });
27477
+ if (entry2.dir) versions2.push({ label: entry2.label, variant: readVariant(PI_SURFACE, resolve18(entry2.dir)) });
27023
27478
  else if (entry2.scope) {
27024
27479
  const fingerprint2 = entry2.version ?? head(root, entry2.scope)?.fingerprint;
27025
27480
  if (!fingerprint2) throw new Error(`The harness '${entry2.scope}' has no head version to evaluate.`);
@@ -27030,7 +27485,7 @@ records: ${join74(root, "refine")}
27030
27485
  }
27031
27486
  const executor = new DemoExecutor(root);
27032
27487
  const summary4 = await runMatrix({ root, id: values2.id ?? "matrix", versions: versions2, tasks, executor, rolloutWidth: values2.width ? Number(values2.width) : void 0 });
27033
- if (values2.out) writeJson(resolve14(values2.out), summary4);
27488
+ if (values2.out) writeJson(resolve18(values2.out), summary4);
27034
27489
  describeMatrix(summary4, io.write);
27035
27490
  return summary4.status === "ok" || summary4.status === "partial" ? 0 : 1;
27036
27491
  }
@@ -27045,7 +27500,7 @@ records: ${join74(root, "refine")}
27045
27500
 
27046
27501
  // src/world/cli.ts
27047
27502
  import { homedir as homedir9 } from "node:os";
27048
- import { join as join75, resolve as resolve15 } from "node:path";
27503
+ import { join as join76, resolve as resolve19 } from "node:path";
27049
27504
  var WEB_USAGE = `Usage: xagent web <status | key <provider> [--remove] | keyless on|off> [--state <dir>] [--data <dir>]
27050
27505
 
27051
27506
  status Which search services are ready
@@ -27071,11 +27526,11 @@ var BROWSER_USAGE = `Usage: xagent browser [status | set <options>] [--state <di
27071
27526
  `;
27072
27527
  function stateOf2(argv, home) {
27073
27528
  const args = [];
27074
- let stateDir = join75(home, ".xingular-harness", "state");
27075
- let dataRoot = join75(home, ".xingular-harness", "data");
27529
+ let stateDir = join76(home, ".xingular-harness", "state");
27530
+ let dataRoot = join76(home, ".xingular-harness", "data");
27076
27531
  for (let index = 0; index < argv.length; index += 1) {
27077
- if (argv[index] === "--state") stateDir = resolve15(argv[++index] ?? stateDir);
27078
- else if (argv[index] === "--data") dataRoot = resolve15(argv[++index] ?? dataRoot);
27532
+ if (argv[index] === "--state") stateDir = resolve19(argv[++index] ?? stateDir);
27533
+ else if (argv[index] === "--data") dataRoot = resolve19(argv[++index] ?? dataRoot);
27079
27534
  else args.push(argv[index]);
27080
27535
  }
27081
27536
  return { args, stateDir, dataRoot };
@@ -27090,7 +27545,7 @@ async function readStdin2() {
27090
27545
  async function runWebCli(argv, dependencies = {}) {
27091
27546
  const write4 = dependencies.write ?? ((text11) => void process.stdout.write(text11));
27092
27547
  const { args, stateDir, dataRoot } = stateOf2(argv, dependencies.home ?? homedir9());
27093
- const secrets = dependencies.secrets ?? fileSecretStore(join75(stateDir, "secrets.json"));
27548
+ const secrets = dependencies.secrets ?? fileSecretStore(join76(stateDir, "secrets.json"));
27094
27549
  const [command, name] = args;
27095
27550
  if (!command || command === "help") {
27096
27551
  write4(WEB_USAGE);
@@ -27324,7 +27779,7 @@ var RpcPeer = class {
27324
27779
  /** Asks the host to perform a named operation. */
27325
27780
  callHost(call, params, timeoutMs = 3e4) {
27326
27781
  const id2 = `s${++this.nextCallId}`;
27327
- return new Promise((resolve19, reject2) => {
27782
+ return new Promise((resolve23, reject2) => {
27328
27783
  const timer = setTimeout(() => {
27329
27784
  this.pendingCalls.delete(id2);
27330
27785
  reject2(new HarnessError("host_timeout", `The host did not answer ${call}.`));
@@ -27332,7 +27787,7 @@ var RpcPeer = class {
27332
27787
  this.pendingCalls.set(id2, {
27333
27788
  resolve: (value) => {
27334
27789
  clearTimeout(timer);
27335
- resolve19(value);
27790
+ resolve23(value);
27336
27791
  },
27337
27792
  reject: (error) => {
27338
27793
  clearTimeout(timer);
@@ -27508,25 +27963,6 @@ function ensureDir(dir2) {
27508
27963
  return dir2;
27509
27964
  }
27510
27965
 
27511
- // src/plugins/commandPrefix.ts
27512
- import { basename as basename17, dirname as dirname25, extname as extname3 } from "node:path";
27513
- var slug4 = (value) => {
27514
- const normalized = value.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
27515
- const prefix = /^[a-z]/.test(normalized) ? normalized : `plugin-${normalized || "command"}`;
27516
- return prefix === "plugin" || prefix === "skill" ? `extension-${prefix}` : prefix;
27517
- };
27518
- function pluginCommandPrefix(source) {
27519
- if (source.origin === "package" && source.baseDir) {
27520
- const manifest = packageManifest(source.baseDir);
27521
- return manifest.commandPrefix ?? slug4(manifest.name);
27522
- }
27523
- if (source.path.startsWith("builtin:")) return slug4(source.path.slice("builtin:".length));
27524
- const synthetic = /^<[^:>]+:([^>]+)>$/.exec(source.path);
27525
- if (synthetic) return slug4(synthetic[1]);
27526
- const file2 = basename17(source.path, extname3(source.path));
27527
- return slug4(file2 === "index" ? basename17(dirname25(source.path)) : file2);
27528
- }
27529
-
27530
27966
  // src/commands/registry.ts
27531
27967
  var CORE_COMMANDS = [
27532
27968
  { name: "help", description: "List the commands", group: "Core", kind: "local" },
@@ -27571,38 +28007,6 @@ var CORE_COMMANDS = [
27571
28007
  ];
27572
28008
  var ALIASES = new Map(CORE_COMMANDS.flatMap((command) => ("aliases" in command ? command.aliases : []).map((alias) => [alias, command.name])));
27573
28009
 
27574
- // src/commands/routes.ts
27575
- function commandRoutes(commands) {
27576
- const routes = /* @__PURE__ */ new Map();
27577
- const byName = /* @__PURE__ */ new Map();
27578
- const ownersByPrefix = /* @__PURE__ */ new Map();
27579
- const builtinPrefixes = new Map(BUILTIN_PLUGIN_REGISTRY.commands().map((command) => [command.prefix, command.pluginId]));
27580
- for (const command of commands) {
27581
- const owners = ownersByPrefix.get(command.prefix) ?? /* @__PURE__ */ new Map();
27582
- const paths2 = owners.get(command.owner) ?? /* @__PURE__ */ new Set();
27583
- paths2.add(command.path);
27584
- owners.set(command.owner, paths2);
27585
- ownersByPrefix.set(command.prefix, owners);
27586
- const route = `${command.prefix}:${command.name}`;
27587
- routes.set(route, [...routes.get(route) ?? [], command]);
27588
- byName.set(command.name, [...byName.get(command.name) ?? [], command]);
27589
- }
27590
- const conflictingPrefixes = /* @__PURE__ */ new Map();
27591
- for (const [prefix, owners] of ownersByPrefix) {
27592
- const builtin = builtinPrefixes.get(prefix);
27593
- if (owners.size > 1 || builtin || prefix === "plugin" || prefix === "skill") {
27594
- conflictingPrefixes.set(prefix, [
27595
- ...builtin ? [`built-in plugin ${builtin}`] : [],
27596
- ...[...owners.values()].flatMap((paths2) => [...paths2])
27597
- ]);
27598
- }
27599
- }
27600
- return { routes, byName, ownersByPrefix, conflictingPrefixes };
27601
- }
27602
- function promptCommandAvailable(name, taken, extensionCommands = /* @__PURE__ */ new Set()) {
27603
- return !name.includes(":") && !taken.has(name) && !extensionCommands.has(name);
27604
- }
27605
-
27606
28010
  // src/commands/list.ts
27607
28011
  function listCommands(plugins) {
27608
28012
  const out = /* @__PURE__ */ new Map();
@@ -27656,8 +28060,24 @@ function listCommands(plugins) {
27656
28060
  });
27657
28061
  }
27658
28062
  }
27659
- if (contribution2.kind === "command" && contribution2.path && promptCommandAvailable(contribution2.name, taken, extensionCommandNames) && !out.has(contribution2.name)) {
27660
- out.set(contribution2.name, { name: contribution2.name, description: contribution2.description, group: "Prompts", source: "prompt", owner: plugin.id });
28063
+ if (contribution2.kind === "command" && contribution2.path) {
28064
+ const packageRoot = plugin.source === "package" || plugin.id.startsWith("package:") ? plugin.path : null;
28065
+ const producer = plugin.contributions.find((item) => item.kind === "code" && item.state === "on" && item.path && item.registered?.resources?.some((resource) => resource.kind === "command" && resource.path === contribution2.path));
28066
+ if (packageRoot || producer?.path) {
28067
+ const prefix = pluginCommandPrefix(packageRoot ? { path: contribution2.path, origin: "package", baseDir: packageRoot } : { path: producer.path });
28068
+ const name = packageRoot && packageManifest(packageRoot).defaultPrompt === contribution2.name ? "" : contribution2.name;
28069
+ if (/^[a-z][a-z0-9-]*$/.test(name) || name === "") {
28070
+ extensions.push({
28071
+ prefix,
28072
+ name,
28073
+ owner: packageRoot ? `package:${packageRoot}` : `extension:${producer.path}`,
28074
+ path: contribution2.path,
28075
+ listed: { name: `${prefix}:${name}`, description: contribution2.description, group: "Prompts", source: "prompt", owner: plugin.id }
28076
+ });
28077
+ }
28078
+ } else if (promptCommandAvailable(contribution2.name, taken, extensionCommandNames) && !out.has(contribution2.name)) {
28079
+ out.set(contribution2.name, { name: contribution2.name, description: contribution2.description, group: "Prompts", source: "prompt", owner: plugin.id });
28080
+ }
27661
28081
  }
27662
28082
  if (contribution2.kind === "skill") {
27663
28083
  const name = `skill:${contribution2.name}`;
@@ -27673,8 +28093,8 @@ function listCommands(plugins) {
27673
28093
  }
27674
28094
 
27675
28095
  // src/variant/seed.ts
27676
- import { existsSync as existsSync36, readdirSync as readdirSync31, readFileSync as readFileSync36, statSync as statSync23 } from "node:fs";
27677
- import { join as join76 } from "node:path";
28096
+ import { existsSync as existsSync36, readdirSync as readdirSync32, readFileSync as readFileSync36, statSync as statSync24 } from "node:fs";
28097
+ import { join as join77 } from "node:path";
27678
28098
  function body(text11) {
27679
28099
  return (parse(text11)?.body ?? text11).trim();
27680
28100
  }
@@ -27687,15 +28107,15 @@ function read4(path4) {
27687
28107
  }
27688
28108
  function folders2(dir2) {
27689
28109
  try {
27690
- return readdirSync31(dir2).filter((name) => !name.startsWith(".")).sort();
28110
+ return readdirSync32(dir2).filter((name) => !name.startsWith(".")).sort();
27691
28111
  } catch {
27692
28112
  return [];
27693
28113
  }
27694
28114
  }
27695
28115
  function blocksDir(root) {
27696
- if (existsSync36(join76(root, "wiki", "blocks"))) return join76(root, "wiki", "blocks");
27697
- const moved = folders2(join76(root, "legacy")).filter((name) => name.startsWith("wiki-")).reverse();
27698
- for (const name of moved) if (existsSync36(join76(root, "legacy", name, "blocks"))) return join76(root, "legacy", name, "blocks");
28116
+ if (existsSync36(join77(root, "wiki", "blocks"))) return join77(root, "wiki", "blocks");
28117
+ const moved = folders2(join77(root, "legacy")).filter((name) => name.startsWith("wiki-")).reverse();
28118
+ for (const name of moved) if (existsSync36(join77(root, "legacy", name, "blocks"))) return join77(root, "legacy", name, "blocks");
27699
28119
  return null;
27700
28120
  }
27701
28121
  function skillName2(stem) {
@@ -27706,20 +28126,20 @@ function legacyPieces(root, input) {
27706
28126
  const scope = { agentId: input.agentId ?? null, projectId: input.projectId ?? null };
27707
28127
  const on = (id2) => attached(choices, [id2], scope, false);
27708
28128
  const rules = [];
27709
- const rulesDir = join76(root, "registry", "rules");
28129
+ const rulesDir = join77(root, "registry", "rules");
27710
28130
  for (const name of folders2(rulesDir)) {
27711
28131
  const stem = name.replace(/\.md$/, "");
27712
- const path4 = name.endsWith(".md") ? join76(rulesDir, name) : join76(rulesDir, name, "SKILL.md");
28132
+ const path4 = name.endsWith(".md") ? join77(rulesDir, name) : join77(rulesDir, name, "SKILL.md");
27713
28133
  const text11 = read4(path4);
27714
28134
  if (text11 && on(`rules/${stem}`)) rules.push(body(text11));
27715
28135
  }
27716
28136
  const skills = {};
27717
- const skillsDir = join76(root, "registry", "skills");
28137
+ const skillsDir = join77(root, "registry", "skills");
27718
28138
  for (const name of folders2(skillsDir)) {
27719
28139
  const stem = name.replace(/\.md$/, "");
27720
- let path4 = join76(skillsDir, name);
28140
+ let path4 = join77(skillsDir, name);
27721
28141
  try {
27722
- if (statSync23(path4).isDirectory()) path4 = join76(path4, "SKILL.md");
28142
+ if (statSync24(path4).isDirectory()) path4 = join77(path4, "SKILL.md");
27723
28143
  } catch {
27724
28144
  continue;
27725
28145
  }
@@ -27738,13 +28158,13 @@ ${body(text11)}
27738
28158
  const memory = [];
27739
28159
  const blocks = blocksDir(root);
27740
28160
  if (blocks) {
27741
- const user = read4(join76(blocks, "shared", "user.md"));
28161
+ const user = read4(join77(blocks, "shared", "user.md"));
27742
28162
  if (user && body(user)) memory.push(`## About the person
27743
28163
 
27744
28164
  ${body(user)}`);
27745
28165
  if (input.agentId && /^[A-Za-z0-9_-]+$/.test(input.agentId)) {
27746
28166
  for (const [file2, title2] of [["persona.md", "Persona"], ["notes.md", "Notes"]]) {
27747
- const text11 = read4(join76(blocks, "agents", input.agentId, file2));
28167
+ const text11 = read4(join77(blocks, "agents", input.agentId, file2));
27748
28168
  if (text11 && body(text11)) memory.push(`## ${title2}
27749
28169
 
27750
28170
  ${body(text11)}`);
@@ -27942,6 +28362,7 @@ function methods(harness) {
27942
28362
  return params.render ? { pack, text: render(env, pack) } : pack;
27943
28363
  },
27944
28364
  "context.runPack": (params) => load2(root(), text7(params, "runId")),
28365
+ "context.runDetails": (params) => runDetails(root(), text7(params, "runId")),
27945
28366
  // What the request was made of: one row per contributor, reconciled to the provider's figure.
27946
28367
  "context.attribution": (params) => attribution(env, text7(params, "runId")),
27947
28368
  // ---- Plugins: everything that shapes a session, and the trust a working folder's code needs ---
@@ -28079,12 +28500,12 @@ import { spawn as nodeSpawn } from "node:child_process";
28079
28500
  import { createHash as createHash16, randomUUID as randomUUID19 } from "node:crypto";
28080
28501
  import { appendFileSync as appendFileSync2 } from "node:fs";
28081
28502
  import { homedir as homedir11 } from "node:os";
28082
- import { isAbsolute as isAbsolute13, join as join78, resolve as resolve16 } from "node:path";
28503
+ import { isAbsolute as isAbsolute13, join as join79, resolve as resolve20 } from "node:path";
28083
28504
 
28084
28505
  // src/core/interactiveProfile.ts
28085
- import { closeSync as closeSync7, fsyncSync as fsyncSync3, linkSync as linkSync2, mkdirSync as mkdirSync29, openSync as openSync7, readFileSync as readFileSync37, rmSync as rmSync24, statSync as statSync24, writeFileSync as writeFileSync14 } from "node:fs";
28506
+ import { closeSync as closeSync7, fsyncSync as fsyncSync3, linkSync as linkSync2, mkdirSync as mkdirSync29, openSync as openSync7, readFileSync as readFileSync37, rmSync as rmSync24, statSync as statSync25, writeFileSync as writeFileSync14 } from "node:fs";
28086
28507
  import { homedir as homedir10 } from "node:os";
28087
- import { dirname as dirname26, isAbsolute as isAbsolute12, join as join77 } from "node:path";
28508
+ import { dirname as dirname27, isAbsolute as isAbsolute12, join as join78 } from "node:path";
28088
28509
  var fail2 = () => {
28089
28510
  throw new Error("Invalid interactive profile configuration.");
28090
28511
  };
@@ -28125,7 +28546,7 @@ function validateInteractiveProfile(value) {
28125
28546
  };
28126
28547
  }
28127
28548
  function interactiveProfilePath(home = homedir10()) {
28128
- return join77(absolute(home), ".xingular-harness", "profile.json");
28549
+ return join78(absolute(home), ".xingular-harness", "profile.json");
28129
28550
  }
28130
28551
  function selectedConfigPath(location = {}) {
28131
28552
  return location.configPath === void 0 ? interactiveProfilePath(location.home) : absolute(location.configPath);
@@ -28133,7 +28554,7 @@ function selectedConfigPath(location = {}) {
28133
28554
  function readInteractiveProfile(location = {}) {
28134
28555
  const path4 = selectedConfigPath(location);
28135
28556
  try {
28136
- const info = statSync24(path4);
28557
+ const info = statSync25(path4);
28137
28558
  if (!info.isFile() || info.size > 16384) return fail2();
28138
28559
  return validateInteractiveProfile(JSON.parse(readFileSync37(path4, "utf8")));
28139
28560
  } catch (error) {
@@ -28149,16 +28570,16 @@ function overrides(value) {
28149
28570
  }
28150
28571
  function resolveInteractiveProfile(options2 = {}) {
28151
28572
  const home = absolute(options2.home ?? homedir10());
28152
- const root = join77(home, ".xingular-harness");
28573
+ const root = join78(home, ".xingular-harness");
28153
28574
  const selected = readInteractiveProfile(options2);
28154
28575
  const legacy = overrides(options2.legacyDefaults);
28155
28576
  const explicit = overrides(options2.overrides);
28156
- const dataRoot = explicit.dataRoot ?? selected?.dataRoot ?? legacy.dataRoot ?? join77(root, "data");
28157
- const stateDir = explicit.stateDir ?? selected?.stateDir ?? legacy.stateDir ?? join77(root, "state");
28577
+ const dataRoot = explicit.dataRoot ?? selected?.dataRoot ?? legacy.dataRoot ?? join78(root, "data");
28578
+ const stateDir = explicit.stateDir ?? selected?.stateDir ?? legacy.stateDir ?? join78(root, "state");
28158
28579
  const defaultVariantScope = explicit.defaultVariantScope ?? selected?.defaultVariantScope ?? legacy.defaultVariantScope ?? "default";
28159
28580
  const secretBackend = explicit.secretBackend ?? selected?.secretBackend ?? legacy.secretBackend ?? {
28160
28581
  kind: "file",
28161
- path: join77(stateDir, "secrets.json")
28582
+ path: join78(stateDir, "secrets.json")
28162
28583
  };
28163
28584
  return validateInteractiveProfile({ version: 1, dataRoot, stateDir, defaultVariantScope, secretBackend });
28164
28585
  }
@@ -28209,27 +28630,27 @@ function resolveStandaloneOptions(argv, home = homedir11(), cwd = process.cwd())
28209
28630
  if (argument !== "--data" && argument !== "--state") throw new Error(`Unknown standalone option: ${argument}`);
28210
28631
  const value = argv[index + 1];
28211
28632
  if (!value || value.startsWith("--")) throw new Error(`${argument} needs a directory path.`);
28212
- if (argument === "--data") overrides2.dataRoot = resolve16(cwd, value);
28213
- else overrides2.stateDir = resolve16(cwd, value);
28633
+ if (argument === "--data") overrides2.dataRoot = resolve20(cwd, value);
28634
+ else overrides2.stateDir = resolve20(cwd, value);
28214
28635
  given.add(argument);
28215
28636
  index += 1;
28216
28637
  }
28217
28638
  if (!isAbsolute13(home) && !(given.has("--data") && given.has("--state"))) {
28218
28639
  throw new Error(`The home folder "${home}" is not an absolute path, so the default data and state folders cannot be placed. Set HOME to your home folder, or pass --data and --state with absolute folders.`);
28219
28640
  }
28220
- const devRoot = isAbsolute13(home) ? join78(home, ".xingular-harness-dev") : null;
28221
- const devData = devRoot ? join78(devRoot, "data") : null;
28222
- const devState = devRoot ? join78(devRoot, "state") : null;
28641
+ const devRoot = isAbsolute13(home) ? join79(home, ".xingular-harness-dev") : null;
28642
+ const devData = devRoot ? join79(devRoot, "data") : null;
28643
+ const devState = devRoot ? join79(devRoot, "state") : null;
28223
28644
  const development = !!devRoot && (overrides2.dataRoot === devData || overrides2.stateDir === devState);
28224
28645
  const resolvedProfile = isAbsolute13(home) ? resolveInteractiveProfile({
28225
28646
  home,
28226
28647
  ...development ? {
28227
- configPath: join78(devRoot, "profile.json"),
28648
+ configPath: join79(devRoot, "profile.json"),
28228
28649
  legacyDefaults: {
28229
28650
  dataRoot: devData,
28230
28651
  stateDir: devState,
28231
28652
  defaultVariantScope: "default",
28232
- secretBackend: { kind: "file", path: join78(devState, "secrets.json") }
28653
+ secretBackend: { kind: "file", path: join79(devState, "secrets.json") }
28233
28654
  }
28234
28655
  } : {},
28235
28656
  overrides: overrides2
@@ -28238,9 +28659,9 @@ function resolveStandaloneOptions(argv, home = homedir11(), cwd = process.cwd())
28238
28659
  dataRoot: overrides2.dataRoot,
28239
28660
  stateDir: overrides2.stateDir,
28240
28661
  defaultVariantScope: "default",
28241
- secretBackend: { kind: "file", path: join78(overrides2.stateDir, "secrets.json") }
28662
+ secretBackend: { kind: "file", path: join79(overrides2.stateDir, "secrets.json") }
28242
28663
  };
28243
- const profile = development ? { ...resolvedProfile, secretBackend: { kind: "file", path: join78(resolvedProfile.stateDir, "secrets.json") } } : resolvedProfile;
28664
+ const profile = development ? { ...resolvedProfile, secretBackend: { kind: "file", path: join79(resolvedProfile.stateDir, "secrets.json") } } : resolvedProfile;
28244
28665
  return {
28245
28666
  profile,
28246
28667
  paths: {
@@ -28321,14 +28742,14 @@ async function runStandaloneTerminal(argv, dependencies = {}) {
28321
28742
  if (!(dependencies.stdinIsTTY ?? process.stdin.isTTY) || !(dependencies.stdoutIsTTY ?? process.stdout.isTTY)) {
28322
28743
  throw new Error("The interactive Harness terminal needs a TTY. Run it from a terminal, or use `xagent serve` for a host process.");
28323
28744
  }
28324
- let cwd = resolve16(dependencies.cwd ?? process.cwd());
28745
+ let cwd = resolve20(dependencies.cwd ?? process.cwd());
28325
28746
  const { paths: paths2, profile } = resolveStandaloneOptions(argv, dependencies.home, cwd);
28326
28747
  const secrets = await interactiveProfileSecretStore(profile, dependencies.hostSecretStore);
28327
28748
  const makeHarness = dependencies.createHarness ?? createHarness;
28328
28749
  const harness = makeHarness({ dataRoot: paths2.dataRoot, stateDir: paths2.stateDir, productName: DEFAULT_PRODUCT_NAME, secrets });
28329
28750
  const newSessionId = dependencies.createSessionId ?? randomUUID19;
28330
28751
  let current2 = { sessionId: newSessionId(), accountId: null, nativeSessionId: null };
28331
- const logFile = join78(paths2.stateDir, "harness.log");
28752
+ const logFile = join79(paths2.stateDir, "harness.log");
28332
28753
  setLogSink((text11) => {
28333
28754
  try {
28334
28755
  appendFileSync2(logFile, `${(/* @__PURE__ */ new Date()).toISOString()} ${text11}
@@ -28350,7 +28771,7 @@ async function runStandaloneTerminal(argv, dependencies = {}) {
28350
28771
  worktreeId = worktree.id;
28351
28772
  }
28352
28773
  for (; ; ) {
28353
- const sessionFolder = join78(paths2.dataRoot, "sessions", "terminal", createHash16("sha256").update(cwd).digest("hex"));
28774
+ const sessionFolder = join79(paths2.dataRoot, "sessions", "terminal", createHash16("sha256").update(cwd).digest("hex"));
28354
28775
  const launch = await harness.terminals.prepare({ ...current2, cwd, sessionFolder, variant: { scope: variantScope }, ...worktreeId ? { run: { worktreeId } } : {} });
28355
28776
  const code = await spawnFrontend(paths2.remoteControl ? { ...launch, env: { ...launch.env, [REMOTE_CONTROL_ENV]: "1" } } : launch, cwd, dependencies.spawn);
28356
28777
  const next = harness.terminals.takeRelaunch(current2.sessionId);
@@ -28374,7 +28795,7 @@ async function runStandaloneTerminal(argv, dependencies = {}) {
28374
28795
 
28375
28796
  // src/worktrees/cli.ts
28376
28797
  import { homedir as homedir12 } from "node:os";
28377
- import { join as join79, resolve as resolve17 } from "node:path";
28798
+ import { join as join80, resolve as resolve21 } from "node:path";
28378
28799
  var WORKTREE_USAGE = `Usage: xagent worktree <command> [--state <dir>]
28379
28800
 
28380
28801
  list Worktrees of the repository you are in
@@ -28409,11 +28830,11 @@ async function findWorktree(worktrees, cwd, key, removed = false) {
28409
28830
  }
28410
28831
  async function runWorktreeCli(argv, dependencies = {}) {
28411
28832
  const write4 = dependencies.write ?? ((text11) => void process.stdout.write(text11));
28412
- const cwd = resolve17(dependencies.cwd ?? process.cwd());
28833
+ const cwd = resolve21(dependencies.cwd ?? process.cwd());
28413
28834
  const args = [];
28414
- let stateDir = join79(dependencies.home ?? homedir12(), ".xingular-harness", "state");
28835
+ let stateDir = join80(dependencies.home ?? homedir12(), ".xingular-harness", "state");
28415
28836
  for (let index = 0; index < argv.length; index += 1) {
28416
- if (argv[index] === "--state") stateDir = resolve17(argv[++index] ?? stateDir);
28837
+ if (argv[index] === "--state") stateDir = resolve21(argv[++index] ?? stateDir);
28417
28838
  else args.push(argv[index]);
28418
28839
  }
28419
28840
  const [command, ...rest] = args;
@@ -28497,7 +28918,7 @@ ${status.changes} changed path(s), ${status.ahead} commit(s) since it started.
28497
28918
  return result.failed.length ? 1 : 0;
28498
28919
  }
28499
28920
  case "adopt": {
28500
- const record3 = await worktrees.adopt(resolve17(cwd, positional[0] ?? ""));
28921
+ const record3 = await worktrees.adopt(resolve21(cwd, positional[0] ?? ""));
28501
28922
  write4(`${describe5(record3)}
28502
28923
  `);
28503
28924
  return 0;
@@ -28579,11 +29000,11 @@ function serveCommand(argv) {
28579
29000
  process.stderr.write("serve needs --state <dir>.\n");
28580
29001
  return Promise.resolve(2);
28581
29002
  }
28582
- const stateDir = resolve18(stateArgument);
29003
+ const stateDir = resolve22(stateArgument);
28583
29004
  const peer = new RpcPeer(stdioTransport());
28584
29005
  const options2 = {
28585
29006
  stateDir,
28586
- dataRoot: values2.data?.[0] ? resolve18(values2.data[0]) : null,
29007
+ dataRoot: values2.data?.[0] ? resolve22(values2.data[0]) : null,
28587
29008
  productName: values2.name?.[0],
28588
29009
  builtins: values2.builtins,
28589
29010
  ...switches.has("host-secrets") ? { secrets: remoteSecrets(peer) } : {},
@@ -28593,12 +29014,12 @@ function serveCommand(argv) {
28593
29014
  const harness = createHarness(options2);
28594
29015
  if (switches.has("host-tools")) harness.sessions.provide(remoteTools(peer));
28595
29016
  attach3(harness, peer);
28596
- return new Promise((resolve19) => {
29017
+ return new Promise((resolve23) => {
28597
29018
  let closing = false;
28598
29019
  const shutdown = () => {
28599
29020
  if (closing) return;
28600
29021
  closing = true;
28601
- void Promise.race([harness.close(), new Promise((done) => setTimeout(done, 5e3))]).then(() => resolve19(0));
29022
+ void Promise.race([harness.close(), new Promise((done) => setTimeout(done, 5e3))]).then(() => resolve23(0));
28602
29023
  };
28603
29024
  peer.handle("harness.shutdown", () => void setImmediate(shutdown));
28604
29025
  process.stdin.on("end", shutdown);