@tea-agent/loop-agent 0.35.1-beta.0 → 0.35.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/AGENTS.md +110 -108
  2. package/CHANGELOG.md +16 -26
  3. package/README.md +165 -165
  4. package/bin/agent-worker.js +0 -0
  5. package/bin/loop-agent.js +57 -21
  6. package/dist/application/task-lifecycle/advance.js +0 -1
  7. package/dist/build-stamp.json +6 -0
  8. package/dist/cli/program.js +2 -2
  9. package/dist/commands/cursor-prompt.js +6 -6
  10. package/dist/commands/init-upgrade.js +19 -351
  11. package/dist/commands/init.js +67 -14
  12. package/dist/commands/loop-benchmark.js +11 -11
  13. package/dist/commands/pi-reuse-benchmark.js +16 -16
  14. package/dist/commands/run-dag-progress.js +0 -14
  15. package/dist/commands/task-advance.js +3 -33
  16. package/dist/shared/operator/capabilities.js +1 -38
  17. package/dist/shared/package-metadata.js +32 -0
  18. package/dist/sidecars/cursor-prompt/executor.js +1 -1
  19. package/dist/worker/console/chat/pi-runtime.js +25 -41
  20. package/dist/worker/console/chat/routes.js +4 -27
  21. package/dist/worker/console/operation-runner.js +0 -24
  22. package/dist/worker/console/operator-actions.js +0 -58
  23. package/dist/worker/console/static/assets/index-fsjzREob.js +56 -0
  24. package/dist/worker/console/static/assets/{index-Dups4sSM.css → index-hJqCPs_g.css} +1 -1
  25. package/dist/worker/console/static/index.html +2 -2
  26. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +2 -13
  27. package/dist/worker/console/static-src/operator-chat/useComposer.js +7 -30
  28. package/dist/worker/loop-agent/loop-agent-client.js +13 -0
  29. package/dist/worker/observe/static/copy.js +67 -67
  30. package/dist/worker/observe/static/dag-layout.d.ts +36 -36
  31. package/dist/worker/observe/static/dom.js +220 -220
  32. package/dist/worker/observe/static/relations.js +133 -133
  33. package/dist/worker/observe/static/run-processing.js +148 -148
  34. package/dist/worker/observe/static/views/batch.js +227 -227
  35. package/dist/worker/observe/static/views/failures.js +143 -143
  36. package/dist/worker/observe/static/views/feature.js +492 -492
  37. package/dist/worker/observe/static/views/run.js +453 -453
  38. package/dist/worker/observe/static/views/shell.js +7 -7
  39. package/dist/worker/observe/static/views/timeline.js +163 -163
  40. package/dist/workflows/dag/canvas-observer.js +275 -275
  41. package/dist/workflows/dag/contract-output-registry.js +14 -0
  42. package/dist/workflows/dag/contract-validator-registrations.js +8 -0
  43. package/dist/workflows/dag/dynamic-runtime/shared.js +9 -1
  44. package/dist/workflows/dag/frontend-implementation-contract.js +233 -39
  45. package/dist/workflows/dag/frontend-prewrite-gate.js +364 -61
  46. package/dist/workflows/dag/frontend-repair.js +219 -18
  47. package/dist/workflows/dag/frontend-verification-trace.js +47 -32
  48. package/dist/workflows/dag/init-hybrid.js +41 -24
  49. package/dist/workflows/dag/node-execution.js +89 -0
  50. package/dist/workflows/dag/runner.js +52 -3
  51. package/dist/workflows/dag/scheduler.js +98 -3
  52. package/dist/workflows/dag/types.js +23 -2
  53. package/docs/architecture/evolution.md +73 -73
  54. package/docs/architecture/system-overview.md +100 -100
  55. package/docs/architecture/worker-and-feature.md +122 -122
  56. package/docs/skills/README.md +7 -7
  57. package/docs/templates/adr.md +60 -60
  58. package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -94
  59. package/docs/templates/agent-dag-decision-envelope.schema.json +213 -213
  60. package/docs/templates/agent-dag-decision-gate.prompt.md +246 -246
  61. package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -98
  62. package/docs/templates/agent-dag-report.schema.json +473 -473
  63. package/docs/templates/agent-dag-review-verdict.prompt.md +68 -68
  64. package/docs/templates/backend-test-result.schema.json +99 -99
  65. package/docs/templates/evaluation/agents-map-slim-v1.md +87 -87
  66. package/docs/templates/evaluation/agents-map-verbose-v0.md +153 -153
  67. package/docs/templates/feature-spec.md +53 -53
  68. package/docs/templates/frontend-design-contract.md +42 -42
  69. package/docs/templates/frontend-eval/fixtures/failures/01-type-build-error.md +17 -17
  70. package/docs/templates/frontend-eval/fixtures/failures/02-unit-component-test-fail.md +16 -16
  71. package/docs/templates/frontend-eval/fixtures/failures/03-fixture-schema-drift.md +16 -16
  72. package/docs/templates/frontend-eval/fixtures/failures/04-missing-loading-empty-error-state.md +16 -16
  73. package/docs/templates/frontend-eval/fixtures/failures/05-forbidden-write-writeset-expansion.md +16 -16
  74. package/docs/templates/frontend-eval/fixtures/failures/06-unapproved-dependency-add.md +16 -16
  75. package/docs/templates/frontend-eval/fixtures/failures/07-mock-production-on.md +21 -21
  76. package/docs/templates/frontend-eval/fixtures/functional/01-simple-component-style.md +29 -29
  77. package/docs/templates/frontend-eval/fixtures/functional/02-form-validation.md +28 -28
  78. package/docs/templates/frontend-eval/fixtures/functional/03-list-detail-page.md +28 -28
  79. package/docs/templates/frontend-eval/fixtures/functional/04-api-mock.md +29 -29
  80. package/docs/templates/frontend-eval/fixtures/functional/05-permission-auth-gated-ui.md +27 -27
  81. package/docs/templates/frontend-eval/fixtures/functional/06-ssr-server-client-boundary.md +28 -28
  82. package/docs/templates/frontend-eval/fixtures/functional/07-shared-public-component-api.md +28 -28
  83. package/docs/templates/frontend-eval/fixtures/functional/08-pure-local-no-remote.md +27 -27
  84. package/docs/templates/frontend-eval/metrics.md +138 -138
  85. package/docs/templates/frontend-eval/smoke-targets.md +53 -53
  86. package/docs/templates/frontend-task-constraints.md +35 -35
  87. package/docs/templates/frontend-task-requirement.md +70 -70
  88. package/docs/templates/init-evolution-review.md +35 -35
  89. package/docs/templates/init-managed-agents.md +154 -156
  90. package/docs/templates/interactive-ui-round2-experiment.md +66 -66
  91. package/docs/templates/knowledge-graph-bootstrap-dag.json +118 -118
  92. package/docs/templates/knowledge-sync-dag.json +178 -178
  93. package/docs/templates/knowledge-sync-draft.schema.json +71 -71
  94. package/docs/templates/product-line/closeout.yaml +9 -9
  95. package/docs/templates/product-line/design.md +13 -13
  96. package/docs/templates/product-line/links.md +10 -10
  97. package/docs/templates/product-line/requirement.md +17 -17
  98. package/docs/templates/product-line/test-plan.md +7 -7
  99. package/docs/templates/project-start-checklist.md +9 -9
  100. package/docs/templates/qa-report.md +48 -48
  101. package/docs/templates/sprint-contract.md +29 -29
  102. package/docs/templates/worker-dogfood-evidence.md +80 -80
  103. package/docs/templates/worker-dogfood-setup.md +68 -68
  104. package/harness.json +2 -5
  105. package/package.json +2 -2
  106. package/scripts/kb-bootstrap-init-skeleton.sh +0 -0
  107. package/scripts/kb-graph-incremental-prepare.mjs +0 -0
  108. package/scripts/kb-graph-materialize.mjs +105 -105
  109. package/scripts/kb-graph-promote.mjs +164 -164
  110. package/scripts/kb-query.mjs +554 -554
  111. package/skills/agent-worker/SKILL.md +48 -48
  112. package/skills/agent-worker/references/agent-worker-operator.md +159 -159
  113. package/skills/ai-engineering-context/SKILL.md +48 -48
  114. package/skills/analyze-product-dependencies/scripts/test-validators.mjs +0 -0
  115. package/skills/analyze-product-dependencies/scripts/validate-api-documentation.mjs +0 -0
  116. package/skills/analyze-product-dependencies/scripts/validate-dependency-analysis.mjs +0 -0
  117. package/skills/analyze-product-dependencies/scripts/validate-product-requirement-input.mjs +0 -0
  118. package/skills/analyze-product-requirements/scripts/compute-source-identity.mjs +0 -0
  119. package/skills/analyze-product-requirements/scripts/test-validators.mjs +0 -0
  120. package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +0 -0
  121. package/skills/analyze-product-requirements/scripts/validate-product-requirement.mjs +0 -0
  122. package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +0 -0
  123. package/skills/browser-tools/browser-content.js +103 -103
  124. package/skills/browser-tools/browser-cookies.js +35 -35
  125. package/skills/browser-tools/browser-eval.js +53 -53
  126. package/skills/browser-tools/browser-hn-scraper.js +108 -108
  127. package/skills/browser-tools/browser-nav.js +44 -44
  128. package/skills/browser-tools/browser-pick.js +162 -162
  129. package/skills/browser-tools/browser-screenshot.js +34 -34
  130. package/skills/browser-tools/browser-start.js +86 -86
  131. package/skills/browser-tools/package-lock.json +2556 -2556
  132. package/skills/browser-tools/package.json +19 -19
  133. package/skills/code-review-core/SKILL.md +20 -20
  134. package/skills/codebase-scout/SKILL.md +19 -19
  135. package/skills/grill-me/SKILL.md +10 -10
  136. package/skills/local-jacoco-coverage/scripts/run-coverage-analysis.sh +0 -0
  137. package/skills/local-jacoco-coverage/scripts/start-jacoco-agent.sh +0 -0
  138. package/skills/loop-agent/SKILL.md +0 -1
  139. package/skills/loop-agent/references/command-reference.md +639 -641
  140. package/skills/loop-agent/references/docs-converge.md +126 -126
  141. package/skills/loop-agent/references/learned/README.md +21 -21
  142. package/skills/loop-agent/references/pi-prompt.md +23 -23
  143. package/skills/loop-agent/references/pi-subagent-assisted-mode.md +84 -84
  144. package/skills/playwright-cli/references/element-attributes.md +23 -23
  145. package/skills/playwright-cli/references/playwright-tests.md +39 -39
  146. package/skills/playwright-cli/references/request-mocking.md +87 -87
  147. package/skills/playwright-cli/references/running-code.md +241 -241
  148. package/skills/playwright-cli/references/session-management.md +225 -225
  149. package/skills/playwright-cli/references/storage-state.md +275 -275
  150. package/skills/playwright-cli/references/test-generation.md +433 -433
  151. package/skills/requesting-code-review/SKILL.md +101 -101
  152. package/skills/requesting-code-review/code-reviewer.md +168 -168
  153. package/skills/systematic-debugging/CREATION-LOG.md +119 -119
  154. package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
  155. package/skills/systematic-debugging/condition-based-waiting.md +115 -115
  156. package/skills/systematic-debugging/defense-in-depth.md +122 -122
  157. package/skills/systematic-debugging/find-polluter.sh +63 -63
  158. package/skills/systematic-debugging/root-cause-tracing.md +169 -169
  159. package/skills/systematic-debugging/test-academic.md +14 -14
  160. package/skills/systematic-debugging/test-pressure-1.md +58 -58
  161. package/skills/systematic-debugging/test-pressure-2.md +68 -68
  162. package/skills/systematic-debugging/test-pressure-3.md +69 -69
  163. package/skills/using-git-worktrees/SKILL.md +215 -215
  164. package/skills/verification-before-completion/SKILL.md +154 -154
  165. package/skills/webapp-testing/SKILL.md +19 -19
  166. package/dist/worker/console/operation-wait.js +0 -241
  167. package/dist/worker/console/static/assets/index-SjjjZnV3.js +0 -56
  168. package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +0 -141
@@ -106,22 +106,22 @@ export function parsePiReuseBenchmarkArgs(args) {
106
106
  };
107
107
  }
108
108
  export function printPiReuseBenchmarkUsage() {
109
- console.log(`usage: pi-reuse-benchmark [options]
110
-
111
- Deterministic Pi runtime reuse benchmark/decision summary (no live Pi calls).
112
-
113
- Options:
114
- --report <path> Benchmark report markdown (approval status)
115
- --approval <path> Explicit approval JSON artifact
116
- --off-executor <path> Baseline executor.jsonl (reuse off)
117
- --on-executor <path> Treatment executor.jsonl (reuse on)
118
- --off-task <task-id> Resolve baseline from .harness/tasks/<id>/logs/executor.jsonl
119
- --on-task <task-id> Resolve treatment from .harness/tasks/<id>/logs/executor.jsonl
120
- --json Emit JSON (default when no format flag is set)
121
- --markdown Emit Markdown summary
122
- -h, --help Show this help
123
-
124
- Recommendations: defer | maintain-opt-in | eligible-for-human-review
109
+ console.log(`usage: pi-reuse-benchmark [options]
110
+
111
+ Deterministic Pi runtime reuse benchmark/decision summary (no live Pi calls).
112
+
113
+ Options:
114
+ --report <path> Benchmark report markdown (approval status)
115
+ --approval <path> Explicit approval JSON artifact
116
+ --off-executor <path> Baseline executor.jsonl (reuse off)
117
+ --on-executor <path> Treatment executor.jsonl (reuse on)
118
+ --off-task <task-id> Resolve baseline from .harness/tasks/<id>/logs/executor.jsonl
119
+ --on-task <task-id> Resolve treatment from .harness/tasks/<id>/logs/executor.jsonl
120
+ --json Emit JSON (default when no format flag is set)
121
+ --markdown Emit Markdown summary
122
+ -h, --help Show this help
123
+
124
+ Recommendations: defer | maintain-opt-in | eligible-for-human-review
125
125
  Never changes CODE_AGENT_PI_REUSE_RUNTIME default (off).`);
126
126
  }
127
127
  function resolveRepoRelative(repoRoot, filePath) {
@@ -1,18 +1,4 @@
1
1
  export const DEFAULT_RUN_DAG_PROGRESS_INTERVAL_MS = 30_000;
2
- /**
3
- * Contract floor for periodic progress output. Values below this are rejected
4
- * at CLI parse time (aligned with `dag execute`'s parseProgressIntervalMs).
5
- * Lives here (not src/application/dag/args.ts) because task-advance shares it
6
- * and src/application/dag/args.ts is outside the task-advance write boundary.
7
- */
8
- export const MIN_RUN_DAG_PROGRESS_INTERVAL_MS = 1_000;
9
- /** Validate a progress interval; throws with the dag execute error contract. */
10
- export function validateRunDagProgressIntervalMs(value) {
11
- if (!Number.isInteger(value) || value < MIN_RUN_DAG_PROGRESS_INTERVAL_MS) {
12
- throw new Error("progress-interval-ms must be an integer >= 1000");
13
- }
14
- return value;
15
- }
16
2
  function formatDuration(durationMs) {
17
3
  const totalSeconds = Math.max(0, Math.floor(durationMs / 1_000));
18
4
  const hours = Math.floor(totalSeconds / 3_600);
@@ -1,6 +1,5 @@
1
1
  import { advanceTaskLifecycle, } from "../application/task-lifecycle/index.js";
2
2
  import { buildOperatorResult, operatorFailed, processExitCodeForOutcome, writeOperatorJson, } from "../shared/operator/index.js";
3
- import { createRunDagProgressObserver, validateRunDagProgressIntervalMs, } from "./run-dag-progress.js";
4
3
  const COMMAND = "task advance";
5
4
  const USAGE = `usage:
6
5
  task advance <task-id> [title]
@@ -25,8 +24,6 @@ const USAGE = `usage:
25
24
  [--dag-output <path>]
26
25
  [--skip-finalize]
27
26
  [--no-strict-models]
28
- [--quiet]
29
- [--progress-interval-ms <ms>]
30
27
  [--dry-run]
31
28
  [--json]`;
32
29
  function pushList(target, value) {
@@ -173,19 +170,6 @@ export function parseTaskAdvanceArgs(args) {
173
170
  options.strictModels = false;
174
171
  continue;
175
172
  }
176
- if (token === "--quiet") {
177
- options.quiet = true;
178
- continue;
179
- }
180
- if (token === "--progress-interval-ms") {
181
- const raw = next();
182
- const parsed = Number(raw);
183
- if (!Number.isFinite(parsed)) {
184
- throw new Error(`progress-interval-ms must be an integer >= 1000\n${USAGE}`);
185
- }
186
- options.progressIntervalMs = validateRunDagProgressIntervalMs(parsed);
187
- continue;
188
- }
189
173
  if (token === "--help" || token === "-h") {
190
174
  throw new Error(USAGE);
191
175
  }
@@ -224,7 +208,7 @@ function mapOutcome(result) {
224
208
  return "blocked";
225
209
  return "succeeded";
226
210
  }
227
- export function toUseCaseInput(repoRoot, taskId, options, observer) {
211
+ function toUseCaseInput(repoRoot, taskId, options) {
228
212
  const timeouts = new Map((options.verifyTimeout ?? []).map((entry) => {
229
213
  const parsed = parseVerifyTimeout(entry);
230
214
  return [parsed.label, parsed.timeoutMs];
@@ -286,8 +270,7 @@ export function toUseCaseInput(repoRoot, taskId, options, observer) {
286
270
  dagOutputPath: options.dagOutputPath,
287
271
  skipFinalize: options.skipFinalize,
288
272
  strictModels: options.strictModels,
289
- ...(observer ? { observer } : {}),
290
- onProgress: options.quiet
273
+ onProgress: options.json
291
274
  ? undefined
292
275
  : (message) => {
293
276
  process.stderr.write(`[task advance] ${message}\n`);
@@ -312,18 +295,8 @@ export async function runTaskAdvance(repoRoot, args) {
312
295
  process.exitCode = processExitCodeForOutcome(envelope.outcome);
313
296
  return;
314
297
  }
315
- let progress;
316
298
  try {
317
- // AC-006/AC-007: periodic DAG progress goes to stderr only (stdout stays
318
- // the single final OperatorCommandResultV1 JSON). The observer is created
319
- // ONLY for the approve-gate execution path and disposed on every exit;
320
- // its timer starts only after onRunStart (double guard, no stray timer).
321
- if (options.approveGate && !options.dryRun && !options.quiet) {
322
- progress = createRunDagProgressObserver({
323
- intervalMs: options.progressIntervalMs,
324
- });
325
- }
326
- const result = await advanceTaskLifecycle(toUseCaseInput(repoRoot, taskId, options, progress?.observer));
299
+ const result = await advanceTaskLifecycle(toUseCaseInput(repoRoot, taskId, options));
327
300
  const outcome = mapOutcome(result);
328
301
  const gateStop = result.lifecycleState === "awaiting-write-set-approval" &&
329
302
  result.blockers.length === 0;
@@ -359,7 +332,4 @@ export async function runTaskAdvance(repoRoot, args) {
359
332
  writeOperatorJson(envelope);
360
333
  process.exitCode = processExitCodeForOutcome(envelope.outcome);
361
334
  }
362
- finally {
363
- progress?.dispose();
364
- }
365
335
  }
@@ -445,43 +445,6 @@ export function buildOperatorCapabilitiesDocument() {
445
445
  modelCallable: "always",
446
446
  humanConfirmation: "none",
447
447
  },
448
- {
449
- action: "operationWait",
450
- cli: "console canonical operation wait (read-only event-driven long poll)",
451
- kind: "read",
452
- inputSchemaVersion: 1,
453
- resultSchemaVersion: 1,
454
- resultPolicy: { readOnly: true, bounded: true, redacted: true },
455
- envelopeSchemaVersion: 1,
456
- requiredErrorCodes: [
457
- "NOT_FOUND",
458
- "INVALID_INPUT",
459
- "EVENT_CURSOR_EXPIRED",
460
- ],
461
- description: "Read-only event-driven wait on the canonical operation event ring. Returns immediately on existing events, terminal state or needs-reconcile; otherwise resolves on the first new event/state change or after maxWaitMs with timedOut:true (a success summary, not a command failure).",
462
- inputParams: [
463
- {
464
- name: "operationId",
465
- type: "string",
466
- required: true,
467
- description: "canonical operation id",
468
- },
469
- {
470
- name: "afterSeq",
471
- type: "number",
472
- required: false,
473
- description: "event cursor; only events with seq > afterSeq count (default 0, >= 0)",
474
- },
475
- {
476
- name: "maxWaitMs",
477
- type: "number",
478
- required: false,
479
- description: "bounded wait budget; clamped server-side (default 180000, floor 60000)",
480
- },
481
- ],
482
- modelCallable: "always",
483
- humanConfirmation: "none",
484
- },
485
448
  {
486
449
  action: "contractShow",
487
450
  cli: "loop-agent task status <taskId> --json",
@@ -1002,7 +965,7 @@ export function buildOperatorCapabilitiesDocument() {
1002
965
  "CONTROLLER_MISMATCH",
1003
966
  "INVALID_INPUT",
1004
967
  ],
1005
- description: "Consume a single-use execution receipt and execute the reviewed DAG (prefer task advance --approve-gate when gate token present). accepted/queued/running/operationId are NOT completion — supervise via operationGet/status/dagReport/dagDoctor/operationWait.",
968
+ description: "Consume a single-use execution receipt and execute the reviewed DAG (prefer task advance --approve-gate when gate token present). accepted/queued/running/operationId are NOT completion — supervise via operationGet/status/dagReport/dagDoctor.",
1006
969
  inputParams: [
1007
970
  {
1008
971
  name: "executionId",
@@ -45,6 +45,38 @@ export function readPackageName(packageDir) {
45
45
  }
46
46
  return undefined;
47
47
  }
48
+ /**
49
+ * Read the build stamp (git SHA + build time) written by the build pipeline
50
+ * into `<packageDir>/dist/build-stamp.json`. `undefined` when absent (e.g.
51
+ * running from source via tsx, or a hand-assembled package), which callers
52
+ * treat as "no stamp available".
53
+ */
54
+ export function readBuildStamp(packageDir) {
55
+ try {
56
+ const raw = readFileSync(path.join(packageDir, "dist", "build-stamp.json"), "utf-8");
57
+ const parsed = JSON.parse(raw);
58
+ if (parsed.schemaVersion !== 1 ||
59
+ typeof parsed.version !== "string" ||
60
+ typeof parsed.builtAt !== "string")
61
+ return undefined;
62
+ return parsed;
63
+ }
64
+ catch {
65
+ return undefined;
66
+ }
67
+ }
68
+ /**
69
+ * Human-readable CLI version label: plain version, enriched with the build
70
+ * stamp (short git SHA + build date) when available so a stale dist/ that was
71
+ * built from an old commit is immediately recognizable in `--version`.
72
+ */
73
+ export function readCliVersionLabel(packageDir) {
74
+ const version = readPackageVersion(packageDir) ?? "0.0.0";
75
+ const stamp = readBuildStamp(packageDir);
76
+ if (!stamp?.gitSha)
77
+ return version;
78
+ return `${version} (build ${stamp.gitSha.slice(0, 8)} ${stamp.builtAt.slice(0, 10)})`;
79
+ }
48
80
  /**
49
81
  * Compute the SHA-256 hash of a file on disk.
50
82
  *
@@ -29,7 +29,7 @@ export function resolveArtifactWriteDir(options) {
29
29
  }
30
30
  export function buildArtifactPathPrompt(writeDir) {
31
31
  if (!writeDir)
32
- return `After changes, write artifacts/修改记录.md and artifacts/验证结果.md with verification evidence.
32
+ return `After changes, write artifacts/修改记录.md and artifacts/验证结果.md with verification evidence.
33
33
  ${ARTIFACT_INSTRUCTIONS}`;
34
34
  return [
35
35
  `After changes, write the following files:`,
@@ -58,11 +58,11 @@ import { RUNTIME_CONTEXT_TEXT_MAX, redactRuntimeText, } from "./runtime-context.
58
58
  */
59
59
  export const OPERATOR_CHAT_SYSTEM_PROMPT_BASE = [
60
60
  "You are the General Operator Chat for loop-agent / agent-worker.",
61
- "Operate through operator_* tools first. Pi read/write/edit/bash/grep/find/ls are available; obey the loaded repository AGENTS.md. apply_patch, full-tools, shell, coding-chat are denied. Prefer safe-read/safe-grep for sensitive probes.",
62
- "If a repository has no loop-agent harness and the user requests initialization, run `loop-agent init instructions --repo-root .` then `loop-agent init --repo-root . --profile full --merge`; finish with init doctor, inspect, docs audit, quick verification. For updates use `loop-agent init upgrade --repo-root . --json` until stable; init check-update only for explicitly read-only requests.",
63
- "Long-running DAGs must run via prepareDagExecution → runDag → operationId held by a Console operation; never foreground-Bash `task advance --approve-gate`, no tail/head pipes, no hand-rolled nohup/Start-Process/start; do not end on accepted/queued/running or an operationId NOT completion. Supervise via operationGet/status/dagReport/dagDoctor/operationWait: 60s 90s 120s 180s backoff; reset to 60s on state change; 30-60s re-checks when stall suspected. Report only meaningful node/rank changes, review/verify/closeout, recovery, liveness, terminal outcomes.",
64
- "On failure read primaryFailure, primaryRecovery, doctor evidence. If meaningful progress exists, wait. Else use a fresh eligible dagRerunPlan and rerun its safe node; when ineligible follow AGENTS.md/runtime recovery for same-task rerun/advance, resume, or Worker retry. Auto-approve only a bounded writeSet inside allowedPaths, outside forbiddenPaths, without broad/destructive risk, with structured verification.",
65
- "Continue until success, user stop, or no safe eligible recovery remains (limits, bindings, auth/quota, external authorization exhausted). Never create a new task for a transient failure or replace repair-pi with direct edits.",
61
+ "Operate through operator_* tools first. Pi read/write/edit/bash/grep/find/ls are available, but obey the loaded repository AGENTS.md. apply_patch, full-tools, shell, and coding-chat are denied alternate runtimes. Prefer safe-read/safe-grep for sensitive probes.",
62
+ "If a repository has no loop-agent harness and the user requests initialization, run `loop-agent init instructions --repo-root .` then `loop-agent init --repo-root . --profile full --merge`; finish the generated setup, init doctor, inspect, docs audit, and quick verification. For updates use `loop-agent init upgrade --repo-root . --json` until stable; use init check-update only for an explicitly read-only request.",
63
+ "After a DAG is started or accepted, do not end on accepted/queued/running or an operationId. Supervise it to a terminal outcome. Poll operationGet, task status, dagReport, and dagDoctor after about 15 seconds on start/change, every 30 seconds during progress, and every 60 seconds after 3 minutes unchanged. Report only meaningful node/rank changes, review/verify/closeout, recovery, liveness concerns, and terminal outcomes.",
64
+ "On failure read primaryFailure, primaryRecovery, and doctor evidence. If meaningful progress exists, wait. Otherwise use a fresh eligible dagRerunPlan and rerun its safe node; when ineligible follow AGENTS.md/runtime recovery for same-task rerun/advance, resume, or Worker retry. Auto-approve only a bounded writeSet inside allowedPaths, outside forbiddenPaths, without broad/destructive risk, and with structured verification.",
65
+ "Continue until success, user stop, or no safe eligible recovery remains because limits, bindings, auth/quota recovery, or required external authorization are exhausted. Never create a new task for a transient failure or replace repair-pi with direct edits.",
66
66
  ].join("\n");
67
67
  /** Compose the inspectable system prompt actually injected into Operator Chat. */
68
68
  export function composeOperatorChatSystemPrompt(input) {
@@ -1280,7 +1280,6 @@ export class ConsolePiRuntime {
1280
1280
  * session is not active — callers fail closed instead of fabricating data.
1281
1281
  */
1282
1282
  /**
1283
- * ConsolePiRuntime.listSlashCommands
1284
1283
  * Browser-safe slash command projection for UI-11.
1285
1284
  * Sources: current Session extension commands, prompt templates, skills.
1286
1285
  * Never returns prompt/skill file bodies.
@@ -1293,36 +1292,29 @@ export class ConsolePiRuntime {
1293
1292
  .replace(/[A-Za-z]:\\Users\\[^\s]+/gi, "~")
1294
1293
  .slice(0, 240);
1295
1294
  };
1296
- // Fail soft at the HTTP layer: bubble readiness errors so routes can attach
1297
- // a compact warning while the UI keeps local Operator/Pi Web commands.
1298
- await this.ensureSessionReady(sessionId);
1295
+ try {
1296
+ await this.ensureSessionReady(sessionId);
1297
+ }
1298
+ catch {
1299
+ return [];
1300
+ }
1299
1301
  const session = this.sessions.get(sessionId);
1300
1302
  if (!session)
1301
- return { commands: [] };
1302
- // Same-name conflict priority (AC-3): extension > skill > prompt.
1303
- // Enum order must not decide the winner — prompt-before-skill still loses.
1304
- const SOURCE_RANK = {
1305
- extension: 0,
1306
- skill: 1,
1307
- prompt: 2,
1308
- };
1309
- const byKey = new Map();
1310
- const sourceWarnings = [];
1303
+ return [];
1304
+ const out = [];
1305
+ const seen = new Set();
1311
1306
  const push = (command, label, description, source) => {
1312
1307
  const name = command.startsWith("/") ? command : `/${command}`;
1313
1308
  const key = name.toLowerCase();
1314
- if (!key || key === "/")
1309
+ if (!key || key === "/" || seen.has(key))
1315
1310
  return;
1316
- const next = {
1311
+ seen.add(key);
1312
+ out.push({
1317
1313
  command: name,
1318
1314
  label: (label || name).slice(0, 80),
1319
1315
  description: scrub(description),
1320
1316
  source,
1321
- };
1322
- const existing = byKey.get(key);
1323
- if (!existing || SOURCE_RANK[source] < SOURCE_RANK[existing.source]) {
1324
- byKey.set(key, next);
1325
- }
1317
+ });
1326
1318
  };
1327
1319
  try {
1328
1320
  const cmds = session.extensionRunner?.getRegisteredCommands?.() ?? [];
@@ -1333,20 +1325,18 @@ export class ConsolePiRuntime {
1333
1325
  push(String(name), String(cmd.name || name), String(cmd.description || ""), "extension");
1334
1326
  }
1335
1327
  }
1336
- catch (error) {
1337
- // Source isolation: keep other sources; surface compact warning (AC-3).
1338
- sourceWarnings.push(`extension: ${scrub(error instanceof Error ? error.message : String(error))}`);
1328
+ catch {
1329
+ // non-blocking
1339
1330
  }
1340
1331
  try {
1341
- // Enumerate prompts before skills on purpose: priority must still let skill win.
1342
1332
  for (const tpl of session.promptTemplates ?? []) {
1343
1333
  if (!tpl?.name)
1344
1334
  continue;
1345
1335
  push(String(tpl.name), String(tpl.name), String(tpl.description || ""), "prompt");
1346
1336
  }
1347
1337
  }
1348
- catch (error) {
1349
- sourceWarnings.push(`prompt: ${scrub(error instanceof Error ? error.message : String(error))}`);
1338
+ catch {
1339
+ // non-blocking
1350
1340
  }
1351
1341
  try {
1352
1342
  const services = this.serviceScopes.get(sessionId);
@@ -1361,16 +1351,10 @@ export class ConsolePiRuntime {
1361
1351
  push(command, skill.name, String(skill.description || ""), "skill");
1362
1352
  }
1363
1353
  }
1364
- catch (error) {
1365
- sourceWarnings.push(`skill: ${scrub(error instanceof Error ? error.message : String(error))}`);
1354
+ catch {
1355
+ // non-blocking
1366
1356
  }
1367
- const warning = sourceWarnings.length > 0
1368
- ? sourceWarnings.join("; ").slice(0, 160)
1369
- : undefined;
1370
- return {
1371
- commands: [...byKey.values()],
1372
- ...(warning ? { warning } : {}),
1373
- };
1357
+ return out;
1374
1358
  }
1375
1359
  async getRuntimeSnapshot(sessionId, options) {
1376
1360
  try {
@@ -1645,24 +1645,14 @@ async function handleChatFiles(res, deps, sessionId, query) {
1645
1645
  }
1646
1646
  export async function handleChatCommands(res, deps, sessionId) {
1647
1647
  try {
1648
- const listed = typeof deps.runtime.listSlashCommands === "function"
1648
+ const commands = typeof deps.runtime.listSlashCommands === "function"
1649
1649
  ? await deps.runtime.listSlashCommands(sessionId)
1650
- : { commands: [] };
1651
- // Partial single-source failures keep successful commands + compact warning (AC-3).
1652
- const commands = listed.commands ?? [];
1653
- const warning = typeof listed.warning === "string" && listed.warning.trim()
1654
- ? listed.warning.trim().slice(0, 160)
1655
- : undefined;
1650
+ : [];
1656
1651
  res.statusCode = 200;
1657
1652
  res.setHeader("content-type", "application/json; charset=utf-8");
1658
- res.end(JSON.stringify({
1659
- ok: true,
1660
- commands,
1661
- ...(warning ? { warning } : {}),
1662
- }));
1653
+ res.end(JSON.stringify({ ok: true, commands }));
1663
1654
  }
1664
1655
  catch (error) {
1665
- // Whole-list / readiness failures stay fail-soft with empty commands + warning.
1666
1656
  res.statusCode = 200;
1667
1657
  res.setHeader("content-type", "application/json; charset=utf-8");
1668
1658
  res.end(JSON.stringify({
@@ -2809,21 +2799,8 @@ export async function handleChatCompact(req, res, deps, sessionId) {
2809
2799
  return;
2810
2800
  }
2811
2801
  }
2812
- let body = {};
2813
- try {
2814
- body = await readJsonBody(req);
2815
- }
2816
- catch {
2817
- sendJson(res, 400, {
2818
- ok: false,
2819
- error: { code: "INVALID_INPUT", message: "invalid json body" },
2820
- });
2821
- return;
2822
- }
2823
- const rawInstructions = typeof body.instructions === "string" ? body.instructions : undefined;
2824
- const customInstructions = rawInstructions?.trim() || undefined;
2825
2802
  try {
2826
- const snapshot = await deps.runtime.compact(sessionId, customInstructions);
2803
+ const snapshot = await deps.runtime.compact(sessionId);
2827
2804
  const event = deps.events.append(sessionId, deps.events.latestTurnId(sessionId) ?? `${sessionId}:compact`, { kind: "compact", data: snapshot });
2828
2805
  sendJson(res, 200, { ok: true, snapshot, eventId: event.eventId });
2829
2806
  }
@@ -74,30 +74,6 @@ export async function runOperation(operationId, deps) {
74
74
  message: chunk,
75
75
  });
76
76
  },
77
- onHeartbeat: (info) => {
78
- // P1 (2026-08-13): project sibling CLI heartbeats into canonical
79
- // operation events so operationWait/operationEventSummary can consume
80
- // them. The projection is best-effort: a failed append must never
81
- // terminate the sibling CLI execution (AC-002). The injected test
82
- // runCommand path has no LoopAgentClient callback guard, so the
83
- // runner protects itself here.
84
- try {
85
- deps.events.append(operationId, {
86
- at: info.at,
87
- kind: "heartbeat",
88
- message: `heartbeat elapsedMs=${info.elapsedMs}`,
89
- data: {
90
- elapsedMs: info.elapsedMs,
91
- action: op.action,
92
- ...(op.taskId ? { taskId: op.taskId } : {}),
93
- ...(op.dagRunId ? { dagRunId: op.dagRunId } : {}),
94
- },
95
- });
96
- }
97
- catch {
98
- // Heartbeat is derived telemetry; ignore projection failures.
99
- }
100
- },
101
77
  });
102
78
  await spawnUpdate;
103
79
  finished = await finalizeFromWorkerResult(operationId, result, deps);
@@ -19,7 +19,6 @@ import { deriveTaskIdentityFromPrd, nextTaskIdRevision, } from "./prd-identity.j
19
19
  import { appendEngineeringCliArgs, buildDraftFromTaskIntake, parseEngineeringBoundaryFromParams, } from "./prd-intake-bridge.js";
20
20
  import { resolveSiblingAgentWorkerBin } from "./sibling-controller.js";
21
21
  import { projectOperationEventSummary, projectOperationForChat, } from "./chat/chat-event-store.js";
22
- import { OperationWaitError, waitForOperationChange, } from "./operation-wait.js";
23
22
  import { MutationGateReceiptStore } from "./mutation-gate-receipt-store.js";
24
23
  import { assessAutonomousExecutionEligibility, deriveEligibilityFactsFromDagSpec, } from "./dag-execution-receipt.js";
25
24
  import { issueHumanGateToken, mutationGatePayloadHash, verifyHumanGateToken, } from "./human-gate-token.js";
@@ -517,63 +516,6 @@ export async function dispatchOperatorAction(ctx, req) {
517
516
  }),
518
517
  };
519
518
  }
520
- case "operationWait": {
521
- const operationId = str(p.operationId);
522
- if (!operationId)
523
- return invalid(action, "operationId is required");
524
- const rawAfterSeq = p.afterSeq ?? 0;
525
- const afterSeq = typeof rawAfterSeq === "number" ? rawAfterSeq : Number(rawAfterSeq);
526
- const rawMaxWaitMs = p.maxWaitMs;
527
- const maxWaitMs = typeof rawMaxWaitMs === "number"
528
- ? rawMaxWaitMs
529
- : rawMaxWaitMs === undefined
530
- ? undefined
531
- : Number(rawMaxWaitMs);
532
- try {
533
- const result = await waitForOperationChange({
534
- operations: ctx.operations,
535
- events: ctx.events,
536
- operationId,
537
- afterSeq,
538
- maxWaitMs,
539
- });
540
- return {
541
- kind: "sync",
542
- status: 200,
543
- body: operatorSucceeded(action, result),
544
- };
545
- }
546
- catch (error) {
547
- if (error instanceof OperationWaitError) {
548
- if (error.code === "NOT_FOUND") {
549
- return {
550
- kind: "error",
551
- status: 404,
552
- body: operatorFailed({
553
- command: action,
554
- outcome: "not-found",
555
- code: "NOT_FOUND",
556
- message: error.message,
557
- }),
558
- };
559
- }
560
- if (error.code === "EVENT_CURSOR_EXPIRED") {
561
- return {
562
- kind: "error",
563
- status: 410,
564
- body: operatorFailed({
565
- command: action,
566
- outcome: "blocked",
567
- code: "EVENT_CURSOR_EXPIRED",
568
- message: error.message,
569
- }),
570
- };
571
- }
572
- return invalid(action, error.message);
573
- }
574
- throw error;
575
- }
576
- }
577
519
  case "inspect": {
578
520
  const body = await runReadCli(ctx, ["inspect", "--json"], "inspect");
579
521
  return { kind: "sync", status: body.ok ? 200 : 400, body };