@tt-a1i/hive 2.1.1 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/README.en.md +5 -460
- package/README.md +338 -187
- package/README.zh.md +389 -0
- package/dist/src/cli/hive-update.d.ts +3 -2
- package/dist/src/cli/hive-update.js +20 -60
- package/dist/src/cli/hive.js +5 -1
- package/dist/src/server/agent-manager-support.js +16 -9
- package/dist/src/server/agent-runtime-contract.d.ts +4 -0
- package/dist/src/server/agent-runtime.js +27 -0
- package/dist/src/server/agent-stdin-dispatcher.d.ts +3 -0
- package/dist/src/server/agent-stdin-dispatcher.js +3 -0
- package/dist/src/server/diagnostics-support-bundle.d.ts +3 -0
- package/dist/src/server/diagnostics-support-bundle.js +3 -0
- package/dist/src/server/hive-team-guidance.js +34 -4
- package/dist/src/server/message-log-store.d.ts +1 -0
- package/dist/src/server/message-log-store.js +13 -0
- package/dist/src/server/orchestrator-autostart.d.ts +2 -0
- package/dist/src/server/orchestrator-autostart.js +30 -11
- package/dist/src/server/post-start-input-writer.d.ts +1 -0
- package/dist/src/server/post-start-input-writer.js +78 -12
- package/dist/src/server/role-templates.d.ts +8 -1
- package/dist/src/server/role-templates.js +102 -48
- package/dist/src/server/route-types.d.ts +6 -0
- package/dist/src/server/routes-scenarios.d.ts +2 -1
- package/dist/src/server/routes-scenarios.js +11 -6
- package/dist/src/server/routes-team.js +21 -15
- package/dist/src/server/routes-workspaces.js +22 -2
- package/dist/src/server/runtime-store-contract.d.ts +2 -0
- package/dist/src/server/runtime-store-helpers.d.ts +11 -2
- package/dist/src/server/runtime-store-helpers.js +29 -5
- package/dist/src/server/runtime-store-workflows.js +20 -0
- package/dist/src/server/runtime-store.js +2 -0
- package/dist/src/server/scenario-presets.d.ts +3 -1
- package/dist/src/server/scenario-presets.js +12 -1
- package/dist/src/server/spawn-worker-defaults.d.ts +2 -0
- package/dist/src/server/spawn-worker-defaults.js +10 -5
- package/dist/src/server/team-operations.d.ts +3 -0
- package/dist/src/server/team-operations.js +97 -29
- package/dist/src/server/terminal-input-profile.d.ts +2 -0
- package/dist/src/server/update-install-plan.d.ts +20 -0
- package/dist/src/server/update-install-plan.js +151 -0
- package/dist/src/server/version-service.d.ts +5 -0
- package/dist/src/server/version-service.js +17 -13
- package/dist/src/server/workflow-agent-call-executor.d.ts +81 -0
- package/dist/src/server/workflow-agent-call-executor.js +259 -0
- package/dist/src/server/workflow-dag-layer-tracker.d.ts +14 -0
- package/dist/src/server/workflow-dag-layer-tracker.js +37 -0
- package/dist/src/server/workflow-dag.d.ts +8 -0
- package/dist/src/server/workflow-dag.js +65 -0
- package/dist/src/server/workflow-dispatch-awaiter.d.ts +7 -1
- package/dist/src/server/workflow-dispatch-awaiter.js +60 -9
- package/dist/src/server/workflow-runner.d.ts +5 -38
- package/dist/src/server/workflow-runner.js +113 -445
- package/dist/src/server/workflow-script-host-dispatcher.d.ts +14 -0
- package/dist/src/server/workflow-script-host-dispatcher.js +42 -0
- package/dist/src/server/workflow-script-loader.d.ts +2 -2
- package/dist/src/server/workflow-script-loader.js +2 -2
- package/dist/src/server/workflow-script-worker.d.ts +14 -0
- package/dist/src/server/workflow-script-worker.js +81 -0
- package/dist/src/server/workflow-vm-worker-source.d.ts +1 -0
- package/dist/src/server/workflow-vm-worker-source.js +1 -0
- package/dist/src/server/workflow-vm-worker.cjs +238 -0
- package/dist/src/server/workspace-ui-language.d.ts +7 -0
- package/dist/src/server/workspace-ui-language.js +9 -0
- package/dist/src/shared/scenario-presets.d.ts +6 -1
- package/dist/src/shared/scenario-presets.js +80 -28
- package/dist/src/shared/types.d.ts +11 -7
- package/dist/src/shared/ui-language.d.ts +4 -0
- package/dist/src/shared/ui-language.js +3 -0
- package/package.json +6 -3
- package/web/dist/assets/{AddWorkerDialog-CTOsPurT.js → AddWorkerDialog-CkCGKa0N.js} +2 -2
- package/web/dist/assets/AddWorkspaceFlow-Cd73iChz.js +1 -0
- package/web/dist/assets/{FirstRunWizard-C24MUssx.js → FirstRunWizard-Db4HWbBQ.js} +1 -1
- package/web/dist/assets/{MarketplaceDrawer-7gVUjgAx.js → MarketplaceDrawer-DHwl1if1.js} +1 -1
- package/web/dist/assets/TaskGraphDrawer-B_OibVQO.js +1 -0
- package/web/dist/assets/{WhatsNewDialog-vnShQWjg.js → WhatsNewDialog-9QXlLVfy.js} +1 -1
- package/web/dist/assets/{WorkerModal-DcxIBPlu.js → WorkerModal-BGdtte3-.js} +1 -1
- package/web/dist/assets/{WorkflowsDrawer-C8csID1I.js → WorkflowsDrawer-DRuS0Ao5.js} +1 -1
- package/web/dist/assets/WorkspaceMemoryDrawer-C8PuHo0x.js +1 -0
- package/web/dist/assets/WorkspaceTaskDrawer-6mbgRoOq.js +1 -0
- package/web/dist/assets/index-BiGD5NhO.js +84 -0
- package/web/dist/assets/index-CG2TurwT.css +1 -0
- package/web/dist/assets/{search-BRQbevR2.js → search-BHvXxsdK.js} +1 -1
- package/web/dist/assets/{square-terminal-Co4pmGzn.js → square-terminal-CzkQzjCU.js} +1 -1
- package/web/dist/index.html +2 -2
- package/web/dist/sw.js +1 -1
- package/web/dist/assets/AddWorkspaceFlow-D_fSv-cO.js +0 -1
- package/web/dist/assets/TaskGraphDrawer-Beh95yUu.js +0 -1
- package/web/dist/assets/WorkspaceMemoryDrawer-C_Aq9-o6.js +0 -1
- package/web/dist/assets/WorkspaceTaskDrawer-CIMYzlpt.js +0 -1
- package/web/dist/assets/index-3Hc1oXlt.css +0 -1
- package/web/dist/assets/index-D9zUE0W6.js +0 -79
|
@@ -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.
|
|
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)\`,
|
|
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
|
-
//
|
|
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.
|
|
78
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
14
|
-
//
|
|
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,14 +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');
|
|
40
|
-
const
|
|
49
|
+
const HIVE_ACK_GATED_MESSAGE_PATTERN = /<(?:hive-system-message\b|hive-message\s+kind="(?:cancel|dispatch|heartbeat|startup)"(?:\s|>))/u;
|
|
41
50
|
export const toBracketedPasteSubmission = (text) => `\u001b[200~${text}\u001b[201~`;
|
|
42
51
|
const toError = (error) => (error instanceof Error ? error : new Error(String(error)));
|
|
43
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}`);
|
|
44
54
|
const getSubmitAfterPasteDelayMs = (command, text) => {
|
|
45
55
|
const commandName = getCommandName(command);
|
|
46
56
|
if (commandName === 'grok')
|
|
47
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
|
+
}
|
|
48
61
|
if (commandName === 'gemini' || commandName === 'qwen') {
|
|
49
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)));
|
|
50
63
|
}
|
|
@@ -55,10 +68,15 @@ export const isInteractiveAgentCommand = (command) => {
|
|
|
55
68
|
const brand = normalizeExecutableToken(command);
|
|
56
69
|
return brand !== null && BUILTIN_INTERACTIVE_COMMANDS.has(brand);
|
|
57
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);
|
|
58
73
|
const hasGeminiPromptReady = (output) => /\bType your message\b/u.test(output);
|
|
59
|
-
const
|
|
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));
|
|
60
79
|
const hasGrokPromptReady = (output) => /\b(?:Enter:send|Composer\s+\S+)/u.test(output);
|
|
61
|
-
const getPlainTerminalOutput = (output) => output.replace(/\r/g, '\n').replace(TERMINAL_CONTROL_PATTERN, '');
|
|
62
80
|
const hasAgyPromptReady = (output) => /(?:^|\n)\s*>\s*\n\s*(?:[─-]{8,}|\?\s*for shortcuts)/u.test(getPlainTerminalOutput(output));
|
|
63
81
|
const getLastNonEmptyTerminalLine = (output) => {
|
|
64
82
|
const normalized = getPlainTerminalOutput(output);
|
|
@@ -69,12 +87,35 @@ const getLastNonEmptyTerminalLine = (output) => {
|
|
|
69
87
|
return '';
|
|
70
88
|
};
|
|
71
89
|
const hasBarePromptLine = (output) => /^[❯›]$/u.test(getLastNonEmptyTerminalLine(output));
|
|
72
|
-
const
|
|
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
|
+
};
|
|
73
113
|
export const hasInteractivePromptReady = (output, command = '') => {
|
|
74
114
|
const commandName = getCommandName(command);
|
|
75
115
|
return (hasBarePromptLine(output) ||
|
|
76
116
|
(commandName === 'agy' && hasAgyPromptReady(output)) ||
|
|
77
117
|
(commandName === 'grok' && hasGrokPromptReady(output)) ||
|
|
118
|
+
(commandName === 'hermes' && hasHermesPromptReady(output)) ||
|
|
78
119
|
((commandName === 'gemini' || commandName === 'qwen') && hasGeminiPromptReady(output)) ||
|
|
79
120
|
(commandName === 'opencode' && hasOpencodePromptReady(output)));
|
|
80
121
|
};
|
|
@@ -90,17 +131,36 @@ const onlyPasteAcknowledgementWasAppended = (before, after) => after.startsWith(
|
|
|
90
131
|
const shouldWaitForPasteAck = (command, text) => {
|
|
91
132
|
const commandName = getCommandName(command);
|
|
92
133
|
if (commandName === 'codex') {
|
|
93
|
-
return
|
|
134
|
+
return text.length >= CODEX_PASTE_ACK_MIN_CHARS || HIVE_ACK_GATED_MESSAGE_PATTERN.test(text);
|
|
94
135
|
}
|
|
95
136
|
return COMMANDS_WAITING_FOR_PASTE_ACK.has(commandName);
|
|
96
137
|
};
|
|
97
138
|
const retriesSubmit = (command) => getCommandName(command) === 'codex';
|
|
98
139
|
const getPasteAckSettleDelayMs = (command) => getCommandName(command) === 'codex' ? CODEX_PASTE_ACK_SETTLE_DELAY_MS : PASTE_ACK_SETTLE_DELAY_MS;
|
|
99
|
-
const getPasteAckTimeoutMs = (command) =>
|
|
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
|
+
};
|
|
100
152
|
const usesBracketedPaste = (command) => COMMANDS_WITH_BRACKETED_PASTE.has(getCommandName(command));
|
|
101
153
|
const canTimeoutBeforePromptReady = (command) => {
|
|
102
154
|
const commandName = getCommandName(command);
|
|
103
|
-
return commandName !== 'agy' &&
|
|
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';
|
|
104
164
|
};
|
|
105
165
|
const isWritableRunStatus = (status) => status === undefined || status === 'starting' || status === 'running';
|
|
106
166
|
const writeIfRunWritable = (agentManager, runId, text) => {
|
|
@@ -236,8 +296,9 @@ export const createPostStartInputWriter = (agentManager, command) => {
|
|
|
236
296
|
rejectDone(createRunInactiveError(runId));
|
|
237
297
|
return;
|
|
238
298
|
}
|
|
239
|
-
|
|
240
|
-
|
|
299
|
+
const firstRunSetupPromptVisible = hasFirstRunSetupPrompt(output);
|
|
300
|
+
if ((!firstRunSetupPromptVisible && hasInteractivePromptReady(output, command)) ||
|
|
301
|
+
(!firstRunSetupPromptVisible &&
|
|
241
302
|
canTimeoutBeforePromptReady(command) &&
|
|
242
303
|
Date.now() - startedAt >= READY_TIMEOUT_MS)) {
|
|
243
304
|
const baselineLength = output.length;
|
|
@@ -254,7 +315,12 @@ export const createPostStartInputWriter = (agentManager, command) => {
|
|
|
254
315
|
rejectDone(toError(error));
|
|
255
316
|
return;
|
|
256
317
|
}
|
|
257
|
-
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));
|
|
258
324
|
return;
|
|
259
325
|
}
|
|
260
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
|
|
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
|
-
'
|
|
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.
|
|
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
|
-
'
|
|
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
|
|
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
|
-
'
|
|
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
|
|
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
|
-
'
|
|
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
|
|
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
|
-
'
|
|
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.
|
|
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
|
-
'
|
|
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
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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];
|