@chengchenccc/oh-my-agent 0.2.0-rc.2 → 0.2.0-rc.3

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 (209) hide show
  1. package/README.md +11 -6
  2. package/dist/cli/args.d.ts +1 -1
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +9 -2
  5. package/dist/cli/gateway-commands.d.ts.map +1 -1
  6. package/dist/cli/gateway-commands.js +35 -5
  7. package/dist/cli/initial-input.d.ts +3 -2
  8. package/dist/cli/initial-input.d.ts.map +1 -1
  9. package/dist/core/coordination/registry.d.ts +11 -0
  10. package/dist/core/coordination/registry.d.ts.map +1 -1
  11. package/dist/core/coordination/registry.js +23 -0
  12. package/dist/core/gateway/artifact.d.ts +0 -1
  13. package/dist/core/gateway/artifact.d.ts.map +1 -1
  14. package/dist/core/gateway/artifact.js +1 -1
  15. package/dist/core/goals/index.d.ts +8 -0
  16. package/dist/core/goals/index.d.ts.map +1 -0
  17. package/dist/core/goals/index.js +7 -0
  18. package/dist/core/goals/runtime.d.ts +112 -0
  19. package/dist/core/goals/runtime.d.ts.map +1 -0
  20. package/dist/core/goals/runtime.js +241 -0
  21. package/dist/core/goals/state.d.ts +82 -0
  22. package/dist/core/goals/state.d.ts.map +1 -0
  23. package/dist/core/goals/state.js +225 -0
  24. package/dist/core/goals/tool.d.ts +10 -0
  25. package/dist/core/goals/tool.d.ts.map +1 -0
  26. package/dist/core/goals/tool.js +87 -0
  27. package/dist/core/index.d.ts.map +1 -1
  28. package/dist/core/index.js +4 -3
  29. package/dist/core/loops/condition.d.ts +57 -0
  30. package/dist/core/loops/condition.d.ts.map +1 -0
  31. package/dist/core/loops/condition.js +153 -0
  32. package/dist/core/loops/index.d.ts +9 -0
  33. package/dist/core/loops/index.d.ts.map +1 -0
  34. package/dist/core/loops/index.js +8 -0
  35. package/dist/core/loops/limits.d.ts +43 -0
  36. package/dist/core/loops/limits.d.ts.map +1 -0
  37. package/dist/core/loops/limits.js +219 -0
  38. package/dist/core/loops/ralph.d.ts +37 -0
  39. package/dist/core/loops/ralph.d.ts.map +1 -0
  40. package/dist/core/loops/ralph.js +72 -0
  41. package/dist/core/loops/runtime.d.ts +95 -0
  42. package/dist/core/loops/runtime.d.ts.map +1 -0
  43. package/dist/core/loops/runtime.js +222 -0
  44. package/dist/core/memory/managed-skills.d.ts +0 -11
  45. package/dist/core/memory/managed-skills.d.ts.map +1 -1
  46. package/dist/core/memory/managed-skills.js +2 -2
  47. package/dist/core/memory/vector-store.d.ts +0 -1
  48. package/dist/core/memory/vector-store.d.ts.map +1 -1
  49. package/dist/core/memory/vector-store.js +0 -3
  50. package/dist/core/plans/index.d.ts +7 -0
  51. package/dist/core/plans/index.d.ts.map +1 -0
  52. package/dist/core/plans/index.js +6 -0
  53. package/dist/core/plans/prompts.d.ts +20 -0
  54. package/dist/core/plans/prompts.d.ts.map +1 -0
  55. package/dist/core/plans/prompts.js +72 -0
  56. package/dist/core/plans/runtime.d.ts +89 -0
  57. package/dist/core/plans/runtime.d.ts.map +1 -0
  58. package/dist/core/plans/runtime.js +170 -0
  59. package/dist/core/plans/state.d.ts +55 -0
  60. package/dist/core/plans/state.d.ts.map +1 -0
  61. package/dist/core/plans/state.js +88 -0
  62. package/dist/core/runtime/agent-loop-messages.d.ts +6 -0
  63. package/dist/core/runtime/agent-loop-messages.d.ts.map +1 -1
  64. package/dist/core/runtime/agent-loop-messages.js +40 -15
  65. package/dist/core/runtime/agent-loop-run.d.ts.map +1 -1
  66. package/dist/core/runtime/agent-loop-run.js +18 -27
  67. package/dist/core/runtime/agent-loop-runner.d.ts +1 -23
  68. package/dist/core/runtime/agent-loop-runner.d.ts.map +1 -1
  69. package/dist/core/runtime/agent-loop-runner.js +84 -30
  70. package/dist/core/runtime/agent-loop-types.d.ts +4 -0
  71. package/dist/core/runtime/agent-loop-types.d.ts.map +1 -1
  72. package/dist/core/runtime/compaction.d.ts +9 -0
  73. package/dist/core/runtime/compaction.d.ts.map +1 -1
  74. package/dist/core/runtime/compaction.js +28 -0
  75. package/dist/core/runtime/context-estimate.d.ts +37 -4
  76. package/dist/core/runtime/context-estimate.d.ts.map +1 -1
  77. package/dist/core/runtime/context-estimate.js +70 -0
  78. package/dist/core/runtime/create-runtime.d.ts +27 -5
  79. package/dist/core/runtime/create-runtime.d.ts.map +1 -1
  80. package/dist/core/runtime/create-runtime.js +22 -2
  81. package/dist/core/runtime/permission-classifier.d.ts.map +1 -1
  82. package/dist/core/runtime/permission-classifier.js +83 -12
  83. package/dist/core/runtime/plugin.d.ts +7 -0
  84. package/dist/core/runtime/plugin.d.ts.map +1 -1
  85. package/dist/core/runtime/plugin.js +13 -0
  86. package/dist/core/runtime/process-tree.d.ts +0 -9
  87. package/dist/core/runtime/process-tree.d.ts.map +1 -1
  88. package/dist/core/runtime/process-tree.js +1 -1
  89. package/dist/core/runtime/run-runtime.d.ts.map +1 -1
  90. package/dist/core/runtime/run-runtime.js +88 -49
  91. package/dist/core/runtime/runtime-catalog.d.ts +0 -4
  92. package/dist/core/runtime/runtime-catalog.d.ts.map +1 -1
  93. package/dist/core/runtime/runtime-catalog.js +1 -1
  94. package/dist/core/runtime/stream-rules.d.ts +9 -2
  95. package/dist/core/runtime/stream-rules.d.ts.map +1 -1
  96. package/dist/core/runtime/stream-rules.js +19 -5
  97. package/dist/core/runtime/title.d.ts +13 -1
  98. package/dist/core/runtime/title.d.ts.map +1 -1
  99. package/dist/core/runtime/title.js +51 -17
  100. package/dist/core/runtime/tool-pruning.d.ts.map +1 -1
  101. package/dist/core/runtime/tool-pruning.js +8 -16
  102. package/dist/core/session/session-file.d.ts +91 -5
  103. package/dist/core/session/session-file.d.ts.map +1 -1
  104. package/dist/core/session/session-file.js +206 -24
  105. package/dist/core/session/session-loop.d.ts +3 -1
  106. package/dist/core/session/session-loop.d.ts.map +1 -1
  107. package/dist/core/session/session-loop.js +8 -4
  108. package/dist/core/settings/project-settings.d.ts +63 -9
  109. package/dist/core/settings/project-settings.d.ts.map +1 -1
  110. package/dist/core/settings/project-settings.js +43 -2
  111. package/dist/core/settings/workspace-context.js +4 -4
  112. package/dist/core/store/in-memory-session-store.d.ts.map +1 -1
  113. package/dist/core/store/in-memory-session-store.js +3 -2
  114. package/dist/core/store/session-store.contract.test-helper.d.ts +7 -5
  115. package/dist/core/store/session-store.contract.test-helper.d.ts.map +1 -1
  116. package/dist/core/store/session-store.contract.test-helper.js +7 -5
  117. package/dist/core/store/session-store.d.ts +4 -2
  118. package/dist/core/store/session-store.d.ts.map +1 -1
  119. package/dist/core/store/session-store.js +4 -2
  120. package/dist/core/tools/ask-question.d.ts.map +1 -1
  121. package/dist/core/tools/ask-question.js +51 -5
  122. package/dist/core/tools/bash-pty.d.ts +13 -4
  123. package/dist/core/tools/bash-pty.d.ts.map +1 -1
  124. package/dist/core/tools/bash-pty.js +15 -8
  125. package/dist/core/tools/bash-sandbox.d.ts +1 -1
  126. package/dist/core/tools/bash.d.ts +1 -1
  127. package/dist/core/tools/bash.d.ts.map +1 -1
  128. package/dist/core/tools/bash.js +18 -8
  129. package/dist/core/tools/browser.d.ts +54 -0
  130. package/dist/core/tools/browser.d.ts.map +1 -1
  131. package/dist/core/tools/browser.js +138 -1
  132. package/dist/core/tools/file-fingerprint.d.ts +24 -0
  133. package/dist/core/tools/file-fingerprint.d.ts.map +1 -0
  134. package/dist/core/tools/file-fingerprint.js +61 -0
  135. package/dist/core/tools/file-tools.d.ts +26 -0
  136. package/dist/core/tools/file-tools.d.ts.map +1 -1
  137. package/dist/core/tools/file-tools.js +113 -17
  138. package/dist/core/tools/index.d.ts +2 -0
  139. package/dist/core/tools/index.d.ts.map +1 -1
  140. package/dist/core/tools/index.js +1 -0
  141. package/dist/core/tools/mcp-mount.d.ts +1 -1
  142. package/dist/core/tools/mcp-mount.d.ts.map +1 -1
  143. package/dist/core/tools/mcp-mount.js +72 -26
  144. package/dist/core/tools/url-guard.d.ts +15 -0
  145. package/dist/core/tools/url-guard.d.ts.map +1 -1
  146. package/dist/core/tools/url-guard.js +54 -14
  147. package/dist/core/tools/web-ports-std.d.ts.map +1 -1
  148. package/dist/core/tools/web-ports-std.js +5 -2
  149. package/dist/modes/print-mode.d.ts +3 -2
  150. package/dist/modes/print-mode.d.ts.map +1 -1
  151. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  152. package/dist/modes/rpc/rpc-mode.js +2 -2
  153. package/dist/modes/shared.d.ts +12 -0
  154. package/dist/modes/shared.d.ts.map +1 -1
  155. package/dist/modes/shared.js +23 -6
  156. package/dist/modes/tui/agent-status.d.ts +20 -0
  157. package/dist/modes/tui/agent-status.d.ts.map +1 -0
  158. package/dist/modes/tui/agent-status.js +35 -0
  159. package/dist/modes/tui/ask-panel.d.ts +166 -0
  160. package/dist/modes/tui/ask-panel.d.ts.map +1 -0
  161. package/dist/modes/tui/ask-panel.js +888 -0
  162. package/dist/modes/tui/pty-console.d.ts +3 -0
  163. package/dist/modes/tui/pty-console.d.ts.map +1 -1
  164. package/dist/modes/tui/pty-console.js +19 -0
  165. package/dist/modes/tui/settings-overlay.d.ts +19 -0
  166. package/dist/modes/tui/settings-overlay.d.ts.map +1 -1
  167. package/dist/modes/tui/settings-overlay.js +46 -10
  168. package/dist/modes/tui/tui-commands-session.d.ts.map +1 -1
  169. package/dist/modes/tui/tui-commands-session.js +20 -6
  170. package/dist/modes/tui/tui-commands.d.ts +44 -3
  171. package/dist/modes/tui/tui-commands.d.ts.map +1 -1
  172. package/dist/modes/tui/tui-commands.js +380 -6
  173. package/dist/modes/tui/tui-components.d.ts +10 -0
  174. package/dist/modes/tui/tui-components.d.ts.map +1 -1
  175. package/dist/modes/tui/tui-components.js +26 -0
  176. package/dist/modes/tui/tui-format.d.ts +53 -4
  177. package/dist/modes/tui/tui-format.d.ts.map +1 -1
  178. package/dist/modes/tui/tui-format.js +137 -42
  179. package/dist/modes/tui/tui-frame-provider.d.ts +11 -3
  180. package/dist/modes/tui/tui-frame-provider.d.ts.map +1 -1
  181. package/dist/modes/tui/tui-frame-provider.js +14 -5
  182. package/dist/modes/tui/tui-interactive.d.ts.map +1 -1
  183. package/dist/modes/tui/tui-interactive.js +14 -0
  184. package/dist/modes/tui/tui-io.d.ts.map +1 -1
  185. package/dist/modes/tui/tui-io.js +150 -55
  186. package/dist/modes/tui/tui-mode.d.ts.map +1 -1
  187. package/dist/modes/tui/tui-mode.js +428 -23
  188. package/dist/modes/tui/tui-overlays.d.ts +6 -10
  189. package/dist/modes/tui/tui-overlays.d.ts.map +1 -1
  190. package/dist/modes/tui/tui-overlays.js +0 -43
  191. package/dist/modes/tui/tui-render.d.ts +10 -0
  192. package/dist/modes/tui/tui-render.d.ts.map +1 -1
  193. package/dist/modes/tui/tui-render.js +73 -38
  194. package/dist/modes/tui/tui-seam.d.ts +28 -6
  195. package/dist/modes/tui/tui-seam.d.ts.map +1 -1
  196. package/dist/modes/tui/tui-slash.d.ts.map +1 -1
  197. package/dist/modes/tui/tui-slash.js +3 -1
  198. package/dist/modes/tui/tui-tool-render.d.ts +14 -1
  199. package/dist/modes/tui/tui-tool-render.d.ts.map +1 -1
  200. package/dist/modes/tui/tui-tool-render.js +158 -50
  201. package/dist/modes/tui/tui-transcript-reconciler.d.ts +4 -0
  202. package/dist/modes/tui/tui-transcript-reconciler.d.ts.map +1 -1
  203. package/dist/modes/tui/tui-transcript-reconciler.js +12 -2
  204. package/dist/modes/tui/view-state.d.ts.map +1 -1
  205. package/dist/modes/tui/view-state.js +4 -1
  206. package/dist/protocol/transport.d.ts +10 -0
  207. package/dist/protocol/transport.d.ts.map +1 -1
  208. package/dist/protocol/transport.js +1 -0
  209. package/package.json +11 -11
@@ -0,0 +1,95 @@
1
+ import { type LoopConditionConfig, type LoopConditionOptions } from "./condition.js";
2
+ import { LOOP_USAGE, type LoopLimitRuntime } from "./limits.js";
3
+ /** What the loop does between iterations before re-submitting the prompt:
4
+ * re-send it, compact the context first, start a fresh session first, or run
5
+ * the build loop (fresh session + a work queue on disk, see ralph.ts). */
6
+ export type LoopAction = "prompt" | "compact" | "reset" | "ralph";
7
+ export type LoopStart = {
8
+ ok: true;
9
+ prompt?: string;
10
+ hidden?: true;
11
+ /** Build loop only: the text to seed into the work queue before the
12
+ * first iteration. Empty = the "write the queue" stub. The runtime
13
+ * decides THAT the queue must exist; the caller, which owns the
14
+ * workspace root, writes it. */
15
+ queueSeed?: string;
16
+ status: string;
17
+ } | {
18
+ ok: false;
19
+ error: string;
20
+ };
21
+ export type LoopIterationDecision = {
22
+ action: "run";
23
+ prompt: string;
24
+ preamble?: "compact" | "reset";
25
+ /** The prompt is protocol, not user text: the transcript must not echo
26
+ * it as a turn the user typed (`isHiddenInput` handles the wrapping). */
27
+ hidden?: true;
28
+ } | {
29
+ action: "stop";
30
+ reason: string;
31
+ } | {
32
+ action: "idle";
33
+ };
34
+ export interface LoopStatus {
35
+ state: "waiting" | "running" | "paused";
36
+ limit?: LoopLimitRuntime;
37
+ /** Remaining budget + continue-condition, for the status bar. */
38
+ label?: string;
39
+ }
40
+ /** Loop mode (the reference loop implementation (enabled / captured prompt / limit)).
41
+ *
42
+ * While enabled, the next prompt the user sends is remembered and
43
+ * re-submitted after every yield. The loop is NOT a scheduler: it fires only
44
+ * when the session is idle and the run has settled. There is no artificial
45
+ * pause between iterations — an unattended loop should not pay latency per
46
+ * round — so the interrupt windows are the real ones: the live run (Esc
47
+ * aborts it) and the continue-condition evaluation, whose verdict is voided
48
+ * if the arming changed while it ran. It is session-scoped and deliberately
49
+ * in-memory — a resumed session never re-enters a loop on its own (same rule
50
+ * as goal mode). */
51
+ export declare class LoopRuntime {
52
+ #private;
53
+ private readonly action;
54
+ constructor(action?: LoopAction);
55
+ get enabled(): boolean;
56
+ get paused(): boolean;
57
+ get prompt(): string | undefined;
58
+ get limit(): LoopLimitRuntime | undefined;
59
+ get condition(): LoopConditionConfig | undefined;
60
+ /** Human-readable condition, for status messages. */
61
+ get conditionLabel(): string | undefined;
62
+ /** The status-bar indicator: waiting / running / paused plus the remaining
63
+ * budget and the condition. The driver owns its wording. */
64
+ statusLabel(): string | undefined;
65
+ /** What the user should call this loop. The build loop is a different way of
66
+ * working (a queue and a fresh session per item), so it names itself; the
67
+ * three context actions all stay "loop". One source for the name keeps the
68
+ * status bar, the paused label and the caller's status prefix agreeing. */
69
+ get driverName(): string;
70
+ status(): LoopStatus | undefined;
71
+ /** `/loop [count|duration] [--while|--until <cmd>] [prompt]`. Already
72
+ * enabled → disable (toggle), matching the reference loop. A malformed
73
+ * limit is a hard error and leaves the mode alone. */
74
+ toggle(args: string, nowMs?: number): LoopStart;
75
+ /** The user's prompt becomes the loop prompt (the reference loop's
76
+ * setLoopPrompt: also clears a pause — the next user prompt re-arms it).
77
+ * Called on EVERY user submit, so the newest prompt wins; a loop keeps
78
+ * replaying whatever the user last typed. */
79
+ capturePrompt(text: string): void;
80
+ /** Pause without exiting: drop the captured prompt, keep mode enabled (omp
81
+ * pauseLoop — Esc between iterations lands here). */
82
+ pause(): void;
83
+ disable(reason?: string): string;
84
+ /** One settled run: may the loop re-submit?
85
+ *
86
+ * Order matters. The condition gate runs BEFORE the budget is consumed, so
87
+ * a halt never burns an iteration that did not run; the duration deadline
88
+ * runs first of all, because an expired clock ends the loop regardless of
89
+ * what the condition would have said. */
90
+ nextIteration(opts?: Omit<LoopConditionOptions, "timeoutMs"> & {
91
+ timeoutMs?: number;
92
+ }, nowMs?: number): Promise<LoopIterationDecision>;
93
+ }
94
+ export { LOOP_USAGE };
95
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/core/loops/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EAC1B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAML,UAAU,EACV,KAAK,gBAAgB,EAGtB,MAAM,aAAa,CAAC;AAGrB;;2EAE2E;AAC3E,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAElE,MAAM,MAAM,SAAS,GACjB;IACE,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,IAAI,CAAC;IACd;;;qCAGiC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjC,MAAM,MAAM,qBAAqB,GAC7B;IACE,MAAM,EAAE,KAAK,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;IAC/B;8EAC0E;IAC1E,MAAM,CAAC,EAAE,IAAI,CAAC;CACf,GACD;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvB,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IACxC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;qBAUqB;AACrB,qBAAa,WAAW;;IAcV,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,GAAE,UAAqB;IAE1D,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,IAAI,MAAM,IAAI,MAAM,GAAG,SAAS,CAE/B;IAED,IAAI,KAAK,IAAI,gBAAgB,GAAG,SAAS,CAExC;IAED,IAAI,SAAS,IAAI,mBAAmB,GAAG,SAAS,CAE/C;IAED,qDAAqD;IACrD,IAAI,cAAc,IAAI,MAAM,GAAG,SAAS,CAEvC;IAED;iEAC6D;IAC7D,WAAW,IAAI,MAAM,GAAG,SAAS;IAOjC;;;gFAG4E;IAC5E,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,MAAM,IAAI,UAAU,GAAG,SAAS;IAoBhC;;2DAEuD;IACvD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAa,GAAG,SAAS;IA6CnD;;;kDAG8C;IAC9C,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAOjC;0DACsD;IACtD,KAAK,IAAI,IAAI;IAOb,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM;IAWhC;;;;;8CAK0C;IACpC,aAAa,CACjB,IAAI,GAAE,IAAI,CAAC,oBAAoB,EAAE,WAAW,CAAC,GAAG;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAEnE,EACD,KAAK,SAAa,GACjB,OAAO,CAAC,qBAAqB,CAAC;CAyClC;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
@@ -0,0 +1,222 @@
1
+ import { DEFAULT_CONDITION_TIMEOUT_MS, describeLoopCondition, evaluateLoopCondition, } from "./condition.js";
2
+ import { consumeLoopLimitIteration, createLoopLimitRuntime, describeLoopLimit, describeLoopLimitRuntime, isLoopDurationExpired, LOOP_USAGE, parseLoopArgs, } from "./limits.js";
3
+ import { RALPH_PROTOCOL, RALPH_QUEUE, ralphCondition } from "./ralph.js";
4
+ /** Loop mode (the reference loop implementation (enabled / captured prompt / limit)).
5
+ *
6
+ * While enabled, the next prompt the user sends is remembered and
7
+ * re-submitted after every yield. The loop is NOT a scheduler: it fires only
8
+ * when the session is idle and the run has settled. There is no artificial
9
+ * pause between iterations — an unattended loop should not pay latency per
10
+ * round — so the interrupt windows are the real ones: the live run (Esc
11
+ * aborts it) and the continue-condition evaluation, whose verdict is voided
12
+ * if the arming changed while it ran. It is session-scoped and deliberately
13
+ * in-memory — a resumed session never re-enters a loop on its own (same rule
14
+ * as goal mode). */
15
+ export class LoopRuntime {
16
+ action;
17
+ #enabled = false;
18
+ #paused = false;
19
+ #prompt;
20
+ #limit;
21
+ #condition;
22
+ /** Bumped by every transition that re-arms or disarms the loop. A condition
23
+ * verdict is only valid for the arming it was computed for: the evaluation
24
+ * is a window of up to two minutes during which `/loop`, Esc and the user's
25
+ * next prompt can all land, and a disable-then-re-arm inside it used to look
26
+ * exactly like "still armed" to the three-way check that tried to catch it
27
+ * by comparing flags. */
28
+ #generation = 0;
29
+ constructor(action = "prompt") {
30
+ this.action = action;
31
+ }
32
+ get enabled() {
33
+ return this.#enabled;
34
+ }
35
+ get paused() {
36
+ return this.#paused;
37
+ }
38
+ get prompt() {
39
+ return this.#prompt;
40
+ }
41
+ get limit() {
42
+ return this.#limit;
43
+ }
44
+ get condition() {
45
+ return this.#condition;
46
+ }
47
+ /** Human-readable condition, for status messages. */
48
+ get conditionLabel() {
49
+ return this.#condition ? describeLoopCondition(this.#condition) : undefined;
50
+ }
51
+ /** The status-bar indicator: waiting / running / paused plus the remaining
52
+ * budget and the condition. The driver owns its wording. */
53
+ statusLabel() {
54
+ const status = this.status();
55
+ if (!status)
56
+ return undefined;
57
+ if (status.state === "paused")
58
+ return `⏸ ${this.driverName} paused`;
59
+ return `↻ ${this.driverName}${status.label ? ` ${status.label}` : ""}`;
60
+ }
61
+ /** What the user should call this loop. The build loop is a different way of
62
+ * working (a queue and a fresh session per item), so it names itself; the
63
+ * three context actions all stay "loop". One source for the name keeps the
64
+ * status bar, the paused label and the caller's status prefix agreeing. */
65
+ get driverName() {
66
+ return this.action === "ralph" ? "ralph" : "loop";
67
+ }
68
+ status() {
69
+ if (!this.#enabled)
70
+ return undefined;
71
+ // The build loop needs no captured prompt: its protocol is fixed and the
72
+ // queue is on disk, so an enabled build loop is running, not waiting.
73
+ const armed = this.action === "ralph" || this.#prompt !== undefined;
74
+ const state = this.#paused ? "paused" : armed ? "running" : "waiting";
75
+ const status = { state };
76
+ if (this.#limit)
77
+ status.limit = this.#limit;
78
+ // The bar shows WHAT will decide the next iteration, not just that one is
79
+ // pending: the remaining budget and, when set, the continue-condition.
80
+ const label = [
81
+ this.#limit ? describeLoopLimitRuntime(this.#limit) : undefined,
82
+ this.conditionLabel,
83
+ ]
84
+ .filter(Boolean)
85
+ .join(" · ");
86
+ if (label)
87
+ status.label = label;
88
+ return status;
89
+ }
90
+ /** `/loop [count|duration] [--while|--until <cmd>] [prompt]`. Already
91
+ * enabled → disable (toggle), matching the reference loop. A malformed
92
+ * limit is a hard error and leaves the mode alone. */
93
+ toggle(args, nowMs = Date.now()) {
94
+ if (this.#enabled) {
95
+ this.disable();
96
+ return { ok: true, status: "loop mode disabled" };
97
+ }
98
+ const parsed = parseLoopArgs(args);
99
+ if (typeof parsed === "string")
100
+ return { ok: false, error: parsed };
101
+ return this.#arm(parsed, nowMs);
102
+ }
103
+ #arm(parsed, nowMs) {
104
+ const build = this.action === "ralph";
105
+ this.#generation++;
106
+ this.#enabled = true;
107
+ this.#paused = false;
108
+ // The build loop's prompt is its protocol, never a captured user turn, so
109
+ // the parsed trailing text seeds the queue instead.
110
+ this.#prompt = build ? undefined : parsed.prompt;
111
+ this.#limit = createLoopLimitRuntime(parsed.limit, nowMs);
112
+ // The queue is the build loop's default authority on whether to continue;
113
+ // an explicit --while/--until replaces it.
114
+ this.#condition = parsed.condition ?? (build ? ralphCondition() : undefined);
115
+ const limitSuffix = parsed.limit ? ` limited to ${describeLoopLimit(parsed.limit)}` : "";
116
+ const conditionSuffix = this.#condition ? ` — ${describeLoopCondition(this.#condition)}` : "";
117
+ const remaining = this.#limit ? ` (${describeLoopLimitRuntime(this.#limit)})` : "";
118
+ if (build) {
119
+ return {
120
+ ok: true,
121
+ prompt: RALPH_PROTOCOL,
122
+ hidden: true,
123
+ // The item seeds the queue; absent means iteration one writes it.
124
+ queueSeed: parsed.prompt ?? "",
125
+ status: `build loop enabled${limitSuffix}${remaining}${conditionSuffix} — one ${RALPH_QUEUE} item per fresh session; /loop again disables, Esc pauses`,
126
+ };
127
+ }
128
+ const tail = parsed.prompt
129
+ ? "repeating it after each turn"
130
+ : "your next prompt will repeat after each turn";
131
+ return {
132
+ ok: true,
133
+ ...(parsed.prompt ? { prompt: parsed.prompt } : {}),
134
+ status: `loop mode enabled${limitSuffix}${remaining}${conditionSuffix} — ${tail}; /loop again disables, Esc pauses`,
135
+ };
136
+ }
137
+ /** The user's prompt becomes the loop prompt (the reference loop's
138
+ * setLoopPrompt: also clears a pause — the next user prompt re-arms it).
139
+ * Called on EVERY user submit, so the newest prompt wins; a loop keeps
140
+ * replaying whatever the user last typed. */
141
+ capturePrompt(text) {
142
+ if (!this.#enabled)
143
+ return;
144
+ this.#generation++;
145
+ this.#prompt = text;
146
+ this.#paused = false;
147
+ }
148
+ /** Pause without exiting: drop the captured prompt, keep mode enabled (omp
149
+ * pauseLoop — Esc between iterations lands here). */
150
+ pause() {
151
+ if (!this.#enabled)
152
+ return;
153
+ this.#generation++;
154
+ this.#prompt = undefined;
155
+ this.#paused = true;
156
+ }
157
+ disable(reason) {
158
+ const wasEnabled = this.#enabled;
159
+ this.#generation++;
160
+ this.#enabled = false;
161
+ this.#paused = false;
162
+ this.#prompt = undefined;
163
+ this.#limit = undefined;
164
+ this.#condition = undefined;
165
+ return wasEnabled && reason ? reason : "loop mode disabled";
166
+ }
167
+ /** One settled run: may the loop re-submit?
168
+ *
169
+ * Order matters. The condition gate runs BEFORE the budget is consumed, so
170
+ * a halt never burns an iteration that did not run; the duration deadline
171
+ * runs first of all, because an expired clock ends the loop regardless of
172
+ * what the condition would have said. */
173
+ async nextIteration(opts = {
174
+ cwd: process.cwd(),
175
+ }, nowMs = Date.now()) {
176
+ if (!this.#enabled)
177
+ return { action: "idle" };
178
+ // The verdict below is only valid for THIS arming; see #generation.
179
+ const generation = this.#generation;
180
+ // The build loop re-injects its protocol rather than a captured turn, so
181
+ // "waiting for a prompt" does not apply to it — but a pause does: pause()
182
+ // is the only thing that must stop it (Esc between iterations).
183
+ const build = this.action === "ralph";
184
+ const prompt = build ? RALPH_PROTOCOL : this.#prompt;
185
+ if (prompt === undefined || this.#paused)
186
+ return { action: "idle" };
187
+ if (isLoopDurationExpired(this.#limit, nowMs)) {
188
+ return { action: "stop", reason: "loop time limit reached" };
189
+ }
190
+ if (this.#condition) {
191
+ const verdict = await evaluateLoopCondition(this.#condition, {
192
+ ...opts,
193
+ timeoutMs: opts.timeoutMs ?? DEFAULT_CONDITION_TIMEOUT_MS,
194
+ });
195
+ if (verdict.kind === "aborted")
196
+ return { action: "idle" };
197
+ // The await above is a window: Esc (pause), /loop (disable) and a
198
+ // disable-then-re-arm can all land while the condition runs, which makes
199
+ // ANY verdict stale — a user who paused, or who restarted the loop, must
200
+ // not have it stopped out from under them by a halt they never saw
201
+ // coming. Comparing arming generations covers all of them at once,
202
+ // including the re-arm that leaves every flag looking unchanged.
203
+ if (this.#generation !== generation)
204
+ return { action: "idle" };
205
+ if (verdict.kind === "halt" || verdict.kind === "error") {
206
+ return { action: "stop", reason: verdict.message };
207
+ }
208
+ }
209
+ if (!consumeLoopLimitIteration(this.#limit, nowMs)) {
210
+ return { action: "stop", reason: "loop limit reached" };
211
+ }
212
+ // The build loop restarts the session every iteration: stale context is
213
+ // the failure mode it exists to avoid, and the queue carries what matters.
214
+ if (build)
215
+ return { action: "run", prompt, preamble: "reset", hidden: true };
216
+ if (this.action === "compact" || this.action === "reset") {
217
+ return { action: "run", prompt, preamble: this.action };
218
+ }
219
+ return { action: "run", prompt };
220
+ }
221
+ }
222
+ export { LOOP_USAGE };
@@ -1,16 +1,5 @@
1
- /** Managed-skills primitives for the `learn` / `manage_skill` tools (omp
2
- * managed-skills port). Managed skills live isolated in
3
- * `<agentDir>/managed-skills` and resolve dead-last in skill discovery, so
4
- * an authored skill of the same name always shadows them. The
5
- * user-authored roots are never written by this module. */
6
- /** Hard cap on a managed SKILL.md body to keep generated skills bounded. */
7
- export declare const MAX_MANAGED_SKILL_BYTES = 64000;
8
1
  /** Resolve the isolated managed-skills directory. */
9
2
  export declare function managedSkillsDir(): string;
10
- /** Validate + normalize a managed-skill name. Throws on anything outside the
11
- * strict allowlist so a bad name can never escape managedSkillsDir()
12
- * (blocks `..`, slashes, empty, and uppercase). */
13
- export declare function sanitizeSkillName(raw: string): string;
14
3
  /** Neutralize a machine-generated managed-skill description so it cannot
15
4
  * break out of the system-prompt skill index (strip control/format chars,
16
5
  * angle brackets, fence delimiters; collapse to one line). Applied on write
@@ -1 +1 @@
1
- {"version":3,"file":"managed-skills.d.ts","sourceRoot":"","sources":["../../../src/core/memory/managed-skills.ts"],"names":[],"mappings":"AAKA;;;;4DAI4D;AAE5D,4EAA4E;AAC5E,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAI9C,qDAAqD;AACrD,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;oDAEoD;AACpD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQrD;AAED;;;8CAG8C;AAC9C,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO9D;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAoBD;iFACiF;AACjF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAiEjF;AAED,uEAAuE;AACvE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAiBrD;AAED;2EAC2E;AAC3E,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,sBAAsB,GAAG,IAAI,CASzE;AAED,qEAAqE;AACrE,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAUxF;AAED,+DAA+D;AAC/D,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7D"}
1
+ {"version":3,"file":"managed-skills.d.ts","sourceRoot":"","sources":["../../../src/core/memory/managed-skills.ts"],"names":[],"mappings":"AAgBA,qDAAqD;AACrD,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAeD;;;8CAG8C;AAC9C,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO9D;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAoBD;iFACiF;AACjF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAiEjF;AAED,uEAAuE;AACvE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAiBrD;AAED;2EAC2E;AAC3E,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,sBAAsB,GAAG,IAAI,CASzE;AAED,qEAAqE;AACrE,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAUxF;AAED,+DAA+D;AAC/D,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7D"}
@@ -8,7 +8,7 @@ import { buildSkillIndex } from "../tools/skills.js";
8
8
  * an authored skill of the same name always shadows them. The
9
9
  * user-authored roots are never written by this module. */
10
10
  /** Hard cap on a managed SKILL.md body to keep generated skills bounded. */
11
- export const MAX_MANAGED_SKILL_BYTES = 64_000;
11
+ const MAX_MANAGED_SKILL_BYTES = 64_000;
12
12
  const SKILL_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
13
13
  /** Resolve the isolated managed-skills directory. */
14
14
  export function managedSkillsDir() {
@@ -17,7 +17,7 @@ export function managedSkillsDir() {
17
17
  /** Validate + normalize a managed-skill name. Throws on anything outside the
18
18
  * strict allowlist so a bad name can never escape managedSkillsDir()
19
19
  * (blocks `..`, slashes, empty, and uppercase). */
20
- export function sanitizeSkillName(raw) {
20
+ function sanitizeSkillName(raw) {
21
21
  const name = raw.trim().toLowerCase();
22
22
  if (!SKILL_NAME_PATTERN.test(name)) {
23
23
  throw new Error(`Invalid skill name "${raw}". Use lowercase letters, digits, and hyphens (1-64 chars).`);
@@ -46,7 +46,6 @@ export interface VectorMemoryStore {
46
46
  supersede(id: string, byId: string): boolean;
47
47
  close(): void;
48
48
  }
49
- export declare function normalizeMemoryContent(content: string): string;
50
49
  export declare function openVectorMemoryStore(dbPath: string): VectorMemoryStore;
51
50
  /** FTS5 MATCH terms for a query: ASCII words OR-joined; CJK has no
52
51
  * token boundaries under unicode61, so CJK-bearing queries fall back to a
@@ -1 +1 @@
1
- {"version":3,"file":"vector-store.d.ts","sourceRoot":"","sources":["../../../src/core/memory/vector-store.ts"],"names":[],"mappings":"AAEA;;;;;;;;iEAQiE;AAEjE,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAAC;IACnC;2EACuE;IACvE,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAAC;IAC/C,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IAC7C,KAAK,IAAI,MAAM,CAAC;IAChB,GAAG,IAAI,SAAS,EAAE,CAAC;IACnB,YAAY,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IAC3C,4EAA4E;IAC5E,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IACrD,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7C,KAAK,IAAI,IAAI,CAAC;CACf;AAuCD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,CA2GvE;AAED;;wEAEwE;AACxE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAO/D;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAElD"}
1
+ {"version":3,"file":"vector-store.d.ts","sourceRoot":"","sources":["../../../src/core/memory/vector-store.ts"],"names":[],"mappings":"AAEA;;;;;;;;iEAQiE;AAEjE,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAAC;IACnC;2EACuE;IACvE,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAAC;IAC/C,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IAC7C,KAAK,IAAI,MAAM,CAAC;IAChB,GAAG,IAAI,SAAS,EAAE,CAAC;IACnB,YAAY,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IAC3C,4EAA4E;IAC5E,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IACrD,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7C,KAAK,IAAI,IAAI,CAAC;CACf;AAuCD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,CA2GvE;AAED;;wEAEwE;AACxE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAO/D;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAElD"}
@@ -17,9 +17,6 @@ function toRow(raw) {
17
17
  createdAt: raw.created_at,
18
18
  };
19
19
  }
20
- export function normalizeMemoryContent(content) {
21
- return content.trim().replace(/\s+/g, " ").toLowerCase();
22
- }
23
20
  export function openVectorMemoryStore(dbPath) {
24
21
  const db = new Database(dbPath, { create: true });
25
22
  db.exec("PRAGMA journal_mode = WAL");
@@ -0,0 +1,7 @@
1
+ /** Plan mode: investigate and draft without changing the working tree, then
2
+ * review the draft and choose how it reaches implementation (fresh session,
3
+ * compacted context, or the current one). */
4
+ export { planModePrompt, planRefinePrompt, planReminderPrompt } from "./prompts.js";
5
+ export { type PlanLoopDecision, PlanRuntime } from "./runtime.js";
6
+ export { enterPlanMode, implementationTurn, newestPlan, type PlanModeState, type PlanReviewChoice, planIsSubstantial, planPathFor, plansDir, planTitle, readPlan, writePlan, } from "./state.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/plans/index.ts"],"names":[],"mappings":"AAAA;;8CAE8C;AAC9C,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACpF,OAAO,EAAE,KAAK,gBAAgB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,UAAU,EACV,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,iBAAiB,EACjB,WAAW,EACX,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,SAAS,GACV,MAAM,YAAY,CAAC"}
@@ -0,0 +1,6 @@
1
+ /** Plan mode: investigate and draft without changing the working tree, then
2
+ * review the draft and choose how it reaches implementation (fresh session,
3
+ * compacted context, or the current one). */
4
+ export { planModePrompt, planRefinePrompt, planReminderPrompt } from "./prompts.js";
5
+ export { PlanRuntime } from "./runtime.js";
6
+ export { enterPlanMode, implementationTurn, newestPlan, planIsSubstantial, planPathFor, plansDir, planTitle, readPlan, writePlan, } from "./state.js";
@@ -0,0 +1,20 @@
1
+ import type { PlanModeState } from "./state.js";
2
+ /** Plan-mode prompt blocks.
3
+ *
4
+ * Read-only is enforced by the FILE TOOL GUARD (write/edit refuse anything
5
+ * outside the plan draft — see core/tools/file-tools.ts), not by an allowlist:
6
+ * investigation needs the full read surface plus bash. Bash is the honest
7
+ * exception and is prompt-guarded only, exactly like the reference
8
+ * implementation: a shell command can still change the tree, so the rule it
9
+ * states is the rule that holds. */
10
+ export declare function planModePrompt(state: PlanModeState): string;
11
+ /** Sent when a planning turn settled without a draft: the model answered as if
12
+ * in a normal turn, so the mode's contract has to be restated once. Capped by
13
+ * the caller — a reminder loop would be worse than a missing plan. */
14
+ export declare function planReminderPrompt(state: PlanModeState): string;
15
+ /** Refinement: a review follow-up goes back to the planning turn as a HIDDEN
16
+ * protocol prompt (the caller wraps it — it is multi-line boilerplate, not
17
+ * something the user typed), with the draft path restated so the model edits
18
+ * THAT document. */
19
+ export declare function planRefinePrompt(state: PlanModeState, feedback: string): string;
20
+ //# sourceMappingURL=prompts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../src/core/plans/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;;;;qCAOqC;AAErC,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CA6B3D;AAED;;uEAEuE;AACvE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAa/D;AAED;;;qBAGqB;AACrB,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAY/E"}
@@ -0,0 +1,72 @@
1
+ /** Plan-mode prompt blocks.
2
+ *
3
+ * Read-only is enforced by the FILE TOOL GUARD (write/edit refuse anything
4
+ * outside the plan draft — see core/tools/file-tools.ts), not by an allowlist:
5
+ * investigation needs the full read surface plus bash. Bash is the honest
6
+ * exception and is prompt-guarded only, exactly like the reference
7
+ * implementation: a shell command can still change the tree, so the rule it
8
+ * states is the rule that holds. */
9
+ export function planModePrompt(state) {
10
+ return [
11
+ "<plan_mode>",
12
+ "Plan mode is active: investigate and produce a PLAN. The working tree is read-only for you.",
13
+ "",
14
+ `Write the plan to: ${state.planPath}`,
15
+ "(the only path write/edit will accept while plan mode is on)",
16
+ "",
17
+ "Rules:",
18
+ "- Do NOT modify the project. read/grep/glob/ls/tree and bash for INSPECTION only",
19
+ " (git status, reading logs, running tests to learn the current state).",
20
+ "- Do NOT create, edit, move, or delete project files; do not run commands that",
21
+ " mutate state (installs, apply, commit, rm, writes).",
22
+ "- Investigate before you draft: read the files the change touches, and check",
23
+ " the assumptions the design depends on. An assumption you did not verify is",
24
+ " a liability — name it in the plan instead of silently relying on it.",
25
+ "",
26
+ "The plan is a Markdown document. Cover, in this order:",
27
+ "1. **Context** — what changes and why, in the user's terms.",
28
+ "2. **Approach** — the design, including the alternatives you rejected and why.",
29
+ "3. **Critical files** — every file to touch, with the change each one gets.",
30
+ "4. **Verification** — the exact commands that prove the change works.",
31
+ "5. **Assumptions** — what you verified, and what remains a guess.",
32
+ "",
33
+ "Prefer explicit steps with an order that can actually be executed; call out",
34
+ "anything that has to happen before something else, and any rollback path.",
35
+ "When the plan is ready, write it to the path above and say so in one line.",
36
+ "</plan_mode>",
37
+ ].join("\n");
38
+ }
39
+ /** Sent when a planning turn settled without a draft: the model answered as if
40
+ * in a normal turn, so the mode's contract has to be restated once. Capped by
41
+ * the caller — a reminder loop would be worse than a missing plan. */
42
+ export function planReminderPrompt(state) {
43
+ return [
44
+ "<plan_mode_reminder>",
45
+ "Plan mode is still active, but no plan was written this turn.",
46
+ "",
47
+ `Write the plan (Markdown, the five sections below) to ${state.planPath} using write,`,
48
+ "then stop. Do not implement anything.",
49
+ "",
50
+ "Sections: Context / Approach / Critical files / Verification / Assumptions.",
51
+ "If you are still missing information, say what you need in one line instead of",
52
+ "guessing — the user can answer and you continue planning.",
53
+ "</plan_mode_reminder>",
54
+ ].join("\n");
55
+ }
56
+ /** Refinement: a review follow-up goes back to the planning turn as a HIDDEN
57
+ * protocol prompt (the caller wraps it — it is multi-line boilerplate, not
58
+ * something the user typed), with the draft path restated so the model edits
59
+ * THAT document. */
60
+ export function planRefinePrompt(state, feedback) {
61
+ return [
62
+ "<plan_mode_refine>",
63
+ `Revise the plan at ${state.planPath}.`,
64
+ "",
65
+ "Feedback on the current draft:",
66
+ feedback.trim(),
67
+ "",
68
+ "Keep everything the feedback does not ask you to change, and write the whole",
69
+ "revised document back to the same path. Do not implement anything yet.",
70
+ "</plan_mode_refine>",
71
+ ].join("\n");
72
+ }
@@ -0,0 +1,89 @@
1
+ import { type PlanModeState } from "./state.js";
2
+ /** Plan mode's runtime: the session-scoped state machine, extracted from the
3
+ * TUI closure so it matches the other two turn drivers (goal, loop) — those
4
+ * are classes their surface merely renders, while plan's five loose variables
5
+ * were spread across twenty call sites. It owns mode state, the draft, the
6
+ * contract/reminder bookkeeping and WHICH model should plan (activeModel);
7
+ * the TUI performs the swap and owns the review surface, because both are
8
+ * presentation. */
9
+ export type PlanLoopDecision =
10
+ /** The turn produced a usable draft: nothing to queue. */
11
+ {
12
+ action: "idle";
13
+ draftTitle?: string;
14
+ }
15
+ /** No usable draft yet and the reminder budget allows one: restate the
16
+ * contract as the next turn. */
17
+ | {
18
+ action: "remind";
19
+ }
20
+ /** No usable draft and the reminder is spent: say so, queue nothing. */
21
+ | {
22
+ action: "stalled";
23
+ };
24
+ export declare class PlanRuntime {
25
+ #private;
26
+ /** Workspace root: plan paths live under it. */
27
+ private readonly workspaceRoot;
28
+ /** Persist a mode transition (session event); `null` = mode off. */
29
+ private readonly persist;
30
+ /** The model to plan with; absent = do not touch the model. */
31
+ private readonly planModel;
32
+ constructor(
33
+ /** Workspace root: plan paths live under it. */
34
+ workspaceRoot: string,
35
+ /** Persist a mode transition (session event); `null` = mode off. */
36
+ persist?: (state: PlanModeState | null, paused: boolean) => void,
37
+ /** The model to plan with; absent = do not touch the model. */
38
+ planModel?: string | undefined);
39
+ get state(): PlanModeState | null;
40
+ get enabled(): boolean;
41
+ get paused(): boolean;
42
+ /** The draft path, or undefined when plan mode is off. */
43
+ get draftPath(): string | undefined;
44
+ /** Model the session should be on right now: the plan model while planning,
45
+ * otherwise whatever was active before (undefined = leave it alone). */
46
+ get activeModel(): string | undefined;
47
+ /** The guard the file tools need while planning: the ONE writable path. */
48
+ get writeGuard(): {
49
+ planPath: string;
50
+ } | undefined;
51
+ /** The status-bar indicator: the driver owns its wording. */
52
+ statusLabel(): string | undefined;
53
+ /** Enter (or re-enter after a pause). A re-entry keeps the draft and does
54
+ * not re-arm the contract: the model already has it in context. */
55
+ enter(sessionId: string, reentry?: boolean): PlanModeState;
56
+ /** Active → paused: the draft and the mode stay, no turn is driven. */
57
+ pause(): void;
58
+ /** Fully off (the draft file stays on disk for /plan-review). The prior
59
+ * model is KEPT: the surface restores it immediately after leaving, so
60
+ * clearing it here would strand the session on the planning model. */
61
+ leave(): void;
62
+ /** Restore from a session's journal: a session that was planning comes back
63
+ * PAUSED, so a resume never silently re-enters a read-only turn. */
64
+ restore(restored: {
65
+ planPath: string;
66
+ } | null, sessionId: string): void;
67
+ /** Record the model the session was on when planning started (called by the
68
+ * surface that performs the switch; undefined is a no-op). */
69
+ notePriorModel(modelId: string | undefined): void;
70
+ readDraft(): string | null;
71
+ hasDraft(): boolean;
72
+ draftTitle(): string | undefined;
73
+ /** The contract prompt, ONCE per entry (it is long, and the mode is sticky).
74
+ * Null when it was already delivered. */
75
+ takeContractPrompt(render: (state: PlanModeState) => string): string | null;
76
+ /** A user turn resets the reminder budget: they just spoke, so the model
77
+ * gets another chance to draft. */
78
+ onUserTurn(): void;
79
+ /** A planning turn settled: did it produce a plan? The draft is checked
80
+ * FIRST — the reminder budget answers "may we nudge again", never "is there
81
+ * a plan". */
82
+ settleTurn(): PlanLoopDecision;
83
+ /** Refinement re-arms planning on the SAME draft (the caller wraps the
84
+ * feedback as the turn's input). */
85
+ refine(sessionId: string): PlanModeState;
86
+ /** Save the draft elsewhere and stop planning without executing. */
87
+ saveCopy(destination: string): boolean;
88
+ }
89
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/core/plans/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAMnB,MAAM,YAAY,CAAC;AAEpB;;;;;;oBAMoB;AAEpB,MAAM,MAAM,gBAAgB;AAC1B,0DAA0D;AACxD;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE;AACzC;iCACiC;GAC/B;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE;AACtB,wEAAwE;GACtE;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC;AAE1B,qBAAa,WAAW;;IASpB,gDAAgD;IAChD,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,oEAAoE;IACpE,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,+DAA+D;IAC/D,OAAO,CAAC,QAAQ,CAAC,SAAS;;IAL1B,gDAAgD;IAC/B,aAAa,EAAE,MAAM;IACtC,oEAAoE;IACnD,OAAO,GAAE,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,EAAE,MAAM,EAAE,OAAO,KAAK,IAAe;IAC3F,+DAA+D;IAC9C,SAAS,GAAE,MAAM,GAAG,SAAqB;IAG5D,IAAI,KAAK,IAAI,aAAa,GAAG,IAAI,CAEhC;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,0DAA0D;IAC1D,IAAI,SAAS,IAAI,MAAM,GAAG,SAAS,CAElC;IAED;6EACyE;IACzE,IAAI,WAAW,IAAI,MAAM,GAAG,SAAS,CAGpC;IAED,2EAA2E;IAC3E,IAAI,UAAU,IAAI;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAGjD;IAED,6DAA6D;IAC7D,WAAW,IAAI,MAAM,GAAG,SAAS;IAOjC;wEACoE;IACpE,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,aAAa;IAYxD,uEAAuE;IACvE,KAAK,IAAI,IAAI;IAMb;;2EAEuE;IACvE,KAAK,IAAI,IAAI;IASb;yEACqE;IACrE,OAAO,CAAC,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAgBvE;mEAC+D;IAC/D,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAIjD,SAAS,IAAI,MAAM,GAAG,IAAI;IAK1B,QAAQ,IAAI,OAAO;IAKnB,UAAU,IAAI,MAAM,GAAG,SAAS;IAKhC;8CAC0C;IAC1C,kBAAkB,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,MAAM,GAAG,MAAM,GAAG,IAAI;IAM3E;wCACoC;IACpC,UAAU,IAAI,IAAI;IAIlB;;mBAEe;IACf,UAAU,IAAI,gBAAgB;IAe9B;yCACqC;IACrC,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa;IAQxC,oEAAoE;IACpE,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;CAMvC"}