@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,170 @@
1
+ import { enterPlanMode, planIsSubstantial, planPathFor, planTitle, readPlan, writePlan, } from "./state.js";
2
+ export class PlanRuntime {
3
+ workspaceRoot;
4
+ persist;
5
+ planModel;
6
+ #state = null;
7
+ #paused = false;
8
+ #contractSent = false;
9
+ #reminders = 0;
10
+ /** Model that was active before the planning model took over. */
11
+ #priorModel;
12
+ constructor(
13
+ /** Workspace root: plan paths live under it. */
14
+ workspaceRoot,
15
+ /** Persist a mode transition (session event); `null` = mode off. */
16
+ persist = () => { },
17
+ /** The model to plan with; absent = do not touch the model. */
18
+ planModel = undefined) {
19
+ this.workspaceRoot = workspaceRoot;
20
+ this.persist = persist;
21
+ this.planModel = planModel;
22
+ }
23
+ get state() {
24
+ return this.#state;
25
+ }
26
+ get enabled() {
27
+ return this.#state?.enabled === true && !this.#paused;
28
+ }
29
+ get paused() {
30
+ return this.#paused;
31
+ }
32
+ /** The draft path, or undefined when plan mode is off. */
33
+ get draftPath() {
34
+ return this.#state?.planPath;
35
+ }
36
+ /** Model the session should be on right now: the plan model while planning,
37
+ * otherwise whatever was active before (undefined = leave it alone). */
38
+ get activeModel() {
39
+ if (this.#state?.enabled === true && this.planModel)
40
+ return this.planModel;
41
+ return this.#priorModel;
42
+ }
43
+ /** The guard the file tools need while planning: the ONE writable path. */
44
+ get writeGuard() {
45
+ const path = this.draftPath;
46
+ return this.enabled && path !== undefined ? { planPath: path } : undefined;
47
+ }
48
+ /** The status-bar indicator: the driver owns its wording. */
49
+ statusLabel() {
50
+ const state = this.#state;
51
+ if (!state)
52
+ return undefined;
53
+ const draft = this.hasDraft() ? " · draft" : "";
54
+ return `✎ plan${this.#paused ? " paused" : ""}${draft}`;
55
+ }
56
+ /** Enter (or re-enter after a pause). A re-entry keeps the draft and does
57
+ * not re-arm the contract: the model already has it in context. */
58
+ enter(sessionId, reentry = false) {
59
+ const path = planPathFor(this.workspaceRoot, sessionId);
60
+ this.#state = enterPlanMode(path, reentry);
61
+ this.#paused = false;
62
+ if (!reentry) {
63
+ this.#contractSent = false;
64
+ this.#reminders = 0;
65
+ }
66
+ this.persist(this.#state, false);
67
+ return this.#state;
68
+ }
69
+ /** Active → paused: the draft and the mode stay, no turn is driven. */
70
+ pause() {
71
+ if (!this.#state)
72
+ return;
73
+ this.#paused = true;
74
+ this.persist(this.#state, true);
75
+ }
76
+ /** Fully off (the draft file stays on disk for /plan-review). The prior
77
+ * model is KEPT: the surface restores it immediately after leaving, so
78
+ * clearing it here would strand the session on the planning model. */
79
+ leave() {
80
+ if (!this.#state && !this.#paused)
81
+ return;
82
+ this.#state = null;
83
+ this.#paused = false;
84
+ this.#contractSent = false;
85
+ this.#reminders = 0;
86
+ this.persist(null, false);
87
+ }
88
+ /** Restore from a session's journal: a session that was planning comes back
89
+ * PAUSED, so a resume never silently re-enters a read-only turn. */
90
+ restore(restored, sessionId) {
91
+ if (!restored) {
92
+ this.#state = null;
93
+ this.#paused = false;
94
+ }
95
+ else {
96
+ this.#state = enterPlanMode(restored.planPath || planPathFor(this.workspaceRoot, sessionId), true);
97
+ this.#paused = true;
98
+ }
99
+ this.#contractSent = false;
100
+ this.#reminders = 0;
101
+ this.#priorModel = undefined;
102
+ }
103
+ /** Record the model the session was on when planning started (called by the
104
+ * surface that performs the switch; undefined is a no-op). */
105
+ notePriorModel(modelId) {
106
+ if (modelId !== undefined)
107
+ this.#priorModel ??= modelId;
108
+ }
109
+ readDraft() {
110
+ const path = this.draftPath;
111
+ return path === undefined ? null : readPlan(path);
112
+ }
113
+ hasDraft() {
114
+ const markdown = this.readDraft();
115
+ return markdown !== null && planIsSubstantial(markdown);
116
+ }
117
+ draftTitle() {
118
+ const markdown = this.readDraft();
119
+ return markdown ? planTitle(markdown) : undefined;
120
+ }
121
+ /** The contract prompt, ONCE per entry (it is long, and the mode is sticky).
122
+ * Null when it was already delivered. */
123
+ takeContractPrompt(render) {
124
+ if (this.#state?.enabled !== true || this.#paused || this.#contractSent)
125
+ return null;
126
+ this.#contractSent = true;
127
+ return render(this.#state);
128
+ }
129
+ /** A user turn resets the reminder budget: they just spoke, so the model
130
+ * gets another chance to draft. */
131
+ onUserTurn() {
132
+ this.#reminders = 0;
133
+ }
134
+ /** A planning turn settled: did it produce a plan? The draft is checked
135
+ * FIRST — the reminder budget answers "may we nudge again", never "is there
136
+ * a plan". */
137
+ settleTurn() {
138
+ if (this.#state?.enabled !== true || this.#paused)
139
+ return { action: "idle" };
140
+ const markdown = this.readDraft();
141
+ if (markdown !== null && planIsSubstantial(markdown)) {
142
+ this.#reminders = 0;
143
+ const title = planTitle(markdown);
144
+ return title ? { action: "idle", draftTitle: title } : { action: "idle" };
145
+ }
146
+ if (this.#reminders < 1) {
147
+ this.#reminders += 1;
148
+ return { action: "remind" };
149
+ }
150
+ return { action: "stalled" };
151
+ }
152
+ /** Refinement re-arms planning on the SAME draft (the caller wraps the
153
+ * feedback as the turn's input). */
154
+ refine(sessionId) {
155
+ if (!this.#state)
156
+ return this.enter(sessionId, true);
157
+ this.#paused = false;
158
+ this.#reminders = 0;
159
+ this.persist(this.#state, false);
160
+ return this.#state;
161
+ }
162
+ /** Save the draft elsewhere and stop planning without executing. */
163
+ saveCopy(destination) {
164
+ const markdown = this.readDraft();
165
+ if (markdown === null)
166
+ return false;
167
+ writePlan(destination, markdown);
168
+ return true;
169
+ }
170
+ }
@@ -0,0 +1,55 @@
1
+ /** Plan mode: investigate and draft a plan WITHOUT changing the working tree,
2
+ * then review it and choose how it reaches implementation.
3
+ *
4
+ * The plan is a durable markdown file (not a transcript message): it is what
5
+ * the review surface reads, what the implementation turn receives, and what a
6
+ * later session can pick up. One canonical path per session keeps the
7
+ * "which draft did I approve" question answerable. */
8
+ export interface PlanModeState {
9
+ enabled: boolean;
10
+ /** Absolute path of this session's plan draft. */
11
+ planPath: string;
12
+ /** True when the mode was restored from a session (a re-entry must not
13
+ * re-switch the model or re-arm anything the user already decided). */
14
+ reentry?: boolean;
15
+ }
16
+ /** What the review surface chose. The three approvals differ in CONTEXT
17
+ * handling, never in the plan's contents. */
18
+ export type PlanReviewChoice = {
19
+ kind: "approve";
20
+ context: "fresh" | "compact" | "keep";
21
+ } | {
22
+ kind: "refine";
23
+ feedback: string;
24
+ } | {
25
+ kind: "save";
26
+ destination: string;
27
+ };
28
+ /** Directory holding a workspace's plan drafts. */
29
+ export declare function plansDir(workspaceRoot: string): string;
30
+ /** The session's canonical draft path. One file per session, so approving
31
+ * always refers to the draft the user actually reviewed. */
32
+ export declare function planPathFor(workspaceRoot: string, sessionId: string): string;
33
+ export declare function readPlan(path: string): string | null;
34
+ export declare function writePlan(path: string, markdown: string): void;
35
+ /** Newest plan draft in the workspace, for /plan-review without a session
36
+ * draft (a resumed session whose draft came from another conversation). */
37
+ export declare function newestPlan(workspaceRoot: string): string | null;
38
+ /** First markdown heading, used as the plan's title (and the session name
39
+ * when implementation starts in a fresh conversation). */
40
+ export declare function planTitle(markdown: string): string | undefined;
41
+ /** Whether a draft is worth reviewing: a stub the model wrote on its way out
42
+ * of the turn is not a plan. */
43
+ export declare function planIsSubstantial(markdown: string): boolean;
44
+ /** Enter transitions for plan mode. The `/plan` toggle is stateful and its
45
+ * three states live in two places on purpose: OFF is the absence of a state
46
+ * object (`PlanRuntime.state === null`), while PAUSED keeps one with
47
+ * `enabled: true` so the draft and its path survive. A caller that reads only
48
+ * `enabled` therefore cannot tell paused from active — ask the runtime. */
49
+ export declare function enterPlanMode(planPath: string, reentry?: boolean): PlanModeState;
50
+ /** The first implementation turn: the approved plan travels as this turn's
51
+ * input, through the HIDDEN channel (the protocol rule): the user reviewed
52
+ * the plan as a document, so replaying kilobytes of it as a chat bubble
53
+ * would be noise. */
54
+ export declare function implementationTurn(planPath: string, markdown: string, contextKept: boolean): string;
55
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/core/plans/state.ts"],"names":[],"mappings":"AAGA;;;;;;uDAMuD;AAEvD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB;4EACwE;IACxE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;8CAC8C;AAC9C,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1C,mDAAmD;AACnD,wBAAgB,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;6DAC6D;AAC7D,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE5E;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMpD;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAG9D;AAED;4EAC4E;AAC5E,wBAAgB,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW/D;AAED;2DAC2D;AAC3D,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAM9D;AAED;iCACiC;AACjC,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAK3D;AAED;;;;4EAI4E;AAC5E,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,aAAa,CAI9E;AAED;;;sBAGsB;AACtB,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,OAAO,GACnB,MAAM,CAeR"}
@@ -0,0 +1,88 @@
1
+ import { mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ /** Directory holding a workspace's plan drafts. */
4
+ export function plansDir(workspaceRoot) {
5
+ return join(workspaceRoot, ".oma", "plans");
6
+ }
7
+ /** The session's canonical draft path. One file per session, so approving
8
+ * always refers to the draft the user actually reviewed. */
9
+ export function planPathFor(workspaceRoot, sessionId) {
10
+ return join(plansDir(workspaceRoot), `${sessionId}.md`);
11
+ }
12
+ export function readPlan(path) {
13
+ try {
14
+ return readFileSync(path, "utf-8");
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ }
20
+ export function writePlan(path, markdown) {
21
+ mkdirSync(join(path, ".."), { recursive: true });
22
+ writeFileSync(path, markdown, "utf-8");
23
+ }
24
+ /** Newest plan draft in the workspace, for /plan-review without a session
25
+ * draft (a resumed session whose draft came from another conversation). */
26
+ export function newestPlan(workspaceRoot) {
27
+ const dir = plansDir(workspaceRoot);
28
+ try {
29
+ const files = readdirSync(dir)
30
+ .filter((f) => f.endsWith(".md"))
31
+ .map((f) => ({ path: join(dir, f), mtime: statSync(join(dir, f)).mtimeMs }))
32
+ .sort((a, b) => b.mtime - a.mtime);
33
+ return files[0]?.path ?? null;
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ /** First markdown heading, used as the plan's title (and the session name
40
+ * when implementation starts in a fresh conversation). */
41
+ export function planTitle(markdown) {
42
+ for (const line of markdown.split("\n")) {
43
+ const m = /^#{1,3}\s+(.+?)\s*$/.exec(line);
44
+ if (m?.[1])
45
+ return m[1].replace(/[*_`]/g, "").trim().slice(0, 80);
46
+ }
47
+ return undefined;
48
+ }
49
+ /** Whether a draft is worth reviewing: a stub the model wrote on its way out
50
+ * of the turn is not a plan. */
51
+ export function planIsSubstantial(markdown) {
52
+ const body = markdown.trim();
53
+ if (body.length < 120)
54
+ return false;
55
+ const headings = body.split("\n").filter((l) => /^#{1,3}\s+/.test(l)).length;
56
+ return headings >= 2;
57
+ }
58
+ /** Enter transitions for plan mode. The `/plan` toggle is stateful and its
59
+ * three states live in two places on purpose: OFF is the absence of a state
60
+ * object (`PlanRuntime.state === null`), while PAUSED keeps one with
61
+ * `enabled: true` so the draft and its path survive. A caller that reads only
62
+ * `enabled` therefore cannot tell paused from active — ask the runtime. */
63
+ export function enterPlanMode(planPath, reentry = false) {
64
+ const state = { enabled: true, planPath };
65
+ if (reentry)
66
+ state.reentry = true;
67
+ return state;
68
+ }
69
+ /** The first implementation turn: the approved plan travels as this turn's
70
+ * input, through the HIDDEN channel (the protocol rule): the user reviewed
71
+ * the plan as a document, so replaying kilobytes of it as a chat bubble
72
+ * would be noise. */
73
+ export function implementationTurn(planPath, markdown, contextKept) {
74
+ return [
75
+ "The plan below was reviewed and APPROVED. Implement it now.",
76
+ "",
77
+ `Durable copy: ${planPath}`,
78
+ contextKept
79
+ ? "The planning conversation is still in context; treat it as background, not as instructions to re-litigate."
80
+ : "Work from the plan document alone.",
81
+ "",
82
+ "<approved_plan>",
83
+ markdown.trim(),
84
+ "</approved_plan>",
85
+ "",
86
+ "Follow the plan's steps in order. If the repository state contradicts the plan (a file moved, an assumption is false), stop and report the contradiction instead of improvising a different approach.",
87
+ ].join("\n");
88
+ }
@@ -23,6 +23,12 @@ type BatchEntry = {
23
23
  message: Message;
24
24
  createdAt: number;
25
25
  };
26
+ /** Attach the producing call's durable telemetry (usage, stop reason) to an
27
+ * assistant message. Both assembly sites — the tool batch and the text-only
28
+ * entry — call this, so the session file's usage record cannot silently
29
+ * diverge between turn shapes. Never sent on the wire: converters read
30
+ * text/blocks only. */
31
+ export declare function attachTurnTelemetry(message: Message, turn: ModelTurn): void;
26
32
  /** Build the atomic assistant(tool_use) + tool_result batch for one turn. */
27
33
  export declare function buildToolBatch(turn: ModelTurn, toolResults: readonly ToolExecutionResult[], opts: {
28
34
  toolFailureReminder?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"agent-loop-messages.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/agent-loop-messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAqBvD;;;kEAGkE;AAClE,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,SAAS,GACd,KAAK,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAMnF;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,WAAW,GAAG,MAAM,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,6EAA6E;AAC7E,wBAAgB,cAAc,CAC5B,IAAI,EAAE,SAAS,EACf,WAAW,EAAE,SAAS,mBAAmB,EAAE,EAC3C,IAAI,EAAE;IAAE,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAAE,GACtC,UAAU,EAAE,CAyFd;AAED,4DAA4D;AAC5D,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,EACf,cAAc,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,GACpD,UAAU,CAuBZ"}
1
+ {"version":3,"file":"agent-loop-messages.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/agent-loop-messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAqBvD;;;kEAGkE;AAClE,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,SAAS,GACd,KAAK,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAMnF;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,WAAW,GAAG,MAAM,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;wBAIwB;AACxB,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI,CAO3E;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAC5B,IAAI,EAAE,SAAS,EACf,WAAW,EAAE,SAAS,mBAAmB,EAAE,EAC3C,IAAI,EAAE;IAAE,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAAE,GACtC,UAAU,EAAE,CA+Fd;AAED,4DAA4D;AAC5D,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,EACf,cAAc,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,GACpD,UAAU,CA2BZ"}
@@ -21,6 +21,19 @@ export function buildThinkingBlock(turn) {
21
21
  // and signature/redacted attach at the end.
22
22
  return [makeThinkingBlock(turn.thinking, turn)];
23
23
  }
24
+ /** Attach the producing call's durable telemetry (usage, stop reason) to an
25
+ * assistant message. Both assembly sites — the tool batch and the text-only
26
+ * entry — call this, so the session file's usage record cannot silently
27
+ * diverge between turn shapes. Never sent on the wire: converters read
28
+ * text/blocks only. */
29
+ export function attachTurnTelemetry(message, turn) {
30
+ if (turn.usage) {
31
+ message.usage = { ...turn.usage };
32
+ }
33
+ if (turn.stopReason) {
34
+ message.stopReason = turn.stopReason;
35
+ }
36
+ }
24
37
  /** Build the atomic assistant(tool_use) + tool_result batch for one turn. */
25
38
  export function buildToolBatch(turn, toolResults, opts) {
26
39
  const collapsedThinking = makeThinkingBlock(turn.thinking, turn);
@@ -36,7 +49,12 @@ export function buildToolBatch(turn, toolResults, opts) {
36
49
  }
37
50
  orderedWithCollapsedThinking.push(b);
38
51
  }
39
- if (!thinkingInserted && turn.thinking) {
52
+ // Gate on the BLOCK, not on the text: a redacted_thinking block carries a
53
+ // signature and no reasoning text at all (agent-loop-run.ts reasoning_signature),
54
+ // so testing turn.thinking alone dropped it — and a replayed tool_use without
55
+ // its redacted_thinking block is rejected by the provider (400). Same
56
+ // predicate buildThinkingBlock uses.
57
+ if (!thinkingInserted && (turn.thinking || turn.thinkingSignature)) {
40
58
  orderedWithCollapsedThinking.push(collapsedThinking);
41
59
  }
42
60
  const assistantMessage = {
@@ -57,6 +75,7 @@ export function buildToolBatch(turn, toolResults, opts) {
57
75
  })),
58
76
  ],
59
77
  };
78
+ attachTurnTelemetry(assistantMessage, turn);
60
79
  return [
61
80
  {
62
81
  type: "message",
@@ -110,24 +129,30 @@ export function buildToolBatch(turn, toolResults, opts) {
110
129
  }
111
130
  /** Build the assistant(text) entry for a text-only turn. */
112
131
  export function buildTextAssistantEntry(turn, thinkingBlocks) {
113
- const blocks = turn.ordered.length > 0
114
- ? turn.ordered.map((b) => (b.type === "thinking" ? makeThinkingBlock(b.text, turn) : b))
115
- : thinkingBlocks.length > 0
116
- ? thinkingBlocks
117
- : undefined;
132
+ // Ordered stream content wins; a signature-only stream falls back to the
133
+ // collapsed thinking block; a bare text turn carries no blocks at all.
134
+ let blocks;
135
+ if (turn.ordered.length > 0) {
136
+ blocks = turn.ordered.map((b) => (b.type === "thinking" ? makeThinkingBlock(b.text, turn) : b));
137
+ }
138
+ else if (thinkingBlocks.length > 0) {
139
+ blocks = thinkingBlocks;
140
+ }
141
+ const message = {
142
+ role: "assistant",
143
+ text: turn.text,
144
+ // Preserve the interleaved thinking/text order from the stream. The
145
+ // single collapsed thinking block (with signature) is still emitted for
146
+ // replay compatibility when the stream had a signature, but the ordered
147
+ // list keeps the trace faithful.
148
+ blocks,
149
+ };
150
+ attachTurnTelemetry(message, turn);
118
151
  return {
119
152
  type: "message",
120
153
  role: "assistant",
121
154
  source: "assistant",
122
- message: {
123
- role: "assistant",
124
- text: turn.text,
125
- // Preserve the interleaved thinking/text order from the stream. The
126
- // single collapsed thinking block (with signature) is still emitted for
127
- // replay compatibility when the stream had a signature, but the ordered
128
- // list keeps the trace faithful.
129
- blocks,
130
- },
155
+ message,
131
156
  createdAt: Date.now(),
132
157
  };
133
158
  }
@@ -1 +1 @@
1
- {"version":3,"file":"agent-loop-run.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/agent-loop-run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AAE1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EACV,SAAS,EACT,iBAAiB,EACjB,eAAe,EAGhB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,UAAU,EAAE,cAAc,CAAC;IAC3B,UAAU,EAAE,eAAe,GAAG,IAAI,CAAC;IACnC,EAAE,EAAE,aAAa,CAAC;IAClB,KAAK,EAAE,gBAAgB,CAAC;CACzB;AAeD,wBAAsB,eAAe,CACnC,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,SAAS,OAAO,EAAE,GAC3B,OAAO,CAAC,SAAS,CAAC,CA4GpB;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,eAAe,EACpB,KAAK,EAAE,SAAS,eAAe,EAAE,GAChC,OAAO,CAAC,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,CAqLvF;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,EAAE,CAAC,CAoCpB;AAGD,YAAY,EAAE,MAAM,EAAE,CAAC"}
1
+ {"version":3,"file":"agent-loop-run.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/agent-loop-run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AAE1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EACV,SAAS,EACT,iBAAiB,EACjB,eAAe,EAGhB,MAAM,uBAAuB,CAAC;AAI/B,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,UAAU,EAAE,cAAc,CAAC;IAC3B,UAAU,EAAE,eAAe,GAAG,IAAI,CAAC;IACnC,EAAE,EAAE,aAAa,CAAC;IAClB,KAAK,EAAE,gBAAgB,CAAC;CACzB;AAeD,wBAAsB,eAAe,CACnC,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,SAAS,OAAO,EAAE,GAC3B,OAAO,CAAC,SAAS,CAAC,CA4GpB;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,eAAe,EACpB,KAAK,EAAE,SAAS,eAAe,EAAE,GAChC,OAAO,CAAC,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,CAqLvF;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,EAAE,CAAC,CA0BpB;AAGD,YAAY,EAAE,MAAM,EAAE,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { debugLog } from "@chengchenccc/agent-contract";
2
2
  import { matchStreamRule, safeParseJson } from "./agent-loop-utils.js";
3
+ import { latestCompaction } from "./compaction.js";
3
4
  import { retryStream } from "./retry.js";
4
5
  /** Coalesce consecutive same-kind stream deltas into one TurnBlock.
5
6
  * `ordered` models contiguous thinking/text SEGMENTS (the trace shape),
@@ -308,18 +309,10 @@ export async function executeTools(ctx, calls) {
308
309
  }
309
310
  export async function readBranchMessages(store, sessionId) {
310
311
  const entries = await store.readBranch(sessionId);
311
- // Find latest CompactionEntry
312
- let compactionSummary = null;
313
- let coveredIds = null;
314
- for (let i = entries.length - 1; i >= 0; i--) {
315
- if (entries[i]?.type === "compaction") {
316
- const comp = entries[i];
317
- compactionSummary = comp.summary;
318
- coveredIds = new Set(comp.coversEntryIds);
319
- break;
320
- }
321
- }
322
- return entries
312
+ const compaction = latestCompaction(entries);
313
+ const compactionSummary = compaction?.summary ?? null;
314
+ const coveredIds = compaction?.coveredIds ?? null;
315
+ const retained = entries
323
316
  .filter((e) => {
324
317
  if (e.type !== "message")
325
318
  return false;
@@ -327,19 +320,17 @@ export async function readBranchMessages(store, sessionId) {
327
320
  return false;
328
321
  return true;
329
322
  })
330
- .map((e) => {
331
- const msg = e.message;
332
- // Prepend compaction summary as a system note if entries were compacted
333
- return msg;
334
- })
335
- .flatMap((msg, _i, _arr) => {
336
- // Insert summary as first user message if compaction applied
337
- if (_i === 0 && compactionSummary && coveredIds && coveredIds.size > 0) {
338
- return [
339
- { role: "user", text: `[Context summary: ${compactionSummary}]` },
340
- msg,
341
- ];
342
- }
343
- return [msg];
344
- });
323
+ .map((e) => e.message);
324
+ // The summary is prepended whenever a compaction applied, INDEPENDENTLY of
325
+ // whether any message survived it: inserted inside the map it would be
326
+ // dropped whenever the compaction covered the whole branch, leaving the
327
+ // model a system prompt and nothing else. compactSession clamps the cut, so
328
+ // this is the second layer of the same guarantee.
329
+ if (compactionSummary && coveredIds && coveredIds.size > 0) {
330
+ return [
331
+ { role: "user", text: `[Context summary: ${compactionSummary}]` },
332
+ ...retained,
333
+ ];
334
+ }
335
+ return retained;
345
336
  }
@@ -1,9 +1,7 @@
1
- import type { BackendInputMessage } from "@chengchenccc/agent-contract";
2
- import type { Message } from "@chengchenccc/message";
1
+ import { type BackendInputMessage } from "@chengchenccc/agent-contract";
3
2
  import type { OmaLoopEvent } from "./agent-event.js";
4
3
  import { type LoopCallContext, type LoopRuntimeState, type LoopToolMapRef } from "./agent-loop-run.js";
5
4
  import type { OmaLoopResult, OmaSession, OmaSessionOptions } from "./agent-loop-types.js";
6
- import { type UsageAnchor } from "./context-estimate.js";
7
5
  import type { CodingLoopInput } from "./loop-input.js";
8
6
  import type { TokenEstimateCache } from "./message-cache.js";
9
7
  import type { PluginTool } from "./plugin.js";
@@ -27,25 +25,6 @@ export interface LoopRunnerContext {
27
25
  baseTools: readonly PluginTool[];
28
26
  mutable: LoopRunnerMutable;
29
27
  }
30
- interface LoopStepState {
31
- messages: Message[];
32
- forceContinues: number;
33
- overflowCompacted: boolean;
34
- thresholdCompacted: boolean;
35
- usageAnchor: UsageAnchor | null;
36
- naturalStop: boolean;
37
- runError: string | undefined;
38
- systemPrompt: string;
39
- }
40
- export type TurnControl = {
41
- action: "break";
42
- } | {
43
- action: "return";
44
- result: OmaLoopResult;
45
- };
46
- /** Run one model-turn retry loop (prune/compact/stream/handle). Returns
47
- * "break" when the turn completed, or a terminal result. */
48
- export declare function runModelTurnLoop(ctx: LoopRunnerContext, stepState: LoopStepState): Promise<TurnControl>;
49
28
  /**
50
29
  * runLoop: inner loop runs model turns and tool calls until the model
51
30
  * stops. Each model turn is accumulated purely (streamModelTurn) and
@@ -56,5 +35,4 @@ export declare function runModelTurnLoop(ctx: LoopRunnerContext, stepState: Loop
56
35
  * (the backend orchestrates follow-ups via branch_input_queue).
57
36
  */
58
37
  export declare function runLoop(ctx: LoopRunnerContext, codingInput: CodingLoopInput, mode: "normal" | "follow_up"): Promise<OmaLoopResult>;
59
- export {};
60
38
  //# sourceMappingURL=agent-loop-runner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-loop-runner.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/agent-loop-runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAExE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAMrD,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EAGpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1F,OAAO,EAGL,KAAK,WAAW,EAEjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKzD,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,eAAe,GAAG,IAAI,CAAC;IACnC,EAAE,EAAE,aAAa,CAAC;IAClB,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,OAAO,EAAE,CAAC,OAAO,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3E,KAAK,EAAE,gBAAgB,CAAC;IACxB,UAAU,EAAE,cAAc,CAAC;IAC3B,OAAO,EAAE,MAAM,eAAe,CAAC;IAC/B,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,SAAS,EAAE,SAAS,UAAU,EAAE,CAAC;IACjC,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED,UAAU,aAAa;IACrB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;CACtB;AA0KD,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CAAC;AAE5F;6DAC6D;AAC7D,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,iBAAiB,EACtB,SAAS,EAAE,aAAa,GACvB,OAAO,CAAC,WAAW,CAAC,CA8QtB;AAED;;;;;;;;GAQG;AACH,wBAAsB,OAAO,CAC3B,GAAG,EAAE,iBAAiB,EACtB,WAAW,EAAE,eAAe,EAC5B,IAAI,EAAE,QAAQ,GAAG,WAAW,GAC3B,OAAO,CAAC,aAAa,CAAC,CAyExB"}
1
+ {"version":3,"file":"agent-loop-runner.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/agent-loop-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,mBAAmB,EAAY,MAAM,8BAA8B,CAAC;AAIlF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAMrD,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EAGpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAQ1F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKzD,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,eAAe,GAAG,IAAI,CAAC;IACnC,EAAE,EAAE,aAAa,CAAC;IAClB,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,OAAO,EAAE,CAAC,OAAO,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3E,KAAK,EAAE,gBAAgB,CAAC;IACxB,UAAU,EAAE,cAAc,CAAC;IAC3B,OAAO,EAAE,MAAM,eAAe,CAAC;IAC/B,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,SAAS,EAAE,SAAS,UAAU,EAAE,CAAC;IACjC,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AA0fD;;;;;;;;GAQG;AACH,wBAAsB,OAAO,CAC3B,GAAG,EAAE,iBAAiB,EACtB,WAAW,EAAE,eAAe,EAC5B,IAAI,EAAE,QAAQ,GAAG,WAAW,GAC3B,OAAO,CAAC,aAAa,CAAC,CAwExB"}