@tt-a1i/hive 1.4.4 → 1.5.0
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 +22 -0
- package/README.md +8 -0
- package/assets/qq-group.jpg +0 -0
- package/dist/bin/team.cmd +1 -0
- package/dist/src/cli/hive-update.d.ts +45 -17
- package/dist/src/cli/hive-update.js +63 -25
- package/dist/src/cli/hive.d.ts +25 -0
- package/dist/src/cli/hive.js +41 -3
- package/dist/src/cli/team.d.ts +1 -0
- package/dist/src/cli/team.js +199 -3
- package/dist/src/server/agent-command-resolver.js +3 -19
- package/dist/src/server/agent-manager-support.d.ts +2 -2
- package/dist/src/server/agent-manager-support.js +98 -24
- package/dist/src/server/agent-run-starter.d.ts +7 -1
- package/dist/src/server/agent-run-starter.js +9 -2
- package/dist/src/server/agent-run-store.d.ts +1 -1
- package/dist/src/server/agent-runtime-close.d.ts +1 -0
- package/dist/src/server/agent-runtime-close.js +25 -1
- package/dist/src/server/agent-runtime-contract.d.ts +2 -1
- package/dist/src/server/agent-runtime.d.ts +1 -1
- package/dist/src/server/agent-runtime.js +8 -2
- package/dist/src/server/agent-startup-instructions.d.ts +8 -1
- package/dist/src/server/agent-startup-instructions.js +15 -9
- package/dist/src/server/agent-stdin-dispatcher.d.ts +12 -5
- package/dist/src/server/agent-stdin-dispatcher.js +129 -40
- package/dist/src/server/cron-util.d.ts +7 -0
- package/dist/src/server/cron-util.js +19 -0
- package/dist/src/server/dispatch-ledger-store.d.ts +22 -0
- package/dist/src/server/dispatch-ledger-store.js +51 -3
- package/dist/src/server/env-sync-message.js +9 -9
- package/dist/src/server/fs-pick-folder.js +4 -0
- package/dist/src/server/fs-sandbox.js +36 -7
- package/dist/src/server/hive-team-guidance.d.ts +11 -6
- package/dist/src/server/hive-team-guidance.js +252 -71
- package/dist/src/server/live-run-registry.d.ts +1 -0
- package/dist/src/server/live-run-registry.js +1 -1
- package/dist/src/server/open-target-commands.js +5 -6
- package/dist/src/server/orchestrator-autostart.d.ts +12 -0
- package/dist/src/server/orchestrator-autostart.js +15 -13
- package/dist/src/server/path-canonicalization.d.ts +3 -0
- package/dist/src/server/path-canonicalization.js +29 -0
- package/dist/src/server/platform-path.d.ts +3 -0
- package/dist/src/server/platform-path.js +13 -0
- package/dist/src/server/post-start-input-writer.d.ts +1 -1
- package/dist/src/server/post-start-input-writer.js +110 -13
- package/dist/src/server/preset-launch-support.d.ts +1 -1
- package/dist/src/server/preset-launch-support.js +33 -2
- package/dist/src/server/recovery-summary.d.ts +6 -1
- package/dist/src/server/recovery-summary.js +17 -17
- package/dist/src/server/restart-policy-support.d.ts +6 -1
- package/dist/src/server/restart-policy-support.js +9 -1
- package/dist/src/server/restart-policy.d.ts +2 -2
- package/dist/src/server/restart-policy.js +3 -1
- package/dist/src/server/role-template-store.d.ts +1 -0
- package/dist/src/server/role-template-store.js +11 -1
- package/dist/src/server/route-types.d.ts +43 -0
- package/dist/src/server/routes-runtime.js +2 -1
- package/dist/src/server/routes-settings.js +76 -0
- package/dist/src/server/routes-team.js +211 -1
- package/dist/src/server/routes-workflow-schedules.d.ts +2 -0
- package/dist/src/server/routes-workflow-schedules.js +58 -0
- package/dist/src/server/routes-workflows.d.ts +2 -0
- package/dist/src/server/routes-workflows.js +83 -0
- package/dist/src/server/routes.js +4 -0
- package/dist/src/server/runtime-restart-policy.d.ts +3 -1
- package/dist/src/server/runtime-restart-policy.js +3 -1
- package/dist/src/server/runtime-store-contract.d.ts +122 -0
- package/dist/src/server/runtime-store-contract.js +1 -0
- package/dist/src/server/runtime-store-helpers.d.ts +9 -0
- package/dist/src/server/runtime-store-helpers.js +101 -2
- package/dist/src/server/runtime-store-workflows.d.ts +6 -0
- package/dist/src/server/runtime-store-workflows.js +100 -0
- package/dist/src/server/runtime-store.d.ts +3 -72
- package/dist/src/server/runtime-store.js +70 -4
- package/dist/src/server/session-capture-codex.d.ts +3 -3
- package/dist/src/server/session-capture-codex.js +9 -7
- package/dist/src/server/session-capture-gemini.d.ts +1 -1
- package/dist/src/server/session-capture-gemini.js +6 -3
- package/dist/src/server/settings-store.d.ts +3 -0
- package/dist/src/server/settings-store.js +1 -0
- package/dist/src/server/sqlite-schema-v19.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v19.js +17 -0
- package/dist/src/server/sqlite-schema-v20.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v20.js +20 -0
- package/dist/src/server/sqlite-schema-v21.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v21.js +20 -0
- package/dist/src/server/sqlite-schema.d.ts +1 -1
- package/dist/src/server/sqlite-schema.js +97 -1
- package/dist/src/server/system-message.d.ts +7 -0
- package/dist/src/server/system-message.js +8 -1
- package/dist/src/server/tasks-file-watcher.d.ts +13 -1
- package/dist/src/server/tasks-file-watcher.js +127 -23
- package/dist/src/server/tasks-file.d.ts +2 -1
- package/dist/src/server/tasks-file.js +32 -9
- package/dist/src/server/tasks-websocket-server.js +13 -14
- package/dist/src/server/team-authz.d.ts +1 -1
- package/dist/src/server/team-authz.js +9 -1
- package/dist/src/server/team-autostaff.d.ts +16 -0
- package/dist/src/server/team-autostaff.js +16 -0
- package/dist/src/server/team-list-serializer.d.ts +1 -1
- package/dist/src/server/team-list-serializer.js +3 -1
- package/dist/src/server/team-operations.d.ts +15 -1
- package/dist/src/server/team-operations.js +116 -11
- package/dist/src/server/terminal-protocol.js +9 -3
- package/dist/src/server/terminal-stream-hub.js +16 -10
- package/dist/src/server/terminal-ws-server.js +10 -8
- package/dist/src/server/websocket-upgrade-safety.d.ts +10 -0
- package/dist/src/server/websocket-upgrade-safety.js +35 -0
- package/dist/src/server/windows-command-line.d.ts +3 -0
- package/dist/src/server/windows-command-line.js +9 -0
- package/dist/src/server/windows-filename.d.ts +2 -0
- package/dist/src/server/windows-filename.js +33 -0
- package/dist/src/server/workflow-cli-policy.d.ts +60 -0
- package/dist/src/server/workflow-cli-policy.js +110 -0
- package/dist/src/server/workflow-dispatch-awaiter.d.ts +12 -0
- package/dist/src/server/workflow-dispatch-awaiter.js +80 -0
- package/dist/src/server/workflow-feature.d.ts +15 -0
- package/dist/src/server/workflow-feature.js +15 -0
- package/dist/src/server/workflow-http-serializers.d.ts +64 -0
- package/dist/src/server/workflow-http-serializers.js +58 -0
- package/dist/src/server/workflow-run-log-store.d.ts +19 -0
- package/dist/src/server/workflow-run-log-store.js +45 -0
- package/dist/src/server/workflow-run-store.d.ts +50 -0
- package/dist/src/server/workflow-run-store.js +103 -0
- package/dist/src/server/workflow-runner.d.ts +147 -0
- package/dist/src/server/workflow-runner.js +401 -0
- package/dist/src/server/workflow-schedule-create.d.ts +14 -0
- package/dist/src/server/workflow-schedule-create.js +41 -0
- package/dist/src/server/workflow-schedule-store.d.ts +43 -0
- package/dist/src/server/workflow-schedule-store.js +112 -0
- package/dist/src/server/workflow-scheduler.d.ts +36 -0
- package/dist/src/server/workflow-scheduler.js +97 -0
- package/dist/src/server/workflow-script-loader.d.ts +34 -0
- package/dist/src/server/workflow-script-loader.js +106 -0
- package/dist/src/server/workspace-path-validation.js +16 -4
- package/dist/src/server/workspace-shell-runtime.d.ts +5 -0
- package/dist/src/server/workspace-shell-runtime.js +24 -2
- package/dist/src/server/workspace-store-contract.d.ts +4 -1
- package/dist/src/server/workspace-store-hydration.js +23 -7
- package/dist/src/server/workspace-store-mutations.js +2 -5
- package/dist/src/server/workspace-store-support.d.ts +4 -0
- package/dist/src/server/workspace-store-support.js +13 -1
- package/dist/src/server/workspace-store.js +38 -4
- package/dist/src/shared/types.d.ts +16 -1
- package/package.json +4 -2
- package/web/dist/assets/{AddWorkerDialog-DeZhTQLi.js → AddWorkerDialog-CcC-7kgG.js} +2 -2
- package/web/dist/assets/AddWorkspaceDialog-BDpOTfmt.js +1 -0
- package/web/dist/assets/{FirstRunWizard-B5wLcat5.js → FirstRunWizard-BYX_ocQn.js} +1 -1
- package/web/dist/assets/{MarketplaceDrawer-BC0eBOEW.js → MarketplaceDrawer-DUxSk7db.js} +1 -1
- package/web/dist/assets/WhatsNewDialog-B_RlCXcV.js +1 -0
- package/web/dist/assets/WorkerModal-D9-7YfZZ.js +1 -0
- package/web/dist/assets/WorkspaceTaskDrawer-BCKoF7qc.js +1 -0
- package/web/dist/assets/{WorkspaceTerminalPanels-CvibsPSd.js → WorkspaceTerminalPanels-Dq8y91t2.js} +1 -1
- package/web/dist/assets/index-BiOvKIVw.css +1 -0
- package/web/dist/assets/index-DMRUklT3.js +73 -0
- package/web/dist/assets/path-join-7MR1s7b1.js +1 -0
- package/web/dist/index.html +2 -2
- package/web/dist/sw.js +1 -1
- package/web/dist/assets/AddWorkspaceDialog-DDpXNEKf.js +0 -1
- package/web/dist/assets/WorkerModal-BwMHq-Bi.js +0 -1
- package/web/dist/assets/WorkspaceTaskDrawer-CxvT4nqs.js +0 -1
- package/web/dist/assets/index-BEsTmfrO.css +0 -1
- package/web/dist/assets/index-Ddb7bDN5.js +0 -75
- package/web/dist/assets/path-join-S7qkXQtP.js +0 -1
|
@@ -1,91 +1,180 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buildOrchestratorReminderTail, buildWorkerReminderTail } from './hive-team-guidance.js';
|
|
2
2
|
import { PtyInactiveError } from './http-errors.js';
|
|
3
3
|
import { createPostStartInputWriter } from './post-start-input-writer.js';
|
|
4
|
-
export const buildOrchestratorReportPayload = (workerName, text, artifacts) => {
|
|
5
|
-
const lines = [
|
|
4
|
+
export const buildOrchestratorReportPayload = (workerName, text, artifacts, workflowsEnabled = false) => {
|
|
5
|
+
const lines = [`<hive-message kind="report" from="@${workerName}">`, text];
|
|
6
6
|
for (const artifact of artifacts)
|
|
7
7
|
lines.push(`artifact: ${artifact}`);
|
|
8
|
-
lines.push('',
|
|
8
|
+
lines.push('</hive-message>', '', buildOrchestratorReminderTail(workflowsEnabled), '');
|
|
9
9
|
return lines.join('\n');
|
|
10
10
|
};
|
|
11
|
-
export const buildOrchestratorStatusPayload = (workerName, text, artifacts) => {
|
|
12
|
-
const lines = [
|
|
11
|
+
export const buildOrchestratorStatusPayload = (workerName, text, artifacts, workflowsEnabled = false) => {
|
|
12
|
+
const lines = [`<hive-message kind="status" from="@${workerName}">`, text];
|
|
13
13
|
for (const artifact of artifacts)
|
|
14
14
|
lines.push(`artifact: ${artifact}`);
|
|
15
|
-
lines.push('',
|
|
15
|
+
lines.push('</hive-message>', '', buildOrchestratorReminderTail(workflowsEnabled), '');
|
|
16
16
|
return lines.join('\n');
|
|
17
17
|
};
|
|
18
|
-
export const buildOrchestratorUserInputPayload = (text) => [text, '',
|
|
18
|
+
export const buildOrchestratorUserInputPayload = (text, workflowsEnabled = false) => [text, '', buildOrchestratorReminderTail(workflowsEnabled), ''].join('\n');
|
|
19
19
|
export const buildWorkerDispatchPayload = (fromAgentName, workerDescription, dispatchId, text) => [
|
|
20
|
-
|
|
20
|
+
`<hive-message kind="dispatch" from="@${fromAgentName}">`,
|
|
21
21
|
'',
|
|
22
|
-
|
|
22
|
+
`Your role: ${workerDescription}`,
|
|
23
23
|
'',
|
|
24
|
-
'
|
|
25
|
-
`-
|
|
26
|
-
'-
|
|
24
|
+
'You must:',
|
|
25
|
+
`- When the task is done, failed, blocked, or partially done, run \`team report "<result>" --dispatch ${dispatchId}\``,
|
|
26
|
+
'- Do not do unrelated work; report as soon as you are done',
|
|
27
27
|
'',
|
|
28
28
|
`dispatch_id: ${dispatchId}`,
|
|
29
29
|
'',
|
|
30
|
-
'
|
|
30
|
+
'Task:',
|
|
31
31
|
text,
|
|
32
|
+
'</hive-message>',
|
|
32
33
|
'',
|
|
33
34
|
buildWorkerReminderTail(dispatchId),
|
|
34
35
|
'',
|
|
35
36
|
].join('\n');
|
|
36
37
|
export const buildWorkerCancelPayload = (dispatchId, reason) => [
|
|
37
|
-
|
|
38
|
+
`<hive-message kind="cancel" dispatch="${dispatchId}">`,
|
|
38
39
|
'',
|
|
39
|
-
'
|
|
40
|
+
'Stop working on this dispatch and do not call team report for it.',
|
|
40
41
|
'',
|
|
41
|
-
'
|
|
42
|
+
'Cancellation reason:',
|
|
42
43
|
reason,
|
|
44
|
+
'</hive-message>',
|
|
43
45
|
'',
|
|
44
46
|
].join('\n');
|
|
45
|
-
export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getWorkspaceId, registry, syncRun, }) => {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
47
|
+
export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getWorkspaceId, registry, syncRun, getWorkflowsEnabled, }) => {
|
|
48
|
+
const workflowsEnabled = () => getWorkflowsEnabled?.() ?? false;
|
|
49
|
+
const chains = new Map();
|
|
50
|
+
const getChain = (agentId) => {
|
|
51
|
+
let chain = chains.get(agentId);
|
|
52
|
+
if (!chain) {
|
|
53
|
+
chain = { busy: false, queue: [] };
|
|
54
|
+
chains.set(agentId, chain);
|
|
55
|
+
}
|
|
56
|
+
return chain;
|
|
57
|
+
};
|
|
58
|
+
const resolveActiveRun = (workspaceId, agentId) => registry
|
|
59
|
+
.list()
|
|
60
|
+
.filter((item) => item.agentId === agentId && getWorkspaceId(item.agentId) === workspaceId)
|
|
61
|
+
.sort((left, right) => right.startedAt - left.startedAt)
|
|
62
|
+
.find((item) => {
|
|
63
|
+
const status = syncRun(item).status;
|
|
64
|
+
return status === 'starting' || status === 'running';
|
|
65
|
+
});
|
|
66
|
+
// Synchronously enforce requireActiveRun (so writeSendPrompt still throws in
|
|
67
|
+
// the caller's stack when there is no live run), then return a thunk that
|
|
68
|
+
// re-resolves the run at EXECUTION time and performs the actual write,
|
|
69
|
+
// returning a promise that settles when the paste→submit sequence is done.
|
|
70
|
+
const prepareWrite = (workspaceId, agentId, text, input) => {
|
|
71
|
+
if (!resolveActiveRun(workspaceId, agentId)) {
|
|
56
72
|
if (input.requireActiveRun) {
|
|
57
73
|
throw new PtyInactiveError(`No active run for agent: ${agentId}`);
|
|
58
74
|
}
|
|
59
|
-
return;
|
|
75
|
+
return () => Promise.resolve();
|
|
60
76
|
}
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
if (
|
|
64
|
-
|
|
77
|
+
return () => {
|
|
78
|
+
const run = resolveActiveRun(workspaceId, agentId);
|
|
79
|
+
if (!run) {
|
|
80
|
+
if (input.requireActiveRun) {
|
|
81
|
+
throw new PtyInactiveError(`No active run for agent: ${agentId}`);
|
|
82
|
+
}
|
|
83
|
+
return Promise.resolve();
|
|
65
84
|
}
|
|
66
|
-
|
|
85
|
+
try {
|
|
86
|
+
const config = getLaunchConfig(workspaceId, agentId);
|
|
87
|
+
if (agentManager && config) {
|
|
88
|
+
return (createPostStartInputWriter(agentManager, config.interactiveCommand ?? config.command)(run.runId, text).catch((error) => {
|
|
89
|
+
throw new PtyInactiveError(error instanceof Error ? error.message : String(error));
|
|
90
|
+
}) ?? Promise.resolve());
|
|
91
|
+
}
|
|
67
92
|
agentManager?.writeInput(run.runId, text);
|
|
93
|
+
return Promise.resolve();
|
|
68
94
|
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
throw new PtyInactiveError(error instanceof Error ? error.message : String(error));
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
const settle = (agentId, promise) => promise.finally(() => {
|
|
101
|
+
const chain = chains.get(agentId);
|
|
102
|
+
if (!chain)
|
|
103
|
+
return;
|
|
104
|
+
chain.busy = false;
|
|
105
|
+
drain(agentId);
|
|
106
|
+
});
|
|
107
|
+
const runQueuedWrite = (agentId, write) => {
|
|
108
|
+
try {
|
|
109
|
+
void settle(agentId, write.run()).then(write.resolve, write.reject);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
write.reject(error);
|
|
113
|
+
const chain = chains.get(agentId);
|
|
114
|
+
if (chain)
|
|
115
|
+
chain.busy = false;
|
|
116
|
+
drain(agentId);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
function drain(agentId) {
|
|
120
|
+
const chain = chains.get(agentId);
|
|
121
|
+
if (!chain)
|
|
122
|
+
return;
|
|
123
|
+
if (chain.busy)
|
|
124
|
+
return;
|
|
125
|
+
const next = chain.queue.shift();
|
|
126
|
+
if (!next) {
|
|
127
|
+
chains.delete(agentId);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
chain.busy = true;
|
|
131
|
+
runQueuedWrite(agentId, next);
|
|
132
|
+
}
|
|
133
|
+
const writeToActiveAgentRun = (workspaceId, agentId, text, input = {}) => {
|
|
134
|
+
const thunk = prepareWrite(workspaceId, agentId, text, input);
|
|
135
|
+
const chain = getChain(agentId);
|
|
136
|
+
if (chain.busy) {
|
|
137
|
+
return new Promise((resolve, reject) => {
|
|
138
|
+
chain.queue.push({ reject, resolve, run: thunk });
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
chain.busy = true;
|
|
142
|
+
try {
|
|
143
|
+
return settle(agentId, thunk()); // uncontended: run now; immediate failures still throw
|
|
69
144
|
}
|
|
70
145
|
catch (error) {
|
|
71
|
-
|
|
146
|
+
chain.busy = false;
|
|
147
|
+
drain(agentId);
|
|
148
|
+
throw error;
|
|
72
149
|
}
|
|
73
150
|
};
|
|
151
|
+
const swallowQueuedFailure = (promise) => {
|
|
152
|
+
void promise.catch(() => {
|
|
153
|
+
// Deferred prompt writes can fail if the PTY exits while queued. Calls
|
|
154
|
+
// that require foreground error reporting use writeSendPrompt's promise.
|
|
155
|
+
});
|
|
156
|
+
};
|
|
74
157
|
return {
|
|
75
158
|
writeReportPrompt(workspaceId, workerName, text, artifacts, input = {}) {
|
|
76
|
-
writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorReportPayload(workerName, text, artifacts), input);
|
|
159
|
+
swallowQueuedFailure(writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorReportPayload(workerName, text, artifacts, workflowsEnabled()), input));
|
|
77
160
|
},
|
|
78
161
|
writeStatusPrompt(workspaceId, workerName, text, artifacts, input = {}) {
|
|
79
|
-
writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorStatusPayload(workerName, text, artifacts), input);
|
|
162
|
+
swallowQueuedFailure(writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorStatusPayload(workerName, text, artifacts, workflowsEnabled()), input));
|
|
80
163
|
},
|
|
81
164
|
writeSendPrompt(workspaceId, workerId, dispatchId, fromAgentName, workerDescription, text) {
|
|
82
|
-
writeToActiveAgentRun(workspaceId, workerId, buildWorkerDispatchPayload(fromAgentName, workerDescription, dispatchId, text), { requireActiveRun: true });
|
|
165
|
+
return writeToActiveAgentRun(workspaceId, workerId, buildWorkerDispatchPayload(fromAgentName, workerDescription, dispatchId, text), { requireActiveRun: true });
|
|
83
166
|
},
|
|
84
167
|
writeCancelPrompt(workspaceId, workerId, dispatchId, reason, input = {}) {
|
|
85
|
-
writeToActiveAgentRun(workspaceId, workerId, buildWorkerCancelPayload(dispatchId, reason), input);
|
|
168
|
+
swallowQueuedFailure(writeToActiveAgentRun(workspaceId, workerId, buildWorkerCancelPayload(dispatchId, reason), input));
|
|
86
169
|
},
|
|
87
170
|
writeUserInputPrompt(workspaceId, text) {
|
|
88
|
-
writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorUserInputPayload(text));
|
|
171
|
+
swallowQueuedFailure(writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorUserInputPayload(text, workflowsEnabled())));
|
|
172
|
+
},
|
|
173
|
+
/** Generic: deliver an opaque text block to a specific agent's PTY.
|
|
174
|
+
* Used by the workflow runner to notify the triggering orchestrator
|
|
175
|
+
* when a run finishes (mirrors Claude Code's <task-notification>). */
|
|
176
|
+
writeSystemMessageToAgent(workspaceId, agentId, text) {
|
|
177
|
+
swallowQueuedFailure(writeToActiveAgentRun(workspaceId, agentId, text));
|
|
89
178
|
},
|
|
90
179
|
};
|
|
91
180
|
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate a 5-field cron string and return its next fire time (ms-epoch).
|
|
3
|
+
* Throws BadRequestError on an unparseable expression. Shared by the UI
|
|
4
|
+
* schedule PATCH route and the agent `team workflow schedule` route so cron
|
|
5
|
+
* validation behaves identically on both paths. UTC, like the scheduler.
|
|
6
|
+
*/
|
|
7
|
+
export declare const validateCronNextRunAt: (cron: string) => number;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CronExpressionParser } from 'cron-parser';
|
|
2
|
+
import { BadRequestError } from './http-errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* Validate a 5-field cron string and return its next fire time (ms-epoch).
|
|
5
|
+
* Throws BadRequestError on an unparseable expression. Shared by the UI
|
|
6
|
+
* schedule PATCH route and the agent `team workflow schedule` route so cron
|
|
7
|
+
* validation behaves identically on both paths. UTC, like the scheduler.
|
|
8
|
+
*/
|
|
9
|
+
export const validateCronNextRunAt = (cron) => {
|
|
10
|
+
try {
|
|
11
|
+
return CronExpressionParser.parse(cron, { currentDate: new Date(), tz: 'UTC' })
|
|
12
|
+
.next()
|
|
13
|
+
.toDate()
|
|
14
|
+
.getTime();
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
throw new BadRequestError(`Invalid cron expression: ${error instanceof Error ? error.message : String(error)}`);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -6,19 +6,27 @@ export interface DispatchRecord {
|
|
|
6
6
|
deliveredAt: number | null;
|
|
7
7
|
fromAgentId: string | null;
|
|
8
8
|
id: string;
|
|
9
|
+
label: string | null;
|
|
10
|
+
phase: string | null;
|
|
9
11
|
reportedAt: number | null;
|
|
10
12
|
reportText: string | null;
|
|
11
13
|
sequence: number | null;
|
|
12
14
|
status: DispatchStatus;
|
|
15
|
+
stepIndex: number | null;
|
|
13
16
|
submittedAt: number | null;
|
|
14
17
|
text: string;
|
|
15
18
|
toAgentId: string;
|
|
19
|
+
workflowRunId: string | null;
|
|
16
20
|
workspaceId: string;
|
|
17
21
|
}
|
|
18
22
|
interface CreateDispatchInput {
|
|
19
23
|
fromAgentId?: string;
|
|
24
|
+
label?: string;
|
|
25
|
+
phase?: string;
|
|
26
|
+
stepIndex?: number;
|
|
20
27
|
text: string;
|
|
21
28
|
toAgentId: string;
|
|
29
|
+
workflowRunId?: string;
|
|
22
30
|
workspaceId: string;
|
|
23
31
|
}
|
|
24
32
|
interface ReportDispatchInput {
|
|
@@ -50,6 +58,12 @@ export declare const createDispatchLedgerStore: (db: Database) => {
|
|
|
50
58
|
worker_id: string;
|
|
51
59
|
workspace_id: string;
|
|
52
60
|
}>;
|
|
61
|
+
listOpenDispatchIdsForRun: (runId: string) => string[];
|
|
62
|
+
listOpenWorkflowDispatchesForWorker: (workspaceId: string, workerId: string) => Array<{
|
|
63
|
+
dispatchId: string;
|
|
64
|
+
runId: string;
|
|
65
|
+
}>;
|
|
66
|
+
listWorkflowRunDispatches: (runId: string) => DispatchRecord[];
|
|
53
67
|
listWorkspaceDispatches: (workspaceId: string, options?: ListDispatchesOptions) => DispatchRecord[];
|
|
54
68
|
markCancelled: (input: CancelDispatchInput) => {
|
|
55
69
|
reportedAt: number;
|
|
@@ -60,10 +74,14 @@ export declare const createDispatchLedgerStore: (db: Database) => {
|
|
|
60
74
|
deliveredAt: number | null;
|
|
61
75
|
fromAgentId: string | null;
|
|
62
76
|
id: string;
|
|
77
|
+
label: string | null;
|
|
78
|
+
phase: string | null;
|
|
63
79
|
sequence: number | null;
|
|
80
|
+
stepIndex: number | null;
|
|
64
81
|
submittedAt: number | null;
|
|
65
82
|
text: string;
|
|
66
83
|
toAgentId: string;
|
|
84
|
+
workflowRunId: string | null;
|
|
67
85
|
workspaceId: string;
|
|
68
86
|
} | undefined;
|
|
69
87
|
markReportedByWorker: (input: ReportDispatchInput) => {
|
|
@@ -75,10 +93,14 @@ export declare const createDispatchLedgerStore: (db: Database) => {
|
|
|
75
93
|
deliveredAt: number | null;
|
|
76
94
|
fromAgentId: string | null;
|
|
77
95
|
id: string;
|
|
96
|
+
label: string | null;
|
|
97
|
+
phase: string | null;
|
|
78
98
|
sequence: number | null;
|
|
99
|
+
stepIndex: number | null;
|
|
79
100
|
submittedAt: number | null;
|
|
80
101
|
text: string;
|
|
81
102
|
toAgentId: string;
|
|
103
|
+
workflowRunId: string | null;
|
|
82
104
|
workspaceId: string;
|
|
83
105
|
} | undefined;
|
|
84
106
|
markSubmitted: (dispatchId: string) => void;
|
|
@@ -18,13 +18,17 @@ const toRecord = (row) => ({
|
|
|
18
18
|
deliveredAt: row.delivered_at,
|
|
19
19
|
fromAgentId: row.from_agent_id,
|
|
20
20
|
id: row.id,
|
|
21
|
+
label: row.label ?? null,
|
|
22
|
+
phase: row.phase ?? null,
|
|
21
23
|
reportedAt: row.reported_at,
|
|
22
24
|
reportText: row.report_text,
|
|
23
25
|
sequence: row.sequence,
|
|
24
26
|
status: row.status,
|
|
27
|
+
stepIndex: row.step_index,
|
|
25
28
|
submittedAt: row.submitted_at,
|
|
26
29
|
text: row.text,
|
|
27
30
|
toAgentId: row.to_agent_id,
|
|
31
|
+
workflowRunId: row.workflow_run_id,
|
|
28
32
|
workspaceId: row.workspace_id,
|
|
29
33
|
});
|
|
30
34
|
export const createDispatchLedgerStore = (db) => {
|
|
@@ -35,13 +39,17 @@ export const createDispatchLedgerStore = (db) => {
|
|
|
35
39
|
deliveredAt: null,
|
|
36
40
|
fromAgentId: input.fromAgentId ?? null,
|
|
37
41
|
id: randomUUID(),
|
|
42
|
+
label: input.label ?? null,
|
|
43
|
+
phase: input.phase ?? null,
|
|
38
44
|
reportedAt: null,
|
|
39
45
|
reportText: null,
|
|
40
46
|
sequence: null,
|
|
41
47
|
status: 'queued',
|
|
48
|
+
stepIndex: input.stepIndex ?? null,
|
|
42
49
|
submittedAt: null,
|
|
43
50
|
text: input.text,
|
|
44
51
|
toAgentId: input.toAgentId,
|
|
52
|
+
workflowRunId: input.workflowRunId ?? null,
|
|
45
53
|
workspaceId: input.workspaceId,
|
|
46
54
|
};
|
|
47
55
|
db.prepare(`INSERT INTO dispatches (
|
|
@@ -56,8 +64,12 @@ export const createDispatchLedgerStore = (db) => {
|
|
|
56
64
|
submitted_at,
|
|
57
65
|
reported_at,
|
|
58
66
|
report_text,
|
|
59
|
-
artifacts
|
|
60
|
-
|
|
67
|
+
artifacts,
|
|
68
|
+
workflow_run_id,
|
|
69
|
+
step_index,
|
|
70
|
+
phase,
|
|
71
|
+
label
|
|
72
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.workspaceId, record.fromAgentId, record.toAgentId, record.text, record.status, record.createdAt, record.deliveredAt, record.submittedAt, record.reportedAt, record.reportText, JSON.stringify(record.artifacts), record.workflowRunId, record.stepIndex, record.phase, record.label);
|
|
61
73
|
return record;
|
|
62
74
|
};
|
|
63
75
|
const deleteDispatch = (dispatchId) => {
|
|
@@ -175,8 +187,41 @@ export const createDispatchLedgerStore = (db) => {
|
|
|
175
187
|
db.prepare('DELETE FROM dispatches WHERE workspace_id = ?').run(workspaceId);
|
|
176
188
|
};
|
|
177
189
|
const deleteWorkerDispatches = (workspaceId, workerId) => {
|
|
178
|
-
|
|
190
|
+
// Preserve workflow-run dispatch history (`workflow_run_id IS NOT NULL`)
|
|
191
|
+
// so the UI's run-detail timeline can still show what each ephemeral
|
|
192
|
+
// worker did after the worker itself is dismissed. Orchestrator-issued
|
|
193
|
+
// dispatches are tied to the worker identity and are dropped with it.
|
|
194
|
+
db.prepare(`DELETE FROM dispatches
|
|
195
|
+
WHERE workspace_id = ? AND to_agent_id = ? AND workflow_run_id IS NULL`).run(workspaceId, workerId);
|
|
179
196
|
};
|
|
197
|
+
// Every dispatch fired by a workflow run carries the run id (M1-B added the
|
|
198
|
+
// column; M2-C plumbs it through). This is the timeline query the UI uses to
|
|
199
|
+
// explode a run row into per-worker activity.
|
|
200
|
+
const listWorkflowRunDispatches = (runId) => {
|
|
201
|
+
const rows = db
|
|
202
|
+
.prepare('SELECT * FROM dispatches WHERE workflow_run_id = ? ORDER BY sequence, created_at')
|
|
203
|
+
.all(runId);
|
|
204
|
+
return rows.map(toRecord);
|
|
205
|
+
};
|
|
206
|
+
// Open dispatch ids tied to a workflow run — drives the runner's stop path
|
|
207
|
+
// (each id gets a notifyCancel so the runner's await rejects).
|
|
208
|
+
const listOpenDispatchIdsForRun = (runId) => db
|
|
209
|
+
.prepare(`SELECT id FROM dispatches
|
|
210
|
+
WHERE workflow_run_id = ? AND status IN ('queued', 'submitted')`)
|
|
211
|
+
.all(runId).map((row) => row.id);
|
|
212
|
+
// Open workflow-tagged dispatches addressed to a specific worker. Drives the
|
|
213
|
+
// PTY-exit cancel path (TIER 1 #1): when a workflow-spawned worker dies
|
|
214
|
+
// without calling `team report`, the runner's `awaitReport` would otherwise
|
|
215
|
+
// hang for DEFAULT_TIMEOUT_MS (10 min). The exit handler enumerates these
|
|
216
|
+
// and `notifyCancel`s each so the surrounding `agent()`/`parallel`/`pipeline`
|
|
217
|
+
// sees an immediate reject.
|
|
218
|
+
const listOpenWorkflowDispatchesForWorker = (workspaceId, workerId) => db
|
|
219
|
+
.prepare(`SELECT id, workflow_run_id FROM dispatches
|
|
220
|
+
WHERE workspace_id = ?
|
|
221
|
+
AND to_agent_id = ?
|
|
222
|
+
AND workflow_run_id IS NOT NULL
|
|
223
|
+
AND status IN ('queued', 'submitted')`)
|
|
224
|
+
.all(workspaceId, workerId).map((row) => ({ dispatchId: row.id, runId: row.workflow_run_id }));
|
|
180
225
|
return {
|
|
181
226
|
createDispatch,
|
|
182
227
|
deleteDispatch,
|
|
@@ -185,6 +230,9 @@ export const createDispatchLedgerStore = (db) => {
|
|
|
185
230
|
findOpenDispatch,
|
|
186
231
|
findOpenDispatchById,
|
|
187
232
|
listOpenDispatchKinds,
|
|
233
|
+
listOpenDispatchIdsForRun,
|
|
234
|
+
listOpenWorkflowDispatchesForWorker,
|
|
235
|
+
listWorkflowRunDispatches,
|
|
188
236
|
listWorkspaceDispatches,
|
|
189
237
|
markCancelled,
|
|
190
238
|
markReportedByWorker,
|
|
@@ -4,7 +4,7 @@ import { TASKS_RELATIVE_PATH } from './tasks-file.js';
|
|
|
4
4
|
const TASKS_HEAD_LIMIT = 1024;
|
|
5
5
|
const formatWorkers = (workers) => {
|
|
6
6
|
if (workers.length === 0)
|
|
7
|
-
return ['-
|
|
7
|
+
return ['- (no other workers)'];
|
|
8
8
|
return workers.map((worker) => `- ${worker.name} (${worker.role}, ${worker.status}, pending_task_count: ${worker.pendingTaskCount})`);
|
|
9
9
|
};
|
|
10
10
|
const formatRestartWindow = (messages) => {
|
|
@@ -12,18 +12,18 @@ const formatRestartWindow = (messages) => {
|
|
|
12
12
|
return message.type === 'send';
|
|
13
13
|
});
|
|
14
14
|
if (sends.length === 0)
|
|
15
|
-
return ['-
|
|
15
|
+
return ['- no new dispatches during the restart'];
|
|
16
16
|
return sends.slice(-5).map((message) => `- send -> ${message.to}: ${message.text}`);
|
|
17
17
|
};
|
|
18
18
|
export const buildEnvSyncMessage = ({ agent, tasksContent, workers, workspace, restartWindowMessages, }) => wrapSystemMessage([
|
|
19
|
-
'
|
|
20
|
-
`-
|
|
21
|
-
'-
|
|
19
|
+
'Hive just restarted you. Environment changes during the restart:',
|
|
20
|
+
`- Current workspace: ${workspace.name}`,
|
|
21
|
+
'- Existing workers:',
|
|
22
22
|
...formatWorkers(workers),
|
|
23
|
-
`- ${TASKS_RELATIVE_PATH}
|
|
24
|
-
tasksContent.slice(0, TASKS_HEAD_LIMIT) || '(
|
|
23
|
+
`- Current ${TASKS_RELATIVE_PATH} contents:`,
|
|
24
|
+
tasksContent.slice(0, TASKS_HEAD_LIMIT) || '(empty)',
|
|
25
25
|
...formatRestartWindow(restartWindowMessages),
|
|
26
|
-
agent.role === 'orchestrator' ? '- Hive worker
|
|
26
|
+
agent.role === 'orchestrator' ? '- Hive worker dispatch rules:' : '- Hive worker boundaries:',
|
|
27
27
|
...getHiveTeamRules(agent).map((rule) => ` - ${rule}`),
|
|
28
|
-
|
|
28
|
+
`Continue. If unsure, run team list / Read ${TASKS_RELATIVE_PATH} to self-check, or ask the user.`,
|
|
29
29
|
].join('\n'));
|
|
@@ -112,7 +112,11 @@ const windowsPick = async (run) => {
|
|
|
112
112
|
|
|
113
113
|
`Add-Type -AssemblyName System.Drawing` is required because Point /
|
|
114
114
|
Size live in System.Drawing.dll, not System.Windows.Forms.dll. */
|
|
115
|
+
// PS 5.1 on zh-CN Windows defaults [Console]::OutputEncoding to cp936/GBK;
|
|
116
|
+
// Node decodes stdout as UTF-8 and CJK paths arrive mojibake'd. Force UTF-8
|
|
117
|
+
// before any output. No-op on PS 7+ which already defaults to UTF-8.
|
|
115
118
|
const script = [
|
|
119
|
+
'[Console]::OutputEncoding = [System.Text.Encoding]::UTF8',
|
|
116
120
|
'Add-Type -AssemblyName System.Windows.Forms',
|
|
117
121
|
'Add-Type -AssemblyName System.Drawing',
|
|
118
122
|
'$owner = New-Object System.Windows.Forms.Form',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { homedir } from 'node:os';
|
|
2
2
|
import { isAbsolute, relative, resolve, sep } from 'node:path';
|
|
3
|
+
import { realpathNative } from './path-canonicalization.js';
|
|
3
4
|
/**
|
|
4
5
|
* Root directory the FS-browse API is allowed to reveal. We sandbox to
|
|
5
6
|
* `$HOME` (override via `HIVE_FS_BROWSE_ROOT` for tests). Anything outside
|
|
@@ -7,7 +8,19 @@ import { isAbsolute, relative, resolve, sep } from 'node:path';
|
|
|
7
8
|
*/
|
|
8
9
|
export const getFsBrowseRoot = () => {
|
|
9
10
|
const override = process.env.HIVE_FS_BROWSE_ROOT;
|
|
10
|
-
|
|
11
|
+
const root = override && override.length > 0 ? resolve(override) : resolve(homedir());
|
|
12
|
+
try {
|
|
13
|
+
return realpathNative(root);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return root;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const isResolvedPathWithinRoot = (rootPath, candidatePath) => {
|
|
20
|
+
if (candidatePath === rootPath)
|
|
21
|
+
return true;
|
|
22
|
+
const rel = relative(rootPath, candidatePath);
|
|
23
|
+
return rel !== '..' && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
|
|
11
24
|
};
|
|
12
25
|
/**
|
|
13
26
|
* True when `candidatePath` is `rootPath` itself or a descendant of it.
|
|
@@ -16,10 +29,26 @@ export const getFsBrowseRoot = () => {
|
|
|
16
29
|
* isPathWithinRoot so the semantics match a project we already trust.
|
|
17
30
|
*/
|
|
18
31
|
export const isPathWithinRoot = (rootPath, candidatePath) => {
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
const lexicalRoot = resolve(rootPath);
|
|
33
|
+
const lexicalCandidate = resolve(candidatePath);
|
|
34
|
+
let resolvedRoot = lexicalRoot;
|
|
35
|
+
let resolvedCandidate = lexicalCandidate;
|
|
36
|
+
try {
|
|
37
|
+
resolvedRoot = realpathNative(resolvedRoot);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Missing / inaccessible roots are handled by the caller's readdir/stat path.
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
resolvedCandidate = realpathNative(resolvedCandidate);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Non-existent children still need lexical sandboxing for "create later"
|
|
47
|
+
// probes; existing symlinks/junctions use the realpath branch above.
|
|
48
|
+
if (isResolvedPathWithinRoot(lexicalRoot, lexicalCandidate)) {
|
|
49
|
+
resolvedRoot = lexicalRoot;
|
|
50
|
+
resolvedCandidate = lexicalCandidate;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return isResolvedPathWithinRoot(resolvedRoot, resolvedCandidate);
|
|
25
54
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AgentSummary } from '../shared/types.js';
|
|
2
|
+
import { type WorkflowCliPolicy } from './workflow-cli-policy.js';
|
|
2
3
|
/**
|
|
3
4
|
* Tail reminder appended to every message that flows INTO the orchestrator
|
|
4
5
|
* (worker reports, worker status updates, user chat input). Re-anchors the
|
|
@@ -11,10 +12,12 @@ import type { AgentSummary } from '../shared/types.js';
|
|
|
11
12
|
* banner noise after a few occurrences, but `<...-system-reminder>` tags
|
|
12
13
|
* mirror the out-of-band envelope LLMs are trained to attend to; placement
|
|
13
14
|
* at the tail (right before the agent's reply turn) maximizes recency
|
|
14
|
-
* weighting; phrasing as a
|
|
15
|
-
*
|
|
15
|
+
* weighting; phrasing as a short action menu is more actionable than abstract
|
|
16
|
+
* identity restatement. Kept deliberately SHORT — the full command syntax and
|
|
17
|
+
* the workflow DSL live in `.hive/PROTOCOL.md`, which agents re-read on demand.
|
|
18
|
+
* A long banner on every turn is itself the noise this envelope exists to beat.
|
|
16
19
|
*/
|
|
17
|
-
export declare const
|
|
20
|
+
export declare const buildOrchestratorReminderTail: (workflowsEnabled: boolean) => string;
|
|
18
21
|
/**
|
|
19
22
|
* Tail reminder appended to dispatches sent TO a worker. Reinforces the
|
|
20
23
|
* worker identity (so the agent does not regress into its normal CLI
|
|
@@ -22,12 +25,14 @@ export declare const ORCHESTRATOR_REMINDER_TAIL: string;
|
|
|
22
25
|
* with dispatch_id pre-bound.
|
|
23
26
|
*/
|
|
24
27
|
export declare const buildWorkerReminderTail: (dispatchId: string) => string;
|
|
25
|
-
export declare const getHiveTeamRules: (agent: Pick<AgentSummary, "role"
|
|
28
|
+
export declare const getHiveTeamRules: (agent: Pick<AgentSummary, "role">, workflowsEnabled?: boolean, autostaffEnabled?: boolean) => readonly string[];
|
|
26
29
|
/**
|
|
27
30
|
* Workspace-local protocol cheat sheet written to `.hive/PROTOCOL.md`. Agents
|
|
28
31
|
* are explicitly trained to look at project root markdown when confused, so
|
|
29
32
|
* keeping a single canonical doc next to `.hive/tasks.md` doubles as a
|
|
30
33
|
* "cat-recover" path when both the startup prompt and the in-message
|
|
31
|
-
* reminders fail to anchor.
|
|
34
|
+
* reminders fail to anchor. This is also the single home of the full command
|
|
35
|
+
* syntax and the workflow DSL reference — the always-on injections only carry
|
|
36
|
+
* the lean core rules and point here.
|
|
32
37
|
*/
|
|
33
|
-
export declare const buildProtocolDoc: () => string;
|
|
38
|
+
export declare const buildProtocolDoc: (cliPolicy?: WorkflowCliPolicy, workflowsEnabled?: boolean, autostaffEnabled?: boolean) => string;
|