@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,119 @@
1
+ import { RECALL_QUERY_MAX_CHARS } from '../shared/team-recall.js';
2
+ import { BadRequestError } from './http-errors.js';
3
+ import { readJsonBody, route, sendJson } from './route-helpers.js';
4
+ import { authenticateCliAgent, requireCommandForRole } from './team-authz.js';
5
+ const DEFAULT_RECALL_LIMIT = 10;
6
+ const requireNonEmptyString = (value, field) => {
7
+ if (typeof value !== 'string' || value.trim().length === 0) {
8
+ throw new BadRequestError(`Missing ${field}`);
9
+ }
10
+ const trimmed = value.trim();
11
+ if (field === 'query' && [...trimmed].length > RECALL_QUERY_MAX_CHARS) {
12
+ throw new BadRequestError(`query must be ${RECALL_QUERY_MAX_CHARS} characters or fewer`);
13
+ }
14
+ return trimmed;
15
+ };
16
+ const optionalNonNegativeInteger = (value, field) => {
17
+ if (value === undefined)
18
+ return undefined;
19
+ if (!Number.isInteger(value) || Number(value) < 0) {
20
+ throw new BadRequestError(`${field} must be a non-negative integer`);
21
+ }
22
+ return Number(value);
23
+ };
24
+ const serializeContext = (item) => ({
25
+ created_at: item.createdAt,
26
+ from_agent_id: item.fromAgentId,
27
+ source_sequence: item.sourceSequence,
28
+ text: item.text,
29
+ to_agent_id: item.toAgentId,
30
+ type: item.type,
31
+ worker_id: item.workerId,
32
+ });
33
+ const serializeRecallResult = (result) => ({
34
+ context: result.context.map(serializeContext),
35
+ created_at: result.createdAt,
36
+ dispatch_id: result.dispatchId,
37
+ dispatch_status: result.dispatchStatus,
38
+ from_agent_id: result.fromAgentId,
39
+ index_name: result.indexName,
40
+ memory_confidence: result.memoryConfidence ?? null,
41
+ memory_id: result.memoryId ?? null,
42
+ memory_kind: result.memoryKind ?? null,
43
+ memory_status: result.memoryStatus ?? null,
44
+ memory_tags: result.memoryTags ?? [],
45
+ message_type: result.messageType,
46
+ report_text: result.reportText,
47
+ score: result.score,
48
+ source_sequence: result.sourceSequence,
49
+ source_type: result.sourceType,
50
+ text: result.text,
51
+ to_agent_id: result.toAgentId,
52
+ worker_id: result.workerId,
53
+ });
54
+ const memoryToRecallResult = (memory) => ({
55
+ context: memory.sources.map((source) => ({
56
+ createdAt: source.createdAt,
57
+ fromAgentId: source.actorAgentIdSnapshot,
58
+ sourceSequence: source.sourceSequence ?? 0,
59
+ text: source.excerpt ?? '',
60
+ toAgentId: null,
61
+ type: source.sourceType,
62
+ workerId: source.actorAgentIdSnapshot ?? '',
63
+ })),
64
+ createdAt: memory.updatedAt,
65
+ dispatchId: null,
66
+ dispatchStatus: null,
67
+ fromAgentId: null,
68
+ indexName: memory.indexName,
69
+ memoryConfidence: memory.confidence,
70
+ memoryId: memory.id,
71
+ memoryKind: memory.kind,
72
+ memoryStatus: memory.status,
73
+ memoryTags: memory.tags,
74
+ messageType: null,
75
+ reportText: null,
76
+ score: memory.score,
77
+ sourceSequence: 0,
78
+ sourceType: 'memory',
79
+ text: memory.body,
80
+ toAgentId: null,
81
+ workerId: null,
82
+ });
83
+ export const teamRecallRoutes = [
84
+ route('POST', '/api/team/recall', async ({ request, response, store }) => {
85
+ const body = await readJsonBody(request);
86
+ const projectId = requireNonEmptyString(body.project_id, 'project_id');
87
+ const fromAgentId = requireNonEmptyString(body.from_agent_id, 'from_agent_id');
88
+ const query = requireNonEmptyString(body.query, 'query');
89
+ const token = typeof body.token === 'string' ? body.token : undefined;
90
+ const agent = authenticateCliAgent({
91
+ fromAgentId,
92
+ getAgent: store.getAgent,
93
+ token,
94
+ validateToken: store.validateAgentToken,
95
+ workspaceId: projectId,
96
+ });
97
+ requireCommandForRole(agent, 'recall');
98
+ const limit = optionalNonNegativeInteger(body.limit, 'limit');
99
+ const window = optionalNonNegativeInteger(body.window, 'window');
100
+ const recallResults = store.recallMessages(projectId, query, {
101
+ ...(limit !== undefined ? { limit } : {}),
102
+ ...(window !== undefined ? { window } : {}),
103
+ });
104
+ const memoryResults = store
105
+ .searchMemoryEntries(projectId, query, {
106
+ ...(limit !== undefined ? { limit } : {}),
107
+ statuses: ['active'],
108
+ })
109
+ .map(memoryToRecallResult);
110
+ const combined = [...recallResults, ...memoryResults]
111
+ .sort((a, b) => a.score - b.score || b.createdAt - a.createdAt)
112
+ .slice(0, limit ?? DEFAULT_RECALL_LIMIT);
113
+ sendJson(response, 200, {
114
+ ok: true,
115
+ query,
116
+ results: combined.map(serializeRecallResult),
117
+ });
118
+ }),
119
+ ];
@@ -26,6 +26,26 @@ const requireNonEmptyString = (value, field) => {
26
26
  return value;
27
27
  };
28
28
  const getArtifacts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === 'string') : [];
29
+ const REPORT_STATUSES = new Set(['success', 'failed']);
30
+ const getReportStatus = (value) => {
31
+ if (value === undefined)
32
+ return undefined;
33
+ if (typeof value !== 'string' || !REPORT_STATUSES.has(value)) {
34
+ throw new BadRequestError('Invalid status; expected success or failed');
35
+ }
36
+ return value;
37
+ };
38
+ const resolveSpawnLaunchConfig = (store, cli) => {
39
+ if (cli === undefined || cli === null || cli === '') {
40
+ return (resolveCommandPresetLaunchConfig(store.settings, 'claude') ?? { command: 'claude', args: [] });
41
+ }
42
+ const cliId = requireNonEmptyString(cli, 'cli');
43
+ const launchConfig = resolveCommandPresetLaunchConfig(store.settings, cliId);
44
+ if (!launchConfig) {
45
+ throw new BadRequestError(`Unsupported cli '${cliId}'`);
46
+ }
47
+ return launchConfig;
48
+ };
29
49
  export const teamRoutes = [
30
50
  route('POST', '/api/team/send', async ({ request, response, store }) => {
31
51
  const body = await readJsonBody(request);
@@ -42,14 +62,13 @@ export const teamRoutes = [
42
62
  });
43
63
  requireCommandForRole(agent, 'send');
44
64
  const dispatch = await store.dispatchTaskByWorkerName(projectId, to, text, {
65
+ autoStartWorker: false,
45
66
  fromAgentId,
46
67
  hivePort: String(request.socket.localPort ?? ''),
47
68
  });
48
- /* `restarted_worker` makes the silent auto-wake transparent when
49
- the worker had no active run at dispatch time, ensureWorkerRun
50
- spawned a fresh PTY for it. The orchestrator's CLI prints a
51
- stderr notice on this flag so the agent can explain the wake-up
52
- to the user instead of being out of sync with worker state. */
69
+ /* The user-facing team send route queues work for stopped workers without
70
+ auto-starting them; internal workflow dispatches may still surface
71
+ restarted_worker when they intentionally wake a worker. */
53
72
  sendJson(response, 202, {
54
73
  dispatch_id: dispatch.id,
55
74
  ok: true,
@@ -72,8 +91,7 @@ export const teamRoutes = [
72
91
  const name = typeof body.name === 'string' && body.name.trim().length > 0
73
92
  ? body.name.trim()
74
93
  : `${role}-${randomUUID()}`;
75
- const launchConfig = (body.cli ? resolveCommandPresetLaunchConfig(store.settings, body.cli) : undefined) ??
76
- { command: body.cli ?? 'claude', args: [] };
94
+ const launchConfig = resolveSpawnLaunchConfig(store, body.cli);
77
95
  // M11: default is persistent (acts like a normal member); --ephemeral
78
96
  // opts into auto-dismiss-after-first-report (mirrors workflow workers,
79
97
  // but orchestrator-spawned). Existing M1-D cascade-on-orchestrator-exit
@@ -289,16 +307,18 @@ export const teamRoutes = [
289
307
  requireActiveRun: true,
290
308
  text: resultText,
291
309
  };
292
- if (typeof body.status === 'string') {
310
+ const status = getReportStatus(body.status);
311
+ if (status !== undefined) {
293
312
  const result = store.reportTask(projectId, fromAgentId, {
294
313
  ...reportInput,
295
- status: body.status,
314
+ status,
296
315
  });
297
316
  sendJson(response, 202, {
298
317
  dispatch_id: result.dispatch?.id ?? null,
299
318
  forward_error: result.forwardError,
300
319
  forwarded: result.forwarded,
301
320
  ok: true,
321
+ ...(result.pendingWarning ? { pending_warning: result.pendingWarning } : {}),
302
322
  });
303
323
  return;
304
324
  }
@@ -309,6 +329,7 @@ export const teamRoutes = [
309
329
  forward_error: result.forwardError,
310
330
  forwarded: result.forwarded,
311
331
  ok: true,
332
+ ...(result.pendingWarning ? { pending_warning: result.pendingWarning } : {}),
312
333
  });
313
334
  return;
314
335
  }
@@ -336,6 +357,7 @@ export const teamRoutes = [
336
357
  forward_error: result.forwardError,
337
358
  forwarded: result.forwarded,
338
359
  ok: true,
360
+ ...(result.pendingWarning ? { pending_warning: result.pendingWarning } : {}),
339
361
  });
340
362
  return;
341
363
  }),
@@ -1,6 +1,16 @@
1
+ import { ForbiddenError } from './http-errors.js';
1
2
  import { route, sendJson } from './route-helpers.js';
2
3
  export const uiRoutes = [
3
- route('GET', '/api/ui/session', ({ response, store }) => {
4
+ route('GET', '/api/ui/session', ({ request, response, store }) => {
5
+ // HARDEN (defense in depth, layer 3): this route mints the master
6
+ // hive_ui_token cookie, the single credential for every /api/* route + WS
7
+ // upgrade. The bridge whitelist already hard-denies /api/ui/session, but if
8
+ // that ever regresses, a tunnel-tagged request must STILL never mint a
9
+ // cookie for a remote device. The tunnel is authorized by the per-boot
10
+ // secret, so it has zero need for the UI cookie — refuse it outright.
11
+ if (store.authorizeRemoteTunnelRequest(request)) {
12
+ throw new ForbiddenError('UI session cookie is not available over the remote tunnel');
13
+ }
4
14
  response.setHeader('set-cookie', `hive_ui_token=${store.getUiToken()}; Path=/; HttpOnly; SameSite=Strict`);
5
15
  sendJson(response, 200, { ok: true });
6
16
  }),
@@ -8,7 +8,7 @@ import { serializeWorkflowSchedule } from './workflow-http-serializers.js';
8
8
  // no human create route here.
9
9
  export const workflowScheduleRoutes = [
10
10
  route('GET', '/api/workspaces/:workspaceId/workflow-schedules', ({ params, request, response, store }) => {
11
- requireUiTokenFromRequest(request, store.validateUiToken);
11
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
12
12
  const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Missing workspaceId');
13
13
  if (!workspaceId)
14
14
  return;
@@ -17,7 +17,7 @@ export const workflowScheduleRoutes = [
17
17
  });
18
18
  }),
19
19
  route('PATCH', '/api/workflow-schedules/:scheduleId', async ({ params, request, response, store }) => {
20
- requireUiTokenFromRequest(request, store.validateUiToken);
20
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
21
21
  const scheduleId = getRequiredParam(response, params, 'scheduleId', 'Missing scheduleId');
22
22
  if (!scheduleId)
23
23
  return;
@@ -43,7 +43,7 @@ export const workflowScheduleRoutes = [
43
43
  });
44
44
  }),
45
45
  route('DELETE', '/api/workflow-schedules/:scheduleId', ({ params, request, response, store }) => {
46
- requireUiTokenFromRequest(request, store.validateUiToken);
46
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
47
47
  const scheduleId = getRequiredParam(response, params, 'scheduleId', 'Missing scheduleId');
48
48
  if (!scheduleId)
49
49
  return;
@@ -8,7 +8,7 @@ import { serializeWorkflowDispatch, serializeWorkflowRun } from './workflow-http
8
8
  // start-by-path, template-install, or source-editor route.
9
9
  export const workflowRoutes = [
10
10
  route('GET', '/api/workspaces/:workspaceId/workflows/runs', ({ params, request, response, store }) => {
11
- requireUiTokenFromRequest(request, store.validateUiToken);
11
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
12
12
  const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Missing workspaceId');
13
13
  if (!workspaceId)
14
14
  return;
@@ -17,7 +17,7 @@ export const workflowRoutes = [
17
17
  });
18
18
  }),
19
19
  route('GET', '/api/workflows/runs/:runId', ({ params, request, response, store }) => {
20
- requireUiTokenFromRequest(request, store.validateUiToken);
20
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
21
21
  const runId = getRequiredParam(response, params, 'runId', 'Missing runId');
22
22
  if (!runId)
23
23
  return;
@@ -29,7 +29,7 @@ export const workflowRoutes = [
29
29
  sendJson(response, 200, { run: serializeWorkflowRun(run) });
30
30
  }),
31
31
  route('POST', '/api/workflows/runs/:runId/stop', ({ params, request, response, store }) => {
32
- requireUiTokenFromRequest(request, store.validateUiToken);
32
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
33
33
  const runId = getRequiredParam(response, params, 'runId', 'Missing runId');
34
34
  if (!runId)
35
35
  return;
@@ -45,7 +45,7 @@ export const workflowRoutes = [
45
45
  });
46
46
  }),
47
47
  route('GET', '/api/workflows/runs/:runId/dispatches', ({ params, request, response, store }) => {
48
- requireUiTokenFromRequest(request, store.validateUiToken);
48
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
49
49
  const runId = getRequiredParam(response, params, 'runId', 'Missing runId');
50
50
  if (!runId)
51
51
  return;
@@ -70,7 +70,7 @@ export const workflowRoutes = [
70
70
  /* TIER 2 #3 — narrator lane. Drawer polls this alongside dispatches.
71
71
  Same auth path; same 404 semantics. */
72
72
  route('GET', '/api/workflows/runs/:runId/logs', ({ params, request, response, store }) => {
73
- requireUiTokenFromRequest(request, store.validateUiToken);
73
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
74
74
  const runId = getRequiredParam(response, params, 'runId', 'Missing runId');
75
75
  if (!runId)
76
76
  return;
@@ -0,0 +1,2 @@
1
+ import type { RouteDefinition } from './route-types.js';
2
+ export declare const workspaceMemoryDreamRoutes: RouteDefinition[];
@@ -0,0 +1,105 @@
1
+ import { BadRequestError, ConflictError } from './http-errors.js';
2
+ import { getRequiredParam, readJsonBody, route, sendJson } from './route-helpers.js';
3
+ import { serializeDreamRun } from './team-memory-dream-http-serializers.js';
4
+ import { DreamRunAlreadyRunningError, DreamRunNotFoundError, DreamRunRevertDataError, DreamRunRevertStatusError, DreamWorkspaceMissingError, } from './team-memory-dream-store.js';
5
+ import { readWorkspaceMemoryDreamEnabled, readWorkspaceMemoryEnabled, workspaceMemoryDreamEnabledKey, workspaceMemoryEnabledKey, } from './team-memory-feature.js';
6
+ import { requireUiTokenFromRequest } from './ui-auth-helpers.js';
7
+ const DREAM_RUN_LIST_MAX_LIMIT = 50;
8
+ const requireWorkspace = (response, store, workspaceId) => {
9
+ if (store.listWorkspaces().some((workspace) => workspace.id === workspaceId))
10
+ return true;
11
+ sendJson(response, 404, { error: `Workspace not found: ${workspaceId}` });
12
+ return false;
13
+ };
14
+ const requireDreamEnabled = (store, workspaceId) => {
15
+ const memoryEnabled = readWorkspaceMemoryEnabled(store.settings.getAppState(workspaceMemoryEnabledKey(workspaceId))?.value);
16
+ const dreamEnabled = readWorkspaceMemoryDreamEnabled(store.settings.getAppState(workspaceMemoryDreamEnabledKey(workspaceId))?.value);
17
+ if (!memoryEnabled)
18
+ throw new ConflictError('Workspace memory is disabled');
19
+ if (!dreamEnabled)
20
+ throw new ConflictError('Workspace memory dream is disabled');
21
+ };
22
+ const hasRequestBody = (request) => {
23
+ const rawContentLength = request.headers['content-length'];
24
+ const contentLength = Array.isArray(rawContentLength) ? rawContentLength[0] : rawContentLength;
25
+ return contentLength !== undefined && Number(contentLength) > 0;
26
+ };
27
+ const parseLimit = (value) => {
28
+ if (value === null)
29
+ return 20;
30
+ if (!/^(0|[1-9][0-9]*)$/.test(value)) {
31
+ throw new BadRequestError('limit must be a non-negative integer');
32
+ }
33
+ return Math.min(Number(value), DREAM_RUN_LIST_MAX_LIMIT);
34
+ };
35
+ export const workspaceMemoryDreamRoutes = [
36
+ route('GET', '/api/ui/workspaces/:workspaceId/memory/dream-runs', ({ params, request, response, store }) => {
37
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
38
+ const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Missing workspaceId');
39
+ if (!workspaceId)
40
+ return;
41
+ if (!requireWorkspace(response, store, workspaceId))
42
+ return;
43
+ const url = new URL(request.url ?? '/', 'http://127.0.0.1');
44
+ const runs = store.listMemoryDreamRuns(workspaceId, parseLimit(url.searchParams.get('limit')));
45
+ sendJson(response, 200, { ok: true, runs: runs.map(serializeDreamRun) });
46
+ }),
47
+ route('POST', '/api/ui/workspaces/:workspaceId/memory/dream-runs', async ({ params, request, response, store }) => {
48
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
49
+ const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Missing workspaceId');
50
+ if (!workspaceId)
51
+ return;
52
+ if (!requireWorkspace(response, store, workspaceId))
53
+ return;
54
+ requireDreamEnabled(store, workspaceId);
55
+ if (hasRequestBody(request))
56
+ await readJsonBody(request);
57
+ try {
58
+ const run = await store.runMemoryDream(workspaceId);
59
+ sendJson(response, 200, { ok: true, run: serializeDreamRun(run) });
60
+ }
61
+ catch (error) {
62
+ if (error instanceof DreamRunAlreadyRunningError) {
63
+ sendJson(response, 409, { error: 'Workspace already has a running dream run' });
64
+ return;
65
+ }
66
+ if (error instanceof DreamWorkspaceMissingError) {
67
+ sendJson(response, 404, { error: `Workspace not found: ${error.workspaceId}` });
68
+ return;
69
+ }
70
+ throw error;
71
+ }
72
+ }),
73
+ route('POST', '/api/ui/workspaces/:workspaceId/memory/dream-runs/:runId/revert', async ({ params, request, response, store }) => {
74
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
75
+ const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Missing workspaceId');
76
+ const runId = getRequiredParam(response, params, 'runId', 'Missing runId');
77
+ if (!workspaceId || !runId)
78
+ return;
79
+ if (!requireWorkspace(response, store, workspaceId))
80
+ return;
81
+ if (hasRequestBody(request))
82
+ await readJsonBody(request);
83
+ try {
84
+ const run = store.revertMemoryDream(workspaceId, runId);
85
+ sendJson(response, 200, { ok: true, run: serializeDreamRun(run) });
86
+ }
87
+ catch (error) {
88
+ if (error instanceof DreamRunNotFoundError) {
89
+ sendJson(response, 404, { error: `Dream run not found: ${error.runId}` });
90
+ return;
91
+ }
92
+ if (error instanceof DreamRunRevertDataError) {
93
+ sendJson(response, 409, { error: 'Dream run cannot be reverted from stored data' });
94
+ return;
95
+ }
96
+ if (error instanceof DreamRunRevertStatusError) {
97
+ sendJson(response, 409, {
98
+ error: `Dream run has status ${error.actualStatus}; expected ${error.expectedStatus}`,
99
+ });
100
+ return;
101
+ }
102
+ throw error;
103
+ }
104
+ }),
105
+ ];
@@ -0,0 +1,2 @@
1
+ import type { RouteDefinition } from './route-types.js';
2
+ export declare const workspaceMemoryRoutes: RouteDefinition[];
@@ -0,0 +1,215 @@
1
+ import { MEMORY_QUERY_MAX_CHARS, MEMORY_SEARCH_MAX_LIMIT } from '../shared/team-memory.js';
2
+ import { BadRequestError } from './http-errors.js';
3
+ import { getRequiredParam, readJsonBody, route, sendJson } from './route-helpers.js';
4
+ import { readWorkspaceMemoryDreamEnabled, readWorkspaceMemoryEnabled, serializeWorkspaceMemoryDreamEnabled, serializeWorkspaceMemoryEnabled, workspaceMemoryDreamEnabledKey, workspaceMemoryEnabledKey, } from './team-memory-feature.js';
5
+ import { serializeMemoryEntry, serializeMemoryInjection } from './team-memory-http-serializers.js';
6
+ import { MemoryEntryNotFoundError, MemoryEntryStatusError, } from './team-memory-store.js';
7
+ import { requireUiTokenFromRequest } from './ui-auth-helpers.js';
8
+ const MEMORY_STATUSES = ['active', 'candidate', 'archived', 'rejected'];
9
+ const parseLimit = (value) => {
10
+ if (value === null)
11
+ return MEMORY_SEARCH_MAX_LIMIT;
12
+ if (!/^(0|[1-9][0-9]*)$/.test(value)) {
13
+ throw new BadRequestError('limit must be a non-negative integer');
14
+ }
15
+ return Math.min(Number(value), MEMORY_SEARCH_MAX_LIMIT);
16
+ };
17
+ const parseStatuses = (value) => {
18
+ if (value === null)
19
+ return ['active'];
20
+ if (value === 'all')
21
+ return MEMORY_STATUSES;
22
+ if (MEMORY_STATUSES.includes(value))
23
+ return [value];
24
+ throw new BadRequestError('status must be active, candidate, archived, rejected, or all');
25
+ };
26
+ const parseQuery = (value) => {
27
+ const query = value?.trim() ?? '';
28
+ if ([...query].length > MEMORY_QUERY_MAX_CHARS) {
29
+ throw new BadRequestError(`query must be ${MEMORY_QUERY_MAX_CHARS} characters or fewer`);
30
+ }
31
+ return query;
32
+ };
33
+ const requireBoolean = (value, field) => {
34
+ if (typeof value !== 'boolean')
35
+ throw new BadRequestError(`${field} must be a boolean`);
36
+ return value;
37
+ };
38
+ const sendMemoryMutationError = (response, error) => {
39
+ if (error instanceof MemoryEntryNotFoundError) {
40
+ sendJson(response, 404, { error: `Memory entry not found: ${error.memoryId}` });
41
+ return true;
42
+ }
43
+ if (error instanceof MemoryEntryStatusError) {
44
+ sendJson(response, 409, {
45
+ error: `Memory entry has status ${error.actualStatus}; expected ${Array.isArray(error.expectedStatus)
46
+ ? error.expectedStatus.join(' or ')
47
+ : error.expectedStatus}`,
48
+ });
49
+ return true;
50
+ }
51
+ return false;
52
+ };
53
+ const getMemoryId = (response, params) => getRequiredParam(response, params, 'memoryId', 'Missing memoryId');
54
+ const requireWorkspace = (response, store, workspaceId) => {
55
+ if (store.listWorkspaces().some((workspace) => workspace.id === workspaceId))
56
+ return true;
57
+ sendJson(response, 404, { error: `Workspace not found: ${workspaceId}` });
58
+ return false;
59
+ };
60
+ export const workspaceMemoryRoutes = [
61
+ route('GET', '/api/ui/workspaces/:workspaceId/memory', ({ params, request, response, store }) => {
62
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
63
+ const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Missing workspaceId');
64
+ if (!workspaceId)
65
+ return;
66
+ if (!requireWorkspace(response, store, workspaceId))
67
+ return;
68
+ const url = new URL(request.url ?? '/', 'http://127.0.0.1');
69
+ const statuses = parseStatuses(url.searchParams.get('status'));
70
+ const limit = parseLimit(url.searchParams.get('limit'));
71
+ const query = parseQuery(url.searchParams.get('query'));
72
+ const memories = query
73
+ ? store.searchMemoryEntries(workspaceId, query, {
74
+ includeDisabled: true,
75
+ limit,
76
+ statuses,
77
+ })
78
+ : store.listMemoryEntries(workspaceId, { limit, statuses });
79
+ sendJson(response, 200, { memories: memories.map(serializeMemoryEntry), ok: true });
80
+ }),
81
+ route('GET', '/api/ui/workspaces/:workspaceId/memory/injections', ({ params, request, response, store }) => {
82
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
83
+ const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Missing workspaceId');
84
+ if (!workspaceId)
85
+ return;
86
+ if (!requireWorkspace(response, store, workspaceId))
87
+ return;
88
+ const url = new URL(request.url ?? '/', 'http://127.0.0.1');
89
+ const dispatchId = url.searchParams.get('dispatch_id')?.trim();
90
+ if (!dispatchId)
91
+ throw new BadRequestError('Missing dispatch_id');
92
+ sendJson(response, 200, {
93
+ injections: store
94
+ .listMemoryInjectionsForDispatch(workspaceId, dispatchId)
95
+ .map(serializeMemoryInjection),
96
+ ok: true,
97
+ });
98
+ }),
99
+ route('GET', '/api/ui/workspaces/:workspaceId/memory/settings', ({ params, request, response, store }) => {
100
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
101
+ const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Missing workspaceId');
102
+ if (!workspaceId)
103
+ return;
104
+ if (!requireWorkspace(response, store, workspaceId))
105
+ return;
106
+ sendJson(response, 200, {
107
+ dream_enabled: readWorkspaceMemoryDreamEnabled(store.settings.getAppState(workspaceMemoryDreamEnabledKey(workspaceId))?.value),
108
+ enabled: readWorkspaceMemoryEnabled(store.settings.getAppState(workspaceMemoryEnabledKey(workspaceId))?.value),
109
+ ok: true,
110
+ });
111
+ }),
112
+ route('PUT', '/api/ui/workspaces/:workspaceId/memory/settings', async ({ params, request, response, store }) => {
113
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
114
+ const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Missing workspaceId');
115
+ if (!workspaceId)
116
+ return;
117
+ if (!requireWorkspace(response, store, workspaceId))
118
+ return;
119
+ const body = await readJsonBody(request);
120
+ if (body.enabled !== undefined) {
121
+ store.settings.setAppState(workspaceMemoryEnabledKey(workspaceId), serializeWorkspaceMemoryEnabled(requireBoolean(body.enabled, 'enabled')));
122
+ }
123
+ if (body.dream_enabled !== undefined) {
124
+ store.settings.setAppState(workspaceMemoryDreamEnabledKey(workspaceId), serializeWorkspaceMemoryDreamEnabled(requireBoolean(body.dream_enabled, 'dream_enabled')));
125
+ }
126
+ sendJson(response, 200, {
127
+ dream_enabled: readWorkspaceMemoryDreamEnabled(store.settings.getAppState(workspaceMemoryDreamEnabledKey(workspaceId))?.value),
128
+ enabled: readWorkspaceMemoryEnabled(store.settings.getAppState(workspaceMemoryEnabledKey(workspaceId))?.value),
129
+ ok: true,
130
+ });
131
+ }),
132
+ route('PATCH', '/api/ui/workspaces/:workspaceId/memory/:memoryId', async ({ params, request, response, store }) => {
133
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
134
+ const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Missing workspaceId');
135
+ const memoryId = getMemoryId(response, params);
136
+ if (!workspaceId || !memoryId)
137
+ return;
138
+ if (!requireWorkspace(response, store, workspaceId))
139
+ return;
140
+ const body = await readJsonBody(request);
141
+ try {
142
+ let memory = store.getMemoryEntry(workspaceId, memoryId);
143
+ if (!memory)
144
+ throw new MemoryEntryNotFoundError(memoryId, workspaceId);
145
+ if (body.pinned !== undefined) {
146
+ memory = store.setMemoryPinned(workspaceId, memoryId, requireBoolean(body.pinned, 'pinned'));
147
+ }
148
+ if (body.disabled !== undefined) {
149
+ memory = store.setMemoryDisabled(workspaceId, memoryId, requireBoolean(body.disabled, 'disabled'));
150
+ }
151
+ sendJson(response, 200, { memory: serializeMemoryEntry(memory), ok: true });
152
+ }
153
+ catch (error) {
154
+ if (!sendMemoryMutationError(response, error))
155
+ throw error;
156
+ }
157
+ }),
158
+ route('POST', '/api/ui/workspaces/:workspaceId/memory/:memoryId/approve', ({ params, request, response, store }) => {
159
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
160
+ const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Missing workspaceId');
161
+ const memoryId = getMemoryId(response, params);
162
+ if (!workspaceId || !memoryId)
163
+ return;
164
+ if (!requireWorkspace(response, store, workspaceId))
165
+ return;
166
+ try {
167
+ sendJson(response, 200, {
168
+ memory: serializeMemoryEntry(store.approveMemoryCandidate(workspaceId, memoryId)),
169
+ ok: true,
170
+ });
171
+ }
172
+ catch (error) {
173
+ if (!sendMemoryMutationError(response, error))
174
+ throw error;
175
+ }
176
+ }),
177
+ route('POST', '/api/ui/workspaces/:workspaceId/memory/:memoryId/reject', ({ params, request, response, store }) => {
178
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
179
+ const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Missing workspaceId');
180
+ const memoryId = getMemoryId(response, params);
181
+ if (!workspaceId || !memoryId)
182
+ return;
183
+ if (!requireWorkspace(response, store, workspaceId))
184
+ return;
185
+ try {
186
+ sendJson(response, 200, {
187
+ memory: serializeMemoryEntry(store.rejectMemoryCandidate(workspaceId, memoryId)),
188
+ ok: true,
189
+ });
190
+ }
191
+ catch (error) {
192
+ if (!sendMemoryMutationError(response, error))
193
+ throw error;
194
+ }
195
+ }),
196
+ route('POST', '/api/ui/workspaces/:workspaceId/memory/:memoryId/archive', ({ params, request, response, store }) => {
197
+ requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
198
+ const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Missing workspaceId');
199
+ const memoryId = getMemoryId(response, params);
200
+ if (!workspaceId || !memoryId)
201
+ return;
202
+ if (!requireWorkspace(response, store, workspaceId))
203
+ return;
204
+ try {
205
+ sendJson(response, 200, {
206
+ memory: serializeMemoryEntry(store.archiveMemoryEntry(workspaceId, memoryId)),
207
+ ok: true,
208
+ });
209
+ }
210
+ catch (error) {
211
+ if (!sendMemoryMutationError(response, error))
212
+ throw error;
213
+ }
214
+ }),
215
+ ];