@kasenri/dsh-orbit 0.5.4 → 0.5.6
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/lib/activation.js +30 -67
- package/lib/client.js +8 -8
- package/lib/index.js +76 -18
- package/lib/kernel.js +6 -2
- package/lib/supervisor.js +22 -10
- package/package.json +1 -1
package/lib/activation.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Deterministic
|
|
2
|
+
* Deterministic Orbit activation.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* -
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
* without one.
|
|
4
|
+
* Both entry points hand control to the host runtime directly:
|
|
5
|
+
* - an explicit `/agent-orbit <goal>` statement (host command or genuine
|
|
6
|
+
* message), whatever the Session toggle says, and
|
|
7
|
+
* - every ordinary user message in a Session whose toggle is ON.
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* The pre-step boundary consumes the turn (no parent model call, no parent
|
|
10
|
+
* mutation) and starts or resumes the existing `OrbitService`; the same
|
|
11
|
+
* `orbit_controller` tool + Supervisor remain the only runtime.
|
|
13
12
|
*/
|
|
14
13
|
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
15
14
|
import { ORBIT_TOGGLE_COMMAND, parseOrbitToggle } from "./session-state.js";
|
|
@@ -55,39 +54,13 @@ export function invokedOrbitMessage(messages) {
|
|
|
55
54
|
}
|
|
56
55
|
return undefined;
|
|
57
56
|
}
|
|
58
|
-
/**
|
|
59
|
-
* The one deterministic directive injected for an explicit activation. It only
|
|
60
|
-
* states the activation and carries the goal verbatim; every Orbit rule still
|
|
61
|
-
* comes from the existing tool, service and protocol.
|
|
62
|
-
*/
|
|
63
|
-
export function buildOrbitActivationDirective(goal) {
|
|
64
|
-
const lines = [
|
|
65
|
-
'Orbit activation is explicit for this turn. Start or resume Orbit through the existing orbit_controller tool; do not ask the user to confirm the mode.',
|
|
66
|
-
'Treat the text following /agent-orbit as the requested goal, without summarizing or rewriting it.',
|
|
67
|
-
];
|
|
68
|
-
lines.push(goal === ''
|
|
69
|
-
? 'No goal was provided — ask the user what Orbit should accomplish; do not start an empty run.'
|
|
70
|
-
: `Goal: ${goal}`);
|
|
71
|
-
return lines.join('\n');
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* The directive for a Session whose toggle is ON: an ordinary user message
|
|
75
|
-
* becomes the Orbit goal without changing the activation machinery.
|
|
76
|
-
*/
|
|
77
|
-
export function buildOrbitSessionDirective(goal) {
|
|
78
|
-
return [
|
|
79
|
-
'Orbit is enabled for this chat, so treat this ordinary user message as the requested goal. Start or resume Orbit through the existing orbit_controller tool; do not ask the user to confirm the mode.',
|
|
80
|
-
'Use the message as the goal, without summarizing or rewriting it.',
|
|
81
|
-
`Goal: ${goal}`,
|
|
82
|
-
].join('\n');
|
|
83
|
-
}
|
|
84
57
|
/**
|
|
85
58
|
* Register the closed-namespace `/agent-orbit` host command.
|
|
86
59
|
*
|
|
87
|
-
* The handler never
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
60
|
+
* The handler never runs Orbit itself. It reposts the original command line as
|
|
61
|
+
* a genuine user message, so the gesture boundary stays the only activation
|
|
62
|
+
* point and a command-registered surface cannot double activate through both
|
|
63
|
+
* routes.
|
|
91
64
|
*/
|
|
92
65
|
export function registerOrbitCommand(ctx) {
|
|
93
66
|
ctx.effect(() => ctx.commands.register({
|
|
@@ -135,43 +108,33 @@ export function registerOrbitToggleCommand(ctx) {
|
|
|
135
108
|
}), 'dsh-orbit: /orbit-toggle host command');
|
|
136
109
|
}
|
|
137
110
|
/**
|
|
138
|
-
* Install the
|
|
139
|
-
* the activation directive once, for the step that carries the genuine user
|
|
140
|
-
* message. The in-step guard keeps a step that already holds an Orbit
|
|
141
|
-
* directive from gaining a second one, without any durable state.
|
|
111
|
+
* Install the activation boundary. It runs for every proposed step.
|
|
142
112
|
*
|
|
143
|
-
* An explicit `/agent-orbit
|
|
144
|
-
* Session toggle says.
|
|
145
|
-
*
|
|
146
|
-
* message
|
|
113
|
+
* An explicit `/agent-orbit <goal>` always wins and always activates, whatever
|
|
114
|
+
* the Session toggle says. Otherwise, an enabled Session hands the ordinary
|
|
115
|
+
* user message to Orbit. Either way the step is consumed with no parent model
|
|
116
|
+
* call while the message stays in the conversation as durable history.
|
|
147
117
|
*/
|
|
148
118
|
export function installOrbitGestureBoundary(ctx, options = {}) {
|
|
149
119
|
ctx.on('agent/pre-step', async ({ agent, messages, signal }, next) => {
|
|
150
120
|
const decision = await next();
|
|
151
121
|
if (decision.kind === 'reject')
|
|
152
122
|
return decision;
|
|
153
|
-
if (decision.messages.some((message) => message.source.kind === 'orbit-command'))
|
|
154
|
-
return decision;
|
|
155
123
|
const explicit = invokedOrbitActivation(messages);
|
|
156
|
-
const
|
|
157
|
-
?
|
|
158
|
-
:
|
|
159
|
-
|
|
124
|
+
const goal = explicit !== undefined
|
|
125
|
+
? explicit.goal
|
|
126
|
+
: options.sessionEnabled?.(agent.session) === true
|
|
127
|
+
? invokedOrbitMessage(messages)?.goal
|
|
128
|
+
: undefined;
|
|
129
|
+
const activate = options.activate;
|
|
130
|
+
if (goal === undefined || goal === '' || activate === undefined)
|
|
160
131
|
return decision;
|
|
161
132
|
signal.throwIfAborted();
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
:
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
messages: [
|
|
169
|
-
...decision.messages,
|
|
170
|
-
createUserMessage({
|
|
171
|
-
content: [{ type: 'text', text: directive }],
|
|
172
|
-
source: { kind: 'orbit-command', ...(activation.goal === '' ? {} : { goal: activation.goal }) },
|
|
173
|
-
}),
|
|
174
|
-
],
|
|
175
|
-
};
|
|
133
|
+
// The claimed batch enters the conversation exactly once: the loop commits
|
|
134
|
+
// `decision.messages`, which this path leaves empty.
|
|
135
|
+
for (const message of messages)
|
|
136
|
+
agent.session.append('user/message', message, { surfaceOp: 'append' });
|
|
137
|
+
await activate(agent, goal, signal);
|
|
138
|
+
return { kind: 'enter', messages: [] };
|
|
176
139
|
});
|
|
177
140
|
}
|
package/lib/client.js
CHANGED
|
@@ -132,21 +132,21 @@ window.__ModuleLoader__.load({
|
|
|
132
132
|
document.head.appendChild(tag);
|
|
133
133
|
}
|
|
134
134
|
var OrbitModelSelect_module_css_default = {
|
|
135
|
-
"check": "f77nca_check",
|
|
136
|
-
"back": "f77nca_back",
|
|
137
|
-
"switchRow": "f77nca_switchRow",
|
|
138
|
-
"hint": "f77nca_hint",
|
|
139
135
|
"rowDetail": "f77nca_rowDetail",
|
|
140
|
-
"group": "f77nca_group",
|
|
141
136
|
"switchControl": "f77nca_switchControl",
|
|
142
137
|
"rowValue": "f77nca_rowValue",
|
|
143
|
-
"chevron": "f77nca_chevron",
|
|
144
138
|
"title": "f77nca_title",
|
|
145
|
-
"
|
|
139
|
+
"chevron": "f77nca_chevron",
|
|
140
|
+
"switchRow": "f77nca_switchRow",
|
|
141
|
+
"group": "f77nca_group",
|
|
142
|
+
"row": "f77nca_row",
|
|
143
|
+
"back": "f77nca_back",
|
|
144
|
+
"check": "f77nca_check",
|
|
146
145
|
"rowLabel": "f77nca_rowLabel",
|
|
147
146
|
"separator": "f77nca_separator",
|
|
147
|
+
"error": "f77nca_error",
|
|
148
|
+
"hint": "f77nca_hint",
|
|
148
149
|
"panel": "f77nca_panel",
|
|
149
|
-
"row": "f77nca_row",
|
|
150
150
|
"rowValueActive": "f77nca_rowValueActive"
|
|
151
151
|
};
|
|
152
152
|
//#endregion
|
package/lib/index.js
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
|
+
import { boundContextSummary, createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
1
2
|
import z from '@deepseek-ai/schemastery';
|
|
2
3
|
import { installOrbitGestureBoundary, registerOrbitCommand, registerOrbitToggleCommand } from "./activation.js";
|
|
4
|
+
import { estimateLoopCount } from "./kernel.js";
|
|
3
5
|
import { createOrbitPreExecuteHandler } from "./pipeline-guard.js";
|
|
4
6
|
import { resolveEffectiveRoutes, sessionSelectionOf } from "./routes.js";
|
|
5
7
|
import { installOrbitSessionProjection, orbitEnabledOf } from "./session-state.js";
|
|
6
8
|
import { OrbitService } from "./service.js";
|
|
7
9
|
import { createOrbitTool } from "./tool.js";
|
|
10
|
+
/**
|
|
11
|
+
* Minimum loop budget for a hard-activated run. The Commander is asked for a
|
|
12
|
+
* 2-5 step plan and every executed step consumes one loop slot, so the host
|
|
13
|
+
* must grant a budget that can finish a full plan; the goal complexity
|
|
14
|
+
* estimate raises it further when the goal asks for more.
|
|
15
|
+
*/
|
|
16
|
+
const HARD_ACTIVATION_MIN_LOOPS = 5;
|
|
8
17
|
export const name = 'dsh-orbit';
|
|
9
18
|
export const inject = ['tools', 'agents', 'subagents'];
|
|
10
19
|
const Route = z.object({
|
|
@@ -100,25 +109,41 @@ export function apply(ctx, config) {
|
|
|
100
109
|
ctx.tools.register(createOrbitTool(ctx));
|
|
101
110
|
ctx.tools.register(createOrbitTool(ctx, { legacy: true }));
|
|
102
111
|
}
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
ctx.inject(['commands'], (commandCtx) => {
|
|
112
|
+
// Slash-command registration is its own switch: the `/agent-orbit` command
|
|
113
|
+
// (surfaces in the Web GUI slash menu through the Harness commands client)
|
|
114
|
+
// rides `slashCommand`, while the per-chat toggle command and the host
|
|
115
|
+
// activation boundary below never depend on it. `commands` is registered
|
|
116
|
+
// lazily: a minimal composition without the command registry keeps Orbit
|
|
117
|
+
// fully functional — the fiber never pends on it, and the activation
|
|
118
|
+
// boundary (explicit `/agent-orbit` messages and enabled-Session ordinary
|
|
119
|
+
// messages) still runs.
|
|
120
|
+
ctx.inject(['commands'], (commandCtx) => {
|
|
121
|
+
registerOrbitToggleCommand(commandCtx);
|
|
122
|
+
if (config.slashCommand)
|
|
115
123
|
registerOrbitCommand(commandCtx);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
124
|
+
});
|
|
125
|
+
installOrbitGestureBoundary(ctx, {
|
|
126
|
+
sessionEnabled: (session) => orbitEnabledOf(ctx, session),
|
|
127
|
+
activate: async (agent, goal, signal) => {
|
|
128
|
+
const cwd = agent.session.header.cwd ?? process.cwd();
|
|
129
|
+
let result;
|
|
130
|
+
try {
|
|
131
|
+
// The initiator scope makes the run's children belong to this Agent;
|
|
132
|
+
// the parent model is never asked to decide or to run the task.
|
|
133
|
+
result = await ctx.agents.withInitiator(agent, () => service.run({
|
|
134
|
+
goal,
|
|
135
|
+
approved_loop_count: Math.max(estimateLoopCount(goal), HARD_ACTIVATION_MIN_LOOPS),
|
|
136
|
+
}, cwd, signal));
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
appendOrbitNotice(agent.session, `Orbit 启动失败:${error instanceof Error ? error.message : String(error)}`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const notice = activationNotice(result);
|
|
143
|
+
if (notice !== undefined)
|
|
144
|
+
appendOrbitNotice(agent.session, notice);
|
|
145
|
+
},
|
|
146
|
+
});
|
|
122
147
|
// Per-Session Orbit enable state: natively durable through the Session log
|
|
123
148
|
// and the projection registry. Minimal compositions without the registry
|
|
124
149
|
// keep the feature inert (OFF) while `/agent-orbit` keeps working.
|
|
@@ -146,3 +171,36 @@ export function apply(ctx, config) {
|
|
|
146
171
|
ctx.on('tools/pre-execute', (exec, next) => handler(exec, next));
|
|
147
172
|
}
|
|
148
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* One durable, user-visible Orbit notice. The DSH `plugin` + `form: 'notice'`
|
|
176
|
+
* source renders as a collapsed notice row instead of a user message, and the
|
|
177
|
+
* text stays part of the conversation so the next turn can read the outcome.
|
|
178
|
+
*/
|
|
179
|
+
function appendOrbitNotice(session, text) {
|
|
180
|
+
session.append('user/message', createUserMessage({
|
|
181
|
+
content: [{ type: 'text', text }],
|
|
182
|
+
source: { kind: 'plugin', plugin: 'dsh-orbit', form: 'notice', summary: boundContextSummary(text) },
|
|
183
|
+
}), { surfaceOp: 'append' });
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* What the user must see when a hard-activated turn did not simply succeed:
|
|
187
|
+
* the waiting run's question, a refused start, or an unfinished run. The
|
|
188
|
+
* host stays the only mutation driver; the notice is reporting, never a
|
|
189
|
+
* hand-back of execution to the parent model.
|
|
190
|
+
*/
|
|
191
|
+
function activationNotice(result) {
|
|
192
|
+
if (result.ok && result.phase !== 'NEEDS_USER')
|
|
193
|
+
return undefined;
|
|
194
|
+
const lastError = result.data?.['last_error'];
|
|
195
|
+
const reason = result.message ?? (typeof lastError === 'string' && lastError !== '' ? lastError : undefined);
|
|
196
|
+
if (result.phase === 'NEEDS_USER') {
|
|
197
|
+
return reason !== undefined && !reason.startsWith('COMMANDER_')
|
|
198
|
+
? `Orbit 需要你的回复:${reason}`
|
|
199
|
+
: 'Orbit 需要你的回复:请回复当前运行需要的信息。';
|
|
200
|
+
}
|
|
201
|
+
if (result.message?.startsWith('ORBIT_ACTIVE_RUN_EXISTS') === true) {
|
|
202
|
+
return `Orbit 未接受这条新任务:${result.message}`;
|
|
203
|
+
}
|
|
204
|
+
const phase = result.phase ?? 'UNKNOWN';
|
|
205
|
+
return `Orbit 运行未完成(phase=${phase})${reason !== undefined ? `:${reason}` : ''}`;
|
|
206
|
+
}
|
package/lib/kernel.js
CHANGED
|
@@ -151,15 +151,19 @@ export function createInitialState(input) {
|
|
|
151
151
|
changed_files: [],
|
|
152
152
|
test_summary: [],
|
|
153
153
|
last_error: null,
|
|
154
|
+
pending_user_reply: null,
|
|
154
155
|
user_hard_constraints: input.userHardConstraints ? [...input.userHardConstraints] : [],
|
|
155
156
|
github_allowed: input.githubAllowed === true,
|
|
156
157
|
interruption_retries: 0,
|
|
157
158
|
};
|
|
158
159
|
}
|
|
159
|
-
/** A user answers a NEEDS_USER run with the
|
|
160
|
-
export function resumeFromNeedsUser(state) {
|
|
160
|
+
/** A user answers a NEEDS_USER run: execution continues with the reply kept durable. */
|
|
161
|
+
export function resumeFromNeedsUser(state, userReply) {
|
|
161
162
|
state.phase = 'EXECUTE';
|
|
162
163
|
state.status = 'running';
|
|
164
|
+
const reply = (userReply ?? '').trim();
|
|
165
|
+
if (reply !== '')
|
|
166
|
+
state.pending_user_reply = reply;
|
|
163
167
|
}
|
|
164
168
|
/** Accept the Commander plan and move to execution. */
|
|
165
169
|
export function applyPlan(state, plan) {
|
package/lib/supervisor.js
CHANGED
|
@@ -6,18 +6,18 @@ import { applyCommanderNeedsUser, applyCorrectionStep, applyExecutorCapabilityUn
|
|
|
6
6
|
import { truncateSafe } from "./sanitize.js";
|
|
7
7
|
import { OrbitStateStore } from "./state-store.js";
|
|
8
8
|
import { COMMANDER_EXTENSION_MS, COMMANDER_HARD_CEILING_MS, COMMANDER_SOFT_DEADLINE_MS, DEFAULT_CAPABILITIES, EXECUTOR_TIMEOUT_MS, GUARD_ESCALATION_THRESHOLD, GUARD_FIRST_INSTRUCTION, GUARD_NEEDS_USER_INSTRUCTION, GUARD_RECOVERY_CAP, GUARD_REPEAT_INSTRUCTION, GUARD_RETRY_INSTRUCTION, MAX_EXECUTOR_INTERRUPT_RETRIES, MAX_WATCHDOG_CALLS_PER_STEP, WATCHDOG_TIMEOUT_MS, } from "./types.js";
|
|
9
|
-
const COMMANDER_PLAN_PROMPT = (goal, constraints) => `You are the Orbit Commander. Produce the smallest set of 2-5 logical engineering steps for this goal.
|
|
9
|
+
const COMMANDER_PLAN_PROMPT = (goal, constraints, userReply) => `You are the Orbit Commander. Produce the smallest set of 2-5 logical engineering steps for this goal.
|
|
10
10
|
Rules: ordinary engineering steps must omit capabilities. Add capability "browser" only when the step must drive a real web page, and "web-api-recon" when it must analyze captured network/API traffic. Keep it minimal.
|
|
11
11
|
Submit your final plan through the structured result protocol.
|
|
12
12
|
Goal: ${goal}
|
|
13
|
-
Hard constraints: ${constraints.join('; ') || 'none'}`;
|
|
13
|
+
Hard constraints: ${constraints.join('; ') || 'none'}${userReply}`;
|
|
14
14
|
const COMMANDER_STEP_PROMPT = (goal, step, evidence, state) => `You are the Orbit Commander doing STEP_EVALUATE. Verify the real project state; do not trust the Executor claim alone. Treat verified execution evidence as authoritative.
|
|
15
15
|
Allowed decisions ONLY: PASS_CURRENT_STEP | CORRECT_CURRENT_STEP | NEEDS_USER.
|
|
16
16
|
- CORRECT_CURRENT_STEP requires a concrete next step goal (optional capabilities).
|
|
17
17
|
Submit your final judgment through the structured result protocol.
|
|
18
18
|
Original goal: ${goal}
|
|
19
19
|
Current step ${step.id}: ${step.goal}
|
|
20
|
-
Iteration counters: loop ${state.loop.used}/${state.loop.max}
|
|
20
|
+
Iteration counters: loop ${state.loop.used}/${state.loop.max}${userReplyLine(state)}
|
|
21
21
|
Executor claim:
|
|
22
22
|
${evidence}`;
|
|
23
23
|
const COMMANDER_FINAL_PROMPT = (goal, plan, evidence, state) => `You are the Orbit Commander doing FINAL_EVALUATE. All planned steps are done. Decide whether the original goal is truly satisfied against the real project state.
|
|
@@ -27,9 +27,16 @@ Submit your final judgment through the structured result protocol.
|
|
|
27
27
|
Original goal: ${goal}
|
|
28
28
|
Plan summary: ${plan.summary}
|
|
29
29
|
Steps: ${plan.steps.map((step) => `${step.id}:${step.goal}[${step.status}]`).join('; ')}
|
|
30
|
-
Loop: ${state.loop.used}/${state.loop.max}
|
|
30
|
+
Loop: ${state.loop.used}/${state.loop.max}${userReplyLine(state)}
|
|
31
31
|
Executor claim:
|
|
32
32
|
${evidence}`;
|
|
33
|
+
/**
|
|
34
|
+
* The durable user reply line for role prompts. The reply is the user's answer
|
|
35
|
+
* to a NEEDS_USER question and never replaces the original goal.
|
|
36
|
+
*/
|
|
37
|
+
function userReplyLine(state) {
|
|
38
|
+
return state.pending_user_reply ? `\nUser reply (the user's answer to the previous question): ${state.pending_user_reply}` : '';
|
|
39
|
+
}
|
|
33
40
|
const COMMANDER_STRATEGY_PROMPT = (goal, base, challenge, state) => `You are the Orbit Commander reconsidering strategy after a repeated correction on ${base} (STRATEGY_RECONSIDER).
|
|
34
41
|
Allowed decisions ONLY: KEEP_APPROACH | REPLACE_CURRENT_STEP | NEEDS_USER.
|
|
35
42
|
- REPLACE_CURRENT_STEP requires a replacement goal.
|
|
@@ -104,7 +111,13 @@ export class OrbitSupervisor {
|
|
|
104
111
|
if (['SUCCESS', 'STOPPED', 'BUDGET_EXHAUSTED'].includes(state.phase) && requestedGoal) {
|
|
105
112
|
state = this.store.writeState(this.createState(input));
|
|
106
113
|
}
|
|
107
|
-
if (
|
|
114
|
+
if (state.phase === 'NEEDS_USER' && requestedGoal) {
|
|
115
|
+
// A reply to the Commander's question is not a new goal: keep the run,
|
|
116
|
+
// its original goal and its frozen routes, and carry the reply durably.
|
|
117
|
+
resumeFromNeedsUser(state, requestedGoal);
|
|
118
|
+
this.store.writeState(state);
|
|
119
|
+
}
|
|
120
|
+
else if (!legacy && requestedGoal && state.goal && state.goal !== requestedGoal) {
|
|
108
121
|
return {
|
|
109
122
|
ok: false,
|
|
110
123
|
action: 'run',
|
|
@@ -112,10 +125,6 @@ export class OrbitSupervisor {
|
|
|
112
125
|
message: 'ORBIT_ACTIVE_RUN_EXISTS: current Lite run owns this project; resume it or stop it before starting a different goal.',
|
|
113
126
|
};
|
|
114
127
|
}
|
|
115
|
-
if (state.phase === 'NEEDS_USER' && requestedGoal) {
|
|
116
|
-
resumeFromNeedsUser(state);
|
|
117
|
-
this.store.writeState(state);
|
|
118
|
-
}
|
|
119
128
|
return this.run(state, signal);
|
|
120
129
|
}
|
|
121
130
|
async run(state, signal) {
|
|
@@ -194,7 +203,7 @@ export class OrbitSupervisor {
|
|
|
194
203
|
}
|
|
195
204
|
// ── commander supervised path ──────────────────────────────────────────────
|
|
196
205
|
async makePlan(state, signal) {
|
|
197
|
-
const outcome = await this.runCommander(state, 'PLAN', COMMANDER_PLAN_PROMPT(state.goal, state.user_hard_constraints), COMMANDER_PLAN_SCHEMA, signal);
|
|
206
|
+
const outcome = await this.runCommander(state, 'PLAN', COMMANDER_PLAN_PROMPT(state.goal, state.user_hard_constraints, userReplyLine(state)), COMMANDER_PLAN_SCHEMA, signal);
|
|
198
207
|
if (outcome.kind === 'needs_user')
|
|
199
208
|
return this.setNeedsUser(state, outcome.reason);
|
|
200
209
|
if (outcome.kind === 'interrupted') {
|
|
@@ -448,6 +457,8 @@ export class OrbitSupervisor {
|
|
|
448
457
|
`Working directory: ${join(this.store.stateDir, '..')}`,
|
|
449
458
|
`Hard constraints: ${state.user_hard_constraints.join('; ') || 'none'}`,
|
|
450
459
|
];
|
|
460
|
+
if (state.pending_user_reply)
|
|
461
|
+
lines.push(`User reply (the user's answer to the previous question): ${state.pending_user_reply}`);
|
|
451
462
|
if ((step.capabilities ?? []).length > 0)
|
|
452
463
|
lines.push(`Capabilities: ${(step.capabilities ?? []).join(', ')}`);
|
|
453
464
|
lines.push('Return a compact evidence summary: what changed, commands/tests run, and residual risks.');
|
|
@@ -773,6 +784,7 @@ export class OrbitSupervisor {
|
|
|
773
784
|
strategy_challenge: state.strategy_challenge,
|
|
774
785
|
guard_recovery: state.guard_recovery,
|
|
775
786
|
last_error: state.last_error,
|
|
787
|
+
pending_user_reply: state.pending_user_reply,
|
|
776
788
|
changed_files: state.changed_files,
|
|
777
789
|
test_summary: state.test_summary,
|
|
778
790
|
driver_ownership: state.driver_ownership,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kasenri/dsh-orbit",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Deterministic engineering orchestration for DeepSeek Harness with Commander, Executor, Smart Watchdog, bounded recovery and durable execution state.",
|
|
6
6
|
"license": "MIT",
|