@selesai/code 0.8.4 → 0.8.6

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 (147) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +7 -1
  3. package/dist/core/slash-commands.js +1 -0
  4. package/dist/defaults/models.json +46 -55
  5. package/dist/defaults/settings.json +7 -8
  6. package/dist/extensions/agent-browser.test.ts +178 -2
  7. package/dist/extensions/agent-browser.ts +2 -0
  8. package/dist/extensions/copy-turn.test.ts +229 -0
  9. package/dist/extensions/copy-turn.ts +11 -0
  10. package/dist/extensions/grep-app/index.test.ts +356 -1
  11. package/dist/extensions/grep-app/index.ts +6 -1
  12. package/dist/extensions/handoff-new.test.ts +351 -161
  13. package/dist/extensions/handoff-new.ts +3 -0
  14. package/dist/extensions/inline-skills.test.ts +93 -0
  15. package/dist/extensions/inline-skills.ts +3 -0
  16. package/dist/extensions/pi-subagents/CHANGELOG.md +26 -3
  17. package/dist/extensions/pi-subagents/README.md +2 -0
  18. package/dist/extensions/pi-subagents/agents/builder.md +5 -2
  19. package/dist/extensions/pi-subagents/agents/commentator.md +1 -1
  20. package/dist/extensions/pi-subagents/docs/configuration.md +54 -2
  21. package/dist/extensions/pi-subagents/docs/observability.md +1 -1
  22. package/dist/extensions/pi-subagents/docs/tool-reference.md +1 -1
  23. package/dist/extensions/pi-subagents/docs/workflows.md +1 -1
  24. package/dist/extensions/pi-subagents/package-lock.json +2 -2
  25. package/dist/extensions/pi-subagents/package.json +1 -1
  26. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +1 -1
  27. package/dist/extensions/pi-subagents/src/agents/agents.ts +9 -3
  28. package/dist/extensions/pi-subagents/src/extension/config.ts +12 -0
  29. package/dist/extensions/pi-subagents/src/extension/doctor.ts +40 -0
  30. package/dist/extensions/pi-subagents/src/extension/index.ts +104 -14
  31. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +5 -0
  32. package/dist/extensions/pi-subagents/src/extension/rpc.ts +3 -9
  33. package/dist/extensions/pi-subagents/src/extension/schemas.ts +2 -2
  34. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +4 -1
  35. package/dist/extensions/pi-subagents/src/missions/lifecycle.ts +4 -7
  36. package/dist/extensions/pi-subagents/src/missions/store.ts +4 -4
  37. package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +6 -2
  38. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +374 -0
  39. package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +9 -5
  40. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +117 -29
  41. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +12 -6
  42. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +7 -1
  43. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +14 -5
  44. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +33 -15
  45. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +5 -2
  46. package/dist/extensions/pi-subagents/src/runs/background/owned-process-tree.ts +104 -0
  47. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +17 -3
  48. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +19 -1
  49. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +2 -2
  50. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +5 -2
  51. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +12 -6
  52. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +3 -3
  53. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +84 -36
  54. package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +2 -1
  55. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +37 -2
  56. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +135 -22
  57. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +12 -0
  58. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +7 -4
  59. package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +171 -0
  60. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +653 -196
  61. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +13 -4
  62. package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +286 -0
  63. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +23 -0
  64. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -0
  65. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +44 -1
  66. package/dist/extensions/pi-subagents/src/runs/shared/run-fanout-budget.ts +280 -0
  67. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +4 -2
  68. package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +19 -3
  69. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +17 -5
  70. package/dist/extensions/pi-subagents/src/shared/session-lineage.ts +71 -0
  71. package/dist/extensions/pi-subagents/src/shared/types.ts +108 -0
  72. package/dist/extensions/pi-subagents/src/shared/utils.ts +3 -1
  73. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +7 -5
  74. package/dist/extensions/pi-subagents/src/tui/fleet.ts +226 -13
  75. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +22 -5
  76. package/dist/extensions/pi-subagents/src/workflows/workflow-auto-relaunch.ts +28 -0
  77. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +87 -0
  78. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +96 -6
  79. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +28 -5
  80. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +8 -5
  81. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +83 -2
  82. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +211 -15
  83. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +41 -3
  84. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +314 -10
  85. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +31 -1
  86. package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +317 -0
  87. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +21 -2
  88. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +16 -1
  89. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +6 -1
  90. package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +75 -4
  91. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +18 -0
  92. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +29 -1
  93. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +31 -5
  94. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +139 -1
  95. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +10 -0
  96. package/dist/extensions/pi-subagents/test/unit/handoff-adoption.test.ts +103 -0
  97. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +22 -0
  98. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +2 -1
  99. package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +171 -0
  100. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +30 -4
  101. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +14 -0
  102. package/dist/extensions/pi-subagents/test/unit/owned-process-tree.test.ts +69 -0
  103. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +83 -0
  104. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +2 -2
  105. package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +55 -2
  106. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +12 -1
  107. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +7 -7
  108. package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +121 -0
  109. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +9 -0
  110. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +4 -3
  111. package/dist/extensions/pi-subagents/test/unit/session-lineage.test.ts +73 -0
  112. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +28 -0
  113. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +10 -1
  114. package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +39 -0
  115. package/dist/extensions/pi-subagents/test/unit/timeout-defaults.test.ts +65 -0
  116. package/dist/extensions/pi-subagents/test/unit/workflow-auto-relaunch.test.ts +45 -0
  117. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +61 -0
  118. package/dist/extensions/ponytail/index.js +11 -0
  119. package/dist/extensions/ponytail/ponytail-config.cjs +2 -0
  120. package/dist/extensions/ponytail/ponytail-instructions.cjs +6 -0
  121. package/dist/extensions/ponytail/test/extension.test.js +274 -140
  122. package/dist/extensions/ponytail/test/helpers.test.js +280 -92
  123. package/dist/extensions/question/index.ts +18 -0
  124. package/dist/extensions/question/question-list.ts +22 -0
  125. package/dist/extensions/question/tests/batch.test.ts +103 -65
  126. package/dist/extensions/question/tests/helpers.test.ts +68 -35
  127. package/dist/extensions/question/tests/question-list.test.ts +506 -204
  128. package/dist/extensions/question/tests/row-layout.test.ts +180 -114
  129. package/dist/extensions/question/tests/schemas.test.ts +42 -19
  130. package/dist/extensions/question/tests/shortcuts.test.ts +106 -84
  131. package/dist/extensions/question/tests/wizard.test.ts +771 -50
  132. package/dist/extensions/rtk.test.ts +180 -1
  133. package/dist/extensions/tokenin-onboarding.ts +3 -0
  134. package/dist/extensions/undo.test.ts +720 -0
  135. package/dist/extensions/undo.ts +6 -0
  136. package/dist/extensions/web-agent-onboarding.test.ts +415 -0
  137. package/dist/extensions/workflow/extension.ts +3 -3
  138. package/dist/extensions/workflow/modes.ts +41 -19
  139. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  140. package/dist/modes/interactive/interactive-mode.js +48 -1
  141. package/dist/skills/pi-subagents/references/execution-controls.md +1 -1
  142. package/docs/plans/workflow-autoloop-reference.md +1 -2
  143. package/docs/plans/workflow-handoff-carryover.md +108 -0
  144. package/docs/quickstart.md +27 -43
  145. package/docs/settings.md +4 -0
  146. package/docs/usage.md +1 -0
  147. package/package.json +5 -1
@@ -54,22 +54,25 @@ import {
54
54
  import { buildSkillInjection, resolveSkillsWithFallback } from "../../agents/skills.ts";
55
55
  import { buildAgentMemoryInjection } from "../../agents/agent-memory.ts";
56
56
  import { evaluateCompletionMutationGuard } from "../shared/completion-guard.ts";
57
+ import { arbitrateCompletionGuardRescue } from "../shared/llm-intent-arbiter.ts";
57
58
  import { getPiSpawnCommand } from "../shared/pi-spawn.ts";
58
59
  import { createJsonlWriter } from "../../shared/jsonl-writer.ts";
59
60
  import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
60
61
  import { resolvePermissionRules } from "../shared/permissions.ts";
61
- import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan } from "../shared/pi-args.ts";
62
+ import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan, type SubagentTaskDelivery } from "../shared/pi-args.ts";
62
63
  import { readRuntimeAcknowledgedExtensions } from "../shared/runtime-acknowledged-extensions.ts";
63
64
  import { assertAgentAllowedByCapabilityCeiling, decodeSubagentCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, SUBAGENT_CAPABILITY_CEILING_ENV } from "../shared/capability-ceiling.ts";
64
65
  import { resolveEffectiveThinking } from "../../shared/model-info.ts";
65
66
  import { MISSING_STRUCTURED_OUTPUT_CALL_ERROR, readStructuredOutput } from "../shared/structured-output.ts";
66
67
  import { formatProcessSignalError, isUnexplainedProcessSignal } from "../shared/process-signal.ts";
67
68
  import { readChildToolDiagnosticError } from "../shared/tool-availability.ts";
68
- import { captureSingleOutputSnapshot, extractChildWrittenOutput, formatSavedOutputReference, injectOutputPathSystemPrompt, resolveSingleOutput, validateFileOnlyOutputMode, type SingleOutputSnapshot } from "../shared/single-output.ts";
69
+ import { captureSingleOutputSnapshot, extractChildWrittenOutput, finalizeSingleOutput, formatSavedOutputReference, injectOutputPathSystemPrompt, resolveSingleOutput, validateFileOnlyOutputMode, type SingleOutputSnapshot } from "../shared/single-output.ts";
69
70
  import {
70
71
  buildModelCandidates,
71
72
  formatModelAttemptNote,
73
+ formatModelRetryNote,
72
74
  isRetryableModelFailure,
75
+ MODEL_RETRY_DELAYS_MS,
73
76
  } from "../shared/model-fallback.ts";
74
77
  import {
75
78
  SUBAGENT_STARTUP_RETRY_DELAYS_MS,
@@ -90,6 +93,7 @@ import {
90
93
  summarizeRecentMutatingFailures,
91
94
  } from "../shared/long-running-guard.ts";
92
95
  import { acceptanceFailureMessage, buildSkippedAcceptanceLedger, evaluateAcceptance, formatAcceptancePrompt, resolveEffectiveAcceptance, stripAcceptanceReport, validateAcceptanceInput } from "../shared/acceptance.ts";
96
+ import { PROMPT_REDACTED } from "../../shared/utils.ts";
93
97
  import { attachContractProjections, isAgentContractV1 } from "../shared/agent-contract.ts";
94
98
  import { appendTurnBudgetSystemPrompt, formatTurnBudgetOutput, initialTurnBudgetState, turnBudgetDecision, turnBudgetDeferredNote, turnBudgetDeferredState, turnBudgetExceededMessage, turnBudgetSoftNote, turnBudgetState } from "../shared/turn-budget.ts";
95
99
  import { initialToolBudgetState, toolBudgetState } from "../shared/tool-budget.ts";
@@ -117,6 +121,12 @@ function withRunContext<T extends SingleResult>(result: T, context: RunSyncOptio
117
121
  return result;
118
122
  }
119
123
 
124
+ function redactResultPrompt<T extends SingleResult>(result: T): T {
125
+ result.task = PROMPT_REDACTED;
126
+ if (result.progress) result.progress.task = PROMPT_REDACTED;
127
+ return result;
128
+ }
129
+
120
130
  function sumUsage(target: Usage, source: Usage): void {
121
131
  target.input += source.input;
122
132
  target.output += source.output;
@@ -139,7 +149,7 @@ function persistSingleResultMetadata(input: {
139
149
  writeMetadata(input.metadataPath, {
140
150
  runId: input.runId,
141
151
  agent: input.agent,
142
- task: input.task,
152
+ task: PROMPT_REDACTED,
143
153
  exitCode: target.exitCode,
144
154
  processSignal: target.processSignal,
145
155
  usage: target.usage,
@@ -231,6 +241,7 @@ function stripAcceptanceReportsFromMessages(messages: Message[] | undefined): vo
231
241
  function snapshotProgress(progress: AgentProgress): AgentProgress {
232
242
  return {
233
243
  ...progress,
244
+ task: PROMPT_REDACTED,
234
245
  skills: progress.skills ? [...progress.skills] : undefined,
235
246
  recentTools: boundStreamedRecentTools(progress.recentTools),
236
247
  recentOutput: boundStreamedRecentOutput(progress.recentOutput),
@@ -240,6 +251,7 @@ function snapshotProgress(progress: AgentProgress): AgentProgress {
240
251
  function snapshotResult(result: SingleResult, progress: AgentProgress): SingleResult {
241
252
  return {
242
253
  ...result,
254
+ task: PROMPT_REDACTED,
243
255
  messages: result.outputMode === "file-only" && result.savedOutputPath ? undefined : result.messages ? [...result.messages] : undefined,
244
256
  usage: { ...result.usage },
245
257
  skills: result.skills ? [...result.skills] : undefined,
@@ -291,6 +303,7 @@ async function runSingleAttempt(
291
303
  attemptNotes: string[];
292
304
  outputSnapshot?: SingleOutputSnapshot;
293
305
  originalTask?: string;
306
+ taskDelivery?: SubagentTaskDelivery;
294
307
  },
295
308
  ): Promise<SingleResult> {
296
309
  const effectiveThinking = options.thinkingOverride ?? agent.thinking;
@@ -312,6 +325,7 @@ async function runSingleAttempt(
312
325
  const { args, env: sharedEnv, tempDir, toolDiagnosticPath, runtimeAcknowledgedExtensionsPath, capabilityAudit } = buildPiArgs({
313
326
  baseArgs: ["--mode", "json", "-p"],
314
327
  task,
328
+ taskDelivery: shared.taskDelivery,
315
329
  sessionEnabled: shared.sessionEnabled,
316
330
  sessionDir: options.sessionDir,
317
331
  sessionFile: options.sessionFile,
@@ -337,6 +351,7 @@ async function runSingleAttempt(
337
351
  parentControlInbox: options.nestedRoute?.controlInbox,
338
352
  parentRootRunId: options.nestedRoute?.rootRunId,
339
353
  parentCapabilityToken: options.nestedRoute?.capabilityToken,
354
+ runFanoutBudget: options.runFanoutBudget,
340
355
  parentSessionId: options.parentSessionId,
341
356
  steerInboxDir: options.steerInboxDir,
342
357
  steerCapabilityPath: options.steerCapabilityPath,
@@ -1277,15 +1292,38 @@ async function runSingleAttempt(
1277
1292
  mcpDirectTools: agent.mcpDirectTools,
1278
1293
  })
1279
1294
  : undefined;
1280
- const completionGuardTriggered = completionGuard?.triggered === true && !observedMutationAttempt;
1295
+ let completionGuardTriggered = completionGuard?.triggered === true && !observedMutationAttempt;
1296
+ // The classifier is deliberately narrow, so a read-only review task can
1297
+ // still be misread as implementation. Arbitrate BEFORE any failure side
1298
+ // effect is published (effects, exit code, progress, notifications,
1299
+ // acceptance, output persistence): only a confident read-only verdict
1300
+ // rescues, and the task text alone is evidence — never the child's own
1301
+ // final message.
1302
+ let arbiterRescued = false;
1303
+ if (completionGuardTriggered) {
1304
+ const arbitration = await arbitrateCompletionGuardRescue({
1305
+ guardTriggered: true,
1306
+ task: shared.originalTask ?? task,
1307
+ arbiter: options.llmIntentArbiter,
1308
+ });
1309
+ completionGuardTriggered = arbitration.triggered;
1310
+ arbiterRescued = arbitration.rescued;
1311
+ }
1281
1312
  if (completionGuard) {
1282
1313
  result.effects = {
1283
1314
  ...(result.effects ?? {}),
1284
1315
  fileMutation: {
1285
- status: completionGuard.expectedMutation ? completionGuardTriggered ? "missing" : "observed" : "not-applicable",
1316
+ status: completionGuard.expectedMutation
1317
+ ? completionGuardTriggered
1318
+ ? "missing"
1319
+ : arbiterRescued
1320
+ ? "not-applicable"
1321
+ : "observed"
1322
+ : "not-applicable",
1286
1323
  expected: completionGuard.expectedMutation,
1287
1324
  attempted: completionGuard.attemptedMutation || observedMutationAttempt,
1288
1325
  ...(completionGuardTriggered ? { message: "Subagent completed without making edits for an implementation task." } : {}),
1326
+ ...(arbiterRescued ? { resolvedBy: "llm-intent-arbiter" } : {}),
1289
1327
  },
1290
1328
  };
1291
1329
  }
@@ -1355,7 +1393,7 @@ async function runSyncCompletion(
1355
1393
  };
1356
1394
  const agent = agents.find((a) => a.name === agentName);
1357
1395
  if (!agent) {
1358
- return withRunContext({
1396
+ return redactResultPrompt(withRunContext({
1359
1397
  index: options.index ?? 0,
1360
1398
  agent: agentName,
1361
1399
  task,
@@ -1363,12 +1401,12 @@ async function runSyncCompletion(
1363
1401
  messages: [],
1364
1402
  usage: emptyUsage(),
1365
1403
  error: `Unknown agent: ${agentName}`,
1366
- }, options.context);
1404
+ }, options.context));
1367
1405
  }
1368
1406
  try {
1369
1407
  assertAgentAllowedByCapabilityCeiling(agent.name, options.capabilityCeiling);
1370
1408
  } catch (error) {
1371
- return withRunContext({
1409
+ return redactResultPrompt(withRunContext({
1372
1410
  index: options.index ?? 0,
1373
1411
  agent: agent.name,
1374
1412
  task,
@@ -1377,11 +1415,11 @@ async function runSyncCompletion(
1377
1415
  usage: emptyUsage(),
1378
1416
  error: error instanceof Error ? error.message : String(error),
1379
1417
  ...(options.capabilityCeiling ? { capabilityCeiling: options.capabilityCeiling } : {}),
1380
- }, options.context);
1418
+ }, options.context));
1381
1419
  }
1382
1420
  const acceptanceErrors = validateAcceptanceInput(options.acceptance);
1383
1421
  if (acceptanceErrors.length > 0) {
1384
- return withRunContext({
1422
+ return redactResultPrompt(withRunContext({
1385
1423
  index: options.index ?? 0,
1386
1424
  agent: agentName,
1387
1425
  task,
@@ -1389,11 +1427,11 @@ async function runSyncCompletion(
1389
1427
  messages: [],
1390
1428
  usage: emptyUsage(),
1391
1429
  error: acceptanceErrors.join(" "),
1392
- }, options.context);
1430
+ }, options.context));
1393
1431
  }
1394
1432
  const outputModeValidationError = validateFileOnlyOutputMode(options.outputMode, options.outputPath, `Single run (${agentName})`);
1395
1433
  if (outputModeValidationError) {
1396
- return withRunContext({
1434
+ return redactResultPrompt(withRunContext({
1397
1435
  index: options.index ?? 0,
1398
1436
  agent: agentName,
1399
1437
  task,
@@ -1402,7 +1440,7 @@ async function runSyncCompletion(
1402
1440
  usage: emptyUsage(),
1403
1441
  outputMode: options.outputMode,
1404
1442
  error: outputModeValidationError,
1405
- }, options.context);
1443
+ }, options.context));
1406
1444
  }
1407
1445
 
1408
1446
  const shareEnabled = options.share === true;
@@ -1419,6 +1457,7 @@ async function runSyncCompletion(
1419
1457
  });
1420
1458
  const acceptancePrompt = formatAcceptancePrompt(effectiveAcceptance, { reportOptional: isAgentContractV1(options.agentContract) });
1421
1459
  const taskWithAcceptance = acceptancePrompt ? `${task}\n${acceptancePrompt}` : task;
1460
+ options.onEffectivePrompt?.(taskWithAcceptance);
1422
1461
  const sessionEnabled = Boolean(options.sessionFile || options.sessionDir) || shareEnabled;
1423
1462
  if (options.context === "fork" && options.sessionFile && existsSync(options.sessionFile)) {
1424
1463
  alignForkedSessionCwd(options.sessionFile, options.cwd ?? runtimeCwd);
@@ -1433,7 +1472,7 @@ async function runSyncCompletion(
1433
1472
  agent.filePath ? path.dirname(agent.filePath) : skillCwd,
1434
1473
  );
1435
1474
  if (skillNames.some((skill) => skill.trim() === "pi-subagents") && missingSkills.includes("pi-subagents")) {
1436
- return withRunContext({
1475
+ return redactResultPrompt(withRunContext({
1437
1476
  index: options.index ?? 0,
1438
1477
  agent: agentName,
1439
1478
  task,
@@ -1441,7 +1480,7 @@ async function runSyncCompletion(
1441
1480
  messages: [],
1442
1481
  usage: emptyUsage(),
1443
1482
  error: "Skills not found: pi-subagents",
1444
- }, options.context);
1483
+ }, options.context));
1445
1484
  }
1446
1485
  let systemPrompt = agent.systemPrompt?.trim() || "";
1447
1486
  if (resolvedSkills.length > 0) {
@@ -1476,7 +1515,7 @@ async function runSyncCompletion(
1476
1515
  artifactPathsResult = getArtifactPaths(options.artifactsDir, options.runId, agentName, options.index);
1477
1516
  ensureArtifactsDir(options.artifactsDir);
1478
1517
  if (options.artifactConfig?.includeInput !== false) {
1479
- writeArtifact(artifactPathsResult.inputPath, `# Task for ${agentName}\n\n${taskWithAcceptance}`);
1518
+ writeArtifact(artifactPathsResult.inputPath, `# Task for ${agentName}\n\n${PROMPT_REDACTED}; live Prompt Audit only.\n`);
1480
1519
  }
1481
1520
  if (options.artifactConfig?.includeJsonl !== false) {
1482
1521
  jsonlPath = artifactPathsResult.jsonlPath;
@@ -1490,7 +1529,7 @@ async function runSyncCompletion(
1490
1529
  childIndex: options.index,
1491
1530
  cwd: options.cwd ?? runtimeCwd,
1492
1531
  });
1493
- transcriptWriter.writeInitialUserMessage(taskWithAcceptance);
1532
+ transcriptWriter.writeInitialUserMessage(`${PROMPT_REDACTED}; live Prompt Audit only.`);
1494
1533
  }
1495
1534
  }
1496
1535
 
@@ -1526,6 +1565,10 @@ async function runSyncCompletion(
1526
1565
  };
1527
1566
  let lastResult: SingleResult | undefined;
1528
1567
  const modelsToTry = candidates.length > 0 ? candidates : [undefined];
1568
+ let modelRetryIndex = 0;
1569
+ // Escalated to "file" after an unexplained zero-activity startup failure so
1570
+ // retries keep the task text out of argv (endpoint pre-exec scans may deny it).
1571
+ let taskDeliveryOverride: SubagentTaskDelivery | undefined;
1529
1572
  modelAttemptsLoop: for (let modelIndex = 0; modelIndex < modelsToTry.length; modelIndex++) {
1530
1573
  const candidate = modelsToTry[modelIndex];
1531
1574
  for (let startupAttemptIndex = 0; ; startupAttemptIndex++) {
@@ -1544,6 +1587,7 @@ async function runSyncCompletion(
1544
1587
  .filter((modelCandidate): modelCandidate is string => Boolean(modelCandidate)),
1545
1588
  outputSnapshot,
1546
1589
  originalTask: task,
1590
+ taskDelivery: taskDeliveryOverride,
1547
1591
  });
1548
1592
  lastResult = result;
1549
1593
  if (startupAttemptIndex === 0) {
@@ -1612,6 +1656,10 @@ async function runSyncCompletion(
1612
1656
  }
1613
1657
  break modelAttemptsLoop;
1614
1658
  }
1659
+ if (!taskDeliveryOverride && result.processSignal === "SIGKILL") {
1660
+ taskDeliveryOverride = "file";
1661
+ attemptNotes.push("[startup-retry] retrying with file task delivery to keep the task text out of the child process argv.");
1662
+ }
1615
1663
  attempt.error = retryNote;
1616
1664
  attemptNotes.push(retryNote);
1617
1665
  continue;
@@ -1630,9 +1678,48 @@ async function runSyncCompletion(
1630
1678
  attempt.error = startupError;
1631
1679
  break modelAttemptsLoop;
1632
1680
  }
1633
- if (!isRetryableModelFailure(result.error) || modelIndex === modelsToTry.length - 1) break modelAttemptsLoop;
1634
- attemptNotes.push(formatModelAttemptNote(attempt, modelsToTry[modelIndex + 1]));
1635
- break;
1681
+ if (isRetryableModelFailure(result.error)) {
1682
+ if (modelIndex < modelsToTry.length - 1) {
1683
+ attemptNotes.push(formatModelAttemptNote(attempt, modelsToTry[modelIndex + 1]));
1684
+ modelRetryIndex = 0;
1685
+ break;
1686
+ }
1687
+ const modelRetryDelayMs = MODEL_RETRY_DELAYS_MS[modelRetryIndex];
1688
+ if (modelRetryDelayMs !== undefined) {
1689
+ const retryNote = formatModelRetryNote({
1690
+ model: attempt.model,
1691
+ attempt: modelRetryIndex + 1,
1692
+ maxAttempts: MODEL_RETRY_DELAYS_MS.length + 1,
1693
+ delayMs: modelRetryDelayMs,
1694
+ });
1695
+ const shouldRetry = await waitForSubagentStartupRetry(modelRetryDelayMs, [options.signal, options.interruptSignal]);
1696
+ if (!shouldRetry) {
1697
+ if (options.interruptSignal?.aborted) {
1698
+ result.exitCode = 0;
1699
+ result.interrupted = true;
1700
+ result.error = undefined;
1701
+ result.finalOutput = "Interrupted. Waiting for explicit next action.";
1702
+ if (result.progress) {
1703
+ result.progress.status = "running";
1704
+ result.progress.error = undefined;
1705
+ }
1706
+ } else {
1707
+ const cancellationError = "Subagent model retry cancelled before relaunch.";
1708
+ result.error = cancellationError;
1709
+ result.finalOutput = cancellationError;
1710
+ attempt.error = cancellationError;
1711
+ if (result.progress) result.progress.error = cancellationError;
1712
+ }
1713
+ break modelAttemptsLoop;
1714
+ }
1715
+ attempt.error = retryNote;
1716
+ attemptNotes.push(retryNote);
1717
+ modelRetryIndex += 1;
1718
+ startupAttemptIndex = -1;
1719
+ continue;
1720
+ }
1721
+ }
1722
+ break modelAttemptsLoop;
1636
1723
  }
1637
1724
  }
1638
1725
 
@@ -1726,13 +1813,39 @@ async function runSyncCompletion(
1726
1813
  stripAcceptanceReportsFromMessages(result.messages);
1727
1814
  if (acceptanceFailure && result.acceptance.explicit && result.exitCode === 0 && !result.interrupted && !result.timedOut && !isAgentContractV1(options.agentContract)) {
1728
1815
  result.exitCode = 1;
1816
+ if (result.savedOutputPath) {
1817
+ result.finalOutput = finalizeSingleOutput({
1818
+ fullOutput: result.finalOutput ?? "",
1819
+ outputPath: options.outputPath,
1820
+ outputMode: result.outputMode,
1821
+ exitCode: result.exitCode,
1822
+ preserveSavedOutput: true,
1823
+ savedPath: result.savedOutputPath,
1824
+ outputReference: result.outputReference,
1825
+ }).displayOutput;
1826
+ artifactOutputByResult.set(result, result.finalOutput);
1827
+ }
1729
1828
  result.error = result.error ? `${result.error}\n${acceptanceFailure}` : acceptanceFailure;
1829
+ if (artifactPathsResult && options.artifactConfig?.enabled !== false && options.artifactConfig?.includeOutput !== false) {
1830
+ try {
1831
+ writeArtifact(artifactPathsResult.outputPath, formatOutputArtifactContent({
1832
+ output: artifactOutputByResult.get(result) ?? result.finalOutput ?? "",
1833
+ error: result.error,
1834
+ transcriptPath: result.transcriptPath,
1835
+ metadataPath: options.artifactConfig?.includeMetadata === false ? undefined : artifactPathsResult.metadataPath,
1836
+ }));
1837
+ } catch (error) {
1838
+ const message = `Artifact output post-processing failed: ${error instanceof Error ? error.message : String(error)}`;
1839
+ result.outputSaveError = result.outputSaveError ? `${result.outputSaveError}\n${message}` : message;
1840
+ }
1841
+ }
1730
1842
  if (result.progress) {
1731
1843
  result.progress.status = "failed";
1732
1844
  result.progress.error = result.error;
1733
1845
  }
1734
1846
  }
1735
1847
  if (isAgentContractV1(options.agentContract)) attachContractProjections(result);
1848
+ redactResultPrompt(result);
1736
1849
  try {
1737
1850
  persistResultMetadata(result);
1738
1851
  } catch (error) {
@@ -1817,7 +1930,7 @@ export async function runSync(
1817
1930
  durationMs: publishedReceipt.progressSummary?.durationMs ?? 0,
1818
1931
  error: failureMessage,
1819
1932
  };
1820
- const failedResult: SingleResult = {
1933
+ const failedResult: SingleResult = redactResultPrompt({
1821
1934
  ...publishedReceipt,
1822
1935
  detached: undefined,
1823
1936
  detachedReason,
@@ -1833,7 +1946,7 @@ export async function runSync(
1833
1946
  acceptance: publishedReceipt.acceptance
1834
1947
  ? buildSkippedAcceptanceLedger(publishedReceipt.acceptance.effectiveAcceptance, { id: "completion-pipeline", message: failureMessage })
1835
1948
  : undefined,
1836
- };
1949
+ });
1837
1950
  if (strictContract) attachContractProjections(failedResult);
1838
1951
  try {
1839
1952
  // Replace the provisional detach receipt metadata with the authoritative
@@ -1,9 +1,15 @@
1
1
  import type { AgentProgress, ForegroundChildControl, ForegroundRunControl } from "../../shared/types.ts";
2
+ import { registerLivePromptAudit, removeLivePromptAudit, type PromptAuditRerunContract } from "./prompt-audit.ts";
2
3
 
3
4
  interface BeginForegroundChildInput {
4
5
  index: number;
5
6
  agent: string;
6
7
  description?: string;
8
+ authoredTask: string;
9
+ effectivePrompt: string;
10
+ cwd?: string;
11
+ outputPath?: string;
12
+ rerun?: PromptAuditRerunContract;
7
13
  model?: string;
8
14
  thinking?: string;
9
15
  interrupt: () => boolean;
@@ -109,6 +115,11 @@ export function beginForegroundChild(control: ForegroundRunControl, input: Begin
109
115
  }
110
116
  control.activeChildren ??= new Map();
111
117
  control.activeChildren.set(input.index, child);
118
+ registerLivePromptAudit(control, input.index, input.authoredTask, input.effectivePrompt, {
119
+ ...(input.cwd ? { cwd: input.cwd } : {}),
120
+ ...(input.outputPath ? { outputPath: input.outputPath } : {}),
121
+ ...(input.rerun ? { rerun: input.rerun } : {}),
122
+ });
112
123
  syncCurrentChild(control, child);
113
124
  }
114
125
 
@@ -121,6 +132,7 @@ export function updateForegroundChild(control: ForegroundRunControl, index: numb
121
132
  }
122
133
 
123
134
  export function finishForegroundChild(control: ForegroundRunControl, index: number): void {
135
+ removeLivePromptAudit(control, index);
124
136
  control.activeChildren?.delete(index);
125
137
  if (control.currentIndex === index) {
126
138
  const next = [...(control.activeChildren?.values() ?? [])]
@@ -121,11 +121,14 @@ export function persistForegroundRunHistory(state: SubagentState, options: { res
121
121
  writeAtomicJson(historyPath(resultsDir), { version: HISTORY_VERSION, runs });
122
122
  }
123
123
 
124
- export function restoreForegroundRunHistory(state: SubagentState, options: { resultsDir?: string; sessionId?: string | null; limit?: number } = {}): number {
125
- const sessionId = options.sessionId ?? state.currentSessionId;
126
- if (!sessionId) return 0;
124
+ export function restoreForegroundRunHistory(state: SubagentState, options: { resultsDir?: string; sessionId?: string | null; sessionIds?: string[]; limit?: number } = {}): number {
125
+ const sessionIds = options.sessionIds
126
+ ?? (options.sessionId ? [options.sessionId] : state.sessionLineage)
127
+ ?? (state.currentSessionId ? [state.currentSessionId] : []);
128
+ if (sessionIds.length === 0) return 0;
129
+ const accepted = new Set(sessionIds);
127
130
  const index = readIndex(options.resultsDir ?? DIRS.results);
128
- const runs = sortAndBound(index.runs.filter((run) => run.sessionId === sessionId), options.limit ?? MAX_REMEMBERED_FOREGROUND_RUNS);
131
+ const runs = sortAndBound(index.runs.filter((run) => run.sessionId !== undefined && accepted.has(run.sessionId)), options.limit ?? MAX_REMEMBERED_FOREGROUND_RUNS);
129
132
  state.foregroundRuns ??= new Map();
130
133
  let restored = 0;
131
134
  for (const run of runs) {
@@ -0,0 +1,171 @@
1
+ import { Agent, type StreamFn, type ThinkingLevel } from "@earendil-works/pi-agent-core";
2
+ import { convertToLlm, type ExtensionContext } from "@selesai/code";
3
+ import { streamSimple } from "@earendil-works/pi-ai/compat";
4
+ import type { Model } from "@earendil-works/pi-ai";
5
+ import type { ForegroundRunControl } from "../../shared/types.ts";
6
+ export type PromptAuditView = "authored" | "runtime" | "effective";
7
+
8
+ export interface PromptAuditRerunContract {
9
+ params: Record<string, unknown>;
10
+ }
11
+
12
+ export interface LivePromptAudit {
13
+ authoredTask: string;
14
+ runtimeAdditions: string;
15
+ finalEffectivePrompt: string;
16
+ cwd?: string;
17
+ outputPath?: string;
18
+ rerun?: PromptAuditRerunContract;
19
+ }
20
+
21
+ const livePrompts = new WeakMap<ForegroundRunControl, Map<number, LivePromptAudit>>();
22
+
23
+ type RegistryModel = Model<any>;
24
+
25
+ function fullModelId(model: Pick<RegistryModel, "provider" | "id">): string {
26
+ return `${model.provider}/${model.id}`;
27
+ }
28
+
29
+ function textContent(value: unknown): string {
30
+ if (typeof value === "string") return value;
31
+ if (!Array.isArray(value)) return "";
32
+ return value.map((item) => item && typeof item === "object" && (item as { type?: unknown }).type === "text" ? String((item as { text?: unknown }).text ?? "") : "").join("");
33
+ }
34
+
35
+ function finalAssistantText(agent: Agent): string {
36
+ for (let index = agent.state.messages.length - 1; index >= 0; index--) {
37
+ const message = agent.state.messages[index];
38
+ if (message && typeof message === "object" && (message as { role?: unknown }).role === "assistant") {
39
+ return textContent((message as { content?: unknown }).content).trim();
40
+ }
41
+ }
42
+ return "";
43
+ }
44
+
45
+ async function resolveRewriteAuth(ctx: ExtensionContext, model: RegistryModel): Promise<{ apiKey?: string; headers?: Record<string, string>; env?: Record<string, string> }> {
46
+ const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
47
+ if (auth.ok === false) throw new Error(`Prompt redo model auth failed for ${fullModelId(model)}: ${auth.error}`);
48
+ return {
49
+ ...(auth.apiKey ? { apiKey: auth.apiKey } : {}),
50
+ ...(auth.headers ? { headers: auth.headers } : {}),
51
+ ...(auth.env ? { env: auth.env } : {}),
52
+ };
53
+ }
54
+
55
+ export async function rewritePromptWithGuidance(input: {
56
+ ctx: ExtensionContext;
57
+ authoredTask: string;
58
+ runtimeAdditions: string;
59
+ finalEffectivePrompt: string;
60
+ guidance: string;
61
+ signal?: AbortSignal;
62
+ streamFn?: StreamFn;
63
+ }): Promise<string> {
64
+ const model = input.ctx.model;
65
+ if (!model) throw new Error("Prompt redo needs the current session model to rewrite the authored task.");
66
+ const auth = await resolveRewriteAuth(input.ctx, model);
67
+ const registeredProvider = (input.ctx.modelRegistry as {
68
+ getRegisteredProviderConfig?: (provider: string) => { api?: string; streamSimple?: StreamFn } | undefined;
69
+ }).getRegisteredProviderConfig?.(model.provider);
70
+ const baseStreamFn = input.streamFn ?? (registeredProvider?.streamSimple && registeredProvider.api === model.api
71
+ ? registeredProvider.streamSimple
72
+ : streamSimple);
73
+ const streamFn: StreamFn = (nextModel, context, streamOptions) => baseStreamFn(nextModel, context, {
74
+ ...streamOptions,
75
+ ...(auth.apiKey ? { apiKey: auth.apiKey } : {}),
76
+ env: auth.env || streamOptions?.env ? { ...(auth.env ?? {}), ...(streamOptions?.env ?? {}) } : undefined,
77
+ headers: { ...(streamOptions?.headers ?? {}), ...(auth.headers ?? {}) },
78
+ });
79
+ const ctxThinking = (input.ctx as { getThinkingLevel?: () => ThinkingLevel }).getThinkingLevel?.();
80
+ const agent = new Agent({
81
+ initialState: {
82
+ systemPrompt: [
83
+ "Rewrite one subagent authored task from Prompt Audit context.",
84
+ "Return only the revised authored task text.",
85
+ "Do not add Markdown fences, explanations, labels, or commentary.",
86
+ "Preserve the original intent unless the guidance explicitly changes it.",
87
+ "Do not include runtime additions unless they are needed as ordinary task context.",
88
+ ].join("\n"),
89
+ model,
90
+ thinkingLevel: ctxThinking ?? "off",
91
+ tools: [],
92
+ },
93
+ convertToLlm,
94
+ streamFn,
95
+ getApiKey: (providerName) => providerName === model.provider ? auth.apiKey : undefined,
96
+ toolExecution: "sequential",
97
+ });
98
+ const abort = () => agent.abort();
99
+ input.ctx.signal?.addEventListener("abort", abort, { once: true });
100
+ input.signal?.addEventListener("abort", abort, { once: true });
101
+ try {
102
+ await agent.prompt([
103
+ "Guidance from the human:",
104
+ input.guidance.trim(),
105
+ "",
106
+ "Authored task:",
107
+ input.authoredTask,
108
+ "",
109
+ "Runtime additions for context only:",
110
+ input.runtimeAdditions,
111
+ "",
112
+ "Final effective prompt for context only:",
113
+ input.finalEffectivePrompt,
114
+ ].join("\n"));
115
+ } finally {
116
+ input.ctx.signal?.removeEventListener("abort", abort);
117
+ input.signal?.removeEventListener("abort", abort);
118
+ }
119
+ const rewritten = finalAssistantText(agent);
120
+ if (!rewritten) throw new Error("Prompt redo rewrite returned an empty prompt.");
121
+ return rewritten;
122
+ }
123
+
124
+ function runtimeAdditions(authoredTask: string, effectivePrompt: string): string {
125
+ if (!authoredTask) return effectivePrompt;
126
+ const authoredIndex = effectivePrompt.indexOf(authoredTask);
127
+ if (authoredIndex < 0) return "(runtime additions unavailable)";
128
+ const before = effectivePrompt.slice(0, authoredIndex).trim();
129
+ const after = effectivePrompt.slice(authoredIndex + authoredTask.length).trim();
130
+ return [before, after].filter(Boolean).join("\n\n") || "(none)";
131
+ }
132
+
133
+ export function registerLivePromptAudit(
134
+ control: ForegroundRunControl,
135
+ index: number,
136
+ authoredTask: string,
137
+ effectivePrompt: string,
138
+ metadata: { cwd?: string; outputPath?: string; rerun?: PromptAuditRerunContract } = {},
139
+ ): void {
140
+ let prompts = livePrompts.get(control);
141
+ if (!prompts) {
142
+ prompts = new Map();
143
+ livePrompts.set(control, prompts);
144
+ }
145
+ prompts.set(index, {
146
+ authoredTask,
147
+ runtimeAdditions: runtimeAdditions(authoredTask, effectivePrompt),
148
+ finalEffectivePrompt: effectivePrompt,
149
+ ...(metadata.cwd ? { cwd: metadata.cwd } : {}),
150
+ ...(metadata.outputPath ? { outputPath: metadata.outputPath } : {}),
151
+ ...(metadata.rerun ? { rerun: metadata.rerun } : {}),
152
+ });
153
+ }
154
+
155
+ export function updateLiveEffectivePrompt(control: ForegroundRunControl, index: number, effectivePrompt: string): void {
156
+ const prompt = livePrompts.get(control)?.get(index);
157
+ if (!prompt) return;
158
+ prompt.finalEffectivePrompt = effectivePrompt;
159
+ prompt.runtimeAdditions = runtimeAdditions(prompt.authoredTask, effectivePrompt);
160
+ }
161
+
162
+ export function getLivePromptAudit(control: ForegroundRunControl, index: number): LivePromptAudit | undefined {
163
+ return livePrompts.get(control)?.get(index);
164
+ }
165
+
166
+ export function removeLivePromptAudit(control: ForegroundRunControl, index: number): void {
167
+ const prompts = livePrompts.get(control);
168
+ if (!prompts) return;
169
+ prompts.delete(index);
170
+ if (prompts.size === 0) livePrompts.delete(control);
171
+ }