@kasenri/dsh-orbit 0.5.6 → 0.5.8
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 +24 -19
- package/cordis.patch.yml +2 -2
- package/lib/activation.js +9 -9
- package/lib/capabilities.js +23 -0
- package/lib/client.js +49 -79
- package/lib/decisions.js +4 -4
- package/lib/dsh-host.js +108 -30
- package/lib/evidence.js +24 -0
- package/lib/guard.js +17 -17
- package/lib/index.js +44 -42
- package/lib/kernel.js +33 -21
- package/lib/pipeline-guard.js +16 -4
- package/lib/routes.js +67 -37
- package/lib/sanitize.js +3 -2
- package/lib/service.js +75 -15
- package/lib/settlement.js +4 -1
- package/lib/state-store.js +15 -7
- package/lib/supervisor.js +197 -91
- package/lib/tool.js +10 -11
- package/lib/types.js +6 -5
- package/package.json +1 -1
package/lib/supervisor.js
CHANGED
|
@@ -1,70 +1,88 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import { buildEvidenceBundle, formatEvidenceBundle } from "./evidence.js";
|
|
3
|
+
import { buildEvidenceBundle, buildStepResult, formatStepResults, formatEvidenceBundle } from "./evidence.js";
|
|
4
4
|
import { assertCommanderDecision, assertStrategyDecision, assertTimeoutDecision, assertWatchdogDecision, assertGuardWatchdogDecision, COMMANDER_FINAL_EVALUATE_SCHEMA, COMMANDER_PLAN_SCHEMA, COMMANDER_STEP_EVALUATE_SCHEMA, COMMANDER_STRATEGY_SCHEMA, WATCHDOG_GUARD_SCHEMA, WATCHDOG_RUNTIME_SCHEMA, WATCHDOG_STRATEGY_SCHEMA, WATCHDOG_TIMEOUT_SCHEMA, } from "./decisions.js";
|
|
5
|
-
import { applyCommanderNeedsUser, applyCorrectionStep, applyExecutorCapabilityUnavailable, applyExecutorInterrupted, applyExecutorResume, applyExecutorSuccess, applyFinalAppend, applyFinalSuccess, applyPlan, applyStepPass, baseStepIdOf, beginStep, clearExecutorChild, clearGuardRecovery, correctionBlockCode, correctionDepthOf, createInitialState, enterBudgetExhausted, enterNeedsUser, markStrategyChallengeUsed, normalizePlan, openWatchdogAttempt, recordGuardRecovery, recordPlanFailure, recordWatchdogDecision, restoreEvaluationState, resumeFromNeedsUser, stopRun, } from "./kernel.js";
|
|
5
|
+
import { applyCommanderNeedsUser, applyCorrectionStep, applyExecutorCapabilityUnavailable, applyExecutorInterrupted, applyExecutorResume, applyExecutorSuccess, applyFinalAppend, applyFinalSuccess, applyPlan, applyStepPass, baseStepIdOf, beginStep, clearExecutorChild, clearGuardRecovery, correctionBlockCode, correctionDepthOf, createInitialState, enterBudgetExhausted, enterNeedsUser, markStrategyChallengeUsed, normalizePlan, openWatchdogAttempt, recordGuardRecovery, recordPlanFailure, recordWatchdogDecision, restoreEvaluationState, resumeFromNeedsUser, stopRun, upsertStepResult, normalizeCapabilities, } from "./kernel.js";
|
|
6
6
|
import { truncateSafe } from "./sanitize.js";
|
|
7
7
|
import { OrbitStateStore } from "./state-store.js";
|
|
8
|
+
import { executorToolsFor, EXECUTOR_READ_ONLY_TOOLS, READ_ONLY_ROLE_TOOLS } from "./capabilities.js";
|
|
8
9
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
10
|
+
import { resolveEffectiveRoutes } from "./routes.js";
|
|
11
|
+
const COMMANDER_PLAN_PROMPT = (goal, constraints, userReply) => `你是 Orbit 指挥官(Commander),当前阶段:PLAN。
|
|
12
|
+
请为下述目标制定最小化的 1-5 个逻辑工程步骤。
|
|
13
|
+
规则:基础读取不声明 capabilities;修改文件使用 "filesystem",执行命令使用 "shell",访问网页/API 使用 "web",驱动真实浏览器使用 "browser"。只选择当前步骤真正需要的能力,可组合,保持最小化。
|
|
14
|
+
请通过结构化结果协议提交最终计划。
|
|
15
|
+
你的自然语言输出、推理说明和总结默认全部使用简体中文;decision 枚举、capability id、代码、命令、路径、provider/model ID 等机器标识保持原样。
|
|
16
|
+
目标:${goal}
|
|
17
|
+
硬性约束:${constraints.join(';') || '无'}${userReply}`;
|
|
18
|
+
const COMMANDER_STEP_PROMPT = (goal, step, evidence, state) => `你是 Orbit 指挥官(Commander),当前阶段:STEP_EVALUATE。
|
|
19
|
+
请核实真实项目状态,不要只相信执行员的说法;以可验证的执行证据为准。
|
|
20
|
+
允许的 decision 仅限:PASS_CURRENT_STEP | CORRECT_CURRENT_STEP | NEEDS_USER。
|
|
21
|
+
- CORRECT_CURRENT_STEP 必须给出具体的下一步目标(可选 capabilities)。
|
|
22
|
+
请通过结构化结果协议提交最终判断。
|
|
23
|
+
你的自然语言输出、推理说明和总结默认全部使用简体中文;decision 枚举、代码、命令、路径、provider/model ID 等机器标识保持原样。
|
|
24
|
+
原始目标:${goal}
|
|
25
|
+
当前步骤 ${step.id}:${step.goal}
|
|
26
|
+
迭代计数:loop ${state.loop.used}/${state.loop.max}${userReplyLine(state)}
|
|
27
|
+
执行员证据:
|
|
22
28
|
${evidence}`;
|
|
23
|
-
const COMMANDER_FINAL_PROMPT = (goal, plan, evidence, state) =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const COMMANDER_FINAL_PROMPT = (goal, plan, evidence, state) => `你是 Orbit 指挥官(Commander),当前阶段:FINAL_EVALUATE。
|
|
30
|
+
所有计划步骤均已执行完毕。请依据真实项目状态判断原始目标是否真正达成。
|
|
31
|
+
允许的 decision 仅限:SUCCESS | APPEND | NEEDS_USER。
|
|
32
|
+
- APPEND 必须给出新增步骤或下一步目标;追加受剩余 loop 预算限制。
|
|
33
|
+
请通过结构化结果协议提交最终判断。
|
|
34
|
+
你的自然语言输出、推理说明和总结默认全部使用简体中文;decision 枚举、代码、命令、路径、provider/model ID 等机器标识保持原样。
|
|
35
|
+
原始目标:${goal}
|
|
36
|
+
计划摘要:${plan.summary}
|
|
37
|
+
步骤:${plan.steps.map((step) => `${step.id}:${step.goal}[${step.status}]`).join('; ')}
|
|
38
|
+
Loop:${state.loop.used}/${state.loop.max}${userReplyLine(state)}
|
|
39
|
+
执行员证据:
|
|
32
40
|
${evidence}`;
|
|
33
41
|
/**
|
|
34
42
|
* The durable user reply line for role prompts. The reply is the user's answer
|
|
35
43
|
* to a NEEDS_USER question and never replaces the original goal.
|
|
36
44
|
*/
|
|
37
45
|
function userReplyLine(state) {
|
|
38
|
-
return state.pending_user_reply ? `\
|
|
46
|
+
return state.pending_user_reply ? `\n用户回复(对上一个问题的回答):${state.pending_user_reply}` : '';
|
|
39
47
|
}
|
|
40
|
-
const COMMANDER_STRATEGY_PROMPT = (goal, base, challenge, state) =>
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
48
|
+
const COMMANDER_STRATEGY_PROMPT = (goal, base, challenge, state) => `你是 Orbit 指挥官(Commander),当前阶段:STRATEGY_RECONSIDER。
|
|
49
|
+
步骤 ${base} 已连续修正多次,请重新审视当前策略。
|
|
50
|
+
允许的 decision 仅限:KEEP_APPROACH | REPLACE_CURRENT_STEP | NEEDS_USER。
|
|
51
|
+
- REPLACE_CURRENT_STEP 必须给出替代目标。
|
|
52
|
+
请通过结构化结果协议提交最终判断。
|
|
53
|
+
你的自然语言输出、推理说明和总结默认全部使用简体中文;decision 枚举、代码、命令、路径、provider/model ID 等机器标识保持原样。
|
|
54
|
+
目标:${goal}
|
|
55
|
+
Loop:${state.loop.used}/${state.loop.max}
|
|
56
|
+
监控模型质疑:${challenge}`;
|
|
57
|
+
const WATCHDOG_RUNTIME_PROMPT = (step, reason, telemetry) => `你是 Orbit 监控模型(Smart Watchdog),当前阶段:RUNTIME_DIAGNOSE。
|
|
58
|
+
只诊断当前运行时异常,不评审代码质量。
|
|
59
|
+
允许的 decision 仅限:RESUME_CHILD | RESTART_STEP | NEEDS_USER | RUNTIME_BUG。
|
|
60
|
+
请通过结构化结果协议提交最终判断。
|
|
61
|
+
你的自然语言输出、推理说明和总结默认全部使用简体中文;decision 枚举、代码、命令、路径、provider/model ID 等机器标识保持原样。
|
|
62
|
+
失败步骤 ${step.id}:${step.goal}
|
|
63
|
+
运行时异常:${reason}
|
|
64
|
+
遥测:${JSON.stringify(telemetry ?? {})}`;
|
|
65
|
+
const WATCHDOG_STRATEGY_PROMPT = (step, reason, state) => `你是 Orbit 监控模型(Smart Watchdog),当前阶段:STRATEGY_CHALLENGE。
|
|
66
|
+
请质疑:当前思路是否陷入隧道视野?这个阻塞是否真的必要?是否存在更简单的路径?
|
|
67
|
+
请通过结构化结果协议提交一个聚焦的质疑问题。
|
|
68
|
+
你的自然语言输出默认使用简体中文;decision 枚举、代码、命令、路径、provider/model ID 等机器标识保持原样。
|
|
69
|
+
步骤 ${step.id}:${step.goal}
|
|
70
|
+
重复修正:${reason}
|
|
71
|
+
Loop:${state.loop.used}/${state.loop.max}`;
|
|
72
|
+
const WATCHDOG_GUARD_PROMPT = (code, count, stepId) => `你是 Orbit 监控模型(Smart Watchdog),当前阶段:GUARD_ESCALATION。
|
|
73
|
+
安全护栏已连续 ${count} 次拦截某个工具调用。
|
|
74
|
+
允许的 decision 仅限:RETRY_DIFFERENTLY | NEEDS_USER。
|
|
75
|
+
请通过结构化结果协议提交最终判断。
|
|
76
|
+
你的自然语言输出默认使用简体中文;decision 枚举、代码、命令、路径、provider/model ID 等机器标识保持原样。
|
|
77
|
+
护栏代码:${code}
|
|
78
|
+
步骤:${stepId}`;
|
|
79
|
+
const WATCHDOG_TIMEOUT_PROMPT = (mode, elapsed, extensions, telemetry) => `你是 Orbit 监控模型(Smart Watchdog),当前阶段:COMMANDER_TIMEOUT_REVIEW。
|
|
80
|
+
指挥官已运行 ${elapsed}ms,期间延长 ${extensions} 次。
|
|
81
|
+
允许的 decision 仅限:EXTEND | INTERRUPT | NEEDS_USER。
|
|
82
|
+
请通过结构化结果协议提交最终判断。
|
|
83
|
+
你的自然语言输出默认使用简体中文;decision 枚举、代码、命令、路径、provider/model ID 等机器标识保持原样。
|
|
84
|
+
模式:${mode}
|
|
85
|
+
遥测:${JSON.stringify(telemetry ?? {})}`;
|
|
68
86
|
export class OrbitSupervisor {
|
|
69
87
|
store;
|
|
70
88
|
host;
|
|
@@ -80,40 +98,108 @@ export class OrbitSupervisor {
|
|
|
80
98
|
return this.host.now();
|
|
81
99
|
}
|
|
82
100
|
createState(input) {
|
|
101
|
+
const ownerSessionId = this.config.resolveOwnerSessionId?.();
|
|
83
102
|
return createInitialState({
|
|
84
103
|
runId: typeof input.run_id === 'string' && input.run_id.length > 0 ? input.run_id : randomUUID(),
|
|
85
104
|
now: this.now(),
|
|
86
105
|
goal: (input.goal ?? '').trim(),
|
|
87
106
|
...(input.preset !== undefined ? { preset: input.preset } : {}),
|
|
88
|
-
routes: this.
|
|
107
|
+
routes: this.resolveNewRoutes(),
|
|
89
108
|
...(input.approved_loop_count !== undefined ? { approvedLoopCount: input.approved_loop_count } : {}),
|
|
90
109
|
...(input.max_loops !== undefined ? { maxLoops: input.max_loops } : {}),
|
|
91
110
|
...(input.user_hard_constraints ? { userHardConstraints: input.user_hard_constraints } : {}),
|
|
92
111
|
githubAllowed: input.github_allowed === true,
|
|
112
|
+
...(ownerSessionId === undefined ? {} : { ownerSessionId }),
|
|
93
113
|
});
|
|
94
114
|
}
|
|
115
|
+
resolveNewRoutes() {
|
|
116
|
+
if (this.config.resolveRoutes)
|
|
117
|
+
return this.config.resolveRoutes();
|
|
118
|
+
return resolveEffectiveRoutes({ configRoutes: this.config.defaultRoutes });
|
|
119
|
+
}
|
|
120
|
+
async preflightRoutes(routes, signal) {
|
|
121
|
+
const issues = await this.host.validateRoutes(routes, signal);
|
|
122
|
+
return issues.length === 0 ? undefined : `ORBIT_ROLE_MODEL_UNAVAILABLE: ${issues.join(';')}。请重新选择可用模型。`;
|
|
123
|
+
}
|
|
95
124
|
async bootstrap(input, signal) {
|
|
125
|
+
if (signal?.aborted)
|
|
126
|
+
return { ok: false, action: 'run', message: 'ORBIT_ABORTED' };
|
|
127
|
+
const competitors = await this.host.otherMutationDrivers(join(this.store.stateDir, '..'));
|
|
128
|
+
if (competitors.length > 0)
|
|
129
|
+
return {
|
|
130
|
+
ok: false, action: 'run', message: `ORBIT_MUTATION_DRIVER_CONFLICT: ${competitors.join(', ')} 已持有此 workspace 的修改权。`,
|
|
131
|
+
};
|
|
96
132
|
const requestedGoal = (input.goal ?? '').trim();
|
|
97
133
|
let state = this.store.readState();
|
|
98
134
|
const raw = this.store.readRawState();
|
|
99
135
|
const legacy = raw !== null && raw['schema_version'] !== 2;
|
|
100
136
|
if (legacy && requestedGoal) {
|
|
101
|
-
|
|
137
|
+
let created;
|
|
138
|
+
try {
|
|
139
|
+
created = this.createState(input);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
return { ok: false, action: 'run', message: error instanceof Error ? error.message : String(error) };
|
|
143
|
+
}
|
|
144
|
+
const invalid = await this.preflightRoutes(created.routes, signal);
|
|
145
|
+
if (invalid)
|
|
146
|
+
return { ok: false, action: 'run', message: invalid };
|
|
147
|
+
state = this.store.writeState(created);
|
|
102
148
|
}
|
|
103
149
|
else if (!state) {
|
|
104
150
|
if (!requestedGoal)
|
|
105
|
-
return { ok: false, action: 'run', message: 'ORBIT_GOAL_REQUIRED:
|
|
106
|
-
|
|
151
|
+
return { ok: false, action: 'run', message: 'ORBIT_GOAL_REQUIRED: 请提供要执行的目标。' };
|
|
152
|
+
let created;
|
|
153
|
+
try {
|
|
154
|
+
created = this.createState(input);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
return { ok: false, action: 'run', message: error instanceof Error ? error.message : String(error) };
|
|
158
|
+
}
|
|
159
|
+
const invalid = await this.preflightRoutes(created.routes, signal);
|
|
160
|
+
if (invalid)
|
|
161
|
+
return { ok: false, action: 'run', message: invalid };
|
|
162
|
+
state = this.store.writeState(created);
|
|
107
163
|
}
|
|
108
164
|
else if (input.run_id && input.run_id !== state.run_id && !legacy) {
|
|
109
165
|
return { ok: false, action: 'run', message: `ORBIT_RUN_NOT_FOUND: ${input.run_id}` };
|
|
110
166
|
}
|
|
111
167
|
if (['SUCCESS', 'STOPPED', 'BUDGET_EXHAUSTED'].includes(state.phase) && requestedGoal) {
|
|
112
|
-
|
|
168
|
+
let created;
|
|
169
|
+
try {
|
|
170
|
+
created = this.createState(input);
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
return { ok: false, action: 'run', message: error instanceof Error ? error.message : String(error) };
|
|
174
|
+
}
|
|
175
|
+
const invalid = await this.preflightRoutes(created.routes, signal);
|
|
176
|
+
if (invalid)
|
|
177
|
+
return { ok: false, action: 'run', message: invalid };
|
|
178
|
+
state = this.store.writeState(created);
|
|
113
179
|
}
|
|
114
180
|
if (state.phase === 'NEEDS_USER' && requestedGoal) {
|
|
115
181
|
// 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
|
|
182
|
+
// its original goal and its frozen routes, and carry the reply durably —
|
|
183
|
+
// but only from the Session that owns the run. Another Session's message
|
|
184
|
+
// must never be consumed as this run's reply.
|
|
185
|
+
const owner = state.owner_session_id;
|
|
186
|
+
const incoming = this.config.resolveOwnerSessionId?.();
|
|
187
|
+
if (owner === undefined) {
|
|
188
|
+
return {
|
|
189
|
+
ok: false, action: 'run', run_id: state.run_id, phase: state.phase,
|
|
190
|
+
message: `ORBIT_NEEDS_USER_OWNER_UNKNOWN: 这是升级前遗留 Run ${state.run_id},无法安全判断所属 Session。请显式 resume,或 stop 后重新开始。`,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
if (incoming === undefined || incoming !== owner) {
|
|
194
|
+
return {
|
|
195
|
+
ok: false,
|
|
196
|
+
action: 'run',
|
|
197
|
+
run_id: state.run_id,
|
|
198
|
+
phase: state.phase,
|
|
199
|
+
message: `ORBIT_NEEDS_USER_OTHER_SESSION: Run ${state.run_id}(goal: ${truncateSafe(state.goal, 120)})仍在等待所属 Session 的用户回复;` +
|
|
200
|
+
'当前消息未被当作回复。请在原 Session 回复,或显式 stop/resume。',
|
|
201
|
+
};
|
|
202
|
+
}
|
|
117
203
|
resumeFromNeedsUser(state, requestedGoal);
|
|
118
204
|
this.store.writeState(state);
|
|
119
205
|
}
|
|
@@ -122,12 +208,12 @@ export class OrbitSupervisor {
|
|
|
122
208
|
ok: false,
|
|
123
209
|
action: 'run',
|
|
124
210
|
run_id: state.run_id,
|
|
125
|
-
message: 'ORBIT_ACTIVE_RUN_EXISTS:
|
|
211
|
+
message: 'ORBIT_ACTIVE_RUN_EXISTS: 当前项目已有活动中的 Orbit Run,请先继续或停止该 Run。',
|
|
126
212
|
};
|
|
127
213
|
}
|
|
128
214
|
return this.run(state, signal);
|
|
129
215
|
}
|
|
130
|
-
async run(state, signal) {
|
|
216
|
+
async run(state, signal, preflight = true) {
|
|
131
217
|
if (signal?.aborted)
|
|
132
218
|
return this.result(state, false, 'ORBIT_ABORTED');
|
|
133
219
|
const projectDir = join(this.store.stateDir, '..');
|
|
@@ -139,7 +225,7 @@ export class OrbitSupervisor {
|
|
|
139
225
|
run_id: state.run_id,
|
|
140
226
|
phase: state.phase,
|
|
141
227
|
status: state.status,
|
|
142
|
-
message: `ORBIT_MUTATION_DRIVER_CONFLICT: ${competitors.join(', ')}
|
|
228
|
+
message: `ORBIT_MUTATION_DRIVER_CONFLICT: ${competitors.join(', ')} 已持有此 workspace 的修改权。`,
|
|
143
229
|
};
|
|
144
230
|
}
|
|
145
231
|
if (state.phase === 'NEEDS_USER')
|
|
@@ -150,6 +236,11 @@ export class OrbitSupervisor {
|
|
|
150
236
|
return this.result(state, true);
|
|
151
237
|
if (state.phase === 'BUDGET_EXHAUSTED')
|
|
152
238
|
return this.result(state, true);
|
|
239
|
+
if (preflight) {
|
|
240
|
+
const routeIssue = await this.preflightRoutes(state.routes, signal);
|
|
241
|
+
if (routeIssue)
|
|
242
|
+
return this.result(state, false, routeIssue);
|
|
243
|
+
}
|
|
153
244
|
if (state.plan.steps.length === 0 && state.phase === 'PLAN') {
|
|
154
245
|
const planOutcome = await this.makePlan(state, signal);
|
|
155
246
|
if (planOutcome)
|
|
@@ -166,7 +257,7 @@ export class OrbitSupervisor {
|
|
|
166
257
|
const applied = await this.applyCommanderOutcome(state, outcome, step, false, signal);
|
|
167
258
|
if (applied)
|
|
168
259
|
return applied;
|
|
169
|
-
return this.run(state, signal);
|
|
260
|
+
return this.run(state, signal, false);
|
|
170
261
|
}
|
|
171
262
|
const step = state.plan.steps.find((candidate) => candidate.status === 'running') ??
|
|
172
263
|
state.plan.steps.find((candidate) => candidate.status === 'pending');
|
|
@@ -175,7 +266,7 @@ export class OrbitSupervisor {
|
|
|
175
266
|
const applied = await this.applyCommanderOutcome(state, outcome, undefined, true, signal);
|
|
176
267
|
if (applied)
|
|
177
268
|
return applied;
|
|
178
|
-
return this.run(state, signal);
|
|
269
|
+
return this.run(state, signal, false);
|
|
179
270
|
}
|
|
180
271
|
if (state.loop.used >= state.loop.max) {
|
|
181
272
|
enterBudgetExhausted(state, 'LOOP_BUDGET_EXHAUSTED');
|
|
@@ -187,7 +278,7 @@ export class OrbitSupervisor {
|
|
|
187
278
|
const executed = await this.executeStep(state, step, signal);
|
|
188
279
|
if (executed.done)
|
|
189
280
|
return this.result(state, executed.ok, executed.message);
|
|
190
|
-
return this.run(state, signal);
|
|
281
|
+
return this.run(state, signal, false);
|
|
191
282
|
}
|
|
192
283
|
// ── tool scoping ───────────────────────────────────────────────────────────
|
|
193
284
|
/**
|
|
@@ -197,7 +288,7 @@ export class OrbitSupervisor {
|
|
|
197
288
|
toolAllow(names, label) {
|
|
198
289
|
const allowed = names.filter((name) => this.host.hasTool(name));
|
|
199
290
|
if (allowed.length === 0) {
|
|
200
|
-
throw new Error(`ORBIT_TOOL_FILTER_EMPTY:
|
|
291
|
+
throw new Error(`ORBIT_TOOL_FILTER_EMPTY: ${label} 没有任何已注册的允许工具:[${names.join(', ')}]`);
|
|
201
292
|
}
|
|
202
293
|
return { allow: allowed };
|
|
203
294
|
}
|
|
@@ -226,7 +317,7 @@ export class OrbitSupervisor {
|
|
|
226
317
|
}
|
|
227
318
|
async commanderEvaluate(state, step, final, signal) {
|
|
228
319
|
const mode = final ? 'FINAL_EVALUATE' : 'STEP_EVALUATE';
|
|
229
|
-
const evidence = this.evidenceFor(state, step) ?? state.commander?.summary ?? state.last_error ?? '
|
|
320
|
+
const evidence = this.evidenceFor(state, step, final) ?? state.commander?.summary ?? state.last_error ?? '未记录执行证据';
|
|
230
321
|
const prompt = final
|
|
231
322
|
? COMMANDER_FINAL_PROMPT(state.goal, state.plan, evidence, state)
|
|
232
323
|
: COMMANDER_STEP_PROMPT(state.goal, step, evidence, state);
|
|
@@ -249,11 +340,13 @@ export class OrbitSupervisor {
|
|
|
249
340
|
* qualifies; after a cold resume the bundle is gone and the caller falls back
|
|
250
341
|
* to the durable state summary.
|
|
251
342
|
*/
|
|
252
|
-
evidenceFor(state, step) {
|
|
343
|
+
evidenceFor(state, step, final = false) {
|
|
344
|
+
if (final)
|
|
345
|
+
return formatStepResults(state);
|
|
253
346
|
const stepId = step?.id ?? state.current_step?.id;
|
|
254
|
-
if (
|
|
255
|
-
return
|
|
256
|
-
return
|
|
347
|
+
if (this.stepEvidence && this.stepEvidence.stepId === stepId)
|
|
348
|
+
return formatEvidenceBundle(this.stepEvidence.bundle);
|
|
349
|
+
return state.step_results?.find((entry) => entry.step_id === stepId)?.evidence;
|
|
257
350
|
}
|
|
258
351
|
/**
|
|
259
352
|
* The single adaptive Commander runner for PLAN, STEP_EVALUATE, FINAL_EVALUATE
|
|
@@ -270,7 +363,8 @@ export class OrbitSupervisor {
|
|
|
270
363
|
label: `commander-${mode.toLowerCase()}`,
|
|
271
364
|
prompt,
|
|
272
365
|
route: state.routes.commander,
|
|
273
|
-
|
|
366
|
+
workspace: join(this.store.stateDir, '..'),
|
|
367
|
+
toolFilter: this.toolAllow(this.config.commanderReadOnlyTools.filter((name) => READ_ONLY_ROLE_TOOLS.includes(name)), `commander ${mode}`),
|
|
274
368
|
outputSchema,
|
|
275
369
|
...(signal ? { signal } : {}),
|
|
276
370
|
});
|
|
@@ -329,27 +423,29 @@ export class OrbitSupervisor {
|
|
|
329
423
|
...(signal ? { signal } : {}),
|
|
330
424
|
});
|
|
331
425
|
if (!result || result.interrupted) {
|
|
332
|
-
return { decision: extensions === 0 ? 'EXTEND' : 'INTERRUPT', reason: '
|
|
426
|
+
return { decision: extensions === 0 ? 'EXTEND' : 'INTERRUPT', reason: '监控模型暂时不可用' };
|
|
333
427
|
}
|
|
334
428
|
try {
|
|
335
429
|
return assertTimeoutDecision(result.structured);
|
|
336
430
|
}
|
|
337
431
|
catch {
|
|
338
|
-
return { decision: extensions === 0 ? 'EXTEND' : 'INTERRUPT', reason: '
|
|
432
|
+
return { decision: extensions === 0 ? 'EXTEND' : 'INTERRUPT', reason: '监控模型返回了无效结果' };
|
|
339
433
|
}
|
|
340
434
|
}
|
|
341
435
|
// ── executor runtime ───────────────────────────────────────────────────────
|
|
342
436
|
async executeStep(state, step, signal) {
|
|
343
|
-
const capabilities = step.capabilities ?? [];
|
|
344
|
-
if (capabilities.includes('browser') && !this.
|
|
437
|
+
const capabilities = normalizeCapabilities(step.capabilities) ?? [];
|
|
438
|
+
if (capabilities.includes('browser') && !this.config.browserTools.some((tool) => this.host.hasTool(tool))) {
|
|
345
439
|
applyExecutorCapabilityUnavailable(state, step.id);
|
|
440
|
+
upsertStepResult(state, buildStepResult(step.id, state.current_step?.attempt ?? 1, buildEvidenceBundle({
|
|
441
|
+
executorOutput: state.commander?.summary,
|
|
442
|
+
})));
|
|
346
443
|
this.store.writeState(state);
|
|
347
444
|
return { done: false, ok: false };
|
|
348
445
|
}
|
|
349
446
|
let toolFilter;
|
|
350
447
|
try {
|
|
351
|
-
|
|
352
|
-
toolFilter = this.toolAllow([...this.config.executorTools, ...capabilityTools], `executor ${step.id}`);
|
|
448
|
+
toolFilter = this.toolAllow(executorToolsFor(capabilities, this.config.browserTools, this.config.executorTools), `executor ${step.id}`);
|
|
353
449
|
}
|
|
354
450
|
catch (error) {
|
|
355
451
|
const reason = truncateSafe(error instanceof Error ? error.message : String(error), 500);
|
|
@@ -364,6 +460,7 @@ export class OrbitSupervisor {
|
|
|
364
460
|
label: `executor-${step.id}`,
|
|
365
461
|
prompt: this.executorPrompt(state, step),
|
|
366
462
|
route: state.routes.executor,
|
|
463
|
+
workspace: join(this.store.stateDir, '..'),
|
|
367
464
|
toolFilter,
|
|
368
465
|
capabilities,
|
|
369
466
|
...(signal ? { signal } : {}),
|
|
@@ -431,6 +528,7 @@ export class OrbitSupervisor {
|
|
|
431
528
|
telemetry: result.telemetry,
|
|
432
529
|
}),
|
|
433
530
|
};
|
|
531
|
+
upsertStepResult(state, buildStepResult(step.id, state.current_step?.attempt ?? 1, this.stepEvidence.bundle, state.test_summary));
|
|
434
532
|
this.store.writeState(state);
|
|
435
533
|
await this.disposeHandle(handle);
|
|
436
534
|
return { done: false, ok: false };
|
|
@@ -441,6 +539,7 @@ export class OrbitSupervisor {
|
|
|
441
539
|
const raced = await this.raceWithSleep(handle.result, timeoutMs, signal);
|
|
442
540
|
if (raced.kind === 'work')
|
|
443
541
|
return raced.value;
|
|
542
|
+
await this.cancelHandle(handle, raced.kind === 'aborted' ? 'ORBIT_ABORTED' : 'EXECUTOR_TIMEOUT');
|
|
444
543
|
const telemetry = await handle.runtimeSnapshot?.();
|
|
445
544
|
return {
|
|
446
545
|
...(handle.childId ? { childId: handle.childId } : {}),
|
|
@@ -452,16 +551,19 @@ export class OrbitSupervisor {
|
|
|
452
551
|
}
|
|
453
552
|
executorPrompt(state, step) {
|
|
454
553
|
const lines = [
|
|
455
|
-
'
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
554
|
+
'你是 Orbit 执行员(Executor)。',
|
|
555
|
+
'你只负责执行当前步骤:不要重新规划整个任务,也不要自行改变当前步骤的目标。',
|
|
556
|
+
'必须使用真实工具完成实际操作,并对结果进行真实验证。',
|
|
557
|
+
'完成后用简体中文提交简洁的执行证据:做了什么、运行了哪些命令/测试、验证结果以及仍存在的风险。',
|
|
558
|
+
'你的自然语言输出、执行说明和总结默认全部使用简体中文;代码、命令、路径、provider/model ID 等机器标识保持原样。',
|
|
559
|
+
`当前步骤 ${step.id}:${step.goal}`,
|
|
560
|
+
`工作目录:${join(this.store.stateDir, '..')}`,
|
|
561
|
+
`硬性约束:${state.user_hard_constraints.join(';') || '无'}`,
|
|
459
562
|
];
|
|
460
563
|
if (state.pending_user_reply)
|
|
461
|
-
lines.push(
|
|
564
|
+
lines.push(`用户回复(对上一个问题的回答):${state.pending_user_reply}`);
|
|
462
565
|
if ((step.capabilities ?? []).length > 0)
|
|
463
|
-
lines.push(
|
|
464
|
-
lines.push('Return a compact evidence summary: what changed, commands/tests run, and residual risks.');
|
|
566
|
+
lines.push(`能力:${(step.capabilities ?? []).join(', ')}`);
|
|
465
567
|
return lines.join('\n');
|
|
466
568
|
}
|
|
467
569
|
// ── decision application ───────────────────────────────────────────────────
|
|
@@ -497,7 +599,7 @@ export class OrbitSupervisor {
|
|
|
497
599
|
if (decision.decision === 'APPEND') {
|
|
498
600
|
const append = applyFinalAppend(state, decision);
|
|
499
601
|
if (append === 'invalid') {
|
|
500
|
-
return this.setNeedsUser(state, 'COMMANDER_EVALUATION_OUTPUT_INVALID:
|
|
602
|
+
return this.setNeedsUser(state, 'COMMANDER_EVALUATION_OUTPUT_INVALID: APPEND 需要 next_steps 或 next_step_goal');
|
|
501
603
|
}
|
|
502
604
|
this.store.writeState(state);
|
|
503
605
|
if (append === 'budget_exhausted')
|
|
@@ -514,7 +616,7 @@ export class OrbitSupervisor {
|
|
|
514
616
|
}
|
|
515
617
|
async applyCorrection(state, step, decision, signal) {
|
|
516
618
|
if (!step || !decision.next_step_goal) {
|
|
517
|
-
return this.setNeedsUser(state, 'COMMANDER_EVALUATION_OUTPUT_INVALID:
|
|
619
|
+
return this.setNeedsUser(state, 'COMMANDER_EVALUATION_OUTPUT_INVALID: CORRECT_CURRENT_STEP 需要 next_step_goal');
|
|
518
620
|
}
|
|
519
621
|
const base = baseStepIdOf(step.id);
|
|
520
622
|
const correctionDepth = correctionDepthOf(step.id);
|
|
@@ -559,7 +661,7 @@ export class OrbitSupervisor {
|
|
|
559
661
|
const challengeValue = challengeResult.structured;
|
|
560
662
|
const challenge = typeof challengeValue.question === 'string' ? challengeValue.question.trim() : '';
|
|
561
663
|
if (!challenge)
|
|
562
|
-
return { kind: 'interrupted', reason: 'SMART_WATCHDOG_STRATEGY_OUTPUT_INVALID: question
|
|
664
|
+
return { kind: 'interrupted', reason: 'SMART_WATCHDOG_STRATEGY_OUTPUT_INVALID: 缺少 question' };
|
|
563
665
|
const outcome = await this.runCommander(state, 'STRATEGY_RECONSIDER', COMMANDER_STRATEGY_PROMPT(state.goal, base, challenge, state), COMMANDER_STRATEGY_SCHEMA, signal);
|
|
564
666
|
if (outcome.kind === 'needs_user')
|
|
565
667
|
return { kind: 'needs_user', reason: outcome.reason };
|
|
@@ -674,7 +776,9 @@ export class OrbitSupervisor {
|
|
|
674
776
|
* even on interruption or timeout.
|
|
675
777
|
*/
|
|
676
778
|
async runAuxRole(state, request) {
|
|
677
|
-
const
|
|
779
|
+
const configured = request.role === 'watchdog' ? this.config.watchdogTools : this.config.commanderReadOnlyTools;
|
|
780
|
+
const readOnly = request.role === 'watchdog' ? EXECUTOR_READ_ONLY_TOOLS : READ_ONLY_ROLE_TOOLS;
|
|
781
|
+
const names = configured.filter((name) => readOnly.includes(name));
|
|
678
782
|
let handle;
|
|
679
783
|
try {
|
|
680
784
|
handle = await this.host.startRole({
|
|
@@ -682,6 +786,7 @@ export class OrbitSupervisor {
|
|
|
682
786
|
label: request.label,
|
|
683
787
|
prompt: request.prompt,
|
|
684
788
|
route: state.routes[request.role],
|
|
789
|
+
workspace: join(this.store.stateDir, '..'),
|
|
685
790
|
toolFilter: this.toolAllow(names, request.role),
|
|
686
791
|
...(request.outputSchema ? { outputSchema: request.outputSchema } : {}),
|
|
687
792
|
...(request.signal ? { signal: request.signal } : {}),
|
|
@@ -755,7 +860,7 @@ export class OrbitSupervisor {
|
|
|
755
860
|
stop(action, runId) {
|
|
756
861
|
const state = this.store.readState();
|
|
757
862
|
if (!state)
|
|
758
|
-
return { ok: false, action, message: 'ORBIT_RUN_NOT_FOUND:
|
|
863
|
+
return { ok: false, action, message: 'ORBIT_RUN_NOT_FOUND: 没有活动中的 Run。' };
|
|
759
864
|
if (runId && runId !== state.run_id)
|
|
760
865
|
return { ok: false, action, message: `ORBIT_RUN_NOT_FOUND: ${runId}` };
|
|
761
866
|
stopRun(state);
|
|
@@ -765,7 +870,7 @@ export class OrbitSupervisor {
|
|
|
765
870
|
async status() {
|
|
766
871
|
const state = this.store.readState();
|
|
767
872
|
if (!state)
|
|
768
|
-
return { ok: false, action: 'status', message: 'ORBIT_RUN_NOT_FOUND:
|
|
873
|
+
return { ok: false, action: 'status', message: 'ORBIT_RUN_NOT_FOUND: 没有 Run 状态。' };
|
|
769
874
|
return this.result(state, true);
|
|
770
875
|
}
|
|
771
876
|
result(state, ok, message) {
|
|
@@ -785,6 +890,7 @@ export class OrbitSupervisor {
|
|
|
785
890
|
guard_recovery: state.guard_recovery,
|
|
786
891
|
last_error: state.last_error,
|
|
787
892
|
pending_user_reply: state.pending_user_reply,
|
|
893
|
+
step_results: state.step_results,
|
|
788
894
|
changed_files: state.changed_files,
|
|
789
895
|
test_summary: state.test_summary,
|
|
790
896
|
driver_ownership: state.driver_ownership,
|
package/lib/tool.js
CHANGED
|
@@ -2,11 +2,10 @@ import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
|
2
2
|
export const ORBIT_TOOL_NAME = 'orbit_controller';
|
|
3
3
|
/** Legacy tool name kept as a backward-compatible alias. */
|
|
4
4
|
export const LEGACY_CX_TOOL_NAME = 'cx_controller';
|
|
5
|
-
const TOOL_DESCRIPTION = '
|
|
6
|
-
'
|
|
7
|
-
'
|
|
8
|
-
|
|
9
|
-
const LEGACY_TOOL_DESCRIPTION = `Legacy compatibility alias. Prefer ${ORBIT_TOOL_NAME}. ${TOOL_DESCRIPTION}`;
|
|
5
|
+
const TOOL_DESCRIPTION = '驱动当前项目的 Orbit 工程编排。Orbit 由确定性的 Supervisor 控制 Commander、Executor 和 Smart Watchdog,' +
|
|
6
|
+
'并将状态保存到 .cx/state.json。使用 action "run" 启动或继续,"resume" 继续持久化运行,"status" 查看状态,' +
|
|
7
|
+
'"stop" 关闭运行,"doctor" 检查环境。只有 Orbit 可以写入 .cx 持久状态。';
|
|
8
|
+
const LEGACY_TOOL_DESCRIPTION = `Legacy compatibility alias(兼容旧接口),请优先使用 ${ORBIT_TOOL_NAME}。${TOOL_DESCRIPTION}`;
|
|
10
9
|
function summarize(result) {
|
|
11
10
|
const lines = [`orbit ${result.action}: ok=${result.ok} phase=${result.phase ?? '-'} status=${result.status ?? '-'}`];
|
|
12
11
|
if (result.run_id)
|
|
@@ -37,12 +36,12 @@ export function createOrbitTool(ctx, options = {}) {
|
|
|
37
36
|
description: legacy ? LEGACY_TOOL_DESCRIPTION : TOOL_DESCRIPTION,
|
|
38
37
|
parameters: {
|
|
39
38
|
action: { type: 'string', required: true, enum: ['run', 'start', 'resume', 'stop', 'status', 'doctor'] },
|
|
40
|
-
goal: { type: 'string', description: '
|
|
41
|
-
preset: { type: 'string', description: 'Run preset id
|
|
42
|
-
approved_loop_count: { type: 'integer', description: '
|
|
43
|
-
run_id: { type: 'string', description: '
|
|
39
|
+
goal: { type: 'string', description: '工程目标,run/start 时必填。' },
|
|
40
|
+
preset: { type: 'string', description: 'Run preset id。' },
|
|
41
|
+
approved_loop_count: { type: 'integer', description: '用户显式批准的 loop 预算,正整数且不超过 10。' },
|
|
42
|
+
run_id: { type: 'string', description: 'resume/stop 的目标 run id。' },
|
|
44
43
|
user_hard_constraints: { type: 'array', items: { type: 'string' } },
|
|
45
|
-
github_allowed: { type: 'boolean', description: '
|
|
44
|
+
github_allowed: { type: 'boolean', description: '是否允许此 Run 执行 GitHub 远程写入。' },
|
|
46
45
|
},
|
|
47
46
|
output: {
|
|
48
47
|
schema: { type: 'json' },
|
|
@@ -71,7 +70,7 @@ export function createOrbitTool(ctx, options = {}) {
|
|
|
71
70
|
if (args.action === 'status')
|
|
72
71
|
return (await service.status(cwd));
|
|
73
72
|
if (args.action === 'stop')
|
|
74
|
-
return service.stop(args.run_id, cwd);
|
|
73
|
+
return (await service.stop(args.run_id, cwd));
|
|
75
74
|
if (args.action === 'doctor')
|
|
76
75
|
return (await service.doctor(cwd));
|
|
77
76
|
if (args.action === 'resume')
|
package/lib/types.js
CHANGED
|
@@ -12,8 +12,9 @@ export const MAX_WATCHDOG_CALLS_PER_STEP = 2;
|
|
|
12
12
|
export const MAX_EXECUTOR_INTERRUPT_RETRIES = 2;
|
|
13
13
|
export const MAX_PLAN_STEPS = 5;
|
|
14
14
|
export const MIN_PLAN_STEPS = 1;
|
|
15
|
-
export const
|
|
16
|
-
export const
|
|
17
|
-
export const
|
|
18
|
-
export const
|
|
19
|
-
export const
|
|
15
|
+
export const DEFAULT_LOOP_BUDGET = 5;
|
|
16
|
+
export const GUARD_FIRST_INSTRUCTION = '请改用更安全的方法继续当前任务,不要原样重试刚被阻断的操作。';
|
|
17
|
+
export const GUARD_REPEAT_INSTRUCTION = '同一操作再次被阻断。请停止重复,并选择不同的安全方案。';
|
|
18
|
+
export const GUARD_RETRY_INSTRUCTION = '之前的方案反复触发 Orbit 安全护栏。请改用不同的安全方案,不要重试被阻断的操作。';
|
|
19
|
+
export const GUARD_NEEDS_USER_INSTRUCTION = '受限操作可能是完成目标所必需的。Orbit 已暂停并等待用户指引。';
|
|
20
|
+
export const DEFAULT_CAPABILITIES = ['filesystem', 'shell', 'web', 'browser'];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kasenri/dsh-orbit",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.8",
|
|
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",
|