@tt-a1i/hive 1.7.0 → 2.0.2

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 (251) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.en.md +73 -11
  3. package/README.md +41 -8
  4. package/dist/src/cli/hive-remote.d.ts +46 -0
  5. package/dist/src/cli/hive-remote.js +257 -0
  6. package/dist/src/cli/hive-update.js +7 -2
  7. package/dist/src/cli/hive.d.ts +6 -0
  8. package/dist/src/cli/hive.js +64 -0
  9. package/dist/src/cli/team.d.ts +22 -0
  10. package/dist/src/cli/team.js +255 -5
  11. package/dist/src/server/agent-command-resolver.js +10 -3
  12. package/dist/src/server/agent-exit-classification.d.ts +6 -0
  13. package/dist/src/server/agent-exit-classification.js +6 -0
  14. package/dist/src/server/agent-manager-support.d.ts +2 -1
  15. package/dist/src/server/agent-manager-support.js +59 -15
  16. package/dist/src/server/agent-manager.d.ts +3 -0
  17. package/dist/src/server/agent-manager.js +22 -7
  18. package/dist/src/server/agent-run-bootstrap.d.ts +14 -0
  19. package/dist/src/server/agent-run-bootstrap.js +11 -4
  20. package/dist/src/server/agent-run-exit-handler.js +14 -8
  21. package/dist/src/server/agent-run-starter.d.ts +3 -1
  22. package/dist/src/server/agent-run-starter.js +22 -5
  23. package/dist/src/server/agent-run-sync.js +13 -5
  24. package/dist/src/server/agent-runtime-types.d.ts +1 -0
  25. package/dist/src/server/agent-runtime.d.ts +2 -1
  26. package/dist/src/server/agent-runtime.js +9 -2
  27. package/dist/src/server/agent-startup-instructions.d.ts +2 -1
  28. package/dist/src/server/agent-startup-instructions.js +8 -4
  29. package/dist/src/server/agent-stdin-dispatcher.d.ts +4 -2
  30. package/dist/src/server/agent-stdin-dispatcher.js +35 -3
  31. package/dist/src/server/command-preset-defaults.d.ts +6 -1
  32. package/dist/src/server/command-preset-defaults.js +56 -0
  33. package/dist/src/server/fs-browse.d.ts +2 -0
  34. package/dist/src/server/fs-browse.js +165 -31
  35. package/dist/src/server/fs-pick-folder.js +6 -69
  36. package/dist/src/server/fs-sandbox.d.ts +5 -3
  37. package/dist/src/server/fs-sandbox.js +5 -3
  38. package/dist/src/server/hive-team-guidance.js +18 -6
  39. package/dist/src/server/machine-name.d.ts +2 -0
  40. package/dist/src/server/machine-name.js +13 -0
  41. package/dist/src/server/open-target-commands.d.ts +1 -0
  42. package/dist/src/server/open-target-commands.js +4 -1
  43. package/dist/src/server/orchestrator-autostart.js +1 -1
  44. package/dist/src/server/platform-path.d.ts +1 -0
  45. package/dist/src/server/platform-path.js +14 -1
  46. package/dist/src/server/post-start-input-writer.js +50 -13
  47. package/dist/src/server/preset-launch-support.js +1 -0
  48. package/dist/src/server/recovery-summary.d.ts +2 -1
  49. package/dist/src/server/recovery-summary.js +2 -1
  50. package/dist/src/server/remote-audit-store.d.ts +51 -0
  51. package/dist/src/server/remote-audit-store.js +108 -0
  52. package/dist/src/server/remote-config-keys.d.ts +17 -0
  53. package/dist/src/server/remote-config-keys.js +27 -0
  54. package/dist/src/server/remote-control-constants.d.ts +30 -0
  55. package/dist/src/server/remote-control-constants.js +29 -0
  56. package/dist/src/server/remote-device-session.d.ts +40 -0
  57. package/dist/src/server/remote-device-session.js +22 -0
  58. package/dist/src/server/remote-device-store.d.ts +36 -0
  59. package/dist/src/server/remote-device-store.js +67 -0
  60. package/dist/src/server/remote-frame-bridge.d.ts +102 -0
  61. package/dist/src/server/remote-frame-bridge.js +791 -0
  62. package/dist/src/server/remote-gateway-client.d.ts +14 -0
  63. package/dist/src/server/remote-gateway-client.js +36 -0
  64. package/dist/src/server/remote-loopback-auth.d.ts +6 -0
  65. package/dist/src/server/remote-loopback-auth.js +112 -0
  66. package/dist/src/server/remote-pairing-tunnel.d.ts +59 -0
  67. package/dist/src/server/remote-pairing-tunnel.js +146 -0
  68. package/dist/src/server/remote-pairing.d.ts +58 -0
  69. package/dist/src/server/remote-pairing.js +237 -0
  70. package/dist/src/server/remote-tunnel.d.ts +113 -0
  71. package/dist/src/server/remote-tunnel.js +514 -0
  72. package/dist/src/server/restart-policy-support.d.ts +4 -1
  73. package/dist/src/server/restart-policy-support.js +3 -1
  74. package/dist/src/server/restart-policy.d.ts +1 -1
  75. package/dist/src/server/restart-policy.js +19 -3
  76. package/dist/src/server/route-types.d.ts +1 -1
  77. package/dist/src/server/routes-dispatches.js +1 -1
  78. package/dist/src/server/routes-fs.js +3 -3
  79. package/dist/src/server/routes-marketplace.js +2 -2
  80. package/dist/src/server/routes-open-workspace.js +1 -1
  81. package/dist/src/server/routes-remote.d.ts +2 -0
  82. package/dist/src/server/routes-remote.js +166 -0
  83. package/dist/src/server/routes-runtime.js +6 -6
  84. package/dist/src/server/routes-settings.js +16 -16
  85. package/dist/src/server/routes-tasks.js +2 -2
  86. package/dist/src/server/routes-team-memory.d.ts +2 -0
  87. package/dist/src/server/routes-team-memory.js +154 -0
  88. package/dist/src/server/routes-team-recall.d.ts +2 -0
  89. package/dist/src/server/routes-team-recall.js +119 -0
  90. package/dist/src/server/routes-team.js +31 -9
  91. package/dist/src/server/routes-ui.js +11 -1
  92. package/dist/src/server/routes-workflow-schedules.js +3 -3
  93. package/dist/src/server/routes-workflows.js +5 -5
  94. package/dist/src/server/routes-workspace-memory-dreams.d.ts +2 -0
  95. package/dist/src/server/routes-workspace-memory-dreams.js +105 -0
  96. package/dist/src/server/routes-workspace-memory.d.ts +2 -0
  97. package/dist/src/server/routes-workspace-memory.js +215 -0
  98. package/dist/src/server/routes-workspaces.js +9 -9
  99. package/dist/src/server/routes.js +10 -0
  100. package/dist/src/server/runtime-database.d.ts +1 -0
  101. package/dist/src/server/runtime-database.js +27 -2
  102. package/dist/src/server/runtime-restart-policy.d.ts +3 -1
  103. package/dist/src/server/runtime-restart-policy.js +2 -1
  104. package/dist/src/server/runtime-store-contract.d.ts +37 -0
  105. package/dist/src/server/runtime-store-dream.d.ts +23 -0
  106. package/dist/src/server/runtime-store-dream.js +16 -0
  107. package/dist/src/server/runtime-store-helpers.d.ts +20 -0
  108. package/dist/src/server/runtime-store-helpers.js +81 -7
  109. package/dist/src/server/runtime-store-memory.d.ts +33 -0
  110. package/dist/src/server/runtime-store-memory.js +37 -0
  111. package/dist/src/server/runtime-store-remote.d.ts +5 -0
  112. package/dist/src/server/runtime-store-remote.js +45 -0
  113. package/dist/src/server/runtime-store-workflows.js +2 -0
  114. package/dist/src/server/runtime-store.js +14 -3
  115. package/dist/src/server/session-capture-claude.d.ts +1 -1
  116. package/dist/src/server/session-capture-claude.js +7 -4
  117. package/dist/src/server/session-capture-codex.js +4 -5
  118. package/dist/src/server/session-capture-gemini.js +4 -5
  119. package/dist/src/server/session-capture-opencode.d.ts +4 -4
  120. package/dist/src/server/session-capture-opencode.js +20 -12
  121. package/dist/src/server/session-capture-qwen.d.ts +5 -0
  122. package/dist/src/server/session-capture-qwen.js +104 -0
  123. package/dist/src/server/session-capture.d.ts +17 -0
  124. package/dist/src/server/session-capture.js +16 -0
  125. package/dist/src/server/sqlite-schema-v23.d.ts +2 -0
  126. package/dist/src/server/sqlite-schema-v23.js +43 -0
  127. package/dist/src/server/sqlite-schema-v24.d.ts +2 -0
  128. package/dist/src/server/sqlite-schema-v24.js +34 -0
  129. package/dist/src/server/sqlite-schema-v25.d.ts +2 -0
  130. package/dist/src/server/sqlite-schema-v25.js +127 -0
  131. package/dist/src/server/sqlite-schema-v26.d.ts +2 -0
  132. package/dist/src/server/sqlite-schema-v26.js +56 -0
  133. package/dist/src/server/sqlite-schema-v27.d.ts +6 -0
  134. package/dist/src/server/sqlite-schema-v27.js +92 -0
  135. package/dist/src/server/sqlite-schema-v28.d.ts +2 -0
  136. package/dist/src/server/sqlite-schema-v28.js +19 -0
  137. package/dist/src/server/sqlite-schema-v29.d.ts +2 -0
  138. package/dist/src/server/sqlite-schema-v29.js +27 -0
  139. package/dist/src/server/sqlite-schema-v30.d.ts +2 -0
  140. package/dist/src/server/sqlite-schema-v30.js +27 -0
  141. package/dist/src/server/sqlite-schema-v31.d.ts +2 -0
  142. package/dist/src/server/sqlite-schema-v31.js +30 -0
  143. package/dist/src/server/sqlite-schema.d.ts +1 -1
  144. package/dist/src/server/sqlite-schema.js +49 -1
  145. package/dist/src/server/startup-command-parser.js +5 -1
  146. package/dist/src/server/tasks-file-watcher.d.ts +2 -0
  147. package/dist/src/server/tasks-file-watcher.js +15 -6
  148. package/dist/src/server/tasks-file.js +30 -5
  149. package/dist/src/server/tasks-websocket-server.js +4 -0
  150. package/dist/src/server/team-authz.d.ts +1 -1
  151. package/dist/src/server/team-authz.js +13 -1
  152. package/dist/src/server/team-list-enrichment.js +3 -1
  153. package/dist/src/server/team-memory-digest.d.ts +52 -0
  154. package/dist/src/server/team-memory-digest.js +200 -0
  155. package/dist/src/server/team-memory-dream-applier.d.ts +5 -0
  156. package/dist/src/server/team-memory-dream-applier.js +234 -0
  157. package/dist/src/server/team-memory-dream-http-serializers.d.ts +13 -0
  158. package/dist/src/server/team-memory-dream-http-serializers.js +12 -0
  159. package/dist/src/server/team-memory-dream-ops.d.ts +40 -0
  160. package/dist/src/server/team-memory-dream-ops.js +153 -0
  161. package/dist/src/server/team-memory-dream-reverter.d.ts +22 -0
  162. package/dist/src/server/team-memory-dream-reverter.js +221 -0
  163. package/dist/src/server/team-memory-dream-run-store.d.ts +23 -0
  164. package/dist/src/server/team-memory-dream-run-store.js +211 -0
  165. package/dist/src/server/team-memory-dream-runner.d.ts +37 -0
  166. package/dist/src/server/team-memory-dream-runner.js +178 -0
  167. package/dist/src/server/team-memory-dream-scheduler.d.ts +32 -0
  168. package/dist/src/server/team-memory-dream-scheduler.js +115 -0
  169. package/dist/src/server/team-memory-dream-store.d.ts +19 -0
  170. package/dist/src/server/team-memory-dream-store.js +16 -0
  171. package/dist/src/server/team-memory-dream-types.d.ts +104 -0
  172. package/dist/src/server/team-memory-dream-types.js +23 -0
  173. package/dist/src/server/team-memory-export.d.ts +22 -0
  174. package/dist/src/server/team-memory-export.js +220 -0
  175. package/dist/src/server/team-memory-feature.d.ts +12 -0
  176. package/dist/src/server/team-memory-feature.js +12 -0
  177. package/dist/src/server/team-memory-http-serializers.d.ts +102 -0
  178. package/dist/src/server/team-memory-http-serializers.js +46 -0
  179. package/dist/src/server/team-memory-injection.d.ts +31 -0
  180. package/dist/src/server/team-memory-injection.js +49 -0
  181. package/dist/src/server/team-memory-store.d.ts +116 -0
  182. package/dist/src/server/team-memory-store.js +513 -0
  183. package/dist/src/server/team-operations.d.ts +5 -1
  184. package/dist/src/server/team-operations.js +46 -16
  185. package/dist/src/server/team-recall-store.d.ts +38 -0
  186. package/dist/src/server/team-recall-store.js +205 -0
  187. package/dist/src/server/terminal-input-profile.d.ts +1 -1
  188. package/dist/src/server/terminal-input-profile.js +18 -0
  189. package/dist/src/server/terminal-ws-server.js +6 -0
  190. package/dist/src/server/ui-auth-helpers.d.ts +1 -1
  191. package/dist/src/server/ui-auth-helpers.js +7 -1
  192. package/dist/src/server/ui-auth.d.ts +3 -0
  193. package/dist/src/server/ui-auth.js +21 -1
  194. package/dist/src/server/workflow-cli-policy.d.ts +2 -3
  195. package/dist/src/server/workflow-cli-policy.js +3 -3
  196. package/dist/src/server/workflow-runner.d.ts +1 -0
  197. package/dist/src/server/workflow-runner.js +9 -4
  198. package/dist/src/server/workspace-path-validation.js +6 -2
  199. package/dist/src/server/workspace-store.d.ts +1 -1
  200. package/dist/src/server/workspace-store.js +35 -9
  201. package/dist/src/shared/fs-browse.d.ts +1 -0
  202. package/dist/src/shared/fs-browse.js +1 -0
  203. package/dist/src/shared/path-input.d.ts +12 -0
  204. package/dist/src/shared/path-input.js +22 -0
  205. package/dist/src/shared/remote-bridge-routing.d.ts +19 -0
  206. package/dist/src/shared/remote-bridge-routing.js +141 -0
  207. package/dist/src/shared/remote-crypto.d.ts +138 -0
  208. package/dist/src/shared/remote-crypto.js +427 -0
  209. package/dist/src/shared/remote-pairing-code.d.ts +7 -0
  210. package/dist/src/shared/remote-pairing-code.js +47 -0
  211. package/dist/src/shared/remote-protocol.d.ts +160 -0
  212. package/dist/src/shared/remote-protocol.js +526 -0
  213. package/dist/src/shared/team-memory.d.ts +11 -0
  214. package/dist/src/shared/team-memory.js +10 -0
  215. package/dist/src/shared/team-recall.d.ts +1 -0
  216. package/dist/src/shared/team-recall.js +1 -0
  217. package/dist/src/shared/types.d.ts +4 -5
  218. package/package.json +12 -5
  219. package/scripts/postinstall-native-artifacts.mjs +113 -0
  220. package/web/dist/assets/AddWorkerDialog-CbV75qUX.js +2 -0
  221. package/web/dist/assets/AddWorkspaceFlow-CwV-7wPx.js +1 -0
  222. package/web/dist/assets/FirstRunWizard-a6PWIK3x.js +1 -0
  223. package/web/dist/assets/MarketplaceDrawer-Dd8WIA8T.js +67 -0
  224. package/web/dist/assets/TaskGraphDrawer-Bk5WFIk_.js +1 -0
  225. package/web/dist/assets/{WhatsNewDialog-CHkZeINH.js → WhatsNewDialog-C2VZaip0.js} +1 -1
  226. package/web/dist/assets/WorkerModal-DucW-9YT.js +1 -0
  227. package/web/dist/assets/WorkflowsDrawer-Bjf4olbR.js +1 -0
  228. package/web/dist/assets/WorkspaceMemoryDrawer-DglCy_5f.js +1 -0
  229. package/web/dist/assets/WorkspaceTaskDrawer-BIWwISvA.js +1 -0
  230. package/web/dist/assets/index-BAiLYajK.css +1 -0
  231. package/web/dist/assets/index-BV2k9Dts.js +73 -0
  232. package/web/dist/assets/search-Bk2HQvO7.js +1 -0
  233. package/web/dist/assets/square-terminal-D93m9hfY.js +1 -0
  234. package/web/dist/cli-icons/agy.png +0 -0
  235. package/web/dist/cli-icons/cursor.ico +0 -0
  236. package/web/dist/cli-icons/grok.ico +0 -0
  237. package/web/dist/cli-icons/qwen.png +0 -0
  238. package/web/dist/index.html +8 -3
  239. package/web/dist/sw.js +1 -1
  240. package/scripts/fix-runtime-artifacts.mjs +0 -33
  241. package/web/dist/assets/AddWorkerDialog-BRUxpa3f.js +0 -2
  242. package/web/dist/assets/AddWorkspaceDialog-D56x5JCb.js +0 -1
  243. package/web/dist/assets/FirstRunWizard-BFVaMIsE.js +0 -1
  244. package/web/dist/assets/MarketplaceDrawer-DeEZ35dN.js +0 -76
  245. package/web/dist/assets/WorkerModal-BBCuMLIa.js +0 -1
  246. package/web/dist/assets/WorkspaceTaskDrawer-CpZHAcj1.js +0 -1
  247. package/web/dist/assets/WorkspaceTerminalPanels-7If2mDyp.js +0 -1
  248. package/web/dist/assets/WorkspaceTerminalPanels-DDGTF8rc.css +0 -1
  249. package/web/dist/assets/index-5zh61jMg.css +0 -1
  250. package/web/dist/assets/index-CxNL0O-C.js +0 -73
  251. package/web/dist/assets/path-join-7MR1s7b1.js +0 -1
@@ -0,0 +1,513 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { MEMORY_QUERY_MAX_CHARS, MEMORY_SEARCH_DEFAULT_LIMIT, MEMORY_SEARCH_MAX_LIMIT, } from '../shared/team-memory.js';
3
+ export class MemoryEntryNotFoundError extends Error {
4
+ memoryId;
5
+ workspaceId;
6
+ constructor(memoryId, workspaceId) {
7
+ super(`Memory entry not found in workspace: ${memoryId}`);
8
+ this.name = 'MemoryEntryNotFoundError';
9
+ this.memoryId = memoryId;
10
+ this.workspaceId = workspaceId;
11
+ }
12
+ }
13
+ export class MemoryEntryStatusError extends Error {
14
+ actualStatus;
15
+ memoryId;
16
+ expectedStatus;
17
+ constructor(memoryId, expectedStatus, actualStatus) {
18
+ const expected = Array.isArray(expectedStatus) ? expectedStatus.join(' or ') : expectedStatus;
19
+ super(`Memory entry ${memoryId} expected status ${expected}, got ${actualStatus}`);
20
+ this.name = 'MemoryEntryStatusError';
21
+ this.memoryId = memoryId;
22
+ this.expectedStatus = expectedStatus;
23
+ this.actualStatus = actualStatus;
24
+ }
25
+ }
26
+ const hashText = (text) => createHash('sha256').update(text).digest('hex');
27
+ const excerptFor = (text) => [...text].slice(0, 500).join('');
28
+ const clampLimit = (value) => {
29
+ if (value === undefined || !Number.isInteger(value) || value < 0) {
30
+ return MEMORY_SEARCH_DEFAULT_LIMIT;
31
+ }
32
+ return Math.min(value, MEMORY_SEARCH_MAX_LIMIT);
33
+ };
34
+ const clampDigestLimit = (value) => {
35
+ if (value === undefined || !Number.isInteger(value) || value <= 0)
36
+ return 20;
37
+ return Math.min(value, 50);
38
+ };
39
+ const clampListLimit = (value) => {
40
+ if (value === undefined || !Number.isInteger(value) || value <= 0)
41
+ return MEMORY_SEARCH_MAX_LIMIT;
42
+ return Math.min(value, MEMORY_SEARCH_MAX_LIMIT);
43
+ };
44
+ const quoteFtsToken = (value) => `"${value.replaceAll('"', '""')}"`;
45
+ const toSearchTerms = (query) => query.trim().split(/\s+/).filter(Boolean);
46
+ const toFtsQuery = (query) => toSearchTerms(query).map(quoteFtsToken).join(' AND ');
47
+ const hasShortTerm = (terms) => terms.some((term) => [...term].length < 3);
48
+ const escapeLike = (value) => `%${value.replaceAll('\\', '\\\\').replaceAll('%', '\\%').replaceAll('_', '\\_')}%`;
49
+ const parseTags = (value) => {
50
+ if (!value)
51
+ return [];
52
+ const parsed = JSON.parse(value);
53
+ return Array.isArray(parsed) ? parsed.filter((tag) => typeof tag === 'string') : [];
54
+ };
55
+ const toEntryRecord = (row) => ({
56
+ archivedAt: row.archived_at,
57
+ body: row.body,
58
+ confidence: row.confidence,
59
+ createdAt: row.created_at,
60
+ disabled: row.disabled === 1,
61
+ id: row.id,
62
+ kind: row.kind,
63
+ lastInjectedAt: row.last_injected_at,
64
+ pinned: row.pinned === 1,
65
+ scope: row.scope,
66
+ source: row.source,
67
+ status: row.status,
68
+ tags: parseTags(row.tags),
69
+ updatedAt: row.updated_at,
70
+ workspaceId: row.workspace_id,
71
+ });
72
+ const toSourceRecord = (row) => ({
73
+ actorAgentIdSnapshot: row.actor_agent_id_snapshot,
74
+ actorNameSnapshot: row.actor_name_snapshot,
75
+ actorRoleSnapshot: row.actor_role_snapshot,
76
+ createdAt: row.created_at,
77
+ excerpt: row.excerpt,
78
+ id: row.id,
79
+ memoryId: row.memory_id,
80
+ sourceId: row.source_id,
81
+ sourceSequence: row.source_sequence,
82
+ sourceType: row.source_type,
83
+ textHash: row.text_hash,
84
+ });
85
+ export const createTeamMemoryStore = (db) => {
86
+ const nextFtsRowid = () => db.prepare('SELECT COALESCE(MAX(fts_rowid), 0) + 1 AS next FROM memory_entries').get().next;
87
+ const listSources = (memoryId) => db
88
+ .prepare(`SELECT *
89
+ FROM memory_sources
90
+ WHERE memory_id = ?
91
+ ORDER BY created_at ASC, id ASC`)
92
+ .all(memoryId).map(toSourceRecord);
93
+ const toEntryWithSources = (row) => ({
94
+ ...toEntryRecord(row),
95
+ sources: listSources(row.id),
96
+ });
97
+ const getEntryWithSources = (workspaceId, memoryId) => {
98
+ const row = db
99
+ .prepare(`SELECT *
100
+ FROM memory_entries
101
+ WHERE id = ?
102
+ AND scope = 'workspace'
103
+ AND workspace_id = ?
104
+ LIMIT 1`)
105
+ .get(memoryId, workspaceId);
106
+ if (!row)
107
+ return undefined;
108
+ return toEntryWithSources(row);
109
+ };
110
+ const requireEntryWithSources = (workspaceId, memoryId) => {
111
+ const entry = getEntryWithSources(workspaceId, memoryId);
112
+ if (!entry)
113
+ throw new MemoryEntryNotFoundError(memoryId, workspaceId);
114
+ return entry;
115
+ };
116
+ const searchFts = (table, workspaceId, ftsQuery, statuses, includeDisabled) => {
117
+ if (statuses.length === 0)
118
+ return [];
119
+ const indexName = table === 'memory_fts' ? 'unicode' : 'trigram';
120
+ const statusPlaceholders = statuses.map(() => '?').join(', ');
121
+ return db
122
+ .prepare(`SELECT
123
+ e.*,
124
+ ? AS index_name,
125
+ bm25(${table}) AS score
126
+ FROM ${table}
127
+ JOIN memory_entries e ON e.fts_rowid = ${table}.rowid
128
+ WHERE ${table} MATCH ?
129
+ AND e.scope = 'workspace'
130
+ AND e.workspace_id = ?
131
+ ${includeDisabled ? '' : 'AND e.disabled = 0'}
132
+ AND e.status IN (${statusPlaceholders})`)
133
+ .all(indexName, ftsQuery, workspaceId, ...statuses);
134
+ };
135
+ const searchLike = (workspaceId, terms, statuses, includeDisabled) => {
136
+ if (terms.length === 0 || statuses.length === 0)
137
+ return [];
138
+ const predicates = terms
139
+ .map(() => "(e.body LIKE ? ESCAPE '\\' OR COALESCE(e.tags, '') LIKE ? ESCAPE '\\')")
140
+ .join(' AND ');
141
+ const statusPlaceholders = statuses.map(() => '?').join(', ');
142
+ return db
143
+ .prepare(`SELECT
144
+ e.*,
145
+ 'like' AS index_name,
146
+ 0 AS score
147
+ FROM memory_entries e
148
+ WHERE e.scope = 'workspace'
149
+ AND e.workspace_id = ?
150
+ ${includeDisabled ? '' : 'AND e.disabled = 0'}
151
+ AND e.status IN (${statusPlaceholders})
152
+ AND ${predicates}`)
153
+ .all(workspaceId, ...statuses, ...terms.flatMap((term) => [escapeLike(term), escapeLike(term)]));
154
+ };
155
+ const searchEntries = (workspaceId, query, options = {}) => {
156
+ if ([...query].length > MEMORY_QUERY_MAX_CHARS)
157
+ return [];
158
+ const ftsQuery = toFtsQuery(query);
159
+ if (!ftsQuery)
160
+ return [];
161
+ const terms = toSearchTerms(query);
162
+ const statuses = options.statuses ?? ['active'];
163
+ const includeDisabled = options.includeDisabled ?? false;
164
+ const rows = [
165
+ ...searchFts('memory_fts', workspaceId, ftsQuery, statuses, includeDisabled),
166
+ ...searchFts('memory_fts_trigram', workspaceId, ftsQuery, statuses, includeDisabled),
167
+ ...(hasShortTerm(terms) ? searchLike(workspaceId, terms, statuses, includeDisabled) : []),
168
+ ];
169
+ const byId = new Map();
170
+ for (const row of rows) {
171
+ const previous = byId.get(row.id);
172
+ if (!previous || row.score < previous.score)
173
+ byId.set(row.id, row);
174
+ }
175
+ return [...byId.values()]
176
+ .sort((a, b) => a.score - b.score || b.updated_at - a.updated_at)
177
+ .slice(0, clampLimit(options.limit))
178
+ .map((row) => ({
179
+ ...toEntryWithSources(row),
180
+ indexName: row.index_name,
181
+ score: row.score,
182
+ }));
183
+ };
184
+ const listEntries = (workspaceId, options = {}) => {
185
+ const statuses = options.statuses ?? ['active'];
186
+ if (statuses.length === 0)
187
+ return [];
188
+ const statusPlaceholders = statuses.map(() => '?').join(', ');
189
+ return db
190
+ .prepare(`SELECT *
191
+ FROM memory_entries
192
+ WHERE scope = 'workspace'
193
+ AND workspace_id = ?
194
+ AND status IN (${statusPlaceholders})
195
+ ORDER BY
196
+ CASE status
197
+ WHEN 'candidate' THEN 0
198
+ WHEN 'active' THEN 1
199
+ WHEN 'archived' THEN 2
200
+ ELSE 3
201
+ END ASC,
202
+ pinned DESC,
203
+ updated_at DESC,
204
+ created_at DESC,
205
+ id ASC
206
+ LIMIT ?`)
207
+ .all(workspaceId, ...statuses, clampListLimit(options.limit)).map(toEntryWithSources);
208
+ };
209
+ const listAllEntries = (workspaceId) => db
210
+ .prepare(`SELECT *
211
+ FROM memory_entries
212
+ WHERE scope = 'workspace'
213
+ AND workspace_id = ?
214
+ ORDER BY
215
+ CASE status
216
+ WHEN 'candidate' THEN 0
217
+ WHEN 'active' THEN 1
218
+ WHEN 'archived' THEN 2
219
+ ELSE 3
220
+ END ASC,
221
+ pinned DESC,
222
+ updated_at DESC,
223
+ created_at DESC,
224
+ id ASC`)
225
+ .all(workspaceId).map(toEntryWithSources);
226
+ const listDigestEntries = (workspaceId, options = {}) => db
227
+ .prepare(`SELECT *
228
+ FROM memory_entries
229
+ WHERE scope = 'workspace'
230
+ AND workspace_id = ?
231
+ AND status = 'active'
232
+ AND disabled = 0
233
+ ORDER BY
234
+ pinned DESC,
235
+ CASE source WHEN 'manual' THEN 0 ELSE 1 END ASC,
236
+ COALESCE(confidence, 0) DESC,
237
+ updated_at DESC,
238
+ created_at DESC,
239
+ id ASC
240
+ LIMIT ?`)
241
+ .all(workspaceId, clampDigestLimit(options.limit)).map(toEntryWithSources);
242
+ const listExportEntries = (workspaceId) => db
243
+ .prepare(`SELECT *
244
+ FROM memory_entries
245
+ WHERE scope = 'workspace'
246
+ AND workspace_id = ?
247
+ AND status = 'active'
248
+ ORDER BY
249
+ kind ASC,
250
+ pinned DESC,
251
+ CASE source WHEN 'manual' THEN 0 ELSE 1 END ASC,
252
+ COALESCE(confidence, 0) DESC,
253
+ updated_at DESC,
254
+ created_at DESC,
255
+ id ASC`)
256
+ .all(workspaceId).map(toEntryWithSources);
257
+ const addEntry = (input) => {
258
+ const id = randomUUID();
259
+ const sourceId = randomUUID();
260
+ const now = Date.now();
261
+ const status = input.actor.role === 'orchestrator' ? 'active' : 'candidate';
262
+ const source = input.source ?? 'manual';
263
+ const confidence = input.confidence ?? (source === 'manual' ? 1 : null);
264
+ const tags = JSON.stringify(input.tags ?? []);
265
+ db.transaction(() => {
266
+ const ftsRowid = nextFtsRowid();
267
+ db.prepare(`INSERT INTO memory_entries (
268
+ id,
269
+ workspace_id,
270
+ scope,
271
+ fts_rowid,
272
+ kind,
273
+ body,
274
+ tags,
275
+ status,
276
+ source,
277
+ confidence,
278
+ pinned,
279
+ disabled,
280
+ created_at,
281
+ updated_at,
282
+ archived_at,
283
+ last_injected_at
284
+ ) VALUES (?, ?, 'workspace', ?, ?, ?, ?, ?, ?, ?, 0, 0, ?, ?, NULL, NULL)`).run(id, input.workspaceId, ftsRowid, input.kind, input.body, tags, status, source, confidence, now, now);
285
+ db.prepare(`INSERT INTO memory_sources (
286
+ id,
287
+ memory_id,
288
+ source_type,
289
+ source_id,
290
+ source_sequence,
291
+ excerpt,
292
+ text_hash,
293
+ actor_agent_id_snapshot,
294
+ actor_name_snapshot,
295
+ actor_role_snapshot,
296
+ created_at
297
+ ) VALUES (?, ?, 'manual', NULL, NULL, ?, ?, ?, ?, ?, ?)`).run(sourceId, id, excerptFor(input.body), hashText(input.body), input.actor.id, input.actor.name, input.actor.role, now);
298
+ })();
299
+ const created = getEntryWithSources(input.workspaceId, id);
300
+ if (!created)
301
+ throw new Error(`Memory entry disappeared after insert: ${id}`);
302
+ return created;
303
+ };
304
+ const logInjections = (input) => {
305
+ if (input.memoryIds.length === 0)
306
+ return [];
307
+ const injectedAt = Date.now();
308
+ const ids = input.memoryIds.map(() => randomUUID());
309
+ db.transaction(() => {
310
+ for (const [index, memoryId] of input.memoryIds.entries()) {
311
+ const memory = db
312
+ .prepare(`SELECT id
313
+ FROM memory_entries
314
+ WHERE id = ?
315
+ AND scope = 'workspace'
316
+ AND workspace_id = ?
317
+ LIMIT 1`)
318
+ .get(memoryId, input.workspaceId);
319
+ if (!memory) {
320
+ throw new MemoryEntryNotFoundError(memoryId, input.workspaceId);
321
+ }
322
+ db.prepare(`INSERT INTO memory_injections (
323
+ id,
324
+ memory_id,
325
+ workspace_id,
326
+ target_agent_id_snapshot,
327
+ context_type,
328
+ dispatch_id,
329
+ injected_at
330
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)`).run(ids[index], memoryId, input.workspaceId, input.targetAgentIdSnapshot ?? null, input.contextType, input.dispatchId ?? null, injectedAt);
331
+ db.prepare(`UPDATE memory_entries
332
+ SET last_injected_at = ?
333
+ WHERE id = ? AND workspace_id = ?`).run(injectedAt, memoryId, input.workspaceId);
334
+ }
335
+ })();
336
+ return ids;
337
+ };
338
+ const deleteInjections = (injectionIds) => {
339
+ if (injectionIds.length === 0)
340
+ return;
341
+ const placeholders = injectionIds.map(() => '?').join(', ');
342
+ db.transaction(() => {
343
+ const rows = db
344
+ .prepare(`SELECT DISTINCT memory_id, workspace_id
345
+ FROM memory_injections
346
+ WHERE id IN (${placeholders})`)
347
+ .all(...injectionIds);
348
+ db.prepare(`DELETE FROM memory_injections WHERE id IN (${placeholders})`).run(...injectionIds);
349
+ for (const row of rows) {
350
+ db.prepare(`UPDATE memory_entries
351
+ SET last_injected_at = (
352
+ SELECT MAX(injected_at)
353
+ FROM memory_injections
354
+ WHERE memory_id = ?
355
+ AND workspace_id = ?
356
+ )
357
+ WHERE id = ?
358
+ AND workspace_id = ?`).run(row.memory_id, row.workspace_id, row.memory_id, row.workspace_id);
359
+ }
360
+ })();
361
+ };
362
+ const archiveEntry = (workspaceId, memoryId) => {
363
+ const current = requireEntryWithSources(workspaceId, memoryId);
364
+ if (current.status === 'archived')
365
+ return current;
366
+ if (current.status !== 'active') {
367
+ throw new MemoryEntryStatusError(memoryId, 'active', current.status);
368
+ }
369
+ const now = Date.now();
370
+ const result = db
371
+ .prepare(`UPDATE memory_entries
372
+ SET status = 'archived',
373
+ archived_at = ?,
374
+ updated_at = ?
375
+ WHERE id = ?
376
+ AND scope = 'workspace'
377
+ AND workspace_id = ?
378
+ AND status = 'active'`)
379
+ .run(now, now, memoryId, workspaceId);
380
+ if (result.changes === 0)
381
+ throw new MemoryEntryNotFoundError(memoryId, workspaceId);
382
+ return requireEntryWithSources(workspaceId, memoryId);
383
+ };
384
+ const approveCandidate = (workspaceId, memoryId) => {
385
+ const current = requireEntryWithSources(workspaceId, memoryId);
386
+ if (current.status !== 'candidate') {
387
+ throw new MemoryEntryStatusError(memoryId, 'candidate', current.status);
388
+ }
389
+ const now = Date.now();
390
+ const result = db
391
+ .prepare(`UPDATE memory_entries
392
+ SET status = 'active',
393
+ archived_at = NULL,
394
+ updated_at = ?
395
+ WHERE id = ?
396
+ AND scope = 'workspace'
397
+ AND workspace_id = ?
398
+ AND status = 'candidate'`)
399
+ .run(now, memoryId, workspaceId);
400
+ if (result.changes === 0)
401
+ throw new MemoryEntryNotFoundError(memoryId, workspaceId);
402
+ return requireEntryWithSources(workspaceId, memoryId);
403
+ };
404
+ const rejectCandidate = (workspaceId, memoryId) => {
405
+ const current = requireEntryWithSources(workspaceId, memoryId);
406
+ if (current.status !== 'candidate') {
407
+ throw new MemoryEntryStatusError(memoryId, 'candidate', current.status);
408
+ }
409
+ const now = Date.now();
410
+ const result = db
411
+ .prepare(`UPDATE memory_entries
412
+ SET status = 'rejected',
413
+ updated_at = ?
414
+ WHERE id = ?
415
+ AND scope = 'workspace'
416
+ AND workspace_id = ?
417
+ AND status = 'candidate'`)
418
+ .run(now, memoryId, workspaceId);
419
+ if (result.changes === 0)
420
+ throw new MemoryEntryNotFoundError(memoryId, workspaceId);
421
+ return requireEntryWithSources(workspaceId, memoryId);
422
+ };
423
+ const setPinned = (workspaceId, memoryId, pinned) => {
424
+ const current = requireEntryWithSources(workspaceId, memoryId);
425
+ if (current.status !== 'active') {
426
+ throw new MemoryEntryStatusError(memoryId, 'active', current.status);
427
+ }
428
+ const now = Date.now();
429
+ const result = db
430
+ .prepare(`UPDATE memory_entries
431
+ SET pinned = ?,
432
+ updated_at = ?
433
+ WHERE id = ?
434
+ AND scope = 'workspace'
435
+ AND workspace_id = ?
436
+ AND status = 'active'`)
437
+ .run(pinned ? 1 : 0, now, memoryId, workspaceId);
438
+ if (result.changes === 0)
439
+ throw new MemoryEntryNotFoundError(memoryId, workspaceId);
440
+ return requireEntryWithSources(workspaceId, memoryId);
441
+ };
442
+ const setDisabled = (workspaceId, memoryId, disabled) => {
443
+ const current = requireEntryWithSources(workspaceId, memoryId);
444
+ if (current.status !== 'active') {
445
+ throw new MemoryEntryStatusError(memoryId, 'active', current.status);
446
+ }
447
+ const now = Date.now();
448
+ const result = db
449
+ .prepare(`UPDATE memory_entries
450
+ SET disabled = ?,
451
+ updated_at = ?
452
+ WHERE id = ?
453
+ AND scope = 'workspace'
454
+ AND workspace_id = ?
455
+ AND status = 'active'`)
456
+ .run(disabled ? 1 : 0, now, memoryId, workspaceId);
457
+ if (result.changes === 0)
458
+ throw new MemoryEntryNotFoundError(memoryId, workspaceId);
459
+ return requireEntryWithSources(workspaceId, memoryId);
460
+ };
461
+ const deleteWorkspaceMemories = (workspaceId) => {
462
+ db.transaction(() => {
463
+ db.prepare('DELETE FROM memory_injections WHERE workspace_id = ?').run(workspaceId);
464
+ const rows = db
465
+ .prepare(`SELECT id
466
+ FROM memory_entries
467
+ WHERE scope = 'workspace'
468
+ AND workspace_id = ?`)
469
+ .all(workspaceId);
470
+ for (const row of rows) {
471
+ db.prepare('DELETE FROM memory_sources WHERE memory_id = ?').run(row.id);
472
+ db.prepare('DELETE FROM memory_injections WHERE memory_id = ?').run(row.id);
473
+ }
474
+ db.prepare(`DELETE FROM memory_entries
475
+ WHERE scope = 'workspace'
476
+ AND workspace_id = ?`).run(workspaceId);
477
+ })();
478
+ };
479
+ const listInjectionsForDispatch = (workspaceId, dispatchId) => db
480
+ .prepare(`SELECT *
481
+ FROM memory_injections
482
+ WHERE workspace_id = ?
483
+ AND dispatch_id = ?
484
+ ORDER BY injected_at ASC, id ASC`)
485
+ .all(workspaceId, dispatchId).map((row) => ({
486
+ contextType: row.context_type,
487
+ dispatchId: row.dispatch_id,
488
+ id: row.id,
489
+ injectedAt: row.injected_at,
490
+ memory: requireEntryWithSources(workspaceId, row.memory_id),
491
+ memoryId: row.memory_id,
492
+ targetAgentIdSnapshot: row.target_agent_id_snapshot,
493
+ workspaceId: row.workspace_id,
494
+ }));
495
+ return {
496
+ addEntry,
497
+ approveCandidate,
498
+ archiveEntry,
499
+ deleteWorkspaceMemories,
500
+ deleteInjections,
501
+ getEntryWithSources,
502
+ listDigestEntries,
503
+ listExportEntries,
504
+ listAllEntries,
505
+ listEntries,
506
+ listInjectionsForDispatch,
507
+ logInjections,
508
+ rejectCandidate,
509
+ searchEntries,
510
+ setDisabled,
511
+ setPinned,
512
+ };
513
+ };
@@ -48,6 +48,7 @@ export interface TeamOperationsInput {
48
48
  dismissEphemeralWorker?: (workspaceId: string, workerId: string) => void;
49
49
  }
50
50
  export interface DispatchTaskInput {
51
+ autoStartWorker?: boolean;
51
52
  fromAgentId?: string;
52
53
  hivePort?: string;
53
54
  workflowRunId?: string;
@@ -59,7 +60,7 @@ export interface ReportTaskInput {
59
60
  artifacts?: string[];
60
61
  dispatchId?: string;
61
62
  requireActiveRun?: boolean;
62
- status?: string;
63
+ status?: 'success' | 'failed';
63
64
  text?: string;
64
65
  }
65
66
  export interface StatusTaskInput {
@@ -75,6 +76,7 @@ export interface ReportTaskResult {
75
76
  dispatch: DispatchRecord | null;
76
77
  forwardError: string | null;
77
78
  forwarded: boolean;
79
+ pendingWarning?: string;
78
80
  }
79
81
  export declare const createTeamOperations: ({ agentRuntime, createDispatch, deleteDispatch, deleteMessage, findOpenDispatch, findOpenDispatchById, insertMessage, markDispatchCancelled, markDispatchReportedByWorker, markDispatchSubmitted, reportOutbox, notifyWebhook, workflowDispatchAwaiter, workspaceStore, dismissEphemeralWorker, }: TeamOperationsInput) => {
80
82
  cancelTask(workspaceId: string, dispatchId: string, input: CancelTaskInput): {
@@ -89,11 +91,13 @@ export declare const createTeamOperations: ({ agentRuntime, createDispatch, dele
89
91
  }>;
90
92
  recordUserInput(workspaceId: string, orchestratorId: string, text: string): void;
91
93
  statusTask(workspaceId: string, workerId: string, input?: StatusTaskInput): {
94
+ pendingWarning?: string;
92
95
  dispatch: null;
93
96
  forwardError: string | null;
94
97
  forwarded: boolean;
95
98
  };
96
99
  reportTask(workspaceId: string, workerId: string, input?: ReportTaskInput): {
100
+ pendingWarning?: string;
97
101
  dispatch: DispatchRecord;
98
102
  forwardError: string | null;
99
103
  forwarded: boolean;
@@ -25,6 +25,10 @@ export const formatUnknownWorkerError = (workerName, roster) => {
25
25
  ].join('\n');
26
26
  };
27
27
  const reportForwardErrorMessage = (error) => error instanceof Error ? error.message : String(error);
28
+ const buildPendingWarning = (workerName, pendingTaskCount, action) => pendingTaskCount > 0
29
+ ? `Hive recorded the ${action}, but ${workerName} still has ${pendingTaskCount} open dispatch${pendingTaskCount === 1 ? '' : 'es'}. ` +
30
+ 'A worker stays working until every dispatch is closed with `team report --dispatch <id>` or `team cancel --dispatch <id>`.'
31
+ : undefined;
28
32
  export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispatch, deleteMessage, findOpenDispatch, findOpenDispatchById, insertMessage, markDispatchCancelled, markDispatchReportedByWorker, markDispatchSubmitted, reportOutbox, notifyWebhook, workflowDispatchAwaiter, workspaceStore, dismissEphemeralWorker, }) => {
29
33
  // Best-effort redelivery of reports a prior orchestrator outage stranded.
30
34
  // Called when a fresh report confirms the orchestrator is reachable and
@@ -82,6 +86,9 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
82
86
  }
83
87
  };
84
88
  const dispatchTask = async (workspaceId, workerId, text, input = {}) => {
89
+ const fromAgentId = input.fromAgentId;
90
+ const sender = fromAgentId ? workspaceStore.getAgent(workspaceId, fromAgentId) : undefined;
91
+ const worker = workspaceStore.getWorker(workspaceId, workerId);
85
92
  const message = createSendMessage(workspaceId, workerId, text, input.fromAgentId);
86
93
  const messageHandle = insertMessage(message);
87
94
  let dispatch;
@@ -92,8 +99,8 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
92
99
  toAgentId: workerId,
93
100
  workspaceId,
94
101
  };
95
- if (input.fromAgentId)
96
- dispatchInput.fromAgentId = input.fromAgentId;
102
+ if (fromAgentId)
103
+ dispatchInput.fromAgentId = fromAgentId;
97
104
  if (input.workflowRunId !== undefined)
98
105
  dispatchInput.workflowRunId = input.workflowRunId;
99
106
  if (input.stepIndex !== undefined)
@@ -104,10 +111,17 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
104
111
  dispatchInput.label = input.label;
105
112
  dispatch = createDispatch(dispatchInput);
106
113
  const dispatchId = dispatch.id;
107
- if (input.fromAgentId) {
108
- const sender = workspaceStore.getAgent(workspaceId, input.fromAgentId);
109
- await ensureWorkerRun(workspaceId, workerId, input.hivePort ?? '');
110
- const worker = workspaceStore.getWorker(workspaceId, workerId);
114
+ if (fromAgentId && sender) {
115
+ const shouldAutoStartWorker = input.autoStartWorker !== false;
116
+ const hadActiveRun = Boolean(agentRuntime.getActiveRunByAgentId(workspaceId, workerId));
117
+ if (!hadActiveRun && shouldAutoStartWorker) {
118
+ await ensureWorkerRun(workspaceId, workerId, input.hivePort ?? '');
119
+ }
120
+ if (!shouldAutoStartWorker && !agentRuntime.getActiveRunByAgentId(workspaceId, workerId)) {
121
+ workspaceStore.markTaskDispatched(workspaceId, workerId);
122
+ pendingMarked = true;
123
+ return dispatch;
124
+ }
111
125
  const isWorkflowDispatch = input.workflowRunId !== undefined || input.fromAgentId === getWorkflowAgentId(workspaceId);
112
126
  markDispatchSubmitted(dispatchId);
113
127
  workspaceStore.markTaskDispatched(workspaceId, workerId);
@@ -204,14 +218,12 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
204
218
  if (!worker) {
205
219
  throw new ConflictError(formatUnknownWorkerError(workerName, roster));
206
220
  }
207
- /* Capture the active-run state *before* dispatchTask runs, because
208
- dispatchTask calls ensureWorkerRun on authenticated team-send calls
209
- and may silently auto-start a stopped worker. Surfacing
210
- `restartedWorker=true` to the orchestrator turns that silent state
211
- change into a transparent one. Internal calls without `fromAgentId`
212
- only queue work and do not wake a PTY, so they must not report a
213
- restart. */
221
+ /* Capture the active-run state *before* dispatchTask runs. Internal
222
+ workflow calls may auto-start a stopped worker; explicit CLI
223
+ `team send` passes autoStartWorker:false so the worker remains
224
+ stopped with a queued dispatch until the user restarts it. */
214
225
  const restartedWorker = input.fromAgentId !== undefined &&
226
+ input.autoStartWorker !== false &&
215
227
  !agentRuntime.getActiveRunByAgentId(workspaceId, worker.id);
216
228
  const dispatch = await dispatchTask(workspaceId, worker.id, text, input);
217
229
  return Object.assign(dispatch, { restartedWorker });
@@ -241,7 +253,13 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
241
253
  console.error('[hive] swallowed:teamStatus.forward', error);
242
254
  }
243
255
  }
244
- return { dispatch: null, forwardError, forwarded };
256
+ const pendingWarning = buildPendingWarning(worker.name, worker.pendingTaskCount, 'status update');
257
+ return {
258
+ dispatch: null,
259
+ forwardError,
260
+ forwarded,
261
+ ...(pendingWarning ? { pendingWarning } : {}),
262
+ };
245
263
  }
246
264
  catch (error) {
247
265
  deleteMessage(messageHandle);
@@ -270,8 +288,10 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
270
288
  throw new ConflictError(`No open dispatch for worker: ${worker.name}`);
271
289
  }
272
290
  workspaceStore.markTaskReported(workspaceId, workerId);
291
+ const remainingPendingTaskCount = workspaceStore.getWorker(workspaceId, workerId).pendingTaskCount;
273
292
  let forwardError = null;
274
293
  let forwarded = false;
294
+ const pendingWarning = buildPendingWarning(worker.name, remainingPendingTaskCount, 'report');
275
295
  // Workflow-sourced dispatches: the source is the in-process runner, not a
276
296
  // PTY. Resolve its awaiting Promise instead of injecting into orchestrator
277
297
  // stdin (which would do nothing — `__workflow__` has no PTY).
@@ -288,7 +308,12 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
288
308
  forwardError = reportForwardErrorMessage(error);
289
309
  console.error('[hive] swallowed:teamReport.workflowForward', error);
290
310
  }
291
- return { dispatch, forwardError, forwarded };
311
+ return {
312
+ dispatch,
313
+ forwardError,
314
+ forwarded,
315
+ ...(pendingWarning ? { pendingWarning } : {}),
316
+ };
292
317
  }
293
318
  // Real worker reported (not a workflow-internal step) — fire the
294
319
  // outbound completion webhook. Best-effort; never blocks the report.
@@ -369,7 +394,12 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
369
394
  }
370
395
  });
371
396
  }
372
- return { dispatch, forwardError, forwarded };
397
+ return {
398
+ dispatch,
399
+ forwardError,
400
+ forwarded,
401
+ ...(pendingWarning ? { pendingWarning } : {}),
402
+ };
373
403
  }
374
404
  catch (error) {
375
405
  deleteMessage(messageHandle);