@chengchenccc/oh-my-agent 0.2.0-rc.2 → 0.2.0-rc.4
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.
- package/README.md +11 -6
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +9 -2
- package/dist/cli/gateway-commands.d.ts.map +1 -1
- package/dist/cli/gateway-commands.js +35 -5
- package/dist/cli/initial-input.d.ts +3 -2
- package/dist/cli/initial-input.d.ts.map +1 -1
- package/dist/core/coordination/registry.d.ts +11 -0
- package/dist/core/coordination/registry.d.ts.map +1 -1
- package/dist/core/coordination/registry.js +23 -0
- package/dist/core/gateway/artifact.d.ts +0 -1
- package/dist/core/gateway/artifact.d.ts.map +1 -1
- package/dist/core/gateway/artifact.js +1 -1
- package/dist/core/goals/index.d.ts +8 -0
- package/dist/core/goals/index.d.ts.map +1 -0
- package/dist/core/goals/index.js +7 -0
- package/dist/core/goals/runtime.d.ts +112 -0
- package/dist/core/goals/runtime.d.ts.map +1 -0
- package/dist/core/goals/runtime.js +241 -0
- package/dist/core/goals/state.d.ts +82 -0
- package/dist/core/goals/state.d.ts.map +1 -0
- package/dist/core/goals/state.js +225 -0
- package/dist/core/goals/tool.d.ts +10 -0
- package/dist/core/goals/tool.d.ts.map +1 -0
- package/dist/core/goals/tool.js +87 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +4 -3
- package/dist/core/loops/condition.d.ts +57 -0
- package/dist/core/loops/condition.d.ts.map +1 -0
- package/dist/core/loops/condition.js +153 -0
- package/dist/core/loops/index.d.ts +9 -0
- package/dist/core/loops/index.d.ts.map +1 -0
- package/dist/core/loops/index.js +8 -0
- package/dist/core/loops/limits.d.ts +43 -0
- package/dist/core/loops/limits.d.ts.map +1 -0
- package/dist/core/loops/limits.js +219 -0
- package/dist/core/loops/ralph.d.ts +37 -0
- package/dist/core/loops/ralph.d.ts.map +1 -0
- package/dist/core/loops/ralph.js +72 -0
- package/dist/core/loops/runtime.d.ts +95 -0
- package/dist/core/loops/runtime.d.ts.map +1 -0
- package/dist/core/loops/runtime.js +222 -0
- package/dist/core/memory/managed-skills.d.ts +0 -11
- package/dist/core/memory/managed-skills.d.ts.map +1 -1
- package/dist/core/memory/managed-skills.js +2 -2
- package/dist/core/memory/vector-store.d.ts +0 -1
- package/dist/core/memory/vector-store.d.ts.map +1 -1
- package/dist/core/memory/vector-store.js +0 -3
- package/dist/core/plans/index.d.ts +7 -0
- package/dist/core/plans/index.d.ts.map +1 -0
- package/dist/core/plans/index.js +6 -0
- package/dist/core/plans/prompts.d.ts +20 -0
- package/dist/core/plans/prompts.d.ts.map +1 -0
- package/dist/core/plans/prompts.js +72 -0
- package/dist/core/plans/runtime.d.ts +89 -0
- package/dist/core/plans/runtime.d.ts.map +1 -0
- package/dist/core/plans/runtime.js +170 -0
- package/dist/core/plans/state.d.ts +55 -0
- package/dist/core/plans/state.d.ts.map +1 -0
- package/dist/core/plans/state.js +88 -0
- package/dist/core/runtime/agent-loop-messages.d.ts +6 -0
- package/dist/core/runtime/agent-loop-messages.d.ts.map +1 -1
- package/dist/core/runtime/agent-loop-messages.js +40 -15
- package/dist/core/runtime/agent-loop-run.d.ts.map +1 -1
- package/dist/core/runtime/agent-loop-run.js +18 -27
- package/dist/core/runtime/agent-loop-runner.d.ts +1 -23
- package/dist/core/runtime/agent-loop-runner.d.ts.map +1 -1
- package/dist/core/runtime/agent-loop-runner.js +84 -30
- package/dist/core/runtime/agent-loop-types.d.ts +4 -0
- package/dist/core/runtime/agent-loop-types.d.ts.map +1 -1
- package/dist/core/runtime/compaction.d.ts +9 -0
- package/dist/core/runtime/compaction.d.ts.map +1 -1
- package/dist/core/runtime/compaction.js +28 -0
- package/dist/core/runtime/context-estimate.d.ts +37 -4
- package/dist/core/runtime/context-estimate.d.ts.map +1 -1
- package/dist/core/runtime/context-estimate.js +70 -0
- package/dist/core/runtime/create-runtime.d.ts +27 -5
- package/dist/core/runtime/create-runtime.d.ts.map +1 -1
- package/dist/core/runtime/create-runtime.js +22 -2
- package/dist/core/runtime/permission-classifier.d.ts.map +1 -1
- package/dist/core/runtime/permission-classifier.js +83 -12
- package/dist/core/runtime/plugin.d.ts +7 -0
- package/dist/core/runtime/plugin.d.ts.map +1 -1
- package/dist/core/runtime/plugin.js +13 -0
- package/dist/core/runtime/process-tree.d.ts +0 -9
- package/dist/core/runtime/process-tree.d.ts.map +1 -1
- package/dist/core/runtime/process-tree.js +1 -1
- package/dist/core/runtime/run-runtime.d.ts.map +1 -1
- package/dist/core/runtime/run-runtime.js +88 -49
- package/dist/core/runtime/runtime-catalog.d.ts +0 -4
- package/dist/core/runtime/runtime-catalog.d.ts.map +1 -1
- package/dist/core/runtime/runtime-catalog.js +1 -1
- package/dist/core/runtime/stream-rules.d.ts +9 -2
- package/dist/core/runtime/stream-rules.d.ts.map +1 -1
- package/dist/core/runtime/stream-rules.js +19 -5
- package/dist/core/runtime/title.d.ts +13 -1
- package/dist/core/runtime/title.d.ts.map +1 -1
- package/dist/core/runtime/title.js +51 -17
- package/dist/core/runtime/tool-pruning.d.ts.map +1 -1
- package/dist/core/runtime/tool-pruning.js +8 -16
- package/dist/core/session/session-file.d.ts +91 -5
- package/dist/core/session/session-file.d.ts.map +1 -1
- package/dist/core/session/session-file.js +206 -24
- package/dist/core/session/session-loop.d.ts +3 -1
- package/dist/core/session/session-loop.d.ts.map +1 -1
- package/dist/core/session/session-loop.js +8 -4
- package/dist/core/settings/project-settings.d.ts +63 -9
- package/dist/core/settings/project-settings.d.ts.map +1 -1
- package/dist/core/settings/project-settings.js +43 -2
- package/dist/core/settings/workspace-context.js +4 -4
- package/dist/core/store/in-memory-session-store.d.ts.map +1 -1
- package/dist/core/store/in-memory-session-store.js +3 -2
- package/dist/core/store/session-store.contract.test-helper.d.ts +7 -5
- package/dist/core/store/session-store.contract.test-helper.d.ts.map +1 -1
- package/dist/core/store/session-store.contract.test-helper.js +7 -5
- package/dist/core/store/session-store.d.ts +4 -2
- package/dist/core/store/session-store.d.ts.map +1 -1
- package/dist/core/store/session-store.js +4 -2
- package/dist/core/tools/ask-question.d.ts.map +1 -1
- package/dist/core/tools/ask-question.js +51 -5
- package/dist/core/tools/bash-pty.d.ts +13 -4
- package/dist/core/tools/bash-pty.d.ts.map +1 -1
- package/dist/core/tools/bash-pty.js +15 -8
- package/dist/core/tools/bash-sandbox.d.ts +1 -1
- package/dist/core/tools/bash.d.ts +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +18 -8
- package/dist/core/tools/browser.d.ts +54 -0
- package/dist/core/tools/browser.d.ts.map +1 -1
- package/dist/core/tools/browser.js +138 -1
- package/dist/core/tools/file-fingerprint.d.ts +24 -0
- package/dist/core/tools/file-fingerprint.d.ts.map +1 -0
- package/dist/core/tools/file-fingerprint.js +61 -0
- package/dist/core/tools/file-tools.d.ts +26 -0
- package/dist/core/tools/file-tools.d.ts.map +1 -1
- package/dist/core/tools/file-tools.js +113 -17
- package/dist/core/tools/index.d.ts +2 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +1 -0
- package/dist/core/tools/mcp-mount.d.ts +1 -1
- package/dist/core/tools/mcp-mount.d.ts.map +1 -1
- package/dist/core/tools/mcp-mount.js +72 -26
- package/dist/core/tools/url-guard.d.ts +15 -0
- package/dist/core/tools/url-guard.d.ts.map +1 -1
- package/dist/core/tools/url-guard.js +54 -14
- package/dist/core/tools/web-ports-std.d.ts.map +1 -1
- package/dist/core/tools/web-ports-std.js +5 -2
- package/dist/modes/print-mode.d.ts +3 -2
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +2 -2
- package/dist/modes/shared.d.ts +12 -0
- package/dist/modes/shared.d.ts.map +1 -1
- package/dist/modes/shared.js +23 -6
- package/dist/modes/tui/agent-status.d.ts +20 -0
- package/dist/modes/tui/agent-status.d.ts.map +1 -0
- package/dist/modes/tui/agent-status.js +35 -0
- package/dist/modes/tui/ask-panel.d.ts +166 -0
- package/dist/modes/tui/ask-panel.d.ts.map +1 -0
- package/dist/modes/tui/ask-panel.js +888 -0
- package/dist/modes/tui/pty-console.d.ts +3 -0
- package/dist/modes/tui/pty-console.d.ts.map +1 -1
- package/dist/modes/tui/pty-console.js +19 -0
- package/dist/modes/tui/settings-overlay.d.ts +19 -0
- package/dist/modes/tui/settings-overlay.d.ts.map +1 -1
- package/dist/modes/tui/settings-overlay.js +46 -10
- package/dist/modes/tui/tui-commands-session.d.ts.map +1 -1
- package/dist/modes/tui/tui-commands-session.js +20 -6
- package/dist/modes/tui/tui-commands.d.ts +44 -3
- package/dist/modes/tui/tui-commands.d.ts.map +1 -1
- package/dist/modes/tui/tui-commands.js +380 -6
- package/dist/modes/tui/tui-components.d.ts +10 -0
- package/dist/modes/tui/tui-components.d.ts.map +1 -1
- package/dist/modes/tui/tui-components.js +26 -0
- package/dist/modes/tui/tui-format.d.ts +53 -4
- package/dist/modes/tui/tui-format.d.ts.map +1 -1
- package/dist/modes/tui/tui-format.js +137 -42
- package/dist/modes/tui/tui-frame-provider.d.ts +11 -3
- package/dist/modes/tui/tui-frame-provider.d.ts.map +1 -1
- package/dist/modes/tui/tui-frame-provider.js +14 -5
- package/dist/modes/tui/tui-interactive.d.ts.map +1 -1
- package/dist/modes/tui/tui-interactive.js +14 -0
- package/dist/modes/tui/tui-io.d.ts.map +1 -1
- package/dist/modes/tui/tui-io.js +150 -55
- package/dist/modes/tui/tui-mode.d.ts.map +1 -1
- package/dist/modes/tui/tui-mode.js +428 -23
- package/dist/modes/tui/tui-overlays.d.ts +6 -10
- package/dist/modes/tui/tui-overlays.d.ts.map +1 -1
- package/dist/modes/tui/tui-overlays.js +0 -43
- package/dist/modes/tui/tui-render.d.ts +10 -0
- package/dist/modes/tui/tui-render.d.ts.map +1 -1
- package/dist/modes/tui/tui-render.js +73 -38
- package/dist/modes/tui/tui-seam.d.ts +28 -6
- package/dist/modes/tui/tui-seam.d.ts.map +1 -1
- package/dist/modes/tui/tui-slash.d.ts.map +1 -1
- package/dist/modes/tui/tui-slash.js +3 -1
- package/dist/modes/tui/tui-tool-render.d.ts +14 -1
- package/dist/modes/tui/tui-tool-render.d.ts.map +1 -1
- package/dist/modes/tui/tui-tool-render.js +158 -50
- package/dist/modes/tui/tui-transcript-reconciler.d.ts +4 -0
- package/dist/modes/tui/tui-transcript-reconciler.d.ts.map +1 -1
- package/dist/modes/tui/tui-transcript-reconciler.js +12 -2
- package/dist/modes/tui/view-state.d.ts.map +1 -1
- package/dist/modes/tui/view-state.js +4 -1
- package/dist/protocol/transport.d.ts +10 -0
- package/dist/protocol/transport.d.ts.map +1 -1
- package/dist/protocol/transport.js +1 -0
- 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,
|
|
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
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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;
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
|
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,
|
|
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"}
|