@tea-agent/loop-agent 0.37.0 → 0.37.1-beta.0

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 (192) hide show
  1. package/AGENTS.md +2 -6
  2. package/CHANGELOG.md +71 -144
  3. package/README.md +1 -1
  4. package/bin/loop-agent.js +1 -37
  5. package/dist/application/task-lifecycle/advance.js +0 -1
  6. package/dist/application/task-lifecycle/observe.js +0 -15
  7. package/dist/application/task-lifecycle/plan-transitions.js +0 -15
  8. package/dist/cli/command-definitions.js +0 -7
  9. package/dist/cli/program.js +3 -8
  10. package/dist/commands/init-upgrade.js +19 -351
  11. package/dist/commands/init.js +67 -14
  12. package/dist/commands/run-dag-progress.js +0 -14
  13. package/dist/commands/task-advance.js +3 -33
  14. package/dist/executors/dag-pi-executor.js +30 -47
  15. package/dist/executors/pi-sdk-executor.js +0 -34
  16. package/dist/executors/shell-executor.js +95 -6
  17. package/dist/shared/operator/capabilities.js +13 -200
  18. package/dist/shared/package-metadata.js +0 -42
  19. package/dist/task/contract/project.js +0 -4
  20. package/dist/task/source-prepare/completeness.js +0 -53
  21. package/dist/task/source-prepare/parse-intent.js +12 -82
  22. package/dist/task/source-prepare/prepare.js +5 -102
  23. package/dist/task/source-prepare/semantic-intake.js +23 -144
  24. package/dist/worker/console/chat/artifact-card.js +1 -8
  25. package/dist/worker/console/chat/chat-event-store.js +0 -152
  26. package/dist/worker/console/chat/human-gate-card.js +1 -9
  27. package/dist/worker/console/chat/operation-card.js +2 -71
  28. package/dist/worker/console/chat/pi-runtime.js +62 -111
  29. package/dist/worker/console/chat/routes.js +10 -43
  30. package/dist/worker/console/chat/session-store.js +1 -9
  31. package/dist/worker/console/chat/shortcuts.js +7 -9
  32. package/dist/worker/console/chat/turn-process.js +23 -204
  33. package/dist/worker/console/chat/workspace-landing.js +1 -2
  34. package/dist/worker/console/operation-runner.js +6 -155
  35. package/dist/worker/console/operator-actions.js +14 -391
  36. package/dist/worker/console/operator-user-error.js +0 -4
  37. package/dist/worker/console/recovery-cta.js +3 -50
  38. package/dist/worker/console/static/assets/index-HX1pbOyl.css +1 -0
  39. package/dist/worker/console/static/assets/index-M0BLEBfh.js +56 -0
  40. package/dist/worker/console/static/index.html +2 -2
  41. package/dist/worker/console/static-src/app/console-types.js +9 -13
  42. package/dist/worker/console/static-src/app/useOperatorActions.js +2 -4
  43. package/dist/worker/console/static-src/app/useRecoveryActions.js +56 -65
  44. package/dist/worker/console/static-src/app/useRecoveryConsole.js +1 -73
  45. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +30 -114
  46. package/dist/worker/console/static-src/operator-chat/refs.js +0 -3
  47. package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +1 -2
  48. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +13 -67
  49. package/dist/worker/console/static-src/operator-chat/useChatThread.js +6 -12
  50. package/dist/worker/console/static-src/operator-chat/useComposer.js +8 -45
  51. package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -7
  52. package/dist/worker/loop-agent/loop-agent-client.js +3 -17
  53. package/dist/worker/observability/read-model.js +0 -20
  54. package/dist/worker/observe/spec-evidence.js +8 -3
  55. package/dist/worker/observe/static/operator-chrome.d.ts +0 -1
  56. package/dist/worker/observe/static/operator-chrome.js +3 -10
  57. package/dist/worker/observe/static/views/dag-inspector.js +71 -6
  58. package/dist/worker/observe/static/views/dag.js +0 -12
  59. package/dist/worker/preflight.js +1 -2
  60. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +707 -37
  61. package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
  62. package/dist/workflows/dag/backend-test-markdown-workflow.js +109 -7
  63. package/dist/workflows/dag/backend-test-module-stem.js +5 -0
  64. package/dist/workflows/dag/backend-test-pytest-collection.js +345 -24
  65. package/dist/workflows/dag/backend-test-scenario-param.js +890 -164
  66. package/dist/workflows/dag/backend-test-writer-completeness.js +47 -16
  67. package/dist/workflows/dag/dynamic-runtime/map.js +24 -8
  68. package/dist/workflows/dag/dynamic-runtime/shared.js +1 -9
  69. package/dist/workflows/dag/frontend-implementation-contract.js +39 -233
  70. package/dist/workflows/dag/frontend-prewrite-gate.js +61 -364
  71. package/dist/workflows/dag/frontend-repair.js +18 -219
  72. package/dist/workflows/dag/frontend-verification-trace.js +32 -47
  73. package/dist/workflows/dag/init-hybrid.js +82 -165
  74. package/dist/workflows/dag/node-execution.js +0 -89
  75. package/dist/workflows/dag/recovery-recommendation.js +0 -58
  76. package/dist/workflows/dag/runner.js +15 -333
  77. package/dist/workflows/dag/scheduler.js +3 -257
  78. package/dist/workflows/dag/types.js +9 -130
  79. package/docs/README.md +3 -3
  80. package/docs/architecture/evolution.md +67 -102
  81. package/docs/templates/backend-test-dag.json +50 -32
  82. package/docs/templates/init-managed-agents.md +2 -5
  83. package/harness.json +2 -2
  84. package/package.json +3 -9
  85. package/skills/loop-agent/SKILL.md +0 -1
  86. package/skills/loop-agent/references/command-reference.md +0 -4
  87. package/dist/build-stamp.json +0 -6
  88. package/dist/commands/dag-request-interrupt.js +0 -20
  89. package/dist/worker/console/chat/assistant-content.js +0 -121
  90. package/dist/worker/console/chat/semantic-activity.js +0 -477
  91. package/dist/worker/console/operation-run-facts.js +0 -190
  92. package/dist/worker/console/operation-wait.js +0 -355
  93. package/dist/worker/console/recovery-error-copy.js +0 -198
  94. package/dist/worker/console/static/assets/abnfDiagram-N423BO3Z-DSvF6RXQ.js +0 -1
  95. package/dist/worker/console/static/assets/arc-DuI4ogtJ.js +0 -1
  96. package/dist/worker/console/static/assets/architectureDiagram-T3A2C74G-DdBbgD4y.js +0 -36
  97. package/dist/worker/console/static/assets/blockDiagram-VBNYF7ZC-xQC2HELm.js +0 -132
  98. package/dist/worker/console/static/assets/c4Diagram-5PPSVZJV-DPMSgwIZ.js +0 -10
  99. package/dist/worker/console/static/assets/channel-DLVz9TYW.js +0 -1
  100. package/dist/worker/console/static/assets/chunk-2GRJ4B5K-WTEKTxin.js +0 -1
  101. package/dist/worker/console/static/assets/chunk-2Q5K7J3B-B6vZ0Pv_.js +0 -1
  102. package/dist/worker/console/static/assets/chunk-5RXB4S5H-BrerNAix.js +0 -231
  103. package/dist/worker/console/static/assets/chunk-5VM5RSS4-D_ch2aaI.js +0 -15
  104. package/dist/worker/console/static/assets/chunk-6Q2QTUOP-VvfacNrq.js +0 -88
  105. package/dist/worker/console/static/assets/chunk-GF5L2VYU-B-uGFIg2.js +0 -206
  106. package/dist/worker/console/static/assets/chunk-JWPE2WC7-BlysCtod.js +0 -1
  107. package/dist/worker/console/static/assets/chunk-KBJHAD2P-BjAUAfcT.js +0 -1
  108. package/dist/worker/console/static/assets/chunk-RYQCIY6F-BeZnBmb-.js +0 -1
  109. package/dist/worker/console/static/assets/chunk-XXDRQBXY-Bbfe3UJG.js +0 -1
  110. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BeAuGcO9.js +0 -1
  111. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BeAuGcO9.js +0 -1
  112. package/dist/worker/console/static/assets/cose-bilkent-JH36ORCC-BKJQnYVM.js +0 -1
  113. package/dist/worker/console/static/assets/cynefin-VYW2F7L2-B1wtI3hU.js +0 -166
  114. package/dist/worker/console/static/assets/cynefinDiagram-MW4NZA55-DCF6DBSu.js +0 -62
  115. package/dist/worker/console/static/assets/cytoscape.esm-yzknjiTM.js +0 -321
  116. package/dist/worker/console/static/assets/dagre-VZM6K2ZE-BJyNGRlP.js +0 -4
  117. package/dist/worker/console/static/assets/defaultLocale-DX6XiGOO.js +0 -1
  118. package/dist/worker/console/static/assets/diagram-7IWD3JNH-B7j1-VsZ.js +0 -30
  119. package/dist/worker/console/static/assets/diagram-B4RE2ZJO-BREJ140a.js +0 -3
  120. package/dist/worker/console/static/assets/diagram-LBJQPF4R-CDPHmHGH.js +0 -24
  121. package/dist/worker/console/static/assets/diagram-Q27KOJAE-BtZLrez1.js +0 -24
  122. package/dist/worker/console/static/assets/diagram-UB23O5K3-mzybu8oU.js +0 -41
  123. package/dist/worker/console/static/assets/ebnfDiagram-BXEA7PRR-Dk3I2uO6.js +0 -1
  124. package/dist/worker/console/static/assets/erDiagram-JOGREHBK-BT97u_tQ.js +0 -85
  125. package/dist/worker/console/static/assets/flowDiagram-UKHOOZJN-CMOtYzui.js +0 -156
  126. package/dist/worker/console/static/assets/ganttDiagram-PKOTCBZU-DiNydkuY.js +0 -292
  127. package/dist/worker/console/static/assets/gitGraphDiagram-DS77QQ5N-Cwt6fLkl.js +0 -106
  128. package/dist/worker/console/static/assets/graph-DOmOIIwC.js +0 -1
  129. package/dist/worker/console/static/assets/index-DQxiso1w.js +0 -325
  130. package/dist/worker/console/static/assets/index-_j0Hzo69.css +0 -1
  131. package/dist/worker/console/static/assets/infoDiagram-6WML65LV-AsGIGKKB.js +0 -2
  132. package/dist/worker/console/static/assets/init-Gi6I4Gst.js +0 -1
  133. package/dist/worker/console/static/assets/ishikawaDiagram-WSZJBQD7-BL8ITZh3.js +0 -70
  134. package/dist/worker/console/static/assets/journeyDiagram-NVQOT4AX-VPKAUX8M.js +0 -139
  135. package/dist/worker/console/static/assets/kanban-definition-27J2QSJJ-BL56hfGp.js +0 -89
  136. package/dist/worker/console/static/assets/layout-D-LzfAck.js +0 -1
  137. package/dist/worker/console/static/assets/linear-BmSG3nIZ.js +0 -1
  138. package/dist/worker/console/static/assets/map-DxJ2ADlA.js +0 -1
  139. package/dist/worker/console/static/assets/mermaid.core-DWGK9kwP.js +0 -308
  140. package/dist/worker/console/static/assets/mindmap-definition-FAOFIHXS--vvKLjo0.js +0 -96
  141. package/dist/worker/console/static/assets/ordinal-Cboi1Yqb.js +0 -1
  142. package/dist/worker/console/static/assets/pegDiagram-VL7TDLO6-B41mxb2-.js +0 -1
  143. package/dist/worker/console/static/assets/pieDiagram-7S7Q4E2Y-DTlu77sw.js +0 -39
  144. package/dist/worker/console/static/assets/quadrantDiagram-CIZ2JOQS-Bk5A7alv.js +0 -7
  145. package/dist/worker/console/static/assets/railroadDiagram-AXF67PYL-Csf9jzQy.js +0 -1
  146. package/dist/worker/console/static/assets/requirementDiagram-LRYGKXZP-CeptoSNx.js +0 -84
  147. package/dist/worker/console/static/assets/sankeyDiagram-W5VNT64P-A-DLCLer.js +0 -40
  148. package/dist/worker/console/static/assets/sequenceDiagram-SI44F4Z6-22VbPdOI.js +0 -162
  149. package/dist/worker/console/static/assets/sizeCapture-X5ZJPWSS-DWvAyBC9.js +0 -1
  150. package/dist/worker/console/static/assets/stateDiagram-OKZ733FA-qICZ75yT.js +0 -1
  151. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DzKvfHTX.js +0 -1
  152. package/dist/worker/console/static/assets/swimlanes-SLNWSIFB-xi7lcMyp.js +0 -2
  153. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Cxqr_qM1.js +0 -8
  154. package/dist/worker/console/static/assets/timeline-definition-Z64GVDOM-Bu6Nl6O2.js +0 -120
  155. package/dist/worker/console/static/assets/vennDiagram-T6HMQDX7-BzMz4Bn9.js +0 -34
  156. package/dist/worker/console/static/assets/wardleyDiagram-T6FBY63Y-CxaJnzc5.js +0 -78
  157. package/dist/worker/console/static/assets/xychartDiagram-ELKLHX3M-5JK-omQK.js +0 -7
  158. package/dist/worker/console/static/fonts/katex/KaTeX_AMS-Regular.woff2 +0 -0
  159. package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Bold.woff2 +0 -0
  160. package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Regular.woff2 +0 -0
  161. package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Bold.woff2 +0 -0
  162. package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Regular.woff2 +0 -0
  163. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Bold.woff2 +0 -0
  164. package/dist/worker/console/static/fonts/katex/KaTeX_Main-BoldItalic.woff2 +0 -0
  165. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Italic.woff2 +0 -0
  166. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Regular.woff2 +0 -0
  167. package/dist/worker/console/static/fonts/katex/KaTeX_Math-BoldItalic.woff2 +0 -0
  168. package/dist/worker/console/static/fonts/katex/KaTeX_Math-Italic.woff2 +0 -0
  169. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Bold.woff2 +0 -0
  170. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Italic.woff2 +0 -0
  171. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Regular.woff2 +0 -0
  172. package/dist/worker/console/static/fonts/katex/KaTeX_Script-Regular.woff2 +0 -0
  173. package/dist/worker/console/static/fonts/katex/KaTeX_Size1-Regular.woff2 +0 -0
  174. package/dist/worker/console/static/fonts/katex/KaTeX_Size2-Regular.woff2 +0 -0
  175. package/dist/worker/console/static/fonts/katex/KaTeX_Size3-Regular.woff2 +0 -0
  176. package/dist/worker/console/static/fonts/katex/KaTeX_Size4-Regular.woff2 +0 -0
  177. package/dist/worker/console/static/fonts/katex/KaTeX_Typewriter-Regular.woff2 +0 -0
  178. package/dist/worker/console/static-src/operator-chat/activity-journey.js +0 -125
  179. package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
  180. package/dist/worker/console/static-src/operator-chat/activity-references.js +0 -20
  181. package/dist/worker/console/static-src/operator-chat/slash-palette-layout.js +0 -24
  182. package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +0 -141
  183. package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
  184. package/dist/worker/observability/interrupt-eligibility.js +0 -264
  185. package/dist/workflows/dag/contract-output-registry.js +0 -14
  186. package/dist/workflows/dag/contract-validator-registrations.js +0 -8
  187. package/dist/workflows/dag/frontend-recovery-plan.js +0 -73
  188. package/dist/workflows/dag/frontend-recovery-root-manifest.js +0 -123
  189. package/dist/workflows/dag/frontend-recovery-run.js +0 -539
  190. package/dist/workflows/dag/frontend-writer-recovery.js +0 -106
  191. package/dist/workflows/dag/frontend-writer-rollback.js +0 -821
  192. package/dist/workflows/dag/interrupt-request.js +0 -559
@@ -28,7 +28,6 @@
28
28
  import { createHash, randomBytes } from "node:crypto";
29
29
  import { access } from "node:fs/promises";
30
30
  import path from "node:path";
31
- import { extractAssistantContentBlocks, extractAssistantText, isAssistantSdkMessage, } from "./assistant-content.js";
32
31
  import { projectCompactSnapshot, } from "./chat-event-store.js";
33
32
  import { extractUsageSample } from "./usage.js";
34
33
  import { OPERATOR_CHAT_ALLOWED_TOOLS, authorizeOperatorChatTool, assertNoWriteToolInList, } from "./tools.js";
@@ -58,12 +57,12 @@ import { RUNTIME_CONTEXT_TEXT_MAX, redactRuntimeText, } from "./runtime-context.
58
57
  * (AC-01: ≤ 1900 chars and ≤ 80% of the 2498-char baseline).
59
58
  */
60
59
  export const OPERATOR_CHAT_SYSTEM_PROMPT_BASE = [
61
- "You are General Operator Chat.",
62
- "Use operator_* tools first. Pi read/write/edit/bash/grep/find/ls are available; obey repository AGENTS.md. apply_patch, full-tools, shell and coding-chat are denied. Prefer safe-read/safe-grep for sensitive probes.",
63
- "No harness and asked to init: `loop-agent init instructions --repo-root .`, then `loop-agent init --repo-root . --profile full --merge`; verify with doctor, inspect and docs audit. Updates: `loop-agent init upgrade --repo-root . --json` until stable; `loop-agent init check-update` is read-only.",
64
- "Long DAGs: prepareDagExecution runDag operationId. Never foreground-Bash task advance, tail/head pipes or nohup/Start-Process/start; do not end on accepted/queued/running or an operationId NOT completion. Supervise operationGet/status/dagReport/dagDoctor/operationWait with 60s 90s 120s 180s backoff; reset to 60s on state change; 30-60s re-checks when stall suspected. Every operationWait continuation MUST use afterSeq=previous nextSeq (prefer recommendedNextCall); never reuse an older cursor. Heartbeats are not meaningful progress.",
65
- "On failure consume terminal runSummary first. If progress exists, wait; else use a fresh dagRerunPlan; when ineligible use same-task rerun/advance, resume, or Worker retry. A run-failed task without an open gate is terminal: do not re-advance a run-failed task. Before run start, two identical lifecycleState/blocker/revision/DAG-hash observations mean no progress: stop and report after one bounded probe; never loop identical bash/read/grep. Auto-approve only bounded writeSet inside allowedPaths, outside forbiddenPaths, with structured verification.",
66
- "Continue until success, user stop, or no safe recovery remains; then stop and report failure/evidence. Never create a new task for a transient failure or replace repair-pi with direct edits.",
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, 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.",
67
66
  ].join("\n");
68
67
  /** Compose the inspectable system prompt actually injected into Operator Chat. */
69
68
  export function composeOperatorChatSystemPrompt(input) {
@@ -564,7 +563,28 @@ export function projectRuntimeSnapshot(input) {
564
563
  hasBash: input.hasBash,
565
564
  };
566
565
  }
567
- export { extractAssistantContentBlocks, extractAssistantText, isAssistantSdkMessage };
566
+ /**
567
+ * Extract the latest assistant text fragment from an SDK message object.
568
+ * Tolerant of multiple message shapes the SDK emits.
569
+ */
570
+ export function extractAssistantText(message) {
571
+ if (!message || typeof message !== "object")
572
+ return "";
573
+ const m = message;
574
+ if (typeof m.text === "string")
575
+ return m.text;
576
+ const contents = Array.isArray(m.content) ? m.content : [];
577
+ const parts = Array.isArray(m.parts) ? m.parts : [];
578
+ const segments = [];
579
+ for (const c of [...contents, ...parts]) {
580
+ if (!c || typeof c !== "object")
581
+ continue;
582
+ const seg = c;
583
+ if (typeof seg.text === "string")
584
+ segments.push(seg.text);
585
+ }
586
+ return segments.join("");
587
+ }
568
588
  function readSessionManagerId(manager) {
569
589
  if (!manager || typeof manager !== "object")
570
590
  return undefined;
@@ -678,12 +698,13 @@ export class ConsolePiRuntime {
678
698
  * closure state).
679
699
  */
680
700
  async buildResourceLoaderOptions(base = {}) {
701
+ const runtime = this;
681
702
  return {
682
703
  ...base,
683
704
  skillsOverride: (current) => {
684
705
  // Synchronous override surface: use last-known prefs via a sync
685
706
  // fail-closed path. Prefer async cache filled by ensurePrefs.
686
- const prefs = this.cachedSkillPrefs ?? {
707
+ const prefs = runtime.cachedSkillPrefs ?? {
687
708
  schemaVersion: 1,
688
709
  skills: {},
689
710
  };
@@ -903,27 +924,10 @@ export class ConsolePiRuntime {
903
924
  const customTools = await this.loadCustomTools();
904
925
  if (this.disposedSessions.has(sessionId))
905
926
  return;
906
- // The SDK filters BOTH built-in tools and customTools against
907
- // `tools` (allowed-tool names) inside _refreshToolRegistry:
908
- // isAllowedTool = (!allowedToolNames || allowedToolNames.has(name))
909
- // && !excludedToolNames.has(name)
910
- // Passing only OPERATOR_CHAT_BUILTIN_EXPLORE_TOOLS here silently dropped
911
- // every custom operator tool (prepareDagExecution / runDag /
912
- // operationWait / safe-read / git-status / ...) from the registry, so the
913
- // model could only ever see bash/read/write/edit/grep/find/ls. The
914
- // allowlist must cover the full tool surface that is later activated via
915
- // setActiveToolsByName(computeOperatorChatActiveToolNames(session)) —
916
- // operator actions + built-in explore tools + safe explore tools.
917
- // Hard write/coding channels stay blocked via excludeTools below (they
918
- // are removed even when present in the allowed list).
919
927
  const { session } = await this.bindings.createSessionFromServices({
920
928
  services,
921
929
  sessionManager: init.sessionManager,
922
- tools: [
923
- ...OPERATOR_CHAT_BUILTIN_EXPLORE_TOOLS,
924
- ...OPERATOR_CHAT_ALLOWED_TOOLS,
925
- ...OPERATOR_CHAT_SAFE_EXPLORE_TOOL_IDS,
926
- ],
930
+ tools: [...OPERATOR_CHAT_BUILTIN_EXPLORE_TOOLS],
927
931
  excludeTools: [
928
932
  "apply_patch",
929
933
  "apply-patch",
@@ -1276,7 +1280,6 @@ export class ConsolePiRuntime {
1276
1280
  * session is not active — callers fail closed instead of fabricating data.
1277
1281
  */
1278
1282
  /**
1279
- * ConsolePiRuntime.listSlashCommands
1280
1283
  * Browser-safe slash command projection for UI-11.
1281
1284
  * Sources: current Session extension commands, prompt templates, skills.
1282
1285
  * Never returns prompt/skill file bodies.
@@ -1289,36 +1292,29 @@ export class ConsolePiRuntime {
1289
1292
  .replace(/[A-Za-z]:\\Users\\[^\s]+/gi, "~")
1290
1293
  .slice(0, 240);
1291
1294
  };
1292
- // Fail soft at the HTTP layer: bubble readiness errors so routes can attach
1293
- // a compact warning while the UI keeps local Operator/Pi Web commands.
1294
- await this.ensureSessionReady(sessionId);
1295
+ try {
1296
+ await this.ensureSessionReady(sessionId);
1297
+ }
1298
+ catch {
1299
+ return [];
1300
+ }
1295
1301
  const session = this.sessions.get(sessionId);
1296
1302
  if (!session)
1297
- return { commands: [] };
1298
- // Same-name conflict priority (AC-3): extension > skill > prompt.
1299
- // Enum order must not decide the winner — prompt-before-skill still loses.
1300
- const SOURCE_RANK = {
1301
- extension: 0,
1302
- skill: 1,
1303
- prompt: 2,
1304
- };
1305
- const byKey = new Map();
1306
- const sourceWarnings = [];
1303
+ return [];
1304
+ const out = [];
1305
+ const seen = new Set();
1307
1306
  const push = (command, label, description, source) => {
1308
1307
  const name = command.startsWith("/") ? command : `/${command}`;
1309
1308
  const key = name.toLowerCase();
1310
- if (!key || key === "/")
1309
+ if (!key || key === "/" || seen.has(key))
1311
1310
  return;
1312
- const next = {
1311
+ seen.add(key);
1312
+ out.push({
1313
1313
  command: name,
1314
1314
  label: (label || name).slice(0, 80),
1315
1315
  description: scrub(description),
1316
1316
  source,
1317
- };
1318
- const existing = byKey.get(key);
1319
- if (!existing || SOURCE_RANK[source] < SOURCE_RANK[existing.source]) {
1320
- byKey.set(key, next);
1321
- }
1317
+ });
1322
1318
  };
1323
1319
  try {
1324
1320
  const cmds = session.extensionRunner?.getRegisteredCommands?.() ?? [];
@@ -1329,20 +1325,18 @@ export class ConsolePiRuntime {
1329
1325
  push(String(name), String(cmd.name || name), String(cmd.description || ""), "extension");
1330
1326
  }
1331
1327
  }
1332
- catch (error) {
1333
- // Source isolation: keep other sources; surface compact warning (AC-3).
1334
- sourceWarnings.push(`extension: ${scrub(error instanceof Error ? error.message : String(error))}`);
1328
+ catch {
1329
+ // non-blocking
1335
1330
  }
1336
1331
  try {
1337
- // Enumerate prompts before skills on purpose: priority must still let skill win.
1338
1332
  for (const tpl of session.promptTemplates ?? []) {
1339
1333
  if (!tpl?.name)
1340
1334
  continue;
1341
1335
  push(String(tpl.name), String(tpl.name), String(tpl.description || ""), "prompt");
1342
1336
  }
1343
1337
  }
1344
- catch (error) {
1345
- sourceWarnings.push(`prompt: ${scrub(error instanceof Error ? error.message : String(error))}`);
1338
+ catch {
1339
+ // non-blocking
1346
1340
  }
1347
1341
  try {
1348
1342
  const services = this.serviceScopes.get(sessionId);
@@ -1357,16 +1351,10 @@ export class ConsolePiRuntime {
1357
1351
  push(command, skill.name, String(skill.description || ""), "skill");
1358
1352
  }
1359
1353
  }
1360
- catch (error) {
1361
- sourceWarnings.push(`skill: ${scrub(error instanceof Error ? error.message : String(error))}`);
1354
+ catch {
1355
+ // non-blocking
1362
1356
  }
1363
- const warning = sourceWarnings.length > 0
1364
- ? sourceWarnings.join("; ").slice(0, 160)
1365
- : undefined;
1366
- return {
1367
- commands: [...byKey.values()],
1368
- ...(warning ? { warning } : {}),
1369
- };
1357
+ return out;
1370
1358
  }
1371
1359
  async getRuntimeSnapshot(sessionId, options) {
1372
1360
  try {
@@ -1833,17 +1821,10 @@ export class ConsolePiRuntime {
1833
1821
  : undefined;
1834
1822
  assertNoWriteToolInList(OPERATOR_CHAT_ALLOWED_TOOLS);
1835
1823
  const customTools = await this.loadCustomTools();
1836
- // Same allowlist rationale as materializeSession: the SDK filters
1837
- // customTools against `tools`, so the allowed names must cover the full
1838
- // operator + explore surface or every custom tool is silently dropped.
1839
1824
  const { session } = await this.bindings.createSessionFromServices({
1840
1825
  services,
1841
1826
  sessionManager: init.sessionManager,
1842
- tools: [
1843
- ...OPERATOR_CHAT_BUILTIN_EXPLORE_TOOLS,
1844
- ...OPERATOR_CHAT_ALLOWED_TOOLS,
1845
- ...OPERATOR_CHAT_SAFE_EXPLORE_TOOL_IDS,
1846
- ],
1827
+ tools: [...OPERATOR_CHAT_BUILTIN_EXPLORE_TOOLS],
1847
1828
  excludeTools: [
1848
1829
  "apply_patch",
1849
1830
  "apply-patch",
@@ -1963,31 +1944,6 @@ export class ConsolePiRuntime {
1963
1944
  if (usage)
1964
1945
  onEvent({ type: "usage", sessionId, usage });
1965
1946
  }
1966
- // Context usage rides the usage event: refresh on agent_settled so the
1967
- // bar reflects the post-turn context window fill (pi-web parity).
1968
- if (event.type === "agent_settled") {
1969
- try {
1970
- const stats = session.getSessionStats?.();
1971
- const contextUsage = stats?.contextUsage;
1972
- if (contextUsage) {
1973
- onEvent({
1974
- type: "usage",
1975
- sessionId,
1976
- usage: {
1977
- inputTokens: 0,
1978
- outputTokens: 0,
1979
- totalTokens: 0,
1980
- ...(contextUsage.tokens === null
1981
- ? {}
1982
- : { contextUsage }),
1983
- },
1984
- });
1985
- }
1986
- }
1987
- catch {
1988
- // Stats getter is best-effort; never break the event stream.
1989
- }
1990
- }
1991
1947
  });
1992
1948
  options?.signal?.addEventListener("abort", onAbort);
1993
1949
  if (options?.signal?.aborted) {
@@ -2160,15 +2116,6 @@ export class ConsolePiRuntime {
2160
2116
  this.dispose(id);
2161
2117
  }
2162
2118
  }
2163
- function mapAssistantMessageEvent(type, sessionId, message) {
2164
- const content = extractAssistantContentBlocks(message);
2165
- return {
2166
- type,
2167
- sessionId,
2168
- text: extractAssistantText(message),
2169
- ...(content.length > 0 ? { content } : {}),
2170
- };
2171
- }
2172
2119
  function mapSdkEvent(sessionId, event) {
2173
2120
  switch (event.type) {
2174
2121
  case "agent_start":
@@ -2180,13 +2127,17 @@ function mapSdkEvent(sessionId, event) {
2180
2127
  willRetry: Boolean(event.willRetry),
2181
2128
  };
2182
2129
  case "message_update":
2183
- if (!isAssistantSdkMessage(event.message))
2184
- return undefined;
2185
- return mapAssistantMessageEvent("message_update", sessionId, event.message);
2130
+ return {
2131
+ type: "message_update",
2132
+ sessionId,
2133
+ text: extractAssistantText(event.message),
2134
+ };
2186
2135
  case "message_end":
2187
- if (!isAssistantSdkMessage(event.message))
2188
- return undefined;
2189
- return mapAssistantMessageEvent("message_end", sessionId, event.message);
2136
+ return {
2137
+ type: "message_end",
2138
+ sessionId,
2139
+ text: extractAssistantText(event.message),
2140
+ };
2190
2141
  case "turn_end":
2191
2142
  // Usage is emitted from message_end (see prompt subscribe). turn_end
2192
2143
  // carries the same final assistant message and would double-count.
@@ -12,10 +12,9 @@
12
12
  * Mutation routes go through the Console mutation gate (boot token + Origin).
13
13
  * Read routes (GET capabilities / sessions) are open to loopback.
14
14
  */
15
- import { parseAssistantContent } from "./assistant-content.js";
16
15
  import { OPERATOR_CHAT_ALLOWED_TOOLS, OPERATOR_CHAT_DENIED_TOOLS, OPERATOR_CHAT_DENIED_OPERATOR_ACTIONS, } from "./tools.js";
17
16
  import { buildModelCallableToolSchemas } from "./tool-adapter.js";
18
- import { cleanChatTitle, acceptedOperationIdFromResult } from "./session-store.js";
17
+ import { cleanChatTitle } from "./session-store.js";
19
18
  import { hashChatTurnPayload, projectChatToolEventPayload, projectCompactSnapshot, } from "./chat-event-store.js";
20
19
  import { THINKING_LEVELS, RuntimeSnapshotUnavailableError, } from "./pi-runtime.js";
21
20
  import { ResourcePreferencesError, setSkillAutoInvocationPreference, } from "./resource-preferences-store.js";
@@ -84,16 +83,12 @@ function turnEventToStorePartial(event) {
84
83
  case "agent_start":
85
84
  return { kind: "agent_start", data: {} };
86
85
  case "message_update":
87
- case "message_end": {
88
- const content = parseAssistantContent(event.content);
89
86
  return {
90
- kind: event.type,
91
- data: {
92
- text: String(event.text ?? ""),
93
- ...(content ? { content } : {}),
94
- },
87
+ kind: "message_update",
88
+ data: { text: String(event.text ?? "") },
95
89
  };
96
- }
90
+ case "message_end":
91
+ return { kind: "message_end", data: { text: String(event.text ?? "") } };
97
92
  case "usage":
98
93
  return { kind: "usage", data: (event.usage ?? {}) };
99
94
  case "tool_call":
@@ -106,10 +101,7 @@ function turnEventToStorePartial(event) {
106
101
  value: event.args,
107
102
  }),
108
103
  };
109
- case "tool_result": {
110
- // Plan A (2026-08-16): surface the accepted-operation link on the live
111
- // SSE frame so the frontend semantic event can resolve operation:<id>.
112
- const resultOperationId = acceptedOperationIdFromResult(String(event.toolName ?? ""), event.result);
104
+ case "tool_result":
113
105
  return {
114
106
  kind: "tool_result",
115
107
  data: projectChatToolEventPayload({
@@ -118,10 +110,8 @@ function turnEventToStorePartial(event) {
118
110
  toolName: String(event.toolName ?? ""),
119
111
  value: event.result,
120
112
  isError: Boolean(event.isError),
121
- ...(resultOperationId ? { operationId: resultOperationId } : {}),
122
113
  }),
123
114
  };
124
- }
125
115
  case "agent_end":
126
116
  return {
127
117
  kind: "agent_end",
@@ -1655,24 +1645,14 @@ async function handleChatFiles(res, deps, sessionId, query) {
1655
1645
  }
1656
1646
  export async function handleChatCommands(res, deps, sessionId) {
1657
1647
  try {
1658
- const listed = typeof deps.runtime.listSlashCommands === "function"
1648
+ const commands = typeof deps.runtime.listSlashCommands === "function"
1659
1649
  ? await deps.runtime.listSlashCommands(sessionId)
1660
- : { commands: [] };
1661
- // Partial single-source failures keep successful commands + compact warning (AC-3).
1662
- const commands = listed.commands ?? [];
1663
- const warning = typeof listed.warning === "string" && listed.warning.trim()
1664
- ? listed.warning.trim().slice(0, 160)
1665
- : undefined;
1650
+ : [];
1666
1651
  res.statusCode = 200;
1667
1652
  res.setHeader("content-type", "application/json; charset=utf-8");
1668
- res.end(JSON.stringify({
1669
- ok: true,
1670
- commands,
1671
- ...(warning ? { warning } : {}),
1672
- }));
1653
+ res.end(JSON.stringify({ ok: true, commands }));
1673
1654
  }
1674
1655
  catch (error) {
1675
- // Whole-list / readiness failures stay fail-soft with empty commands + warning.
1676
1656
  res.statusCode = 200;
1677
1657
  res.setHeader("content-type", "application/json; charset=utf-8");
1678
1658
  res.end(JSON.stringify({
@@ -2819,21 +2799,8 @@ export async function handleChatCompact(req, res, deps, sessionId) {
2819
2799
  return;
2820
2800
  }
2821
2801
  }
2822
- let body = {};
2823
- try {
2824
- body = await readJsonBody(req);
2825
- }
2826
- catch {
2827
- sendJson(res, 400, {
2828
- ok: false,
2829
- error: { code: "INVALID_INPUT", message: "invalid json body" },
2830
- });
2831
- return;
2832
- }
2833
- const rawInstructions = typeof body.instructions === "string" ? body.instructions : undefined;
2834
- const customInstructions = rawInstructions?.trim() || undefined;
2835
2802
  try {
2836
- const snapshot = await deps.runtime.compact(sessionId, customInstructions);
2803
+ const snapshot = await deps.runtime.compact(sessionId);
2837
2804
  const event = deps.events.append(sessionId, deps.events.latestTurnId(sessionId) ?? `${sessionId}:compact`, { kind: "compact", data: snapshot });
2838
2805
  sendJson(res, 200, { ok: true, snapshot, eventId: event.eventId });
2839
2806
  }
@@ -368,9 +368,6 @@ export async function runChatTurn(options) {
368
368
  pending.push(store.appendMessage(sessionId, {
369
369
  role: "assistant",
370
370
  text: event.text,
371
- ...(event.content && event.content.length > 0
372
- ? { content: event.content }
373
- : {}),
374
371
  }));
375
372
  }
376
373
  break;
@@ -402,11 +399,6 @@ export async function runChatTurn(options) {
402
399
  toolCallId: event.toolCallId,
403
400
  toolName: event.toolName,
404
401
  isError: event.isError,
405
- // Durable tool→operation link (plan A): the restored timeline can
406
- // resolve operation:<id> refs without the live operation-ref event.
407
- ...(acceptedOperationId
408
- ? { operationId: acceptedOperationId }
409
- : {}),
410
402
  }));
411
403
  pending.push(store.recordToolInvocation(sessionId, {
412
404
  toolCallId: event.toolCallId,
@@ -436,7 +428,7 @@ export async function runChatTurn(options) {
436
428
  * huge operator-action payload (e.g. a full dagReport) cannot bloat the chat
437
429
  * history record.
438
430
  */
439
- export function acceptedOperationIdFromResult(toolName, result) {
431
+ function acceptedOperationIdFromResult(toolName, result) {
440
432
  const candidates = [result];
441
433
  if (result && typeof result === "object") {
442
434
  const record = result;
@@ -41,6 +41,13 @@ export const SHORTCUTS = [
41
41
  action: "reload",
42
42
  source: "operator",
43
43
  },
44
+ {
45
+ command: "/help",
46
+ label: "Shortcuts",
47
+ description: "显示可用 shortcut",
48
+ action: "show-help",
49
+ source: "operator",
50
+ },
44
51
  ];
45
52
  /** Pi Web commands with real browser-equivalent behavior (UI-11). */
46
53
  export const WEB_SLASH_COMMANDS = [
@@ -96,15 +103,6 @@ export function resolveShortcut(input) {
96
103
  return { command: local.command, action: local.action };
97
104
  return { command: token, action: "unknown" };
98
105
  }
99
- /** Map Operator panel shortcuts onto Console workspace routes. */
100
- export function workspaceForShortcut(action) {
101
- if (action === "focus-panel:task" || action === "focus-panel:contract") {
102
- return "tasks";
103
- }
104
- if (action === "focus-panel:dag")
105
- return "recovery";
106
- return undefined;
107
- }
108
106
  /**
109
107
  * Merge Operator, Web, and dynamic Session commands with conflict dedupe.
110
108
  * Priority: operator/web > extension > skill > prompt. /reload is single-card.