@tt-a1i/hive 1.4.4 → 1.6.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.
Files changed (180) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.en.md +21 -0
  3. package/README.md +16 -0
  4. package/assets/qq-group.jpg +0 -0
  5. package/dist/bin/team.cmd +1 -0
  6. package/dist/src/cli/hive-update.d.ts +45 -17
  7. package/dist/src/cli/hive-update.js +63 -25
  8. package/dist/src/cli/hive.d.ts +25 -0
  9. package/dist/src/cli/hive.js +41 -3
  10. package/dist/src/cli/team.d.ts +1 -0
  11. package/dist/src/cli/team.js +216 -3
  12. package/dist/src/server/agent-command-resolver.js +3 -19
  13. package/dist/src/server/agent-manager-support.d.ts +2 -2
  14. package/dist/src/server/agent-manager-support.js +98 -24
  15. package/dist/src/server/agent-run-starter.d.ts +6 -1
  16. package/dist/src/server/agent-run-starter.js +9 -2
  17. package/dist/src/server/agent-run-store.d.ts +1 -1
  18. package/dist/src/server/agent-runtime-close.d.ts +1 -0
  19. package/dist/src/server/agent-runtime-close.js +25 -1
  20. package/dist/src/server/agent-runtime-contract.d.ts +12 -1
  21. package/dist/src/server/agent-runtime-stop-run.d.ts +1 -1
  22. package/dist/src/server/agent-runtime-stop-run.js +4 -1
  23. package/dist/src/server/agent-runtime.d.ts +2 -1
  24. package/dist/src/server/agent-runtime.js +14 -3
  25. package/dist/src/server/agent-startup-instructions.d.ts +7 -1
  26. package/dist/src/server/agent-startup-instructions.js +17 -9
  27. package/dist/src/server/agent-stdin-dispatcher.d.ts +25 -5
  28. package/dist/src/server/agent-stdin-dispatcher.js +141 -40
  29. package/dist/src/server/cron-util.d.ts +7 -0
  30. package/dist/src/server/cron-util.js +19 -0
  31. package/dist/src/server/dispatch-ledger-store.d.ts +22 -0
  32. package/dist/src/server/dispatch-ledger-store.js +51 -3
  33. package/dist/src/server/env-sync-message.js +9 -9
  34. package/dist/src/server/feature-flags.d.ts +42 -0
  35. package/dist/src/server/feature-flags.js +24 -0
  36. package/dist/src/server/fs-pick-folder.js +4 -0
  37. package/dist/src/server/fs-sandbox.js +36 -7
  38. package/dist/src/server/hive-team-guidance.d.ts +12 -6
  39. package/dist/src/server/hive-team-guidance.js +253 -71
  40. package/dist/src/server/live-run-registry.d.ts +1 -0
  41. package/dist/src/server/live-run-registry.js +1 -1
  42. package/dist/src/server/open-target-commands.js +5 -6
  43. package/dist/src/server/orchestrator-autostart.d.ts +12 -0
  44. package/dist/src/server/orchestrator-autostart.js +15 -13
  45. package/dist/src/server/path-canonicalization.d.ts +3 -0
  46. package/dist/src/server/path-canonicalization.js +29 -0
  47. package/dist/src/server/platform-path.d.ts +3 -0
  48. package/dist/src/server/platform-path.js +13 -0
  49. package/dist/src/server/post-start-input-writer.d.ts +1 -1
  50. package/dist/src/server/post-start-input-writer.js +110 -13
  51. package/dist/src/server/preset-launch-support.d.ts +1 -1
  52. package/dist/src/server/preset-launch-support.js +33 -2
  53. package/dist/src/server/recovery-summary.d.ts +5 -1
  54. package/dist/src/server/recovery-summary.js +18 -17
  55. package/dist/src/server/report-outbox-store.d.ts +36 -0
  56. package/dist/src/server/report-outbox-store.js +33 -0
  57. package/dist/src/server/restart-policy-support.d.ts +5 -1
  58. package/dist/src/server/restart-policy-support.js +9 -1
  59. package/dist/src/server/restart-policy.d.ts +6 -2
  60. package/dist/src/server/restart-policy.js +51 -31
  61. package/dist/src/server/role-template-store.d.ts +1 -0
  62. package/dist/src/server/role-template-store.js +11 -1
  63. package/dist/src/server/route-types.d.ts +43 -0
  64. package/dist/src/server/routes-runtime.js +2 -1
  65. package/dist/src/server/routes-settings.js +76 -0
  66. package/dist/src/server/routes-tasks.js +23 -0
  67. package/dist/src/server/routes-team.js +211 -1
  68. package/dist/src/server/routes-workflow-schedules.d.ts +2 -0
  69. package/dist/src/server/routes-workflow-schedules.js +58 -0
  70. package/dist/src/server/routes-workflows.d.ts +2 -0
  71. package/dist/src/server/routes-workflows.js +83 -0
  72. package/dist/src/server/routes-workspaces.js +5 -0
  73. package/dist/src/server/routes.js +4 -0
  74. package/dist/src/server/runtime-restart-policy.d.ts +3 -1
  75. package/dist/src/server/runtime-restart-policy.js +2 -1
  76. package/dist/src/server/runtime-store-contract.d.ts +125 -0
  77. package/dist/src/server/runtime-store-contract.js +1 -0
  78. package/dist/src/server/runtime-store-helpers.d.ts +11 -0
  79. package/dist/src/server/runtime-store-helpers.js +106 -2
  80. package/dist/src/server/runtime-store-workflows.d.ts +6 -0
  81. package/dist/src/server/runtime-store-workflows.js +108 -0
  82. package/dist/src/server/runtime-store.d.ts +3 -72
  83. package/dist/src/server/runtime-store.js +71 -4
  84. package/dist/src/server/session-capture-codex.d.ts +3 -3
  85. package/dist/src/server/session-capture-codex.js +9 -7
  86. package/dist/src/server/session-capture-gemini.d.ts +1 -1
  87. package/dist/src/server/session-capture-gemini.js +6 -3
  88. package/dist/src/server/settings-store.d.ts +3 -0
  89. package/dist/src/server/settings-store.js +1 -0
  90. package/dist/src/server/sqlite-schema-v19.d.ts +2 -0
  91. package/dist/src/server/sqlite-schema-v19.js +17 -0
  92. package/dist/src/server/sqlite-schema-v20.d.ts +2 -0
  93. package/dist/src/server/sqlite-schema-v20.js +20 -0
  94. package/dist/src/server/sqlite-schema-v21.d.ts +2 -0
  95. package/dist/src/server/sqlite-schema-v21.js +20 -0
  96. package/dist/src/server/sqlite-schema.d.ts +1 -1
  97. package/dist/src/server/sqlite-schema.js +110 -1
  98. package/dist/src/server/system-message.d.ts +7 -0
  99. package/dist/src/server/system-message.js +8 -1
  100. package/dist/src/server/task-deps.d.ts +32 -0
  101. package/dist/src/server/task-deps.js +40 -0
  102. package/dist/src/server/tasks-file-watcher.d.ts +12 -1
  103. package/dist/src/server/tasks-file-watcher.js +128 -23
  104. package/dist/src/server/tasks-file.d.ts +3 -1
  105. package/dist/src/server/tasks-file.js +33 -9
  106. package/dist/src/server/tasks-websocket-server.js +13 -14
  107. package/dist/src/server/team-authz.d.ts +1 -1
  108. package/dist/src/server/team-authz.js +10 -1
  109. package/dist/src/server/team-autostaff.d.ts +16 -0
  110. package/dist/src/server/team-autostaff.js +16 -0
  111. package/dist/src/server/team-list-serializer.d.ts +1 -1
  112. package/dist/src/server/team-list-serializer.js +3 -1
  113. package/dist/src/server/team-operations.d.ts +21 -1
  114. package/dist/src/server/team-operations.js +183 -16
  115. package/dist/src/server/terminal-protocol.js +9 -3
  116. package/dist/src/server/terminal-stream-hub.js +16 -10
  117. package/dist/src/server/terminal-ws-server.js +10 -8
  118. package/dist/src/server/webhook-notifier.d.ts +34 -0
  119. package/dist/src/server/webhook-notifier.js +47 -0
  120. package/dist/src/server/websocket-upgrade-safety.d.ts +10 -0
  121. package/dist/src/server/websocket-upgrade-safety.js +35 -0
  122. package/dist/src/server/windows-command-line.d.ts +3 -0
  123. package/dist/src/server/windows-command-line.js +9 -0
  124. package/dist/src/server/windows-filename.d.ts +2 -0
  125. package/dist/src/server/windows-filename.js +33 -0
  126. package/dist/src/server/workflow-cli-policy.d.ts +60 -0
  127. package/dist/src/server/workflow-cli-policy.js +110 -0
  128. package/dist/src/server/workflow-dispatch-awaiter.d.ts +12 -0
  129. package/dist/src/server/workflow-dispatch-awaiter.js +80 -0
  130. package/dist/src/server/workflow-feature.d.ts +15 -0
  131. package/dist/src/server/workflow-feature.js +15 -0
  132. package/dist/src/server/workflow-http-serializers.d.ts +64 -0
  133. package/dist/src/server/workflow-http-serializers.js +58 -0
  134. package/dist/src/server/workflow-output-schema.d.ts +18 -0
  135. package/dist/src/server/workflow-output-schema.js +41 -0
  136. package/dist/src/server/workflow-run-log-store.d.ts +19 -0
  137. package/dist/src/server/workflow-run-log-store.js +45 -0
  138. package/dist/src/server/workflow-run-store.d.ts +50 -0
  139. package/dist/src/server/workflow-run-store.js +103 -0
  140. package/dist/src/server/workflow-runner.d.ts +147 -0
  141. package/dist/src/server/workflow-runner.js +411 -0
  142. package/dist/src/server/workflow-schedule-create.d.ts +14 -0
  143. package/dist/src/server/workflow-schedule-create.js +41 -0
  144. package/dist/src/server/workflow-schedule-store.d.ts +43 -0
  145. package/dist/src/server/workflow-schedule-store.js +112 -0
  146. package/dist/src/server/workflow-scheduler.d.ts +36 -0
  147. package/dist/src/server/workflow-scheduler.js +97 -0
  148. package/dist/src/server/workflow-script-loader.d.ts +34 -0
  149. package/dist/src/server/workflow-script-loader.js +106 -0
  150. package/dist/src/server/workspace-path-validation.js +16 -4
  151. package/dist/src/server/workspace-shell-runtime.d.ts +5 -0
  152. package/dist/src/server/workspace-shell-runtime.js +24 -2
  153. package/dist/src/server/workspace-store-contract.d.ts +4 -1
  154. package/dist/src/server/workspace-store-hydration.js +23 -7
  155. package/dist/src/server/workspace-store-mutations.js +2 -5
  156. package/dist/src/server/workspace-store-support.d.ts +4 -0
  157. package/dist/src/server/workspace-store-support.js +13 -1
  158. package/dist/src/server/workspace-store.js +38 -4
  159. package/dist/src/shared/types.d.ts +16 -1
  160. package/package.json +4 -2
  161. package/web/dist/assets/{AddWorkerDialog-DeZhTQLi.js → AddWorkerDialog-CGbaxu0T.js} +2 -2
  162. package/web/dist/assets/AddWorkspaceDialog-CNgExu6b.js +1 -0
  163. package/web/dist/assets/{FirstRunWizard-B5wLcat5.js → FirstRunWizard-DxGApUNc.js} +1 -1
  164. package/web/dist/assets/{MarketplaceDrawer-BC0eBOEW.js → MarketplaceDrawer-Bk6cpukn.js} +1 -1
  165. package/web/dist/assets/WhatsNewDialog-CSGzk-2U.js +1 -0
  166. package/web/dist/assets/WorkerModal-i2F3n3nZ.js +1 -0
  167. package/web/dist/assets/WorkspaceTaskDrawer-C_Ta_K13.js +1 -0
  168. package/web/dist/assets/WorkspaceTerminalPanels-VdDxtrQF.js +1 -0
  169. package/web/dist/assets/index-5zh61jMg.css +1 -0
  170. package/web/dist/assets/index-CAgGM6nb.js +75 -0
  171. package/web/dist/assets/path-join-7MR1s7b1.js +1 -0
  172. package/web/dist/index.html +2 -2
  173. package/web/dist/sw.js +1 -1
  174. package/web/dist/assets/AddWorkspaceDialog-DDpXNEKf.js +0 -1
  175. package/web/dist/assets/WorkerModal-BwMHq-Bi.js +0 -1
  176. package/web/dist/assets/WorkspaceTaskDrawer-CxvT4nqs.js +0 -1
  177. package/web/dist/assets/WorkspaceTerminalPanels-CvibsPSd.js +0 -1
  178. package/web/dist/assets/index-BEsTmfrO.css +0 -1
  179. package/web/dist/assets/index-Ddb7bDN5.js +0 -75
  180. package/web/dist/assets/path-join-S7qkXQtP.js +0 -1
@@ -0,0 +1,64 @@
1
+ import type { LiveAgentRun } from './agent-runtime-types.js';
2
+ import type { DispatchRecord } from './dispatch-ledger-store.js';
3
+ import type { WorkflowRunRecord } from './workflow-run-store.js';
4
+ import type { WorkflowScheduleRecord } from './workflow-schedule-store.js';
5
+ export declare const serializeLiveAgentRun: (run: LiveAgentRun) => {
6
+ agent_id: string;
7
+ exit_code: number | null;
8
+ output: string;
9
+ pid: number | null;
10
+ run_id: string;
11
+ started_at: number;
12
+ status: import("./agent-manager.js").RunStatus;
13
+ };
14
+ export declare const serializeWorkflowRun: (run: WorkflowRunRecord) => {
15
+ agent_count: number;
16
+ args: unknown;
17
+ created_at: number;
18
+ error: string | null;
19
+ finished_at: number | null;
20
+ id: string;
21
+ name: string;
22
+ parent_run_id: string | null;
23
+ phase: string | null;
24
+ result: unknown;
25
+ script_hash: string | null;
26
+ script_path: string;
27
+ started_at: number;
28
+ status: import("./workflow-run-store.js").WorkflowRunStatus;
29
+ workspace_id: string;
30
+ };
31
+ export declare const serializeWorkflowDispatch: (dispatch: DispatchRecord & {
32
+ lastPtyLine?: string;
33
+ }) => {
34
+ artifacts: string[];
35
+ created_at: number;
36
+ delivered_at: number | null;
37
+ from_agent_id: string | null;
38
+ id: string;
39
+ label: string | null;
40
+ last_pty_line: string | null;
41
+ phase: string | null;
42
+ reported_at: number | null;
43
+ report_text: string | null;
44
+ sequence: number | null;
45
+ status: import("./dispatch-ledger-store.js").DispatchStatus;
46
+ step_index: number | null;
47
+ submitted_at: number | null;
48
+ text: string;
49
+ to_agent_id: string;
50
+ workflow_run_id: string | null;
51
+ workspace_id: string;
52
+ };
53
+ export declare const serializeWorkflowSchedule: (schedule: WorkflowScheduleRecord) => {
54
+ args: unknown;
55
+ created_at: number;
56
+ cron: string;
57
+ enabled: boolean;
58
+ id: string;
59
+ last_run_at: number | null;
60
+ next_run_at: number;
61
+ script_path: string;
62
+ updated_at: number;
63
+ workspace_id: string;
64
+ };
@@ -0,0 +1,58 @@
1
+ export const serializeLiveAgentRun = (run) => ({
2
+ agent_id: run.agentId,
3
+ exit_code: run.exitCode,
4
+ output: run.output,
5
+ pid: run.pid,
6
+ run_id: run.runId,
7
+ started_at: run.startedAt,
8
+ status: run.status,
9
+ });
10
+ export const serializeWorkflowRun = (run) => ({
11
+ agent_count: run.agentCount,
12
+ args: run.args,
13
+ created_at: run.createdAt,
14
+ error: run.error,
15
+ finished_at: run.finishedAt,
16
+ id: run.id,
17
+ name: run.name,
18
+ parent_run_id: run.parentRunId,
19
+ phase: run.phase,
20
+ result: run.result,
21
+ script_hash: run.scriptHash,
22
+ script_path: run.scriptPath,
23
+ started_at: run.startedAt,
24
+ status: run.status,
25
+ workspace_id: run.workspaceId,
26
+ });
27
+ export const serializeWorkflowDispatch = (dispatch) => ({
28
+ artifacts: dispatch.artifacts,
29
+ created_at: dispatch.createdAt,
30
+ delivered_at: dispatch.deliveredAt,
31
+ from_agent_id: dispatch.fromAgentId,
32
+ id: dispatch.id,
33
+ label: dispatch.label,
34
+ last_pty_line: dispatch.lastPtyLine ?? null,
35
+ phase: dispatch.phase,
36
+ reported_at: dispatch.reportedAt,
37
+ report_text: dispatch.reportText,
38
+ sequence: dispatch.sequence,
39
+ status: dispatch.status,
40
+ step_index: dispatch.stepIndex,
41
+ submitted_at: dispatch.submittedAt,
42
+ text: dispatch.text,
43
+ to_agent_id: dispatch.toAgentId,
44
+ workflow_run_id: dispatch.workflowRunId,
45
+ workspace_id: dispatch.workspaceId,
46
+ });
47
+ export const serializeWorkflowSchedule = (schedule) => ({
48
+ args: schedule.args,
49
+ created_at: schedule.createdAt,
50
+ cron: schedule.cron,
51
+ enabled: schedule.enabled,
52
+ id: schedule.id,
53
+ last_run_at: schedule.lastRunAt,
54
+ next_run_at: schedule.nextRunAt,
55
+ script_path: schedule.scriptPath,
56
+ updated_at: schedule.updatedAt,
57
+ workspace_id: schedule.workspaceId,
58
+ });
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Opt-in structured output for the workflow DSL `agent()` call. When a script
3
+ * passes `outputSchema`, the runner appends an instruction asking the worker to
4
+ * end its report with a fenced ```json block, then parses that block so the
5
+ * script gets an object instead of brittle free-text. Kept deliberately LIGHT:
6
+ * no schema validation library, no retry — the keys are advisory, and a parse
7
+ * miss falls back to `{ text }` in the runner so a script can always branch on
8
+ * a missing field (the documented safe-default discipline).
9
+ */
10
+ /**
11
+ * Extract and parse the LAST fenced json block in a report. Workers often emit
12
+ * a reasoning block then a final answer block, so the last one wins. Returns
13
+ * null on no block / invalid JSON / a non-object payload (so the caller's
14
+ * `?? { text }` fallback fires) rather than throwing.
15
+ */
16
+ export declare const extractJsonBlock: (text: string) => Record<string, unknown> | null;
17
+ /** The terse instruction appended to the worker prompt when a schema is set. */
18
+ export declare const buildSchemaInstruction: (schema: Record<string, unknown>) => string;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Opt-in structured output for the workflow DSL `agent()` call. When a script
3
+ * passes `outputSchema`, the runner appends an instruction asking the worker to
4
+ * end its report with a fenced ```json block, then parses that block so the
5
+ * script gets an object instead of brittle free-text. Kept deliberately LIGHT:
6
+ * no schema validation library, no retry — the keys are advisory, and a parse
7
+ * miss falls back to `{ text }` in the runner so a script can always branch on
8
+ * a missing field (the documented safe-default discipline).
9
+ */
10
+ /**
11
+ * Extract and parse the LAST fenced json block in a report. Workers often emit
12
+ * a reasoning block then a final answer block, so the last one wins. Returns
13
+ * null on no block / invalid JSON / a non-object payload (so the caller's
14
+ * `?? { text }` fallback fires) rather than throwing.
15
+ */
16
+ export const extractJsonBlock = (text) => {
17
+ const matches = [...text.matchAll(/```(?:json)?[ \t]*\r?\n?([\s\S]*?)```/g)];
18
+ const last = matches.at(-1)?.[1];
19
+ if (last === undefined)
20
+ return null;
21
+ try {
22
+ const parsed = JSON.parse(last.trim());
23
+ return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)
24
+ ? parsed
25
+ : null;
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ };
31
+ /** The terse instruction appended to the worker prompt when a schema is set. */
32
+ export const buildSchemaInstruction = (schema) => {
33
+ const keys = Object.keys(schema);
34
+ const keyList = keys.length > 0 ? keys.join(', ') : '(see the task)';
35
+ return [
36
+ '',
37
+ 'When you are done, end your report with a single fenced ```json block as the LAST thing in your message.',
38
+ `That JSON object should use these keys: ${keyList}.`,
39
+ 'If you cannot determine a field, omit it — the caller treats a missing field as the safe default.',
40
+ ].join('\n');
41
+ };
@@ -0,0 +1,19 @@
1
+ import type { Database } from 'better-sqlite3';
2
+ export interface WorkflowRunLogRecord {
3
+ id: number;
4
+ runId: string;
5
+ ts: number;
6
+ message: string;
7
+ }
8
+ export declare const createWorkflowRunLogStore: (db: Database) => {
9
+ append(runId: string, message: string, ts?: number): void;
10
+ listForRun(runId: string): WorkflowRunLogRecord[];
11
+ /** Last `n` log messages for the run, oldest-first. Used by the
12
+ * onRunFinished reminder to include a short narrator tail in the
13
+ * orchestrator's completion notification without overwhelming it. */
14
+ tailForRun(runId: string, n: number): string[];
15
+ deleteForRun(runId: string): void;
16
+ /** Cascade hook: deleted alongside workflow_runs when a workspace
17
+ * is removed (TIER 1 #4 cascade). */
18
+ deleteForWorkspace(workspaceId: string): void;
19
+ };
@@ -0,0 +1,45 @@
1
+ /* TIER 2 #3 — narrator lane storage. `log()` calls from a workflow script
2
+ * append here; the Drawer polls listForRun(runId) alongside the dispatch
3
+ * timeline; the orchestrator's completion reminder splices in the last
4
+ * few lines so a run that finished with a long sequence of `log()` calls
5
+ * can be summarised back to the user.
6
+ *
7
+ * Append-only. No retention policy yet — TIER 3 work if log volume grows
8
+ * to where it matters; CC's Workflow tool keeps logs for the run's
9
+ * lifetime so we mirror that. */
10
+ export const createWorkflowRunLogStore = (db) => {
11
+ const insert = db.prepare('INSERT INTO workflow_run_logs (run_id, ts, message) VALUES (?, ?, ?)');
12
+ const listStmt = db.prepare('SELECT id, run_id, ts, message FROM workflow_run_logs WHERE run_id = ? ORDER BY id');
13
+ const tailStmt = db.prepare('SELECT message FROM workflow_run_logs WHERE run_id = ? ORDER BY id DESC LIMIT ?');
14
+ const deleteForRunStmt = db.prepare('DELETE FROM workflow_run_logs WHERE run_id = ?');
15
+ const deleteForWorkspaceStmt = db.prepare(`DELETE FROM workflow_run_logs
16
+ WHERE run_id IN (SELECT id FROM workflow_runs WHERE workspace_id = ?)`);
17
+ return {
18
+ append(runId, message, ts = Date.now()) {
19
+ insert.run(runId, ts, message);
20
+ },
21
+ listForRun(runId) {
22
+ return listStmt.all(runId).map((row) => ({
23
+ id: row.id,
24
+ runId: row.run_id,
25
+ ts: row.ts,
26
+ message: row.message,
27
+ }));
28
+ },
29
+ /** Last `n` log messages for the run, oldest-first. Used by the
30
+ * onRunFinished reminder to include a short narrator tail in the
31
+ * orchestrator's completion notification without overwhelming it. */
32
+ tailForRun(runId, n) {
33
+ const rows = tailStmt.all(runId, n);
34
+ return rows.map((r) => r.message).reverse();
35
+ },
36
+ deleteForRun(runId) {
37
+ deleteForRunStmt.run(runId);
38
+ },
39
+ /** Cascade hook: deleted alongside workflow_runs when a workspace
40
+ * is removed (TIER 1 #4 cascade). */
41
+ deleteForWorkspace(workspaceId) {
42
+ deleteForWorkspaceStmt.run(workspaceId);
43
+ },
44
+ };
45
+ };
@@ -0,0 +1,50 @@
1
+ import type { Database } from 'better-sqlite3';
2
+ export type WorkflowRunStatus = 'running' | 'completed' | 'failed' | 'interrupted' | 'stopped';
3
+ export interface WorkflowRunRecord {
4
+ id: string;
5
+ workspaceId: string;
6
+ scriptPath: string;
7
+ scriptHash: string | null;
8
+ name: string;
9
+ status: WorkflowRunStatus;
10
+ phase: string | null;
11
+ args: unknown;
12
+ result: unknown;
13
+ startedAt: number;
14
+ finishedAt: number | null;
15
+ error: string | null;
16
+ createdAt: number;
17
+ /** Count of `agent()` calls dispatched by this run. TIER 1 #14 — lets the
18
+ * Drawer show "12 agents" on the row without an extra round-trip per
19
+ * expand. Cheap subquery against the indexed workflow_run_id column. */
20
+ agentCount: number;
21
+ /** TIER 2 #5: parent workflow run id for nested workflow() calls.
22
+ * Null on top-level runs. The Drawer uses this to render a child run
23
+ * indented under its parent (and to collapse/expand together). */
24
+ parentRunId: string | null;
25
+ }
26
+ interface CreateRunInput {
27
+ workspaceId: string;
28
+ scriptPath: string;
29
+ name: string;
30
+ scriptHash?: string;
31
+ args?: unknown;
32
+ /** TIER 2 #5 — set when this run is being created from a nested
33
+ * workflow() call. Null/omitted for top-level runs. */
34
+ parentRunId?: string | null;
35
+ }
36
+ interface UpdateRunInput {
37
+ status?: WorkflowRunStatus;
38
+ phase?: string;
39
+ finishedAt?: number;
40
+ error?: string;
41
+ result?: unknown;
42
+ }
43
+ export declare const createWorkflowRunStore: (db: Database) => {
44
+ createRun: (input: CreateRunInput) => WorkflowRunRecord;
45
+ updateRun: (id: string, input: UpdateRunInput) => void;
46
+ getRun: (id: string) => WorkflowRunRecord | undefined;
47
+ listWorkspaceRuns: (workspaceId: string) => WorkflowRunRecord[];
48
+ markUnfinishedRunsInterrupted: () => void;
49
+ };
50
+ export {};
@@ -0,0 +1,103 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ const parseArgs = (value) => {
3
+ if (!value)
4
+ return null;
5
+ try {
6
+ return JSON.parse(value);
7
+ }
8
+ catch {
9
+ return null;
10
+ }
11
+ };
12
+ const toRecord = (row) => ({
13
+ id: row.id,
14
+ workspaceId: row.workspace_id,
15
+ scriptPath: row.script_path,
16
+ scriptHash: row.script_hash,
17
+ name: row.name,
18
+ status: row.status,
19
+ phase: row.phase,
20
+ args: parseArgs(row.args),
21
+ result: parseArgs(row.result),
22
+ startedAt: row.started_at,
23
+ finishedAt: row.finished_at,
24
+ error: row.error,
25
+ createdAt: row.created_at,
26
+ agentCount: Number(row.agent_count ?? 0),
27
+ parentRunId: row.parent_run_id ?? null,
28
+ });
29
+ // Selects every workflow_runs column plus the agent_count subquery; used by
30
+ // both getRun and listWorkspaceRuns so the WorkflowRunRecord shape is
31
+ // uniform regardless of which path produced it.
32
+ const SELECT_RUN_COLUMNS = 'workflow_runs.*, ' +
33
+ '(SELECT COUNT(*) FROM dispatches WHERE dispatches.workflow_run_id = workflow_runs.id) AS agent_count';
34
+ export const createWorkflowRunStore = (db) => {
35
+ const createRun = (input) => {
36
+ const now = Date.now();
37
+ const record = {
38
+ id: randomUUID(),
39
+ workspaceId: input.workspaceId,
40
+ scriptPath: input.scriptPath,
41
+ scriptHash: input.scriptHash ?? null,
42
+ name: input.name,
43
+ status: 'running',
44
+ phase: null,
45
+ args: input.args ?? null,
46
+ result: null,
47
+ startedAt: now,
48
+ finishedAt: null,
49
+ error: null,
50
+ createdAt: now,
51
+ agentCount: 0,
52
+ parentRunId: input.parentRunId ?? null,
53
+ };
54
+ db.prepare(`INSERT INTO workflow_runs (
55
+ id, workspace_id, script_path, script_hash, name, status, phase, args,
56
+ started_at, finished_at, error, created_at, parent_run_id
57
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.id, record.workspaceId, record.scriptPath, record.scriptHash, record.name, record.status, record.phase, record.args === null ? null : JSON.stringify(record.args), record.startedAt, record.finishedAt, record.error, record.createdAt, record.parentRunId);
58
+ return record;
59
+ };
60
+ const updateRun = (id, input) => {
61
+ const sets = [];
62
+ const values = [];
63
+ if (input.status !== undefined) {
64
+ sets.push('status = ?');
65
+ values.push(input.status);
66
+ }
67
+ if (input.phase !== undefined) {
68
+ sets.push('phase = ?');
69
+ values.push(input.phase);
70
+ }
71
+ if (input.finishedAt !== undefined) {
72
+ sets.push('finished_at = ?');
73
+ values.push(input.finishedAt);
74
+ }
75
+ if (input.error !== undefined) {
76
+ sets.push('error = ?');
77
+ values.push(input.error);
78
+ }
79
+ if (input.result !== undefined) {
80
+ sets.push('result = ?');
81
+ values.push(input.result === null ? null : JSON.stringify(input.result));
82
+ }
83
+ if (sets.length === 0)
84
+ return;
85
+ values.push(id);
86
+ db.prepare(`UPDATE workflow_runs SET ${sets.join(', ')} WHERE id = ?`).run(...values);
87
+ };
88
+ const getRun = (id) => {
89
+ const row = db
90
+ .prepare(`SELECT ${SELECT_RUN_COLUMNS} FROM workflow_runs WHERE id = ?`)
91
+ .get(id);
92
+ return row ? toRecord(row) : undefined;
93
+ };
94
+ const listWorkspaceRuns = (workspaceId) => db
95
+ .prepare(`SELECT ${SELECT_RUN_COLUMNS} FROM workflow_runs WHERE workspace_id = ? ORDER BY created_at DESC, id DESC`)
96
+ .all(workspaceId).map(toRecord);
97
+ // Boot sweep: a run still 'running' after a restart can never resume, so mark
98
+ // it interrupted (spec §13 — the UI offers Resume; we never auto-resume).
99
+ const markUnfinishedRunsInterrupted = () => {
100
+ db.prepare("UPDATE workflow_runs SET status = 'interrupted', finished_at = ? WHERE status = 'running'").run(Date.now());
101
+ };
102
+ return { createRun, updateRun, getRun, listWorkspaceRuns, markUnfinishedRunsInterrupted };
103
+ };
@@ -0,0 +1,147 @@
1
+ import type { WorkerRole } from '../shared/types.js';
2
+ import type { AgentLaunchConfigInput } from './agent-run-store.js';
3
+ import { type WorkflowCliPolicy } from './workflow-cli-policy.js';
4
+ import type { WorkflowDispatchAwaiter } from './workflow-dispatch-awaiter.js';
5
+ import type { WorkflowRunRecord, WorkflowRunStatus } from './workflow-run-store.js';
6
+ export interface RunWorkflowInput {
7
+ workspaceId: string;
8
+ scriptPath: string;
9
+ hivePort: string;
10
+ args?: unknown;
11
+ /** Agent (usually the orchestrator) that fired this workflow; the runner
12
+ * notifies its PTY when the run finishes. Optional — workflows fired by
13
+ * cron / UI have no triggering agent. */
14
+ triggeredByAgentId?: string;
15
+ /** TIER 2 #5 — set internally when the runner is spawning a nested
16
+ * workflow() from inside another run. External callers leave it
17
+ * undefined; the row goes in as a top-level run. */
18
+ parentRunId?: string | null;
19
+ }
20
+ export interface RunInlineWorkflowInput {
21
+ workspaceId: string;
22
+ source: string;
23
+ /** Synthetic path stamped into workflow_runs.script_path for display only;
24
+ * no file is read or written. Defaults to `<inline>` if omitted. */
25
+ scriptPath?: string;
26
+ hivePort: string;
27
+ args?: unknown;
28
+ triggeredByAgentId?: string;
29
+ }
30
+ /** TIER 2 #4 — narrow port the runner needs to clone a custom role
31
+ * into an ephemeral worker. Returns `undefined` if the name doesn't
32
+ * match any template (built-in or custom); the runner falls back to
33
+ * built-in-role semantics in that case. */
34
+ export interface RoleTemplateResolver {
35
+ findByName(name: string): {
36
+ name: string;
37
+ roleType: WorkerRole | 'orchestrator';
38
+ defaultCommand: string;
39
+ defaultArgs: string[];
40
+ } | undefined;
41
+ }
42
+ interface RunnerStorePort {
43
+ addWorkerWithLaunch: (workspaceId: string, input: {
44
+ name: string;
45
+ role: WorkerRole;
46
+ ephemeral: true;
47
+ spawnedBy: 'workflow';
48
+ }, launchConfig: AgentLaunchConfigInput) => {
49
+ id: string;
50
+ name: string;
51
+ };
52
+ startAgent: (workspaceId: string, agentId: string, input: {
53
+ hivePort: string;
54
+ }) => Promise<unknown>;
55
+ dispatchTaskByWorkerName: (workspaceId: string, workerName: string, text: string, input: {
56
+ fromAgentId: string;
57
+ hivePort: string;
58
+ workflowRunId: string;
59
+ stepIndex: number;
60
+ phase?: string;
61
+ label?: string;
62
+ }) => Promise<{
63
+ id: string;
64
+ }>;
65
+ deleteWorker: (workspaceId: string, workerId: string) => void;
66
+ }
67
+ /** TIER 2 #3 — narrator lane sink. The runner calls append(runId, message)
68
+ * every time the script invokes log(). Implementation lives in
69
+ * workflow-run-log-store; the port keeps the runner free of DB types. */
70
+ export interface WorkflowRunLogPort {
71
+ append(runId: string, message: string, ts?: number): void;
72
+ }
73
+ interface WorkflowRunStorePort {
74
+ createRun: (input: {
75
+ workspaceId: string;
76
+ scriptPath: string;
77
+ name: string;
78
+ scriptHash?: string;
79
+ args?: unknown;
80
+ parentRunId?: string | null;
81
+ }) => WorkflowRunRecord;
82
+ updateRun: (id: string, input: {
83
+ status?: WorkflowRunStatus;
84
+ phase?: string;
85
+ finishedAt?: number;
86
+ error?: string;
87
+ result?: unknown;
88
+ }) => void;
89
+ getRun: (id: string) => WorkflowRunRecord | undefined;
90
+ }
91
+ export interface WorkflowRunner {
92
+ /** Runs the script to completion; returns the FINAL record. Used by tests
93
+ * and any caller that wants to wait synchronously. */
94
+ runWorkflow: (input: RunWorkflowInput) => Promise<WorkflowRunRecord>;
95
+ /** Creates the run row + kicks off execution in the background; returns the
96
+ * INITIAL ('running') record. Used by the HTTP route so the response is
97
+ * fast — clients poll `getWorkflowRun(id)` for progress. */
98
+ startWorkflow: (input: RunWorkflowInput) => Promise<WorkflowRunRecord>;
99
+ /** Like startWorkflow but takes raw source (no file). Used by `team workflow
100
+ * run --stdin/--inline` so the orchestrator can fire workflows from a PTY
101
+ * without writing a file first — matches Claude Code's Workflow tool
102
+ * invocation model. */
103
+ startWorkflowInline: (input: RunInlineWorkflowInput) => Promise<WorkflowRunRecord>;
104
+ /** Cancel any in-flight `agent()` calls for `runId` (rejects their awaiters)
105
+ * and mark the next executeWorkflow catch as 'stopped' instead of 'failed'.
106
+ * Returns true if the run was running and got stopped, false otherwise. */
107
+ stopRun: (runId: string) => boolean;
108
+ }
109
+ interface ListDispatchesForStopPort {
110
+ listOpenDispatchIdsForRun: (runId: string) => string[];
111
+ }
112
+ export declare const createWorkflowRunner: (deps: {
113
+ store: RunnerStorePort;
114
+ workflowRunStore: WorkflowRunStorePort;
115
+ awaiter: WorkflowDispatchAwaiter;
116
+ dispatchPort: ListDispatchesForStopPort;
117
+ /** Resolves the workspace's on-disk path. The nested `workflow(name)` DSL
118
+ * call needs it to locate sibling scripts when the parent run was fired
119
+ * inline via `team workflow run --stdin` (TIER 1 #7) — its scriptPath is
120
+ * a synthetic `<inline>` token that dirname() can't traverse. */
121
+ resolveWorkspacePath: (workspaceId: string) => string;
122
+ /** TIER 2 #4 — used when agent() opts.agentType isn't a built-in role.
123
+ * The runner asks the resolver for a matching custom template; on hit
124
+ * the template's command + args replace the defaults, on miss the
125
+ * runner throws a clear error rather than silently spawning claude. */
126
+ roleTemplateResolver: RoleTemplateResolver;
127
+ /** TIER 2 #3 — sink for the script's `log()` calls. The runner used
128
+ * to drop them on server stdout; routing through this port lets the
129
+ * Drawer render them as a narrator lane and the completion
130
+ * reminder splice the tail into the orchestrator's notification. */
131
+ logStore: WorkflowRunLogPort;
132
+ /** Global workflow CLI policy (default + allowlist). Replaces the old
133
+ * hard-coded `claude` default: an `agent()` that omits `cli` now uses
134
+ * the user's configured default, and an explicit `cli` outside the
135
+ * allowlist fails the call with a clear, fixable error. */
136
+ getWorkflowCliPolicy: () => WorkflowCliPolicy;
137
+ /** Called when a run reaches a terminal state (completed/failed/stopped).
138
+ * The runtime uses this to inject a `<hive-system-reminder>` into the
139
+ * triggering agent's PTY so the orchestrator picks the result back up,
140
+ * mirroring Claude Code's `<task-notification>` flow. */
141
+ onRunFinished?: (input: {
142
+ runId: string;
143
+ triggeredByAgentId: string;
144
+ finalRecord: WorkflowRunRecord;
145
+ }) => void;
146
+ }) => WorkflowRunner;
147
+ export {};