@tt-a1i/hive 2.1.0 → 2.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.en.md +5 -460
  3. package/README.md +338 -187
  4. package/README.zh.md +389 -0
  5. package/dist/src/cli/hive-update.d.ts +3 -2
  6. package/dist/src/cli/hive-update.js +20 -60
  7. package/dist/src/cli/hive.js +5 -1
  8. package/dist/src/server/agent-manager-support.js +16 -9
  9. package/dist/src/server/agent-runtime-contract.d.ts +4 -0
  10. package/dist/src/server/agent-runtime.js +27 -0
  11. package/dist/src/server/agent-stdin-dispatcher.d.ts +3 -0
  12. package/dist/src/server/agent-stdin-dispatcher.js +3 -0
  13. package/dist/src/server/diagnostics-support-bundle.d.ts +3 -0
  14. package/dist/src/server/diagnostics-support-bundle.js +3 -0
  15. package/dist/src/server/hive-team-guidance.js +34 -4
  16. package/dist/src/server/message-log-store.d.ts +1 -0
  17. package/dist/src/server/message-log-store.js +13 -0
  18. package/dist/src/server/orchestrator-autostart.d.ts +2 -0
  19. package/dist/src/server/orchestrator-autostart.js +30 -11
  20. package/dist/src/server/post-start-input-writer.d.ts +1 -0
  21. package/dist/src/server/post-start-input-writer.js +80 -12
  22. package/dist/src/server/role-templates.d.ts +8 -1
  23. package/dist/src/server/role-templates.js +102 -48
  24. package/dist/src/server/route-types.d.ts +6 -0
  25. package/dist/src/server/routes-scenarios.d.ts +2 -1
  26. package/dist/src/server/routes-scenarios.js +11 -6
  27. package/dist/src/server/routes-team.js +21 -15
  28. package/dist/src/server/routes-workspaces.js +22 -2
  29. package/dist/src/server/runtime-store-contract.d.ts +2 -0
  30. package/dist/src/server/runtime-store-helpers.d.ts +11 -2
  31. package/dist/src/server/runtime-store-helpers.js +29 -5
  32. package/dist/src/server/runtime-store-workflows.js +20 -0
  33. package/dist/src/server/runtime-store.js +2 -0
  34. package/dist/src/server/scenario-presets.d.ts +3 -1
  35. package/dist/src/server/scenario-presets.js +12 -1
  36. package/dist/src/server/spawn-worker-defaults.d.ts +2 -0
  37. package/dist/src/server/spawn-worker-defaults.js +10 -5
  38. package/dist/src/server/team-operations.d.ts +3 -0
  39. package/dist/src/server/team-operations.js +97 -29
  40. package/dist/src/server/terminal-input-profile.d.ts +2 -0
  41. package/dist/src/server/update-install-plan.d.ts +20 -0
  42. package/dist/src/server/update-install-plan.js +151 -0
  43. package/dist/src/server/version-service.d.ts +5 -0
  44. package/dist/src/server/version-service.js +17 -13
  45. package/dist/src/server/workflow-agent-call-executor.d.ts +81 -0
  46. package/dist/src/server/workflow-agent-call-executor.js +259 -0
  47. package/dist/src/server/workflow-dag-layer-tracker.d.ts +14 -0
  48. package/dist/src/server/workflow-dag-layer-tracker.js +37 -0
  49. package/dist/src/server/workflow-dag.d.ts +8 -0
  50. package/dist/src/server/workflow-dag.js +65 -0
  51. package/dist/src/server/workflow-dispatch-awaiter.d.ts +7 -1
  52. package/dist/src/server/workflow-dispatch-awaiter.js +60 -9
  53. package/dist/src/server/workflow-runner.d.ts +5 -38
  54. package/dist/src/server/workflow-runner.js +113 -445
  55. package/dist/src/server/workflow-script-host-dispatcher.d.ts +14 -0
  56. package/dist/src/server/workflow-script-host-dispatcher.js +42 -0
  57. package/dist/src/server/workflow-script-loader.d.ts +2 -2
  58. package/dist/src/server/workflow-script-loader.js +2 -2
  59. package/dist/src/server/workflow-script-worker.d.ts +14 -0
  60. package/dist/src/server/workflow-script-worker.js +81 -0
  61. package/dist/src/server/workflow-vm-worker-source.d.ts +1 -0
  62. package/dist/src/server/workflow-vm-worker-source.js +1 -0
  63. package/dist/src/server/workflow-vm-worker.cjs +238 -0
  64. package/dist/src/server/workspace-ui-language.d.ts +7 -0
  65. package/dist/src/server/workspace-ui-language.js +9 -0
  66. package/dist/src/shared/scenario-presets.d.ts +6 -1
  67. package/dist/src/shared/scenario-presets.js +80 -28
  68. package/dist/src/shared/types.d.ts +11 -7
  69. package/dist/src/shared/ui-language.d.ts +4 -0
  70. package/dist/src/shared/ui-language.js +3 -0
  71. package/package.json +4 -2
  72. package/web/dist/assets/{AddWorkerDialog-DBLhwb91.js → AddWorkerDialog-DHwOZ75i.js} +2 -2
  73. package/web/dist/assets/AddWorkspaceFlow-qOI1CmqC.js +1 -0
  74. package/web/dist/assets/{FirstRunWizard-DlEPnWWw.js → FirstRunWizard-D5kGMD_V.js} +1 -1
  75. package/web/dist/assets/{MarketplaceDrawer-CfSiRi8e.js → MarketplaceDrawer-BuCbtqFk.js} +1 -1
  76. package/web/dist/assets/TaskGraphDrawer-e_bg3f5u.js +1 -0
  77. package/web/dist/assets/{WhatsNewDialog-vP7buLos.js → WhatsNewDialog-BB9ieq3T.js} +1 -1
  78. package/web/dist/assets/{WorkerModal-CSorwcdP.js → WorkerModal-CGeWO3Dg.js} +1 -1
  79. package/web/dist/assets/{WorkflowsDrawer-BXS3w9Uq.js → WorkflowsDrawer-CVvGxxdR.js} +1 -1
  80. package/web/dist/assets/WorkspaceMemoryDrawer-BamHuJEp.js +1 -0
  81. package/web/dist/assets/WorkspaceTaskDrawer-CqntbH9-.js +1 -0
  82. package/web/dist/assets/index-BOpmn5Ij.js +84 -0
  83. package/web/dist/assets/index-rPYd0MUk.css +1 -0
  84. package/web/dist/assets/{search-BZw4T67h.js → search-DKgM9IgX.js} +1 -1
  85. package/web/dist/assets/{square-terminal-B7E57In1.js → square-terminal-DMWazpEv.js} +1 -1
  86. package/web/dist/index.html +2 -2
  87. package/web/dist/sw.js +1 -1
  88. package/web/dist/assets/AddWorkspaceFlow-cxvhVAsT.js +0 -1
  89. package/web/dist/assets/TaskGraphDrawer-C2JufcPs.js +0 -1
  90. package/web/dist/assets/WorkspaceMemoryDrawer-D71ivohr.js +0 -1
  91. package/web/dist/assets/WorkspaceTaskDrawer-CGCTSHKa.js +0 -1
  92. package/web/dist/assets/index-BcwN8cCw.js +0 -79
  93. package/web/dist/assets/index-StXTPHls.css +0 -1
@@ -33,6 +33,9 @@ export declare const createAgentStdinDispatcher: ({ agentManager, getLaunchConfi
33
33
  requireActiveRun?: boolean;
34
34
  }): void;
35
35
  writeUserInputPrompt(workspaceId: string, text: string): void;
36
+ deliverUserInputToOrchestrator(workspaceId: string, text: string, input?: {
37
+ requireActiveRun?: boolean;
38
+ }): Promise<void>;
36
39
  /** Generic: deliver an opaque text block to a specific agent's PTY.
37
40
  * Used by the workflow runner to notify the triggering orchestrator
38
41
  * when a run finishes (mirrors Claude Code's <task-notification>). */
@@ -211,6 +211,9 @@ export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getW
211
211
  writeUserInputPrompt(workspaceId, text) {
212
212
  swallowQueuedFailure(writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorUserInputPayload(text, flags())));
213
213
  },
214
+ deliverUserInputToOrchestrator(workspaceId, text, input = {}) {
215
+ return writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorUserInputPayload(text, flags()), input);
216
+ },
214
217
  /** Generic: deliver an opaque text block to a specific agent's PTY.
215
218
  * Used by the workflow runner to notify the triggering orchestrator
216
219
  * when a run finishes (mirrors Claude Code's <task-notification>). */
@@ -6,10 +6,13 @@ export declare const buildDiagnosticsSupportBundle: (input: {
6
6
  version: VersionInfoPayload;
7
7
  }) => {
8
8
  app: {
9
+ can_run_hive_update: boolean;
9
10
  current_version: string;
10
11
  install_hint: string;
12
+ install_source: import("./update-install-plan.js").UpdateInstallSource;
11
13
  latest_version: string;
12
14
  package_name: string;
15
+ update_note: string;
13
16
  update_available: boolean;
14
17
  };
15
18
  generated_at: number;
@@ -127,10 +127,13 @@ export const buildDiagnosticsSupportBundle = (input) => {
127
127
  const includedWorkspaces = workspaces.slice(0, MAX_INCLUDED_WORKSPACES);
128
128
  return {
129
129
  app: {
130
+ can_run_hive_update: input.version.can_run_hive_update,
130
131
  current_version: input.version.current_version,
131
132
  install_hint: input.version.install_hint,
133
+ install_source: input.version.install_source,
132
134
  latest_version: input.version.latest_version,
133
135
  package_name: input.version.package_name,
136
+ update_note: input.version.update_note,
134
137
  update_available: input.version.update_available,
135
138
  },
136
139
  generated_at: now,
@@ -61,7 +61,7 @@ const CORE_ORCHESTRATOR_RULES = [
61
61
  'All Hive workers in one workspace share the same filesystem root with no per-worker isolation. Split dispatches so no two workers edit the same files/modules at the same time; if work would collide, serialize it or assign one owner.',
62
62
  'When you spawn a reviewer to audit work another CLI produced, prefer a different `--cli` than the implementer used — cross-provider review catches blind spots a model shares with itself.',
63
63
  'When the user cancels or changes direction on an open dispatch, close it explicitly with `team cancel --dispatch <id> "<reason>"` — do not just say "cancel" in prose.',
64
- 'A `team send` to a STOPPED worker parks the dispatch (the response carries `queued: true`): it is delivered automatically when the user starts that worker. If the task should run now, tell the user the worker needs a start, or `team cancel` it and dispatch to a running worker.',
64
+ 'A `team send` to a user-added STOPPED worker parks the dispatch (the response carries `queued: true`): it is delivered automatically when the user starts that worker. Workers you created with `team spawn` are woken by the follow-up `team send`. If a user-added stopped worker should run now, tell the user the worker needs a start, or `team cancel` it and dispatch to a running worker.',
65
65
  'Each `team send` opens a SEPARATE dispatch with its own pending count and its own required report — it is not a way to tack context onto a dispatch already in flight. Send the same worker twice and it owes you TWO reports; one report closes only one dispatch (the one its `--dispatch <id>` names, or the oldest open one if the flag is omitted), so the other stays open and the worker is pinned on `working`. To change or extend an in-flight task, `team cancel --dispatch <id> "<reason>"` the stale dispatch and re-send the whole task, or wait for the worker to report and dispatch the follow-up after.',
66
66
  'Only orchestrators can add memory directly; workers should report durable findings for the orchestrator or Dream to capture. Search memory before adding: use `team memory search "<query>"` to avoid duplicates. Use `team memory add "<body>"` only for rare, evidence-backed workspace facts, decisions, and pitfalls that should help future Hive agents across sessions. Nothing worth saving is a normal outcome; do not add memory just to prove you used it. Use `team memory forget <memory-id>` only to archive obsolete memory.',
67
67
  "Never substitute your CLI's own built-in background agents, exploration helpers, subagent runners, or workflow runners for Hive workers or Hive workflows — they run inside your CLI process, bypass Hive's PTY fleet, never appear in the UI or `team list`, and the stop button cannot reach them.",
@@ -74,7 +74,7 @@ const CORE_ORCHESTRATOR_RULES = [
74
74
  * avoids it firing a command the runtime will reject.
75
75
  */
76
76
  const WORKFLOW_ORCHESTRATOR_RULES = [
77
- 'Choosing to use a workflow is YOUR call, judged from the task\'s shape — the user describes what they want in plain language and will not (and need not) ask for a "workflow" by name. When a task fans out across 3+ workers, needs a staged review→fix, or loops until results converge, author an orchestration-only workflow script and fire it with ONE `team workflow run --stdin`: the script may decide phases and call `agent()`, but it must not read/write files, shell out, fetch the network, or perform the task itself in JavaScript. Decide this on your own, do not wait to be told, and do not approximate it with a loop of `team send` (no barrier, no UI grouping, no stop button). The DSL and ready-made patterns are in `.hive/PROTOCOL.md`.',
77
+ 'Choosing to use a workflow is YOUR call, judged from the task\'s shape — the user describes what they want in plain language and will not (and need not) ask for a "workflow" by name. When a task fans out across 3+ workers, needs a staged review→fix, has an explicit dependency graph, or loops until results converge, author an orchestration-only workflow script and fire it with ONE `team workflow run --stdin`: the script may decide phases and call `agent()` / `dag()`, but it must not read/write files, shell out, fetch the network, or perform the task itself in JavaScript. Decide this on your own, do not wait to be told, and do not approximate it with a loop of `team send` (no barrier, no UI grouping, no stop button). The DSL and ready-made patterns are in `.hive/PROTOCOL.md`.',
78
78
  ];
79
79
  /**
80
80
  * Auto-staff rule — only injected when the experimental auto-staff feature is
@@ -154,15 +154,26 @@ runtime; never use your CLI's own built-in workflow or subagent runner.
154
154
 
155
155
  Host functions injected into the script: \`agent(prompt, opts)\`,
156
156
  \`parallel(thunks)\`, \`pipeline(items, ...stages)\`, \`phase(title)\`,
157
- \`log(msg)\`, plus the \`args\` global.
157
+ \`log(msg)\`, \`workflow(scriptName, childArgs?)\`, \`dag(spec)\`, plus the
158
+ \`args\` global.
158
159
 
159
160
  \`agent(prompt, opts)\` opts: \`{ agentType?: "coder"|"reviewer"|"tester"|"custom"|<custom-role-name>, cli?: "${BUILTIN_COMMAND_PRESET_CLI_LIST.replace(/\|/gu, '"|"')}", model?: string, outputSchema?: object, label?: string, timeoutMs?: number }\` — other fields are silently ignored. \`agentType\` also accepts the name of a workspace custom role template (case-insensitive); a typo throws rather than silently falling back to coder. \`model\` is passed through to the worker launch config (\`--model <id>\`), so a 100-way fan-out can use a cheap model and the synthesizer a strong one. \`outputSchema\` makes \`agent()\` resolve to a parsed object instead of a string: the worker is told to end its report with a fenced \`\`\`json block whose keys match the schema. On a parse miss it falls back to \`{ text: "<raw report>" }\`, so ALWAYS treat a missing field as the SAFE default (never assume success). The worker auto-dismisses when its \`agent()\` call resolves — you never dismiss it.
160
161
 
161
162
  \`parallel()\` takes an array of THUNKS (\`() => agent(...)\`), NOT already-started promises: \`parallel([agent(...), agent(...)])\` degrades to unordered concurrency counted as a single step (a no-op grouping), because the promises already started at construction time. \`pipeline(items, ...stages)\` stages are also functions, shape \`(prev, item, i) => agent(...)\`.
162
163
 
164
+ \`dag(spec)\` is for explicit dependency graphs. Pass either an array of nodes
165
+ or \`{ nodes }\`. Each node is \`{ id, needs?: string[], run }\` (aliases:
166
+ \`dependsOn\` / \`after\`). Nodes whose dependencies are complete run in
167
+ parallel; dependent nodes wait for all upstream results. Missing dependencies,
168
+ duplicate ids, and cycles fail the run before unsafe downstream dispatches.
169
+ \`run(deps, results)\` receives an object containing only that node's direct
170
+ dependencies plus the full result map. The return value is
171
+ \`{ order: string[], results: Record<string, unknown> }\`, where \`undefined\`
172
+ node results are stored as \`null\`.
173
+
163
174
  \`log("...")\` writes one narrator line — stored in the DB, shown when the run row is expanded in the Drawer, and its last 8 lines are spliced into the completion reminder sent back to you. Use it for a readable progress summary, e.g. \`log('Discovered 47 endpoints')\`.
164
175
 
165
- Runtime limits: each run defaults to at most 1000 \`agent()\` calls and 60 minutes wall-clock; the concurrency cap is \`min(16, cores-2)\` and parallel/pipeline queue against it automatically. Override in \`meta\`, e.g. \`meta = { name, description, maxAgentCalls: 50, maxDurationMs }\`.
176
+ Runtime limits: each run defaults to at most 1000 \`agent()\` calls and 60 minutes wall-clock; the concurrency cap is \`min(16, cores-2)\` and parallel/pipeline/dag queue against it automatically. Override in \`meta\`, e.g. \`meta = { name, description, maxAgentCalls: 50, maxDurationMs }\`.
166
177
 
167
178
  \`meta\` must be a pure literal (no variables). Scripts are JS, not TS (type annotations error). The body may use top-level \`await\` and a trailing \`return\`.
168
179
 
@@ -186,6 +197,25 @@ phase('Synthesize')
186
197
  return await agent(\`Synthesize: \${reports.filter(Boolean).join('\\n---\\n')}\`, { agentType: 'reviewer', cli: 'claude', label: 'synth' })
187
198
  \`\`\`
188
199
 
200
+ Explicit DAG (use when later steps need named upstream results):
201
+ \`\`\`
202
+ export const meta = { name: 'dag-review-fix', description: 'review, fix, verify as a graph' }
203
+ phase('Graph')
204
+ const graph = await dag({
205
+ nodes: [
206
+ { id: 'review', run: () => agent('Find correctness bugs.', { agentType: 'reviewer', label: 'review' }) },
207
+ { id: 'tests', run: () => agent('Find missing regression tests.', { agentType: 'tester', label: 'tests' }) },
208
+ {
209
+ id: 'fix',
210
+ needs: ['review', 'tests'],
211
+ run: (deps) => agent('Implement the fix using this evidence:\\n' + deps.review + '\\n---\\n' + deps.tests, { agentType: 'coder', label: 'fix' }),
212
+ },
213
+ { id: 'verify', needs: ['fix'], run: (deps) => agent('Verify the fix:\\n' + deps.fix, { agentType: 'tester', label: 'verify' }) },
214
+ ],
215
+ })
216
+ return graph
217
+ \`\`\`
218
+
189
219
  Passing args (so a saved script is reusable instead of hard-coding values):
190
220
  \`\`\`
191
221
  team workflow run --stdin --args '["src/a.ts","src/b.ts"]' <<'EOF'
@@ -44,6 +44,7 @@ interface MessageKindRow {
44
44
  }
45
45
  export declare const createMessageLogStore: (db: Database) => {
46
46
  deleteMessage: (handle: MessageLogHandle) => void;
47
+ hasUserInputSince: (workspaceId: string, agentId: string, sinceMs: number) => boolean;
47
48
  insertMessage: (input: MessageLogRecord) => MessageLogHandle;
48
49
  listMessageKinds: () => MessageKindRow[];
49
50
  listMessagesForRecovery: (workspaceId: string, sinceMs: number) => RecoveryMessage[];
@@ -26,6 +26,18 @@ export const createMessageLogStore = (db) => {
26
26
  const deleteMessage = (handle) => {
27
27
  db.prepare('DELETE FROM messages WHERE sequence = ?').run(handle.sequence);
28
28
  };
29
+ const hasUserInputSince = (workspaceId, agentId, sinceMs) => {
30
+ const row = db
31
+ .prepare(`SELECT 1
32
+ FROM messages
33
+ WHERE workspace_id = ?
34
+ AND type = 'user_input'
35
+ AND worker_id = ?
36
+ AND created_at >= ?
37
+ LIMIT 1`)
38
+ .get(workspaceId, agentId, sinceMs);
39
+ return row !== undefined;
40
+ };
29
41
  const parseArtifacts = (value) => {
30
42
  if (!value) {
31
43
  return [];
@@ -83,6 +95,7 @@ export const createMessageLogStore = (db) => {
83
95
  };
84
96
  return {
85
97
  deleteMessage,
98
+ hasUserInputSince,
86
99
  insertMessage,
87
100
  listMessageKinds,
88
101
  listMessagesForRecovery,
@@ -10,7 +10,9 @@ interface AutostartPort {
10
10
  getLiveRun: (runId: string) => {
11
11
  status: string;
12
12
  exitCode: number | null;
13
+ output?: string;
13
14
  };
15
+ waitForRunExit?: (runId: string, timeoutMs: number) => Promise<boolean>;
14
16
  peekAgentLaunchConfig: (workspaceId: string, agentId: string) => AgentLaunchConfigInput | undefined;
15
17
  }
16
18
  export interface OrchestratorStartResult {
@@ -2,9 +2,15 @@ import { getStartupCommandExecutable } from './startup-command-parser.js';
2
2
  // SETTLE_WAIT_MS: how long we wait before declaring autostart "ok". Must be
3
3
  // long enough to observe an early exit when the child shell prints
4
4
  // "command not found" then dies with exit 127 (POSIX) or 9009 (Windows)
5
- // typically <100ms in practice. 800ms balances reliability vs the perceived
6
- // workspace-create latency cost.
5
+ // - typically <100ms in practice. 800ms balances reliability vs the perceived
6
+ // workspace-create latency cost. Production runtime exposes an exit promise so
7
+ // we don't miss the event between polling ticks; stubs fall back to polling.
7
8
  const SETTLE_WAIT_MS = process.platform === 'win32' ? 2000 : 800;
9
+ // node-pty's spawn helper can take several seconds to finish executing a
10
+ // shebang script that exits immediately. Only use this longer window while the
11
+ // PTY is still completely silent and "starting"; real CLIs that print output
12
+ // keep the normal fast path above.
13
+ const SILENT_STARTING_SETTLE_WAIT_MS = process.platform === 'win32' ? 5000 : 4000;
8
14
  const POLL_INTERVAL_MS = 25;
9
15
  // Shells emit a "command not found" exit code when the requested binary is
10
16
  // missing on PATH. node-pty does NOT raise a synchronous spawn error for that
@@ -74,26 +80,39 @@ export const autostartAgent = async (port, workspaceId, agentId, hivePort, optio
74
80
  try {
75
81
  const run = await port.startAgent(workspaceId, agentId, { hivePort });
76
82
  // node-pty often doesn't throw on missing binaries — it spawns then exits
77
- // fast via onExit with a non-zero code. Poll briefly so we surface the
78
- // failure synchronously in the response instead of returning a fake "ok".
79
- //
80
- // Loop condition is "until we hit a terminal state OR deadline". Earlier
81
- // versions exited as soon as `status === 'running'`, which missed the case
82
- // where bash prints `command not found` (status flips to running) and then
83
- // exits 127 a few ms later. The tighter loop catches that path.
83
+ // fast via onExit with a non-zero code. Wait briefly for that real exit
84
+ // event so we surface the failure synchronously instead of returning a fake
85
+ // "ok". Older test ports without waitForRunExit use the polling fallback.
84
86
  let exitCode = run.exitCode;
85
87
  let status = run.status;
86
- const deadline = Date.now() + SETTLE_WAIT_MS;
88
+ let output = '';
89
+ const startedAt = Date.now();
90
+ let deadline = startedAt + SETTLE_WAIT_MS;
91
+ const silentStartingDeadline = startedAt + SILENT_STARTING_SETTLE_WAIT_MS;
87
92
  while (status !== 'exited' && status !== 'error' && Date.now() < deadline) {
88
- await sleep(POLL_INTERVAL_MS);
93
+ const remainingMs = Math.max(0, deadline - Date.now());
94
+ const waitMs = Math.min(POLL_INTERVAL_MS, remainingMs);
95
+ if (port.waitForRunExit) {
96
+ await port.waitForRunExit(run.runId, waitMs);
97
+ }
98
+ else {
99
+ await sleep(waitMs);
100
+ }
89
101
  try {
90
102
  const live = port.getLiveRun(run.runId);
91
103
  status = live.status;
92
104
  exitCode = live.exitCode;
105
+ output = live.output ?? output;
93
106
  }
94
107
  catch {
95
108
  break;
96
109
  }
110
+ if (status === 'starting' &&
111
+ output.length === 0 &&
112
+ deadline < silentStartingDeadline &&
113
+ Date.now() >= deadline) {
114
+ deadline = silentStartingDeadline;
115
+ }
97
116
  }
98
117
  if (status === 'error' || (status === 'exited' && (exitCode ?? 0) !== 0)) {
99
118
  return {
@@ -1,6 +1,7 @@
1
1
  import type { AgentManager } from './agent-manager.js';
2
2
  export declare const toBracketedPasteSubmission: (text: string) => string;
3
3
  export declare const isInteractiveAgentCommand: (command: string) => boolean;
4
+ export declare const hasFirstRunSetupPrompt: (output: string) => boolean;
4
5
  export declare const hasInteractivePromptReady: (output: string, command?: string) => boolean;
5
6
  export declare const hasBracketedPasteAcknowledgement: (output: string, baselineLength: number) => boolean;
6
7
  export declare const createPostStartInputWriter: (agentManager: AgentManager, command: string) => ((runId: string, text: string) => Promise<void>);
@@ -2,6 +2,8 @@ import { BUILTIN_INTERACTIVE_COMMANDS } from './command-preset-defaults.js';
2
2
  import { normalizeExecutableToken } from './startup-command-parser.js';
3
3
  const READY_CHECK_INTERVAL_MS = 50;
4
4
  const READY_TIMEOUT_MS = 3000;
5
+ const PROMPT_READY_HARD_TIMEOUT_MS = 30000;
6
+ const PROMPT_READY_TAIL_CHARS = 8000;
5
7
  const MIN_SUBMIT_AFTER_PASTE_DELAY_MS = 600;
6
8
  const MAX_SUBMIT_AFTER_PASTE_DELAY_MS = 1500;
7
9
  const PASTE_CHARS_PER_DELAY_MS = 4;
@@ -9,9 +11,13 @@ const PASTE_ACK_CHECK_INTERVAL_MS = 50;
9
11
  const PASTE_ACK_SETTLE_DELAY_MS = 100;
10
12
  const CODEX_PASTE_ACK_SETTLE_DELAY_MS = 250;
11
13
  const PASTE_ACK_TIMEOUT_MS = 3000;
14
+ const CLAUDE_HIVE_PASTE_ACK_TIMEOUT_MS = 10000;
15
+ const CLAUDE_HIVE_PASTE_ACK_MIN_CHARS = 2000;
12
16
  // Codex can take several seconds to render "[Pasted Content ...]" on Windows
13
- // conpty for large bracketed-paste payloads. Keep Claude's shorter fallback,
14
- // but give Codex enough time to use the ack-gated path in normal operation.
17
+ // conpty for large bracketed-paste payloads. Claude gets the longer window only
18
+ // on Windows and only for large Hive envelopes; small prompts need the shorter
19
+ // fallback so a CLI that does not render paste acknowledgements still receives
20
+ // Enter promptly.
15
21
  const CODEX_PASTE_ACK_TIMEOUT_MS = 10000;
16
22
  // Codex only emits "[Pasted Content ...]" for sufficiently large pastes. Short
17
23
  // Hive report/status/user-input payloads are usually rendered literally in the
@@ -20,6 +26,9 @@ const CODEX_PASTE_ACK_TIMEOUT_MS = 10000;
20
26
  const CODEX_PASTE_ACK_MIN_CHARS = 2000;
21
27
  const CODEX_SUBMIT_RETRY_DELAY_MS = 500;
22
28
  const GROK_SUBMIT_AFTER_PASTE_DELAY_MS = 100;
29
+ const OPENCODE_MIN_SUBMIT_AFTER_PASTE_DELAY_MS = 2500;
30
+ const OPENCODE_MAX_SUBMIT_AFTER_PASTE_DELAY_MS = 5000;
31
+ const OPENCODE_PASTE_CHARS_PER_DELAY_MS = 2;
23
32
  const GEMINI_STYLE_MAX_SUBMIT_AFTER_PASTE_DELAY_MS = 4000;
24
33
  const GEMINI_STYLE_PASTE_CHARS_PER_DELAY_MS = 2;
25
34
  const COMMANDS_WITH_BRACKETED_PASTE = new Set([
@@ -37,13 +46,18 @@ const PASTE_ACK_ONLY_DELTA_PATTERN = /^[\s\r\n]*(?:[❯›]\s*)?\[(?:Pasted text
37
46
  const ESCAPE = String.fromCharCode(27);
38
47
  const BELL = String.fromCharCode(7);
39
48
  const TERMINAL_CONTROL_PATTERN = new RegExp(`${ESCAPE}\\[[0-?]*[ -/]*[@-~]|${ESCAPE}\\][^${BELL}${ESCAPE}]*(?:${BELL}|${ESCAPE}\\\\)`, 'gu');
49
+ const HIVE_ACK_GATED_MESSAGE_PATTERN = /<(?:hive-system-message\b|hive-message\s+kind="(?:cancel|dispatch|heartbeat|startup)"(?:\s|>))/u;
40
50
  export const toBracketedPasteSubmission = (text) => `\u001b[200~${text}\u001b[201~`;
41
51
  const toError = (error) => (error instanceof Error ? error : new Error(String(error)));
42
52
  const createRunInactiveError = (runId) => new Error(`Run became inactive before input was submitted: ${runId}`);
53
+ const createPromptReadyTimeoutError = (runId, command) => new Error(`Timed out waiting for ${getCommandName(command) || command} prompt readiness: ${runId}`);
43
54
  const getSubmitAfterPasteDelayMs = (command, text) => {
44
55
  const commandName = getCommandName(command);
45
56
  if (commandName === 'grok')
46
57
  return GROK_SUBMIT_AFTER_PASTE_DELAY_MS;
58
+ if (commandName === 'opencode') {
59
+ return Math.min(OPENCODE_MAX_SUBMIT_AFTER_PASTE_DELAY_MS, Math.max(OPENCODE_MIN_SUBMIT_AFTER_PASTE_DELAY_MS, Math.ceil(text.length / OPENCODE_PASTE_CHARS_PER_DELAY_MS)));
60
+ }
47
61
  if (commandName === 'gemini' || commandName === 'qwen') {
48
62
  return Math.min(GEMINI_STYLE_MAX_SUBMIT_AFTER_PASTE_DELAY_MS, Math.max(MIN_SUBMIT_AFTER_PASTE_DELAY_MS, Math.ceil(text.length / GEMINI_STYLE_PASTE_CHARS_PER_DELAY_MS)));
49
63
  }
@@ -54,10 +68,15 @@ export const isInteractiveAgentCommand = (command) => {
54
68
  const brand = normalizeExecutableToken(command);
55
69
  return brand !== null && BUILTIN_INTERACTIVE_COMMANDS.has(brand);
56
70
  };
71
+ const getPlainTerminalOutput = (output) => output.replace(/\r/g, '\n').replace(TERMINAL_CONTROL_PATTERN, '');
72
+ const getPlainTerminalTail = (output) => getPlainTerminalOutput(output).slice(-PROMPT_READY_TAIL_CHARS);
57
73
  const hasGeminiPromptReady = (output) => /\bType your message\b/u.test(output);
58
- const hasOpencodePromptReady = (output) => /\bAsk anything\.\.\./u.test(output);
74
+ const hasHermesPromptReady = (output) => {
75
+ const plain = getPlainTerminalTail(output);
76
+ return /\bWelcome to Hermes Agent![\s\S]*❯/u.test(plain);
77
+ };
78
+ const hasOpencodePromptReady = (output) => /\bAsk anything\.\.\./u.test(getPlainTerminalTail(output));
59
79
  const hasGrokPromptReady = (output) => /\b(?:Enter:send|Composer\s+\S+)/u.test(output);
60
- const getPlainTerminalOutput = (output) => output.replace(/\r/g, '\n').replace(TERMINAL_CONTROL_PATTERN, '');
61
80
  const hasAgyPromptReady = (output) => /(?:^|\n)\s*>\s*\n\s*(?:[─-]{8,}|\?\s*for shortcuts)/u.test(getPlainTerminalOutput(output));
62
81
  const getLastNonEmptyTerminalLine = (output) => {
63
82
  const normalized = getPlainTerminalOutput(output);
@@ -68,12 +87,35 @@ const getLastNonEmptyTerminalLine = (output) => {
68
87
  return '';
69
88
  };
70
89
  const hasBarePromptLine = (output) => /^[❯›]$/u.test(getLastNonEmptyTerminalLine(output));
71
- const hasCodexChoicePrompt = (output, command) => getCommandName(command) === 'codex' && /^[❯›]\s+\S/u.test(getLastNonEmptyTerminalLine(output));
90
+ const SETUP_PROMPT_PATTERNS = [
91
+ /\bDo you trust\b/iu,
92
+ /\btrust this (?:directory|folder|workspace|project)\b/iu,
93
+ /\b(?:Log in|Login required|Sign in)\b/iu,
94
+ /\b(?:Enter|Return)\s+to\s+confirm\b/iu,
95
+ /\bEsc\s+to\s+(?:cancel|exit)\b/iu,
96
+ /\b(?:Choose|Pick|Select)\s+(?:a\s+)?(?:theme|option|provider)\b/iu,
97
+ ];
98
+ export const hasFirstRunSetupPrompt = (output) => {
99
+ const plainTail = getPlainTerminalTail(output);
100
+ if (hasBarePromptLine(plainTail))
101
+ return false;
102
+ const recentLines = plainTail
103
+ .split('\n')
104
+ .map((line) => line.trim())
105
+ .filter(Boolean)
106
+ .slice(-8);
107
+ const lastLine = recentLines.at(-1) ?? '';
108
+ if (/^[❯›]\s+(?:\d+\.?\s*)?\S/u.test(lastLine))
109
+ return true;
110
+ const recent = recentLines.join('\n');
111
+ return SETUP_PROMPT_PATTERNS.some((pattern) => pattern.test(recent));
112
+ };
72
113
  export const hasInteractivePromptReady = (output, command = '') => {
73
114
  const commandName = getCommandName(command);
74
115
  return (hasBarePromptLine(output) ||
75
116
  (commandName === 'agy' && hasAgyPromptReady(output)) ||
76
117
  (commandName === 'grok' && hasGrokPromptReady(output)) ||
118
+ (commandName === 'hermes' && hasHermesPromptReady(output)) ||
77
119
  ((commandName === 'gemini' || commandName === 'qwen') && hasGeminiPromptReady(output)) ||
78
120
  (commandName === 'opencode' && hasOpencodePromptReady(output)));
79
121
  };
@@ -88,17 +130,37 @@ export const hasBracketedPasteAcknowledgement = (output, baselineLength) => {
88
130
  const onlyPasteAcknowledgementWasAppended = (before, after) => after.startsWith(before) && PASTE_ACK_ONLY_DELTA_PATTERN.test(after.slice(before.length));
89
131
  const shouldWaitForPasteAck = (command, text) => {
90
132
  const commandName = getCommandName(command);
91
- if (commandName === 'codex')
92
- return text.length >= CODEX_PASTE_ACK_MIN_CHARS;
133
+ if (commandName === 'codex') {
134
+ return text.length >= CODEX_PASTE_ACK_MIN_CHARS || HIVE_ACK_GATED_MESSAGE_PATTERN.test(text);
135
+ }
93
136
  return COMMANDS_WAITING_FOR_PASTE_ACK.has(commandName);
94
137
  };
95
138
  const retriesSubmit = (command) => getCommandName(command) === 'codex';
96
139
  const getPasteAckSettleDelayMs = (command) => getCommandName(command) === 'codex' ? CODEX_PASTE_ACK_SETTLE_DELAY_MS : PASTE_ACK_SETTLE_DELAY_MS;
97
- const getPasteAckTimeoutMs = (command) => getCommandName(command) === 'codex' ? CODEX_PASTE_ACK_TIMEOUT_MS : PASTE_ACK_TIMEOUT_MS;
140
+ const getPasteAckTimeoutMs = (command, text) => {
141
+ const commandName = getCommandName(command);
142
+ if (commandName === 'codex')
143
+ return CODEX_PASTE_ACK_TIMEOUT_MS;
144
+ if (commandName === 'claude' &&
145
+ process.platform === 'win32' &&
146
+ text.length >= CLAUDE_HIVE_PASTE_ACK_MIN_CHARS &&
147
+ HIVE_ACK_GATED_MESSAGE_PATTERN.test(text)) {
148
+ return CLAUDE_HIVE_PASTE_ACK_TIMEOUT_MS;
149
+ }
150
+ return PASTE_ACK_TIMEOUT_MS;
151
+ };
98
152
  const usesBracketedPaste = (command) => COMMANDS_WITH_BRACKETED_PASTE.has(getCommandName(command));
99
153
  const canTimeoutBeforePromptReady = (command) => {
100
154
  const commandName = getCommandName(command);
101
- return commandName !== 'agy' && commandName !== 'gemini' && commandName !== 'qwen';
155
+ return (commandName !== 'agy' &&
156
+ commandName !== 'gemini' &&
157
+ commandName !== 'hermes' &&
158
+ commandName !== 'opencode' &&
159
+ commandName !== 'qwen');
160
+ };
161
+ const hasPromptReadyHardTimeout = (command) => {
162
+ const commandName = getCommandName(command);
163
+ return commandName === 'hermes' || commandName === 'opencode';
102
164
  };
103
165
  const isWritableRunStatus = (status) => status === undefined || status === 'starting' || status === 'running';
104
166
  const writeIfRunWritable = (agentManager, runId, text) => {
@@ -234,8 +296,9 @@ export const createPostStartInputWriter = (agentManager, command) => {
234
296
  rejectDone(createRunInactiveError(runId));
235
297
  return;
236
298
  }
237
- if (hasInteractivePromptReady(output, command) ||
238
- (!hasCodexChoicePrompt(output, command) &&
299
+ const firstRunSetupPromptVisible = hasFirstRunSetupPrompt(output);
300
+ if ((!firstRunSetupPromptVisible && hasInteractivePromptReady(output, command)) ||
301
+ (!firstRunSetupPromptVisible &&
239
302
  canTimeoutBeforePromptReady(command) &&
240
303
  Date.now() - startedAt >= READY_TIMEOUT_MS)) {
241
304
  const baselineLength = output.length;
@@ -252,7 +315,12 @@ export const createPostStartInputWriter = (agentManager, command) => {
252
315
  rejectDone(toError(error));
253
316
  return;
254
317
  }
255
- submitPastedInteractiveInput(agentManager, runId, command, text, baselineLength, shouldWaitForPasteAck(command, text), getPasteAckSettleDelayMs(command), getPasteAckTimeoutMs(command), retriesSubmit(command), resolveDone, rejectDone);
318
+ submitPastedInteractiveInput(agentManager, runId, command, text, baselineLength, shouldWaitForPasteAck(command, text), getPasteAckSettleDelayMs(command), getPasteAckTimeoutMs(command, text), retriesSubmit(command), resolveDone, rejectDone);
319
+ return;
320
+ }
321
+ if (hasPromptReadyHardTimeout(command) &&
322
+ Date.now() - startedAt >= PROMPT_READY_HARD_TIMEOUT_MS) {
323
+ rejectDone(createPromptReadyTimeoutError(runId, command));
256
324
  return;
257
325
  }
258
326
  setTimeout(tryWrite, READY_CHECK_INTERVAL_MS);
@@ -1,8 +1,15 @@
1
1
  import type { WorkerRole } from '../shared/types.js';
2
+ import type { UiLanguage } from '../shared/ui-language.js';
2
3
  export declare const ORCHESTRATOR_ROLE_DESCRIPTION: string;
3
4
  export declare const CODER_ROLE_DESCRIPTION: string;
4
5
  export declare const REVIEWER_ROLE_DESCRIPTION: string;
5
6
  export declare const TESTER_ROLE_DESCRIPTION: string;
6
7
  export declare const SENTINEL_ROLE_DESCRIPTION: string;
7
8
  export declare const CUSTOM_ROLE_DESCRIPTION: string;
8
- export declare const getDefaultRoleDescription: (role: WorkerRole | "orchestrator") => string;
9
+ export declare const ORCHESTRATOR_ROLE_DESCRIPTION_ZH: string;
10
+ export declare const CODER_ROLE_DESCRIPTION_ZH: string;
11
+ export declare const REVIEWER_ROLE_DESCRIPTION_ZH: string;
12
+ export declare const TESTER_ROLE_DESCRIPTION_ZH: string;
13
+ export declare const SENTINEL_ROLE_DESCRIPTION_ZH: string;
14
+ export declare const CUSTOM_ROLE_DESCRIPTION_ZH: string;
15
+ export declare const getDefaultRoleDescription: (role: WorkerRole | "orchestrator", language?: UiLanguage) => string;
@@ -1,68 +1,122 @@
1
1
  import { TASKS_RELATIVE_PATH } from './tasks-file.js';
2
2
  export const ORCHESTRATOR_ROLE_DESCRIPTION = [
3
3
  'You are the Hive Orchestrator. You reply to the user directly and coordinate real workers shown on the right.',
4
- '工作方式 / How to work:',
5
- '- Clarify the goal and split it into dispatchable tasks. / 澄清目标,把需求拆成可派发的小任务。',
6
- '- Default to Hive dispatch: use `team send`, `team spawn`, or Hive `team workflow` for implementation, audit/review, test, validation, multi-file, or parallel work; do not use your CLI built-in subagents/workflows or keep a non-trivial branch for yourself. / 默认走 Hive 派单:实现、审计/复核、测试、验证、多文件或并行任务用 `team send`、`team spawn` 或 Hive `team workflow`;不要用当前 CLI 内建子代理/工作流,也不要把非平凡分支留给自己。',
7
- `- Maintain ${TASKS_RELATIVE_PATH} so plan, progress, and blockers stay trackable. / 维护 ${TASKS_RELATIVE_PATH},让当前计划、进度和阻塞可追踪。`,
8
- '- Drive the next step from worker reports; do not bounce choices back to the user unless a real decision is missing. / 根据成员汇报推进下一步,不把选择题无谓丢回给用户。',
4
+ 'How to work:',
5
+ '- Clarify the goal and split it into dispatchable tasks.',
6
+ '- Default to Hive dispatch: use `team send`, `team spawn`, or Hive `team workflow` for implementation, audit/review, test, validation, multi-file, or parallel work; do not use your CLI built-in subagents/workflows or keep a non-trivial branch for yourself.',
7
+ `- Maintain ${TASKS_RELATIVE_PATH} so plan, progress, and blockers stay trackable.`,
8
+ '- Drive the next step from worker reports; do not bounce choices back to the user unless a real decision is missing.',
9
9
  ].join('\n');
10
10
  export const CODER_ROLE_DESCRIPTION = [
11
11
  'You are an implementation Coder. Turn clear tasks into minimal, correct code changes.',
12
- '工作方式 / How to work:',
13
- '- Read relevant files and existing patterns before editing. / 先阅读相关文件和现有模式,再动手。',
14
- '- Prefer small scoped changes; avoid unrelated refactors and scope creep. / 优先小步修改,避免无关重构和范围扩张。',
15
- '- After editing, run validation commands that cover the risk; if you cannot validate, say why. / 改动后运行能覆盖风险的验证命令;不能验证时说明原因。',
16
- 'Delivery / 交付说明: include changed files, validation results, and remaining risks or blockers.',
12
+ 'How to work:',
13
+ '- Read relevant files and existing patterns before editing.',
14
+ '- Prefer small scoped changes; avoid unrelated refactors and scope creep.',
15
+ '- After editing, run validation commands that cover the risk; if you cannot validate, say why.',
16
+ 'Delivery: include changed files, validation results, and remaining risks or blockers.',
17
17
  ].join('\n');
18
18
  export const REVIEWER_ROLE_DESCRIPTION = [
19
19
  'You are a Reviewer. Audit quality; do not replace the Orchestrator and do not edit code by default.',
20
- '工作方式 / How to work:',
21
- '- Prioritize real bugs, regression risks, edge cases, and test gaps. / 优先找真实 bug、回归风险、边界条件和测试缺口。',
22
- '- For each issue, give severity, file/line, trigger condition, and the smallest credible fix. / 发现问题时给出严重度、文件/行号、触发条件和最小修复建议。',
23
- '- If there is no high-risk issue, state residual risk and what was not verified. / 没有高风险问题时明确说清剩余风险和未验证范围。',
24
- 'Delivery / 交付说明: sort by severity and list blocking issues first.',
20
+ 'How to work:',
21
+ '- Prioritize real bugs, regression risks, edge cases, and test gaps.',
22
+ '- For each issue, give severity, file/line, trigger condition, and the smallest credible fix.',
23
+ '- If there is no high-risk issue, state residual risk and what was not verified.',
24
+ 'Delivery: sort by severity and list blocking issues first.',
25
25
  ].join('\n');
26
26
  export const TESTER_ROLE_DESCRIPTION = [
27
27
  'You are a Tester. Reproduce, test, and produce evidence-backed validation.',
28
- '工作方式 / How to work:',
29
- '- First identify the behavior, entry point, and failure condition to validate. / 先明确要验证的行为、入口和失败条件。',
30
- '- Prefer real commands or real end-to-end paths; add minimal tests only when needed. / 优先跑真实命令或真实链路;必要时补充最小测试。',
31
- '- Record commands, results, key output, and scenarios you could not cover. / 记录命令、结果、关键输出和不能覆盖的场景。',
32
- 'Delivery / 交付说明: separate passed, failed, unverified, and recommended next steps.',
28
+ 'How to work:',
29
+ '- First identify the behavior, entry point, and failure condition to validate.',
30
+ '- Prefer real commands or real end-to-end paths; add minimal tests only when needed.',
31
+ '- Record commands, results, key output, and scenarios you could not cover.',
32
+ 'Delivery: separate passed, failed, unverified, and recommended next steps.',
33
33
  ].join('\n');
34
34
  export const SENTINEL_ROLE_DESCRIPTION = [
35
35
  'You are a read-only Sentinel. Observe team health; do not execute tasks or modify anything.',
36
- '工作方式 / How to work:',
37
- '- Hive periodically injects workspace snapshots: member states, open dispatches, and possible orphaned work. / 系统会定期向你注入工作区快照。',
38
- '- Compare snapshots with your own observations and escalate only anomalies worth Orchestrator attention. / 把快照与你自己的观察对照,只上报值得 Orchestrator 注意的异常。',
39
- '- Long silence is not automatically stuck; large tasks can be slow. / 长时间无汇报不等于卡死。',
40
- 'Hard boundaries / 硬性边界:',
41
- '- Observe only: do not edit files, run side-effecting commands, or dispatch work. / 只观察:不改文件、不跑有副作用的命令、不派活。',
42
- '- You take no dispatches and do not have `team report`; use `team status "<finding>"` for findings. / 你不接派单,也没有 `team report`;发现统一用 `team status "<巡检发现>"` 汇报。',
43
- '- Stay quiet when nothing is abnormal. / 没有异常时保持安静。',
36
+ 'How to work:',
37
+ '- Hive periodically injects workspace snapshots: member states, open dispatches, and possible orphaned work.',
38
+ '- Compare snapshots with your own observations and escalate only anomalies worth Orchestrator attention.',
39
+ '- Long silence is not automatically stuck; large tasks can be slow.',
40
+ 'Hard boundaries:',
41
+ '- Observe only: do not edit files, run side-effecting commands, or dispatch work.',
42
+ '- You take no dispatches and do not have `team report`; use `team status "<finding>"` for findings.',
43
+ '- Stay quiet when nothing is abnormal.',
44
44
  ].join('\n');
45
45
  export const CUSTOM_ROLE_DESCRIPTION = [
46
46
  'You are a custom Hive member. Replace this with the member-specific behavior contract.',
47
- '建议包含 / Include:',
48
- '- Goal: what this member is responsible for. / 目标:这个成员主要负责什么。',
49
- '- Boundaries: what it may and may not do. / 边界:哪些事可以做,哪些事不要做。',
50
- '- Working style: how it investigates, edits, validates, or reviews. / 工作方式:如何调查、修改、验证或审查。',
51
- '- Done criteria: what results, risks, and blockers to report. / 完成标准:交付时需要说明哪些结果、风险和阻塞。',
47
+ 'Recommended shape:',
48
+ '- Goal: what this member is responsible for.',
49
+ '- Boundaries: what it may and may not do.',
50
+ '- Working style: how it investigates, edits, validates, or reviews.',
51
+ '- Done criteria: what results, risks, and blockers to report.',
52
52
  ].join('\n');
53
- export const getDefaultRoleDescription = (role) => {
54
- switch (role) {
55
- case 'orchestrator':
56
- return ORCHESTRATOR_ROLE_DESCRIPTION;
57
- case 'coder':
58
- return CODER_ROLE_DESCRIPTION;
59
- case 'reviewer':
60
- return REVIEWER_ROLE_DESCRIPTION;
61
- case 'tester':
62
- return TESTER_ROLE_DESCRIPTION;
63
- case 'sentinel':
64
- return SENTINEL_ROLE_DESCRIPTION;
65
- case 'custom':
66
- return CUSTOM_ROLE_DESCRIPTION;
67
- }
53
+ export const ORCHESTRATOR_ROLE_DESCRIPTION_ZH = [
54
+ '你是 Hive Orchestrator。你直接回复用户,并协调右侧真实 worker。',
55
+ '工作方式:',
56
+ '- 澄清目标,把需求拆成可派发的小任务。',
57
+ '- 默认走 Hive 派单:实现、审计/复核、测试、验证、多文件或并行任务用 `team send`、`team spawn` 或 Hive `team workflow`;不要用当前 CLI 内建子代理/工作流,也不要把非平凡分支留给自己。',
58
+ `- 维护 ${TASKS_RELATIVE_PATH},让当前计划、进度和阻塞可追踪。`,
59
+ '- 根据成员汇报推进下一步,不把选择题无谓丢回给用户。',
60
+ ].join('\n');
61
+ export const CODER_ROLE_DESCRIPTION_ZH = [
62
+ '你是实现型 Coder,负责把明确任务落成最小正确代码改动。',
63
+ '工作方式:',
64
+ '- 先阅读相关文件和现有模式,再动手。',
65
+ '- 优先小步修改,避免无关重构和范围扩张。',
66
+ '- 改动后运行能覆盖风险的验证命令;不能验证时说明原因。',
67
+ '交付说明:包含改动文件、验证结果、剩余风险或阻塞。',
68
+ ].join('\n');
69
+ export const REVIEWER_ROLE_DESCRIPTION_ZH = [
70
+ '你是 Reviewer,负责质量审查;不要替代 Orchestrator,也不要默认改代码。',
71
+ '工作方式:',
72
+ '- 优先找真实 bug、回归风险、边界条件和测试缺口。',
73
+ '- 发现问题时给出严重度、文件/行号、触发条件和最小修复建议。',
74
+ '- 没有高风险问题时明确说清剩余风险和未验证范围。',
75
+ '交付说明:按严重度排序,先列 blocking 问题。',
76
+ ].join('\n');
77
+ export const TESTER_ROLE_DESCRIPTION_ZH = [
78
+ '你是 Tester,负责复现、测试和证据化验证。',
79
+ '工作方式:',
80
+ '- 先明确要验证的行为、入口和失败条件。',
81
+ '- 优先跑真实命令或真实链路;必要时补充最小测试。',
82
+ '- 记录命令、结果、关键输出和不能覆盖的场景。',
83
+ '交付说明:区分通过、失败、未验证和建议下一步。',
84
+ ].join('\n');
85
+ export const SENTINEL_ROLE_DESCRIPTION_ZH = [
86
+ '你是只读 Sentinel,负责观察团队运行状态;不执行任务、不修改任何东西。',
87
+ '工作方式:',
88
+ '- Hive 会周期性注入工作区快照:成员状态、未关闭派单和疑似孤儿单。',
89
+ '- 把快照与你自己的观察对照,只上报值得 Orchestrator 注意的异常。',
90
+ '- 长时间无汇报不等于卡死;正常的大任务本来就慢。',
91
+ '硬性边界:',
92
+ '- 只观察:不改文件、不跑有副作用的命令、不派活。',
93
+ '- 你不接派单,也没有 `team report`;发现统一用 `team status "<巡检发现>"` 汇报。',
94
+ '- 没有异常时保持安静。',
95
+ ].join('\n');
96
+ export const CUSTOM_ROLE_DESCRIPTION_ZH = [
97
+ '你是自定义 Hive 成员。请把这段改成该成员的行为契约。',
98
+ '建议包含:',
99
+ '- 目标:这个成员主要负责什么。',
100
+ '- 边界:哪些事可以做,哪些事不要做。',
101
+ '- 工作方式:如何调查、修改、验证或审查。',
102
+ '- 完成标准:交付时需要说明哪些结果、风险和阻塞。',
103
+ ].join('\n');
104
+ const DEFAULT_ROLE_DESCRIPTIONS = {
105
+ en: {
106
+ orchestrator: ORCHESTRATOR_ROLE_DESCRIPTION,
107
+ coder: CODER_ROLE_DESCRIPTION,
108
+ reviewer: REVIEWER_ROLE_DESCRIPTION,
109
+ tester: TESTER_ROLE_DESCRIPTION,
110
+ sentinel: SENTINEL_ROLE_DESCRIPTION,
111
+ custom: CUSTOM_ROLE_DESCRIPTION,
112
+ },
113
+ zh: {
114
+ orchestrator: ORCHESTRATOR_ROLE_DESCRIPTION_ZH,
115
+ coder: CODER_ROLE_DESCRIPTION_ZH,
116
+ reviewer: REVIEWER_ROLE_DESCRIPTION_ZH,
117
+ tester: TESTER_ROLE_DESCRIPTION_ZH,
118
+ sentinel: SENTINEL_ROLE_DESCRIPTION_ZH,
119
+ custom: CUSTOM_ROLE_DESCRIPTION_ZH,
120
+ },
68
121
  };
122
+ export const getDefaultRoleDescription = (role, language = 'en') => DEFAULT_ROLE_DESCRIPTIONS[language][role];