@kasenri/dsh-orbit 0.5.8 → 0.5.10

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 CHANGED
@@ -3,10 +3,12 @@
3
3
  Community plugin for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH).
4
4
  Not affiliated with or endorsed by DeepSeek.
5
5
 
6
+ **让 AI 项目可以在无人监管下持续推进。**
7
+ Orbit 会自动规划任务、分工执行、逐步检查并继续完成后续工作;不同环节可以使用不同模型,让低成本模型承担执行任务、强模型负责规划和审核,从而降低整体 AI 使用成本。Watchdog 还会监控运行异常,在任务卡死或中断时协助恢复。
8
+
6
9
  **Orbit — Deterministic Engineering Orchestration for DeepSeek Harness**
7
10
 
8
- Orbit is a deterministic engineering orchestration runtime for DSH, implemented
9
- as a Cordis plugin:
11
+ Technical integration:
10
12
 
11
13
  - an `OrbitService` on `ctx.orbit`
12
14
  - a model-facing `orbit_controller` tool (`cx_controller` remains a legacy alias)
@@ -104,12 +106,16 @@ Orbit supports three activation styles:
104
106
 
105
107
  All three routes converge on the existing `orbit_controller` tool and
106
108
  `OrbitService`; the activation layer never starts a run of its own.
109
+ After a hard-activated run reaches `SUCCESS`, the initiating Session displays
110
+ the Final Commander's user-visible result directly, with the Commander's frozen
111
+ provider/model provenance. The parent model and parent tools remain bypassed.
107
112
 
108
113
  ## Model configuration (Web)
109
114
 
110
- The Orbit model control sits immediately left of the native composer model
111
- seat (`conversation.input.right` renders before `conversation.input.model`).
112
- Its button names the Commander's model; the menu edits three roles:
115
+ The Orbit model control is the final entry in `conversation.input.right`, so
116
+ other composer-side controls (for example DSH's rollback control) stay to its
117
+ left while the native `conversation.input.model` seat stays immediately to its
118
+ right. Its button names the Commander's model; the menu edits three roles:
113
119
 
114
120
  - **Commander** and **Watchdog** pick from the same native model catalog and
115
121
  persist into the DSH `orbit` settings namespace (`settings.yaml`). Picking a
@@ -140,6 +146,11 @@ PLAN → EXECUTE → EVALUATE → SUCCESS
140
146
  ```
141
147
 
142
148
  - Only a normally completed Executor step consumes one loop from the budget.
149
+ - Runs without an explicit user/tool budget start from the historical floor of
150
+ 5 loops; after PLAN, Orbit deterministically reserves two extra bounded slots
151
+ beyond the accepted base plan (`max(5, steps + 2)`, therefore at most 7 for
152
+ the 1–5-step plan schema). Explicit `approved_loop_count` / `max_loops`
153
+ values are never enlarged automatically.
143
154
  - Corrections are limited per base step and reserve budget for the remaining
144
155
  planned steps.
145
156
  - Commander decisions are validated in code (`PASS_CURRENT_STEP` /
package/lib/activation.js CHANGED
@@ -116,7 +116,8 @@ export function registerOrbitToggleCommand(ctx) {
116
116
  * call while the message stays in the conversation as durable history.
117
117
  */
118
118
  export function installOrbitGestureBoundary(ctx, options = {}) {
119
- ctx.on('agent/pre-step', async ({ agent, messages, signal }, next) => {
119
+ ctx.on('agent/pre-step', async (payload, next) => {
120
+ const { agent, messages, signal } = payload;
120
121
  const decision = await next();
121
122
  if (decision.kind === 'reject')
122
123
  return decision;
@@ -134,7 +135,7 @@ export function installOrbitGestureBoundary(ctx, options = {}) {
134
135
  // `decision.messages`, which this path leaves empty.
135
136
  for (const message of messages)
136
137
  agent.session.append('user/message', message, { surfaceOp: 'append' });
137
- await activate(agent, goal, signal);
138
+ await activate(agent, goal, { turn: payload.turn, step: payload.step }, signal);
138
139
  return { kind: 'enter', messages: [] };
139
140
  });
140
141
  }
package/lib/client.js CHANGED
@@ -94,22 +94,22 @@ window.__ModuleLoader__.load({
94
94
  document.head.appendChild(tag);
95
95
  }
96
96
  var OrbitModelSelect_module_css_default = {
97
- "hint": "f77nca_hint",
98
- "rowValueActive": "f77nca_rowValueActive",
99
- "rowValue": "f77nca_rowValue",
100
- "switchControl": "f77nca_switchControl",
97
+ "check": "f77nca_check",
98
+ "chevron": "f77nca_chevron",
101
99
  "rowDetail": "f77nca_rowDetail",
102
- "rowLabel": "f77nca_rowLabel",
103
- "switchRow": "f77nca_switchRow",
100
+ "title": "f77nca_title",
104
101
  "separator": "f77nca_separator",
105
- "chevron": "f77nca_chevron",
106
102
  "row": "f77nca_row",
107
103
  "group": "f77nca_group",
108
- "error": "f77nca_error",
109
- "check": "f77nca_check",
110
- "title": "f77nca_title",
104
+ "panel": "f77nca_panel",
105
+ "rowLabel": "f77nca_rowLabel",
106
+ "rowValue": "f77nca_rowValue",
107
+ "hint": "f77nca_hint",
111
108
  "back": "f77nca_back",
112
- "panel": "f77nca_panel"
109
+ "switchControl": "f77nca_switchControl",
110
+ "switchRow": "f77nca_switchRow",
111
+ "error": "f77nca_error",
112
+ "rowValueActive": "f77nca_rowValueActive"
113
113
  };
114
114
  //#endregion
115
115
  //#region client/OrbitModelSelect.tsx
@@ -592,6 +592,13 @@ window.__ModuleLoader__.load({
592
592
  //#region client/index.ts
593
593
  /** The host settings namespace Orbit registers for its own two roles. */
594
594
  const ORBIT_SETTINGS_NS = "orbit";
595
+ /**
596
+ * Keep Orbit as the final list entry in `conversation.input.right`.
597
+ * DSH renders the whole right list before the named model seat, so this gives
598
+ * the stable local order: other right-side controls → Orbit → model selector.
599
+ */
600
+ const ORBIT_INPUT_RIGHT_PRIORITY = Number.MAX_SAFE_INTEGER;
601
+ const ORBIT_INPUT_RIGHT_ORDER = Number.MAX_SAFE_INTEGER;
595
602
  /** Required client services: slots, the shared model directory, Remote (settings namespace), and locale. */
596
603
  const inject = [
597
604
  "slots",
@@ -718,13 +725,16 @@ window.__ModuleLoader__.load({
718
725
  scope.slots.inject("conversation.input.right", () => scope.slots.register({
719
726
  name: "conversation.input.right",
720
727
  id: "orbit-model",
721
- order: 10,
728
+ priority: ORBIT_INPUT_RIGHT_PRIORITY,
729
+ order: ORBIT_INPUT_RIGHT_ORDER,
722
730
  locale: NS,
723
731
  inject: injected
724
732
  }, OrbitModelSelect));
725
733
  });
726
734
  }
727
735
  //#endregion
736
+ exports.ORBIT_INPUT_RIGHT_ORDER = ORBIT_INPUT_RIGHT_ORDER;
737
+ exports.ORBIT_INPUT_RIGHT_PRIORITY = ORBIT_INPUT_RIGHT_PRIORITY;
728
738
  exports.ORBIT_SETTINGS_NS = ORBIT_SETTINGS_NS;
729
739
  exports.apply = apply;
730
740
  exports.inject = inject;
package/lib/dsh-host.js CHANGED
@@ -13,6 +13,9 @@ function contentToText(blocks) {
13
13
  .map((block) => (block.type === 'text' ? block.text : `[${block.type}]`))
14
14
  .join('\n');
15
15
  }
16
+ function visibleContentToText(blocks) {
17
+ return blocks?.flatMap((block) => block.type === 'text' && block.text.trim() !== '' ? [block.text] : []).join('\n') ?? '';
18
+ }
16
19
  /**
17
20
  * Wire the Orbit supervisor to DeepSeek Harness native Agent/Subagent services.
18
21
  *
@@ -81,6 +84,7 @@ export class DshOrbitHost {
81
84
  .then((value) => ({
82
85
  childId: run.id,
83
86
  output: contentToText(value.output),
87
+ visibleOutput: visibleContentToText(value.output),
84
88
  interrupted: value.stopReason !== 'completed',
85
89
  ...(value.stopReason !== 'completed' ? { reason: value.stopReason } : {}),
86
90
  ...(value.diagnostic ? { testSummary: [value.diagnostic] } : {}),
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { boundContextSummary, createUserMessage } from '@deepseek-ai/dsh-llm';
1
+ import { AssistantStreamAccumulator, boundContextSummary, createAssistantMessage, createUserMessage } from '@deepseek-ai/dsh-llm';
2
2
  import z from '@deepseek-ai/schemastery';
3
3
  import { installOrbitGestureBoundary, registerOrbitCommand, registerOrbitToggleCommand } from "./activation.js";
4
4
  import { createOrbitPreExecuteHandler } from "./pipeline-guard.js";
@@ -6,11 +6,6 @@ import { agentDefaultSelectionOf, resolveEffectiveRoutes, sessionModelSelectionO
6
6
  import { installOrbitSessionProjection, orbitEnabledOf } from "./session-state.js";
7
7
  import { OrbitService } from "./service.js";
8
8
  import { createOrbitTool } from "./tool.js";
9
- /**
10
- * Fixed hard-activation budget. Explicit tool calls may still supply a bounded
11
- * user-owned budget; ordinary activation never guesses complexity from words.
12
- */
13
- const HARD_ACTIVATION_LOOP_BUDGET = 5;
14
9
  export const name = 'dsh-orbit';
15
10
  export const inject = ['tools', 'agents', 'subagents'];
16
11
  const Route = z.object({
@@ -120,21 +115,22 @@ export function apply(ctx, config) {
120
115
  });
121
116
  installOrbitGestureBoundary(ctx, {
122
117
  sessionEnabled: (session) => orbitEnabledOf(ctx, session),
123
- activate: async (agent, goal, signal) => {
118
+ activate: async (agent, goal, position, signal) => {
124
119
  const cwd = agent.session.header.cwd ?? process.cwd();
125
120
  let result;
126
121
  try {
127
122
  // The initiator scope makes the run's children belong to this Agent;
128
123
  // the parent model is never asked to decide or to run the task.
129
- result = await ctx.agents.withInitiator(agent, () => service.run({
130
- goal,
131
- approved_loop_count: HARD_ACTIVATION_LOOP_BUDGET,
132
- }, cwd, signal));
124
+ result = await ctx.agents.withInitiator(agent, () => service.run({ goal }, cwd, signal));
133
125
  }
134
126
  catch (error) {
135
127
  appendOrbitNotice(agent.session, `Orbit 启动失败:${error instanceof Error ? error.message : String(error)}`);
136
128
  return;
137
129
  }
130
+ if (result.ok && result.phase === 'SUCCESS' && result.final_output !== undefined) {
131
+ appendOrbitFinalOutput(agent.session, position, result.final_output);
132
+ return;
133
+ }
138
134
  const notice = activationNotice(result);
139
135
  if (notice !== undefined)
140
136
  appendOrbitNotice(agent.session, notice);
@@ -168,6 +164,26 @@ export function apply(ctx, config) {
168
164
  ctx.on('tools/pre-execute', (exec, next) => handler(exec, next));
169
165
  }
170
166
  }
167
+ /** Append the existing Final Commander answer as this consumed turn's sole assistant result. */
168
+ function appendOrbitFinalOutput(session, position, output) {
169
+ const message = createAssistantMessage({
170
+ content: [{ type: 'text', text: output.text }],
171
+ source: { provider: output.provider, model: output.model },
172
+ });
173
+ const stream = new AssistantStreamAccumulator();
174
+ const time = Date.now();
175
+ stream.push({ time, chunk: { type: 'block-start', index: 0, blockType: 'text' } });
176
+ stream.push({ time, chunk: { type: 'text-delta', index: 0, text: output.text } });
177
+ stream.push({ time, chunk: { type: 'block-end', index: 0, block: { type: 'text', text: output.text } } });
178
+ stream.push({ time, chunk: { type: 'finish', reason: { kind: 'stop' } } });
179
+ session.append('step/start', position);
180
+ try {
181
+ session.append('assistant/message', { ...position, message, stream: [...stream.snapshot()] }, { surfaceOp: 'append' });
182
+ }
183
+ finally {
184
+ session.append('step/end', position);
185
+ }
186
+ }
171
187
  /**
172
188
  * One durable, user-visible Orbit notice. The DSH `plugin` + `form: 'notice'`
173
189
  * source renders as a collapsed notice row instead of a user message, and the
package/lib/kernel.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * read a clock, or use randomness. Runtime values (routes, timestamps, run ids)
6
6
  * are supplied by the supervisor, which owns orchestration and persistence.
7
7
  */
8
- import { MAX_CORRECTION_DEPTH, DEFAULT_LOOP_BUDGET, MAX_PLAN_STEPS, MAX_WATCHDOG_CALLS_PER_STEP, MIN_PLAN_STEPS, ORBIT_SCHEMA_VERSION, } from "./types.js";
8
+ import { MAX_CORRECTION_DEPTH, DEFAULT_LOOP_BUDGET, AUTOMATIC_LOOP_RECOVERY_RESERVE, MAX_AUTOMATIC_LOOP_BUDGET, MAX_PLAN_STEPS, MAX_WATCHDOG_CALLS_PER_STEP, MIN_PLAN_STEPS, ORBIT_SCHEMA_VERSION, } from "./types.js";
9
9
  /** Capabilities a plan step may request. */
10
10
  export const ORBIT_CAPABILITIES = ['filesystem', 'shell', 'web', 'browser'];
11
11
  const STEP_CAPABILITIES = new Set(ORBIT_CAPABILITIES);
@@ -83,6 +83,29 @@ export function updateLoopBudget(state, budget) {
83
83
  state.loop_count = state.loop.used;
84
84
  state.remaining_budget = Math.max(0, budget - state.loop.used);
85
85
  }
86
+ /**
87
+ * Deterministic budget for an Orbit-owned run after PLAN is known.
88
+ * Small plans keep the historical floor of five; four/five-step plans gain two
89
+ * bounded recovery slots, with a hard automatic ceiling of seven.
90
+ */
91
+ export function automaticLoopBudgetForPlan(stepCount) {
92
+ if (!Number.isSafeInteger(stepCount) || stepCount < MIN_PLAN_STEPS || stepCount > MAX_PLAN_STEPS) {
93
+ throw new Error(`ORBIT_PLAN_STEP_COUNT_INVALID: expected ${MIN_PLAN_STEPS}-${MAX_PLAN_STEPS}, got ${stepCount}`);
94
+ }
95
+ return Math.min(MAX_AUTOMATIC_LOOP_BUDGET, Math.max(DEFAULT_LOOP_BUDGET, stepCount + AUTOMATIC_LOOP_RECOVERY_RESERVE));
96
+ }
97
+ /**
98
+ * Expand only Orbit-owned automatic budgets. Explicit user/tool budgets and
99
+ * old states without a recorded mode are never silently increased.
100
+ */
101
+ export function ensureAutomaticLoopBudgetForPlan(state) {
102
+ if (state.loop_budget_mode !== 'automatic')
103
+ return;
104
+ const baseSteps = state.plan.steps.filter((step) => isBaseStepId(step.id)).length;
105
+ const target = automaticLoopBudgetForPlan(baseSteps);
106
+ if (target > state.loop.max)
107
+ updateLoopBudget(state, target);
108
+ }
86
109
  export function hashGoal(goal) {
87
110
  let hash = 0;
88
111
  for (let index = 0; index < goal.length; index += 1) {
@@ -123,8 +146,8 @@ export function correctionBlockCode(state, step) {
123
146
  return undefined;
124
147
  }
125
148
  export function createInitialState(input) {
126
- const max = explicitLoopBudget({ approved_loop_count: input.approvedLoopCount, max_loops: input.maxLoops }) ??
127
- DEFAULT_LOOP_BUDGET;
149
+ const explicit = explicitLoopBudget({ approved_loop_count: input.approvedLoopCount, max_loops: input.maxLoops });
150
+ const max = explicit ?? DEFAULT_LOOP_BUDGET;
128
151
  return {
129
152
  schema_version: ORBIT_SCHEMA_VERSION,
130
153
  active_run_id: input.runId,
@@ -139,6 +162,7 @@ export function createInitialState(input) {
139
162
  preset: input.preset ?? 'orbit-lite',
140
163
  routes: structuredClone(input.routes),
141
164
  loop: { used: 0, max },
165
+ loop_budget_mode: explicit === undefined ? 'automatic' : 'explicit',
142
166
  approved_loop_count: max,
143
167
  remaining_budget: max,
144
168
  loop_count: 0,
package/lib/supervisor.js CHANGED
@@ -2,7 +2,7 @@ import { join } from 'node:path';
2
2
  import { randomUUID } from 'node:crypto';
3
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, upsertStepResult, normalizeCapabilities, } from "./kernel.js";
5
+ import { applyCommanderNeedsUser, applyCorrectionStep, applyExecutorCapabilityUnavailable, applyExecutorInterrupted, applyExecutorResume, applyExecutorSuccess, applyFinalAppend, applyFinalSuccess, applyPlan, applyStepPass, baseStepIdOf, beginStep, clearExecutorChild, clearGuardRecovery, correctionBlockCode, correctionDepthOf, createInitialState, enterBudgetExhausted, enterNeedsUser, ensureAutomaticLoopBudgetForPlan, 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
8
  import { executorToolsFor, EXECUTOR_READ_ONLY_TOOLS, READ_ONLY_ROLE_TOOLS } from "./capabilities.js";
@@ -305,6 +305,7 @@ export class OrbitSupervisor {
305
305
  try {
306
306
  const plan = normalizePlan(outcome.structured);
307
307
  applyPlan(state, plan);
308
+ ensureAutomaticLoopBudgetForPlan(state);
308
309
  this.store.writeState(state);
309
310
  return undefined;
310
311
  }
@@ -328,7 +329,7 @@ export class OrbitSupervisor {
328
329
  return { kind: 'interrupted', reason: outcome.reason };
329
330
  try {
330
331
  const decision = assertCommanderDecision(outcome.structured, mode);
331
- return { kind: 'decision', decision };
332
+ return { kind: 'decision', decision, output: outcome.output };
332
333
  }
333
334
  catch (error) {
334
335
  // Invalid/temporary output is recoverable; it must not become NEEDS_USER.
@@ -387,7 +388,7 @@ export class OrbitSupervisor {
387
388
  if (raced.value.structured === undefined) {
388
389
  return { kind: 'interrupted', reason: `${mode}_STRUCTURED_OUTPUT_MISSING` };
389
390
  }
390
- return { kind: 'output', output: raced.value.output, structured: raced.value.structured };
391
+ return { kind: 'output', output: raced.value.visibleOutput ?? raced.value.output, structured: raced.value.structured };
391
392
  }
392
393
  if (raced.kind === 'aborted' || signal?.aborted) {
393
394
  await this.cancelHandle(handle, 'ORBIT_ABORTED');
@@ -586,7 +587,7 @@ export class OrbitSupervisor {
586
587
  if (decision.decision === 'SUCCESS') {
587
588
  applyFinalSuccess(state, decision.summary);
588
589
  this.store.writeState(state);
589
- return this.result(state, true);
590
+ return this.result(state, true, undefined, outcome.output);
590
591
  }
591
592
  if (decision.decision === 'PASS_CURRENT_STEP') {
592
593
  applyStepPass(state, step, decision.summary);
@@ -873,7 +874,7 @@ export class OrbitSupervisor {
873
874
  return { ok: false, action: 'status', message: 'ORBIT_RUN_NOT_FOUND: 没有 Run 状态。' };
874
875
  return this.result(state, true);
875
876
  }
876
- result(state, ok, message) {
877
+ result(state, ok, message, finalOutput) {
877
878
  // Tool output must be lossless JSON: optional state fields are omitted, not
878
879
  // emitted as `undefined`.
879
880
  const data = {
@@ -896,6 +897,7 @@ export class OrbitSupervisor {
896
897
  driver_ownership: state.driver_ownership,
897
898
  state_revision: state.state_revision,
898
899
  };
900
+ const displayText = state.phase === 'SUCCESS' ? finalOutput?.trim() || state.commander?.summary?.trim() : undefined;
899
901
  return {
900
902
  ok,
901
903
  action: 'run',
@@ -903,6 +905,13 @@ export class OrbitSupervisor {
903
905
  phase: state.phase,
904
906
  status: state.status,
905
907
  ...(message ? { message } : {}),
908
+ ...(displayText ? {
909
+ final_output: {
910
+ text: displayText,
911
+ provider: state.routes.commander.provider,
912
+ model: state.routes.commander.model,
913
+ },
914
+ } : {}),
906
915
  data: Object.fromEntries(Object.entries(data).filter(([, value]) => value !== undefined)),
907
916
  };
908
917
  }
package/lib/types.js CHANGED
@@ -13,6 +13,9 @@ export const MAX_EXECUTOR_INTERRUPT_RETRIES = 2;
13
13
  export const MAX_PLAN_STEPS = 5;
14
14
  export const MIN_PLAN_STEPS = 1;
15
15
  export const DEFAULT_LOOP_BUDGET = 5;
16
+ /** Automatic runs reserve two bounded execution slots beyond the accepted base plan. */
17
+ export const AUTOMATIC_LOOP_RECOVERY_RESERVE = 2;
18
+ export const MAX_AUTOMATIC_LOOP_BUDGET = MAX_PLAN_STEPS + AUTOMATIC_LOOP_RECOVERY_RESERVE;
16
19
  export const GUARD_FIRST_INSTRUCTION = '请改用更安全的方法继续当前任务,不要原样重试刚被阻断的操作。';
17
20
  export const GUARD_REPEAT_INSTRUCTION = '同一操作再次被阻断。请停止重复,并选择不同的安全方案。';
18
21
  export const GUARD_RETRY_INSTRUCTION = '之前的方案反复触发 Orbit 安全护栏。请改用不同的安全方案,不要重试被阻断的操作。';
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@kasenri/dsh-orbit",
3
- "version": "0.5.8",
3
+ "version": "0.5.10",
4
4
  "type": "module",
5
- "description": "Deterministic engineering orchestration for DeepSeek Harness with Commander, Executor, Smart Watchdog, bounded recovery and durable execution state.",
5
+ "description": " AI 项目可以在无人监管下持续推进。Orbit 会自动规划任务、分工执行、逐步检查并继续完成后续工作;不同环节可以使用不同模型,让低成本模型承担执行任务、强模型负责规划和审核,从而降低整体 AI 使用成本。Watchdog 还会监控运行异常,在任务卡死或中断时协助恢复。",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",