@swarmclawai/swarmclaw 1.2.0 → 1.2.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 (241) hide show
  1. package/README.md +19 -0
  2. package/package.json +5 -2
  3. package/skills/coding-agent/SKILL.md +111 -0
  4. package/skills/github/SKILL.md +140 -0
  5. package/skills/nano-banana-pro/SKILL.md +62 -0
  6. package/skills/nano-banana-pro/scripts/generate_image.py +235 -0
  7. package/skills/nano-pdf/SKILL.md +53 -0
  8. package/skills/openai-image-gen/SKILL.md +78 -0
  9. package/skills/openai-image-gen/scripts/gen.py +328 -0
  10. package/skills/resourceful-problem-solving/SKILL.md +49 -0
  11. package/skills/skill-creator/SKILL.md +147 -0
  12. package/skills/skill-creator/scripts/init_skill.py +378 -0
  13. package/skills/skill-creator/scripts/quick_validate.py +159 -0
  14. package/skills/summarize/SKILL.md +77 -0
  15. package/src/app/api/auth/route.ts +20 -5
  16. package/src/app/api/chats/[id]/deploy/route.ts +11 -6
  17. package/src/app/api/chats/[id]/devserver/route.ts +17 -20
  18. package/src/app/api/chats/[id]/messages/route.ts +15 -11
  19. package/src/app/api/chats/[id]/route.ts +9 -10
  20. package/src/app/api/chats/[id]/stop/route.ts +5 -7
  21. package/src/app/api/chats/messages-route.test.ts +8 -6
  22. package/src/app/api/chats/route.ts +9 -10
  23. package/src/app/api/credentials/[id]/route.ts +4 -1
  24. package/src/app/api/extensions/marketplace/route.ts +5 -2
  25. package/src/app/api/ip/route.ts +2 -2
  26. package/src/app/api/memory/maintenance/route.ts +5 -2
  27. package/src/app/api/preview-server/route.ts +15 -12
  28. package/src/app/api/projects/[id]/route.ts +7 -46
  29. package/src/app/api/system/status/route.ts +11 -0
  30. package/src/app/api/upload/route.ts +4 -1
  31. package/src/cli/index.js +7 -0
  32. package/src/cli/spec.js +1 -0
  33. package/src/components/agents/agent-files-editor.tsx +44 -32
  34. package/src/components/agents/personality-builder.tsx +13 -7
  35. package/src/components/agents/trash-list.tsx +1 -1
  36. package/src/components/chat/chat-area.tsx +45 -23
  37. package/src/components/chat/message-bubble.test.ts +35 -0
  38. package/src/components/chat/message-bubble.tsx +20 -9
  39. package/src/components/chat/message-list.tsx +62 -42
  40. package/src/components/chat/swarm-status-card.tsx +10 -3
  41. package/src/components/input/chat-input.tsx +34 -14
  42. package/src/components/layout/daemon-indicator.tsx +7 -8
  43. package/src/components/layout/update-banner.tsx +8 -13
  44. package/src/components/logs/log-list.tsx +1 -1
  45. package/src/components/memory/memory-card.tsx +3 -1
  46. package/src/components/org-chart/org-chart-view.tsx +4 -0
  47. package/src/components/projects/project-list.tsx +4 -2
  48. package/src/components/projects/tabs/overview-tab.tsx +3 -2
  49. package/src/components/secrets/secret-sheet.tsx +1 -1
  50. package/src/components/secrets/secrets-list.tsx +1 -1
  51. package/src/components/shared/agent-switch-dialog.tsx +12 -6
  52. package/src/components/shared/dir-browser.tsx +22 -18
  53. package/src/components/skills/skill-sheet.tsx +2 -3
  54. package/src/components/tasks/task-list.tsx +1 -1
  55. package/src/components/tasks/task-sheet.tsx +1 -1
  56. package/src/hooks/use-openclaw-gateway.ts +46 -27
  57. package/src/instrumentation.ts +10 -7
  58. package/src/lib/chat/assistant-render-id.ts +3 -0
  59. package/src/lib/chat/chat-streaming-state.test.ts +42 -3
  60. package/src/lib/chat/chat-streaming-state.ts +20 -8
  61. package/src/lib/chat/chat.ts +18 -2
  62. package/src/lib/chat/queued-message-queue.test.ts +23 -1
  63. package/src/lib/chat/queued-message-queue.ts +11 -2
  64. package/src/lib/providers/anthropic.ts +6 -3
  65. package/src/lib/providers/claude-cli.ts +9 -3
  66. package/src/lib/providers/cli-utils.test.ts +124 -0
  67. package/src/lib/providers/cli-utils.ts +15 -0
  68. package/src/lib/providers/codex-cli.ts +9 -3
  69. package/src/lib/providers/gemini-cli.ts +6 -2
  70. package/src/lib/providers/index.ts +4 -1
  71. package/src/lib/providers/ollama.ts +5 -2
  72. package/src/lib/providers/openai.ts +8 -5
  73. package/src/lib/providers/opencode-cli.ts +6 -2
  74. package/src/lib/server/activity/activity-log.ts +21 -0
  75. package/src/lib/server/agents/agent-availability.test.ts +10 -5
  76. package/src/lib/server/agents/agent-cascade.ts +79 -59
  77. package/src/lib/server/agents/agent-registry.ts +23 -4
  78. package/src/lib/server/agents/agent-repository.ts +90 -0
  79. package/src/lib/server/agents/delegation-job-repository.ts +53 -0
  80. package/src/lib/server/agents/delegation-jobs.ts +11 -4
  81. package/src/lib/server/agents/guardian-checkpoint-repository.ts +35 -0
  82. package/src/lib/server/agents/guardian.ts +2 -2
  83. package/src/lib/server/agents/main-agent-loop.ts +14 -6
  84. package/src/lib/server/agents/main-loop-state-repository.ts +38 -0
  85. package/src/lib/server/agents/subagent-runtime.ts +9 -6
  86. package/src/lib/server/agents/subagent-swarm.ts +3 -2
  87. package/src/lib/server/agents/task-session.ts +3 -4
  88. package/src/lib/server/approvals/approval-repository.ts +30 -0
  89. package/src/lib/server/autonomy/supervisor-incident-repository.ts +42 -0
  90. package/src/lib/server/autonomy/supervisor-reflection.ts +14 -1
  91. package/src/lib/server/chat-execution/chat-execution-types.ts +38 -0
  92. package/src/lib/server/chat-execution/chat-execution-utils.ts +1 -1
  93. package/src/lib/server/chat-execution/chat-execution.ts +84 -1914
  94. package/src/lib/server/chat-execution/chat-turn-finalization.ts +620 -0
  95. package/src/lib/server/chat-execution/chat-turn-partial-persistence.ts +221 -0
  96. package/src/lib/server/chat-execution/chat-turn-preflight.ts +133 -0
  97. package/src/lib/server/chat-execution/chat-turn-preparation.ts +817 -0
  98. package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +296 -0
  99. package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +5 -5
  100. package/src/lib/server/chat-execution/continuation-evaluator.ts +4 -3
  101. package/src/lib/server/chat-execution/continuation-limits.ts +6 -3
  102. package/src/lib/server/chat-execution/message-classifier.test.ts +329 -0
  103. package/src/lib/server/chat-execution/message-classifier.ts +5 -2
  104. package/src/lib/server/chat-execution/post-stream-finalization.ts +5 -2
  105. package/src/lib/server/chat-execution/prompt-builder.ts +22 -1
  106. package/src/lib/server/chat-execution/prompt-sections.ts +55 -13
  107. package/src/lib/server/chat-execution/response-completeness.ts +5 -2
  108. package/src/lib/server/chat-execution/situational-awareness.ts +12 -7
  109. package/src/lib/server/chat-execution/stream-agent-chat.ts +58 -25
  110. package/src/lib/server/chatrooms/chatroom-memory-bridge.ts +6 -3
  111. package/src/lib/server/chatrooms/chatroom-repository.ts +32 -0
  112. package/src/lib/server/connectors/bluebubbles.ts +7 -4
  113. package/src/lib/server/connectors/connector-inbound.ts +16 -13
  114. package/src/lib/server/connectors/connector-lifecycle.ts +11 -8
  115. package/src/lib/server/connectors/connector-outbound.ts +6 -3
  116. package/src/lib/server/connectors/connector-repository.ts +58 -0
  117. package/src/lib/server/connectors/discord.ts +10 -7
  118. package/src/lib/server/connectors/email.ts +17 -14
  119. package/src/lib/server/connectors/googlechat.ts +7 -4
  120. package/src/lib/server/connectors/inbound-audio-transcription.ts +5 -2
  121. package/src/lib/server/connectors/matrix.ts +6 -3
  122. package/src/lib/server/connectors/openclaw.ts +20 -17
  123. package/src/lib/server/connectors/outbox.ts +4 -1
  124. package/src/lib/server/connectors/runtime-state.test.ts +117 -0
  125. package/src/lib/server/connectors/runtime-state.ts +19 -0
  126. package/src/lib/server/connectors/session-consolidation.ts +5 -2
  127. package/src/lib/server/connectors/signal.ts +9 -6
  128. package/src/lib/server/connectors/slack.ts +13 -10
  129. package/src/lib/server/connectors/teams.ts +8 -5
  130. package/src/lib/server/connectors/telegram.ts +15 -12
  131. package/src/lib/server/connectors/whatsapp.ts +32 -29
  132. package/src/lib/server/credentials/credential-repository.ts +7 -0
  133. package/src/lib/server/embeddings.ts +4 -1
  134. package/src/lib/server/gateways/gateway-profile-repository.ts +4 -0
  135. package/src/lib/server/link-understanding.ts +4 -1
  136. package/src/lib/server/memory/memory-abstract.test.ts +59 -0
  137. package/src/lib/server/memory/memory-abstract.ts +59 -0
  138. package/src/lib/server/memory/memory-db.ts +40 -14
  139. package/src/lib/server/missions/mission-repository.ts +74 -0
  140. package/src/lib/server/missions/mission-service/actions.ts +6 -0
  141. package/src/lib/server/missions/mission-service/bindings.ts +9 -0
  142. package/src/lib/server/missions/mission-service/context.ts +4 -0
  143. package/src/lib/server/missions/mission-service/core.ts +2269 -0
  144. package/src/lib/server/missions/mission-service/queries.ts +12 -0
  145. package/src/lib/server/missions/mission-service/recovery.ts +5 -0
  146. package/src/lib/server/missions/mission-service/ticks.ts +9 -0
  147. package/src/lib/server/missions/mission-service.test.ts +9 -2
  148. package/src/lib/server/missions/mission-service.ts +6 -2263
  149. package/src/lib/server/openclaw/gateway.ts +8 -5
  150. package/src/lib/server/persistence/repository-utils.ts +154 -0
  151. package/src/lib/server/persistence/storage-context.ts +51 -0
  152. package/src/lib/server/persistence/transaction.ts +1 -0
  153. package/src/lib/server/project-utils.ts +13 -0
  154. package/src/lib/server/projects/project-repository.ts +36 -0
  155. package/src/lib/server/projects/project-service.ts +79 -0
  156. package/src/lib/server/protocols/protocol-agent-turn.ts +5 -2
  157. package/src/lib/server/protocols/protocol-normalization.test.ts +6 -4
  158. package/src/lib/server/protocols/protocol-run-lifecycle.ts +5 -2
  159. package/src/lib/server/protocols/protocol-step-helpers.ts +4 -1
  160. package/src/lib/server/provider-health.ts +18 -0
  161. package/src/lib/server/query-expansion.ts +4 -1
  162. package/src/lib/server/runtime/alert-dispatch.ts +8 -7
  163. package/src/lib/server/runtime/daemon-policy.ts +1 -1
  164. package/src/lib/server/runtime/daemon-state/core.ts +1570 -0
  165. package/src/lib/server/runtime/daemon-state/health.ts +6 -0
  166. package/src/lib/server/runtime/daemon-state/policy.ts +7 -0
  167. package/src/lib/server/runtime/daemon-state/supervisor.ts +6 -0
  168. package/src/lib/server/runtime/daemon-state.test.ts +48 -0
  169. package/src/lib/server/runtime/daemon-state.ts +3 -1331
  170. package/src/lib/server/runtime/estop-repository.ts +4 -0
  171. package/src/lib/server/runtime/estop.ts +3 -1
  172. package/src/lib/server/runtime/heartbeat-service.test.ts +2 -2
  173. package/src/lib/server/runtime/heartbeat-service.ts +78 -34
  174. package/src/lib/server/runtime/heartbeat-wake.ts +6 -4
  175. package/src/lib/server/runtime/idle-window.ts +6 -3
  176. package/src/lib/server/runtime/network.ts +11 -0
  177. package/src/lib/server/runtime/orchestrator-events.ts +2 -2
  178. package/src/lib/server/runtime/perf.ts +4 -1
  179. package/src/lib/server/runtime/process-manager.ts +7 -4
  180. package/src/lib/server/runtime/queue/claims.ts +4 -0
  181. package/src/lib/server/runtime/queue/core.ts +2079 -0
  182. package/src/lib/server/runtime/queue/execution.ts +7 -0
  183. package/src/lib/server/runtime/queue/followups.ts +4 -0
  184. package/src/lib/server/runtime/queue/queries.ts +12 -0
  185. package/src/lib/server/runtime/queue/recovery.ts +7 -0
  186. package/src/lib/server/runtime/queue-recovery.test.ts +48 -13
  187. package/src/lib/server/runtime/queue-repository.ts +17 -0
  188. package/src/lib/server/runtime/queue.ts +5 -2058
  189. package/src/lib/server/runtime/run-ledger.ts +6 -5
  190. package/src/lib/server/runtime/run-repository.ts +73 -0
  191. package/src/lib/server/runtime/runtime-lock-repository.ts +8 -0
  192. package/src/lib/server/runtime/runtime-settings.ts +1 -1
  193. package/src/lib/server/runtime/runtime-state.ts +99 -0
  194. package/src/lib/server/runtime/scheduler.ts +13 -8
  195. package/src/lib/server/runtime/session-run-manager/cancellation.ts +157 -0
  196. package/src/lib/server/runtime/session-run-manager/drain.ts +246 -0
  197. package/src/lib/server/runtime/session-run-manager/enqueue.ts +287 -0
  198. package/src/lib/server/runtime/session-run-manager/queries.ts +117 -0
  199. package/src/lib/server/runtime/session-run-manager/recovery.ts +238 -0
  200. package/src/lib/server/runtime/session-run-manager/state.ts +441 -0
  201. package/src/lib/server/runtime/session-run-manager/types.ts +74 -0
  202. package/src/lib/server/runtime/session-run-manager.ts +72 -1374
  203. package/src/lib/server/runtime/watch-job-repository.ts +35 -0
  204. package/src/lib/server/runtime/watch-jobs.ts +3 -1
  205. package/src/lib/server/sandbox/bridge-auth-registry.ts +6 -0
  206. package/src/lib/server/sandbox/novnc-auth.ts +10 -0
  207. package/src/lib/server/schedules/schedule-repository.ts +42 -0
  208. package/src/lib/server/session-tools/context.ts +14 -0
  209. package/src/lib/server/session-tools/discovery.ts +9 -6
  210. package/src/lib/server/session-tools/index.ts +3 -1
  211. package/src/lib/server/session-tools/platform.ts +1 -1
  212. package/src/lib/server/session-tools/subagent.ts +23 -2
  213. package/src/lib/server/session-tools/wallet.ts +4 -1
  214. package/src/lib/server/sessions/session-repository.ts +85 -0
  215. package/src/lib/server/settings/settings-repository.ts +25 -0
  216. package/src/lib/server/skills/clawhub-client.ts +4 -1
  217. package/src/lib/server/skills/runtime-skill-resolver.ts +8 -2
  218. package/src/lib/server/skills/skill-discovery.test.ts +2 -2
  219. package/src/lib/server/skills/skill-discovery.ts +2 -2
  220. package/src/lib/server/skills/skill-eligibility.ts +6 -0
  221. package/src/lib/server/skills/skill-repository.ts +14 -0
  222. package/src/lib/server/solana.ts +6 -0
  223. package/src/lib/server/storage-auth.ts +5 -5
  224. package/src/lib/server/storage-normalization.ts +4 -0
  225. package/src/lib/server/storage.ts +32 -32
  226. package/src/lib/server/tasks/task-followups.ts +4 -1
  227. package/src/lib/server/tasks/task-repository.ts +54 -0
  228. package/src/lib/server/tool-loop-detection.ts +8 -3
  229. package/src/lib/server/tool-planning.ts +226 -0
  230. package/src/lib/server/tool-retry.ts +4 -3
  231. package/src/lib/server/usage/usage-repository.ts +30 -0
  232. package/src/lib/server/wallet/wallet-portfolio.ts +29 -0
  233. package/src/lib/server/webhooks/webhook-repository.ts +10 -0
  234. package/src/lib/server/ws-hub.ts +5 -2
  235. package/src/lib/strip-internal-metadata.test.ts +78 -37
  236. package/src/lib/strip-internal-metadata.ts +20 -6
  237. package/src/stores/use-approval-store.ts +7 -1
  238. package/src/stores/use-chat-store.test.ts +54 -0
  239. package/src/stores/use-chat-store.ts +26 -6
  240. package/src/types/index.ts +6 -0
  241. /package/{bundled-skills → skills}/google-workspace/SKILL.md +0 -0
@@ -0,0 +1,329 @@
1
+ import assert from 'node:assert/strict'
2
+ import { describe, it, before, after } from 'node:test'
3
+
4
+ const originalEnv = {
5
+ SWARMCLAW_BUILD_MODE: process.env.SWARMCLAW_BUILD_MODE,
6
+ }
7
+
8
+ let mod: typeof import('@/lib/server/chat-execution/message-classifier')
9
+
10
+ before(async () => {
11
+ process.env.SWARMCLAW_BUILD_MODE = '1'
12
+ mod = await import('@/lib/server/chat-execution/message-classifier')
13
+ })
14
+
15
+ after(() => {
16
+ if (originalEnv.SWARMCLAW_BUILD_MODE === undefined) delete process.env.SWARMCLAW_BUILD_MODE
17
+ else process.env.SWARMCLAW_BUILD_MODE = originalEnv.SWARMCLAW_BUILD_MODE
18
+ })
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // parseClassificationResponse
22
+ // ---------------------------------------------------------------------------
23
+
24
+ describe('parseClassificationResponse', () => {
25
+ const validJson = JSON.stringify({
26
+ isDeliverableTask: true,
27
+ isBroadGoal: false,
28
+ walletIntent: 'none',
29
+ hasHumanSignals: false,
30
+ hasSignificantEvent: false,
31
+ isResearchSynthesis: false,
32
+ explicitToolRequests: [],
33
+ confidence: 0.9,
34
+ })
35
+
36
+ it('parses valid JSON with all schema fields', () => {
37
+ const result = mod.parseClassificationResponse(validJson)
38
+ assert.ok(result)
39
+ assert.equal(result!.isDeliverableTask, true)
40
+ assert.equal(result!.isBroadGoal, false)
41
+ assert.equal(result!.walletIntent, 'none')
42
+ assert.equal(result!.confidence, 0.9)
43
+ assert.deepEqual(result!.explicitToolRequests, [])
44
+ })
45
+
46
+ it('returns null for malformed JSON', () => {
47
+ assert.equal(mod.parseClassificationResponse('not json at all'), null)
48
+ assert.equal(mod.parseClassificationResponse('{broken'), null)
49
+ })
50
+
51
+ it('returns null for JSON missing required keys', () => {
52
+ const partial = JSON.stringify({ isDeliverableTask: true })
53
+ assert.equal(mod.parseClassificationResponse(partial), null)
54
+ })
55
+
56
+ it('tolerates extra keys in JSON', () => {
57
+ const withExtra = JSON.stringify({
58
+ isDeliverableTask: true,
59
+ isBroadGoal: false,
60
+ walletIntent: 'none',
61
+ hasHumanSignals: false,
62
+ hasSignificantEvent: false,
63
+ isResearchSynthesis: false,
64
+ explicitToolRequests: ['shell'],
65
+ confidence: 0.85,
66
+ extraKey: 'should be ignored',
67
+ })
68
+ const result = mod.parseClassificationResponse(withExtra)
69
+ assert.ok(result)
70
+ assert.equal(result!.isDeliverableTask, true)
71
+ })
72
+
73
+ it('extracts embedded JSON from prose text', () => {
74
+ const prose = `Here is my classification:\n${validJson}\nEnd of classification.`
75
+ const result = mod.parseClassificationResponse(prose)
76
+ assert.ok(result)
77
+ assert.equal(result!.isDeliverableTask, true)
78
+ })
79
+
80
+ it('returns null for empty text', () => {
81
+ assert.equal(mod.parseClassificationResponse(''), null)
82
+ assert.equal(mod.parseClassificationResponse(' '), null)
83
+ })
84
+ })
85
+
86
+ // ---------------------------------------------------------------------------
87
+ // isDeliverableTask
88
+ // ---------------------------------------------------------------------------
89
+
90
+ describe('isDeliverableTask', () => {
91
+ it('uses classification value when provided', () => {
92
+ const cls = makeClassification({ isDeliverableTask: true })
93
+ assert.equal(mod.isDeliverableTask(cls, 'anything'), true)
94
+
95
+ const cls2 = makeClassification({ isDeliverableTask: false })
96
+ assert.equal(mod.isDeliverableTask(cls2, 'build me a landing page'), false)
97
+ })
98
+
99
+ it('falls back to regex when classification is null', () => {
100
+ // A message that looks like a deliverable task
101
+ const result = mod.isDeliverableTask(null, 'Create a detailed marketing report with competitor analysis and market sizing. Include charts and recommendations for Q3 strategy across all regions.')
102
+ assert.equal(typeof result, 'boolean')
103
+ })
104
+ })
105
+
106
+ // ---------------------------------------------------------------------------
107
+ // isBroadGoal
108
+ // ---------------------------------------------------------------------------
109
+
110
+ describe('isBroadGoal', () => {
111
+ it('uses classification value when provided', () => {
112
+ assert.equal(mod.isBroadGoal(makeClassification({ isBroadGoal: true }), ''), true)
113
+ assert.equal(mod.isBroadGoal(makeClassification({ isBroadGoal: false }), ''), false)
114
+ })
115
+
116
+ it('falls back to regex when classification is null', () => {
117
+ const result = mod.isBroadGoal(null, 'I want to build a complete e-commerce platform with user authentication, product catalog, shopping cart, and payment processing')
118
+ assert.equal(typeof result, 'boolean')
119
+ })
120
+ })
121
+
122
+ // ---------------------------------------------------------------------------
123
+ // hasWalletIntent
124
+ // ---------------------------------------------------------------------------
125
+
126
+ describe('hasWalletIntent', () => {
127
+ it('walletIntent none returns false', () => {
128
+ assert.equal(mod.hasWalletIntent(makeClassification({ walletIntent: 'none' }), ''), false)
129
+ })
130
+
131
+ it('walletIntent read_only returns true', () => {
132
+ assert.equal(mod.hasWalletIntent(makeClassification({ walletIntent: 'read_only' }), ''), true)
133
+ })
134
+
135
+ it('walletIntent transactional returns true', () => {
136
+ assert.equal(mod.hasWalletIntent(makeClassification({ walletIntent: 'transactional' }), ''), true)
137
+ })
138
+
139
+ it('falls back to regex when classification is null', () => {
140
+ const result = mod.hasWalletIntent(null, 'check my wallet balance')
141
+ assert.equal(typeof result, 'boolean')
142
+ })
143
+ })
144
+
145
+ // ---------------------------------------------------------------------------
146
+ // hasTransactionalWalletIntent
147
+ // ---------------------------------------------------------------------------
148
+
149
+ describe('hasTransactionalWalletIntent', () => {
150
+ it('only transactional returns true', () => {
151
+ assert.equal(mod.hasTransactionalWalletIntent(makeClassification({ walletIntent: 'transactional' }), ''), true)
152
+ assert.equal(mod.hasTransactionalWalletIntent(makeClassification({ walletIntent: 'read_only' }), ''), false)
153
+ assert.equal(mod.hasTransactionalWalletIntent(makeClassification({ walletIntent: 'none' }), ''), false)
154
+ })
155
+
156
+ it('falls back to regex when classification is null', () => {
157
+ const result = mod.hasTransactionalWalletIntent(null, 'swap 1 ETH for USDC')
158
+ assert.equal(typeof result, 'boolean')
159
+ })
160
+ })
161
+
162
+ // ---------------------------------------------------------------------------
163
+ // hasHumanSignals
164
+ // ---------------------------------------------------------------------------
165
+
166
+ describe('hasHumanSignals', () => {
167
+ it('uses classification value when provided', () => {
168
+ assert.equal(mod.hasHumanSignals(makeClassification({ hasHumanSignals: true }), ''), true)
169
+ assert.equal(mod.hasHumanSignals(makeClassification({ hasHumanSignals: false }), ''), false)
170
+ })
171
+
172
+ it('regex detects personal text', () => {
173
+ assert.equal(mod.hasHumanSignals(null, 'my birthday is next week'), true)
174
+ })
175
+
176
+ it('regex returns false for task-only text', () => {
177
+ assert.equal(mod.hasHumanSignals(null, 'deploy the app'), false)
178
+ })
179
+ })
180
+
181
+ // ---------------------------------------------------------------------------
182
+ // hasSignificantEvent
183
+ // ---------------------------------------------------------------------------
184
+
185
+ describe('hasSignificantEvent', () => {
186
+ it('uses classification value when provided', () => {
187
+ assert.equal(mod.hasSignificantEvent(makeClassification({ hasSignificantEvent: true }), ''), true)
188
+ assert.equal(mod.hasSignificantEvent(makeClassification({ hasSignificantEvent: false }), ''), false)
189
+ })
190
+
191
+ it('regex detects significant events', () => {
192
+ assert.equal(mod.hasSignificantEvent(null, 'I just got promoted at work'), true)
193
+ assert.equal(mod.hasSignificantEvent(null, 'my graduation ceremony is on Friday'), true)
194
+ })
195
+
196
+ it('regex returns false for non-event text', () => {
197
+ assert.equal(mod.hasSignificantEvent(null, 'fix the login bug'), false)
198
+ })
199
+ })
200
+
201
+ // ---------------------------------------------------------------------------
202
+ // isResearchSynthesis
203
+ // ---------------------------------------------------------------------------
204
+
205
+ describe('isResearchSynthesis', () => {
206
+ it('uses classification value when provided', () => {
207
+ assert.equal(mod.isResearchSynthesis(makeClassification({ isResearchSynthesis: true }), null), true)
208
+ assert.equal(mod.isResearchSynthesis(makeClassification({ isResearchSynthesis: false }), null), false)
209
+ })
210
+
211
+ it('falls back to routingIntent when classification is null', () => {
212
+ assert.equal(mod.isResearchSynthesis(null, 'research'), true)
213
+ assert.equal(mod.isResearchSynthesis(null, 'browsing'), true)
214
+ assert.equal(mod.isResearchSynthesis(null, 'coding'), false)
215
+ assert.equal(mod.isResearchSynthesis(null, null), false)
216
+ })
217
+ })
218
+
219
+ // ---------------------------------------------------------------------------
220
+ // classifyMessage — with generateText override
221
+ // ---------------------------------------------------------------------------
222
+
223
+ describe('classifyMessage', () => {
224
+ it('returns valid classification from mock generateText', async () => {
225
+ const mockResponse = JSON.stringify({
226
+ isDeliverableTask: true,
227
+ isBroadGoal: false,
228
+ walletIntent: 'none',
229
+ hasHumanSignals: false,
230
+ hasSignificantEvent: false,
231
+ isResearchSynthesis: false,
232
+ explicitToolRequests: ['shell'],
233
+ confidence: 0.95,
234
+ })
235
+
236
+ const result = await mod.classifyMessage(
237
+ { sessionId: 'test-session', message: 'Build me a dashboard' },
238
+ { generateText: async () => mockResponse },
239
+ )
240
+
241
+ assert.ok(result)
242
+ assert.equal(result!.isDeliverableTask, true)
243
+ assert.equal(result!.walletIntent, 'none')
244
+ assert.deepEqual(result!.explicitToolRequests, ['shell'])
245
+ })
246
+
247
+ it('returns null for empty message', async () => {
248
+ const result = await mod.classifyMessage(
249
+ { sessionId: 'test-session', message: '' },
250
+ { generateText: async () => '{}' },
251
+ )
252
+ assert.equal(result, null)
253
+ })
254
+
255
+ it('returns null for whitespace-only message', async () => {
256
+ const result = await mod.classifyMessage(
257
+ { sessionId: 'test-session', message: ' ' },
258
+ { generateText: async () => '{}' },
259
+ )
260
+ assert.equal(result, null)
261
+ })
262
+
263
+ it('returns null when generateText times out', async () => {
264
+ const result = await mod.classifyMessage(
265
+ { sessionId: 'test-session', message: 'A message that will timeout for classification purposes' },
266
+ {
267
+ generateText: () => new Promise((resolve) => {
268
+ // Never resolves within 2s timeout
269
+ setTimeout(() => resolve('{}'), 10_000)
270
+ }),
271
+ },
272
+ )
273
+ assert.equal(result, null)
274
+ })
275
+
276
+ it('caches results for the same message', async () => {
277
+ let callCount = 0
278
+ const mockResponse = JSON.stringify({
279
+ isDeliverableTask: false,
280
+ isBroadGoal: false,
281
+ walletIntent: 'none',
282
+ hasHumanSignals: false,
283
+ hasSignificantEvent: false,
284
+ isResearchSynthesis: false,
285
+ explicitToolRequests: [],
286
+ confidence: 0.8,
287
+ })
288
+
289
+ const generateText = async () => {
290
+ callCount++
291
+ return mockResponse
292
+ }
293
+
294
+ // Use a unique message to avoid cache from other tests
295
+ const uniqueMsg = `cache-test-${Date.now()}-${Math.random()}`
296
+
297
+ const first = await mod.classifyMessage(
298
+ { sessionId: 'test-session', message: uniqueMsg },
299
+ { generateText },
300
+ )
301
+ const second = await mod.classifyMessage(
302
+ { sessionId: 'test-session', message: uniqueMsg },
303
+ { generateText },
304
+ )
305
+
306
+ assert.ok(first)
307
+ assert.ok(second)
308
+ assert.deepEqual(first, second)
309
+ assert.equal(callCount, 1, 'generateText should only be called once due to cache')
310
+ })
311
+ })
312
+
313
+ // ---------------------------------------------------------------------------
314
+ // Helper
315
+ // ---------------------------------------------------------------------------
316
+
317
+ function makeClassification(overrides: Partial<import('@/lib/server/chat-execution/message-classifier').MessageClassification>): import('@/lib/server/chat-execution/message-classifier').MessageClassification {
318
+ return {
319
+ isDeliverableTask: false,
320
+ isBroadGoal: false,
321
+ walletIntent: 'none',
322
+ hasHumanSignals: false,
323
+ hasSignificantEvent: false,
324
+ isResearchSynthesis: false,
325
+ explicitToolRequests: [],
326
+ confidence: 0.9,
327
+ ...overrides,
328
+ }
329
+ }
@@ -13,9 +13,12 @@ import crypto from 'node:crypto'
13
13
  import { HumanMessage } from '@langchain/core/messages'
14
14
  import { z } from 'zod'
15
15
  import { buildLLM } from '@/lib/server/build-llm'
16
+ import { log } from '@/lib/server/logger'
16
17
  import { hmrSingleton } from '@/lib/shared-utils'
17
18
  import type { Message } from '@/types'
18
19
 
20
+ const TAG = 'message-classifier'
21
+
19
22
  // ---------------------------------------------------------------------------
20
23
  // Schema
21
24
  // ---------------------------------------------------------------------------
@@ -221,7 +224,7 @@ export async function classifyMessage(
221
224
  ])
222
225
 
223
226
  const durationMs = Date.now() - startMs
224
- console.log(`[message-classifier] session=${input.sessionId} completed in ${durationMs}ms`)
227
+ log.info(TAG, `session=${input.sessionId} completed in ${durationMs}ms`)
225
228
 
226
229
  const classification = parseClassificationResponse(responseText)
227
230
  if (classification) {
@@ -230,7 +233,7 @@ export async function classifyMessage(
230
233
  return classification
231
234
  } catch (err: unknown) {
232
235
  const durationMs = Date.now() - startMs
233
- console.warn(`[message-classifier] session=${input.sessionId} failed in ${durationMs}ms: ${err instanceof Error ? err.message : 'unknown'}`)
236
+ log.warn(TAG, `session=${input.sessionId} failed in ${durationMs}ms: ${err instanceof Error ? err.message : 'unknown'}`)
234
237
  return null
235
238
  }
236
239
  }
@@ -6,11 +6,14 @@
6
6
  * and OpenClaw sync.
7
7
  */
8
8
  import type { Session, UsageRecord } from '@/types'
9
+ import { log } from '@/lib/server/logger'
9
10
  import type { ChatTurnState } from '@/lib/server/chat-execution/chat-turn-state'
11
+
12
+ const TAG = 'post-stream'
10
13
  import { extractSuggestions } from '@/lib/server/suggestions'
11
14
  import type { StructuredToolInterface } from '@langchain/core/tools'
12
15
  import { estimateCost, buildExtensionDefinitionCosts } from '@/lib/server/cost'
13
- import { appendUsage } from '@/lib/server/storage'
16
+ import { appendUsage } from '@/lib/server/usage/usage-repository'
14
17
  import { runCapabilityHook } from '@/lib/server/native-capabilities'
15
18
  import {
16
19
  shouldForceExternalServiceSummary,
@@ -39,7 +42,7 @@ function stripLeakedClassificationJson(text: string): { cleaned: string; strippe
39
42
  else if (text[i] === '}') { depth--; if (depth === 0) { end = i + 1; break } }
40
43
  }
41
44
  if (end === -1) return { cleaned: text, stripped: false }
42
- console.warn('[post-stream-finalization] Stripped leaked classification JSON from model output')
45
+ log.warn(TAG, 'Stripped leaked classification JSON from model output')
43
46
  return { cleaned: (text.slice(0, startIdx) + text.slice(end)).trimStart(), stripped: true }
44
47
  }
45
48
 
@@ -73,13 +73,24 @@ function buildExtensionCapabilityLines(enabledExtensions: string[], opts?: { del
73
73
  return lines
74
74
  }
75
75
 
76
+ const DISPLAY_TOOL_ALIASES: Record<string, string[]> = {
77
+ files: ['send_file'],
78
+ shell: ['sandbox_exec', 'sandbox_list_runtimes'],
79
+ }
80
+
76
81
  function buildExactToolNameList(enabledExtensions: string[]): string[] {
77
82
  const planning = getEnabledToolPlanningView(enabledExtensions)
83
+ const displayAliases = dedup(
84
+ enabledExtensions
85
+ .map((toolId) => canonicalizeExtensionId(toolId))
86
+ .flatMap((toolId) => DISPLAY_TOOL_ALIASES[toolId] || []),
87
+ )
78
88
  const extensionToolNames = getExtensionManager()
79
89
  .getTools(enabledExtensions)
80
90
  .map(({ tool }) => tool.name)
81
91
  const combined = [
82
92
  ...planning.displayToolIds,
93
+ ...displayAliases,
83
94
  ...planning.entries.map((entry) => entry.toolName),
84
95
  ...extensionToolNames,
85
96
  ]
@@ -120,6 +131,16 @@ export function buildToolDisciplineLines(enabledExtensions: string[]): string[]
120
131
 
121
132
  lines.push(...planning.disciplineGuidance)
122
133
 
134
+ // Universal tool efficiency guidance — tool-specific lines live in CORE_TOOL_PLANNING (tool-planning.ts)
135
+ lines.push(
136
+ '## Tool Efficiency',
137
+ 'Plan your approach before starting tool calls. State what you will do, then do it.',
138
+ 'Prefer fewer, larger tool calls over many small ones.',
139
+ 'Do not poll for status in a loop. If waiting on a process, check once and move on.',
140
+ 'If stuck after 2-3 attempts with the same approach, stop and state the blocker — do not keep retrying.',
141
+ 'When delegating to subagents, use waitForCompletion or wait/wait_all instead of polling status in a loop.',
142
+ )
143
+
123
144
  const researchSearchTools = getToolsForCapability(enabledExtensions, TOOL_CAPABILITY.researchSearch)
124
145
  const researchFetchTools = getToolsForCapability(enabledExtensions, TOOL_CAPABILITY.researchFetch)
125
146
  const browserCaptureTools = getToolsForCapability(enabledExtensions, TOOL_CAPABILITY.browserCapture)
@@ -275,7 +296,7 @@ const GOAL_DECOMPOSITION_BLOCK = [
275
296
  '## Goal Decomposition',
276
297
  'When you receive a broad, open-ended goal:',
277
298
  '1. Break it into 3-7 concrete, sequentially-executable subtasks before taking action.',
278
- '2. If manage_tasks is available, use it only for durable tracking: multi-turn work, delegation, explicit backlog requests, or work you expect to resume later. Do not create a task for every micro-step.',
299
+ '2. If manage_tasks is available, use it only for durable tracking: multi-turn work, delegation, explicit backlog requests, or work you expect to resume later. Do not create a task for every micro-step. Do not re-read the task list after every update. Read once, make your changes, then move on.',
279
300
  'Single-step instructions are not broad goals. For direct actions like storing a memory, answering a recall question, editing one file, or sending one message, execute the relevant tool immediately instead of creating tasks or delegating.',
280
301
  '3. Present the plan as a short checklist or numbered list in plain language. If durable tracking is unnecessary, keep it inline instead of creating tasks.',
281
302
  '4. Execute the first substantive subtask immediately — do not stop after planning.',
@@ -9,7 +9,8 @@
9
9
  import type { Session, Agent } from '@/types'
10
10
  import type { ActiveProjectContext } from '@/lib/server/project-context'
11
11
  import { buildIdentityContinuityContext } from '@/lib/server/identity-continuity'
12
- import { loadSkills, loadAgents } from '@/lib/server/storage'
12
+ import { getAgent, listAgents } from '@/lib/server/agents/agent-repository'
13
+ import { loadSkills } from '@/lib/server/skills/skill-repository'
13
14
  import { buildRuntimeSkillPromptBlocks, resolveRuntimeSkills } from '@/lib/server/skills/runtime-skill-resolver'
14
15
  import { resolveTeam } from '@/lib/server/agents/team-resolution'
15
16
 
@@ -123,7 +124,20 @@ export async function buildAgentAwarenessSection(
123
124
  if (!hasMultiAgentTool || !session.agentId) return null
124
125
  try {
125
126
  const { buildAgentAwarenessBlock } = await import('@/lib/server/agents/agent-registry')
126
- return buildAgentAwarenessBlock(session.agentId) || null
127
+
128
+ // Load agent to get delegation settings so the awareness block respects them
129
+ let delegationOpts: { delegationTargetMode?: 'all' | 'selected'; delegationTargetAgentIds?: string[] } | undefined
130
+ try {
131
+ const agent = getAgent(session.agentId)
132
+ if (agent?.delegationTargetMode === 'selected') {
133
+ delegationOpts = {
134
+ delegationTargetMode: 'selected',
135
+ delegationTargetAgentIds: agent.delegationTargetAgentIds || [],
136
+ }
137
+ }
138
+ } catch { /* non-critical */ }
139
+
140
+ return buildAgentAwarenessBlock(session.agentId, delegationOpts) || null
127
141
  } catch { return null }
128
142
  }
129
143
 
@@ -185,6 +199,8 @@ export function buildProjectSection(
185
199
  `project secrets ${summary.secretCount}`,
186
200
  ]
187
201
  if (summary.topTaskTitles.length > 0) lines.push(`Top open tasks: ${summary.topTaskTitles.join('; ')}`)
202
+ if (summary.failedTaskCount > 0) lines.push(`Failed tasks needing attention: ${summary.failedTaskCount}`)
203
+ if (summary.staleTaskCount > 0) lines.push(`Stale tasks (no update in 3+ days): ${summary.staleTaskCount}`)
188
204
  if (summary.scheduleNames.length > 0) lines.push(`Active schedules: ${summary.scheduleNames.join('; ')}`)
189
205
  if (summary.secretNames.length > 0) lines.push(`Known project secrets: ${summary.secretNames.join('; ')}`)
190
206
  lines.push(`Project resource summary: ${resourceBits.join(', ')}.`)
@@ -257,6 +273,11 @@ export function buildSuggestionsSection(
257
273
  // Proactive Memory Recall (async)
258
274
  // ---------------------------------------------------------------------------
259
275
 
276
+ export interface ProactiveMemoryResult {
277
+ section: string | null
278
+ injectedIds: Record<string, number>
279
+ }
280
+
260
281
  export async function buildProactiveMemorySection(
261
282
  session: Session,
262
283
  agent: Agent | null | undefined,
@@ -264,9 +285,10 @@ export async function buildProactiveMemorySection(
264
285
  activeProjectRoot: string | null,
265
286
  isMinimalPrompt: boolean,
266
287
  currentThreadRecallRequest: boolean,
267
- ): Promise<string | null> {
268
- if (isMinimalPrompt || !session.agentId || currentThreadRecallRequest || message.length <= 12) return null
269
- if (!agent?.proactiveMemory) return null
288
+ ): Promise<ProactiveMemoryResult> {
289
+ const noResult: ProactiveMemoryResult = { section: null, injectedIds: {} }
290
+ if (isMinimalPrompt || !session.agentId || currentThreadRecallRequest || message.length <= 12) return noResult
291
+ if (!agent?.proactiveMemory) return noResult
270
292
  try {
271
293
  const { getMemoryDb } = await import('@/lib/server/memory/memory-db')
272
294
  const { buildSessionMemoryScopeFilter } = await import('@/lib/server/memory/session-memory-scope')
@@ -274,13 +296,29 @@ export async function buildProactiveMemorySection(
274
296
  const recalled = memDb.search(message, session.agentId, {
275
297
  scope: buildSessionMemoryScopeFilter(session, agent.memoryScopeMode || null, activeProjectRoot),
276
298
  })
277
- const topRecalled = recalled.slice(0, 3)
299
+
300
+ // Dedup: skip memories already injected 2+ times in this session
301
+ const priorCounts = session.injectedMemoryIds || {}
302
+ const filtered = recalled.filter((entry) => (priorCounts[entry.id] || 0) < 2)
303
+
304
+ const topRecalled = filtered.slice(0, 3)
278
305
  if (topRecalled.length > 0) {
279
- const recalledLines = topRecalled.map((entry) => `- ${entry.content.slice(0, 300)}`)
280
- return `## Recalled Context\nRelevant memories from previous interactions:\n${recalledLines.join('\n')}`
306
+ // Track injection counts
307
+ const updatedCounts: Record<string, number> = { ...priorCounts }
308
+ for (const entry of topRecalled) {
309
+ updatedCounts[entry.id] = (updatedCounts[entry.id] || 0) + 1
310
+ }
311
+
312
+ const recalledLines = topRecalled.map((entry) =>
313
+ `- ${entry.abstract || entry.content.slice(0, 300)}`,
314
+ )
315
+ return {
316
+ section: `## Recalled Context\nRelevant memories from previous interactions:\n${recalledLines.join('\n')}`,
317
+ injectedIds: updatedCounts,
318
+ }
281
319
  }
282
320
  } catch { /* non-critical */ }
283
- return null
321
+ return noResult
284
322
  }
285
323
 
286
324
  // ---------------------------------------------------------------------------
@@ -296,7 +334,7 @@ export function buildCoordinatorSection(
296
334
  ): string | null {
297
335
  if (!agent || agent.role !== 'coordinator') return null
298
336
 
299
- const allAgents = loadAgents()
337
+ const allAgents = listAgents()
300
338
  const selfId = agent.id
301
339
 
302
340
  // Resolve which agents this coordinator can delegate to
@@ -328,7 +366,7 @@ export function buildCoordinatorSection(
328
366
  for (const w of listed) {
329
367
  const caps = w.capabilities?.length ? ` [${w.capabilities.join(', ')}]` : ''
330
368
  const desc = w.description ? ` — ${w.description.slice(0, 100)}` : ''
331
- const line = `- **${w.name}**${caps}${desc}`
369
+ const line = `- **${w.name}** [id: ${w.id}]${caps}${desc}`
332
370
  if (charBudget - line.length < 0) break
333
371
  charBudget -= line.length + 1
334
372
  lines.push(line)
@@ -338,6 +376,11 @@ export function buildCoordinatorSection(
338
376
  lines.push(`- ... and ${workers.length - COORDINATOR_MAX_WORKERS} more workers`)
339
377
  }
340
378
 
379
+ if (delegateMode === 'selected') {
380
+ lines.push('')
381
+ lines.push('**IMPORTANT:** You may ONLY delegate to the workers listed above. Do NOT attempt to delegate to any other agents — such attempts will be rejected.')
382
+ }
383
+
341
384
  lines.push('')
342
385
  lines.push('### Orchestration Tools')
343
386
  lines.push('- **`spawn_subagent`** — Simple fire-and-forget delegation. Best for: single tasks, batch parallel/serial, basic swarm. Use when tasks are independent.')
@@ -468,8 +511,7 @@ export function buildCliDelegationContext(opts: {
468
511
  // Team roster summary
469
512
  if (opts.agent?.id) {
470
513
  try {
471
- const agents = loadAgents() as Record<string, Agent>
472
- const team = resolveTeam(opts.agent.id, agents)
514
+ const team = resolveTeam(opts.agent.id, listAgents())
473
515
  if (team.mode === 'team') {
474
516
  const teammates = [
475
517
  ...(team.coordinator ? [`${team.coordinator.name} (coordinator)`] : []),
@@ -9,6 +9,9 @@ import crypto from 'node:crypto'
9
9
  import { HumanMessage } from '@langchain/core/messages'
10
10
  import { z } from 'zod'
11
11
  import { buildLLM } from '@/lib/server/build-llm'
12
+ import { log } from '@/lib/server/logger'
13
+
14
+ const TAG = 'response-completeness'
12
15
 
13
16
  // ---------------------------------------------------------------------------
14
17
  // Schema
@@ -191,7 +194,7 @@ export async function evaluateResponseCompleteness(
191
194
  ])
192
195
 
193
196
  const durationMs = Date.now() - startMs
194
- console.log(`[response-completeness] session=${input.sessionId} completed in ${durationMs}ms`)
197
+ log.info(TAG, `session=${input.sessionId} completed in ${durationMs}ms`)
195
198
 
196
199
  const completeness = parseCompletenessResponse(responseText)
197
200
  if (completeness) {
@@ -200,7 +203,7 @@ export async function evaluateResponseCompleteness(
200
203
  return completeness
201
204
  } catch (err: unknown) {
202
205
  const durationMs = Date.now() - startMs
203
- console.warn(`[response-completeness] session=${input.sessionId} failed in ${durationMs}ms: ${err instanceof Error ? err.message : 'unknown'}`)
206
+ log.warn(TAG, `session=${input.sessionId} failed in ${durationMs}ms: ${err instanceof Error ? err.message : 'unknown'}`)
204
207
  return null
205
208
  }
206
209
  }
@@ -1,4 +1,11 @@
1
- import { loadTasks, loadSchedules, loadSupervisorIncidents, loadMission, loadAgents, loadConnectors, loadChatrooms, loadUsage } from '@/lib/server/storage'
1
+ import { listAgentIncidents } from '@/lib/server/autonomy/supervisor-incident-repository'
2
+ import { listAgents } from '@/lib/server/agents/agent-repository'
3
+ import { loadChatrooms } from '@/lib/server/chatrooms/chatroom-repository'
4
+ import { loadConnectors } from '@/lib/server/connectors/connector-repository'
5
+ import { loadMission } from '@/lib/server/missions/mission-repository'
6
+ import { loadSchedules } from '@/lib/server/schedules/schedule-repository'
7
+ import { loadTasks } from '@/lib/server/tasks/task-repository'
8
+ import { loadUsage } from '@/lib/server/usage/usage-repository'
2
9
  import { listPersistedRuns } from '@/lib/server/runtime/run-ledger'
3
10
  import type { BoardTask, Mission, Schedule, SupervisorIncident, SessionRunRecord } from '@/types'
4
11
 
@@ -258,7 +265,7 @@ export function buildPlatformStatusSummary(): string {
258
265
  const oneDayAgo = now - 86_400_000
259
266
 
260
267
  // Agents
261
- const agents = Object.values(loadAgents())
268
+ const agents = Object.values(listAgents())
262
269
  const activeAgents = agents.filter((a) => a.lastUsedAt && a.lastUsedAt > oneHourAgo)
263
270
 
264
271
  // Tasks
@@ -276,7 +283,7 @@ export function buildPlatformStatusSummary(): string {
276
283
  const connectors = Object.values(loadConnectors())
277
284
  const connectorLines: string[] = []
278
285
  for (const c of connectors) {
279
- const status = c.status === 'running' || c.status === 'connected' ? '✓' : `✗ (${c.status})`
286
+ const status = c.status === 'running' ? '✓' : `✗ (${c.status})`
280
287
  connectorLines.push(`${c.platform || c.id} ${status}`)
281
288
  }
282
289
 
@@ -285,8 +292,7 @@ export function buildPlatformStatusSummary(): string {
285
292
  const activeChatrooms = chatrooms.filter((c) => !c.archivedAt && !c.temporary)
286
293
 
287
294
  // Incidents
288
- const allIncidents = Object.values(loadSupervisorIncidents())
289
- const recentIncidents = allIncidents.filter((i) => i.createdAt > oneDayAgo)
295
+ const recentIncidents = listAgentIncidents().filter((i) => i.createdAt > oneDayAgo)
290
296
  const warnings = recentIncidents.filter((i) => i.severity === 'medium').length
291
297
  const errors = recentIncidents.filter((i) => i.severity === 'high').length
292
298
 
@@ -348,8 +354,7 @@ export function buildSituationalAwarenessBlock(input: SituationalAwarenessInput)
348
354
 
349
355
  const failedRuns = listPersistedRuns({ sessionId, status: 'failed', limit: 10 })
350
356
 
351
- const allIncidents = loadSupervisorIncidents()
352
- const incidents = Object.values(allIncidents).filter((i) => i.agentId === agentId)
357
+ const incidents = listAgentIncidents(agentId)
353
358
 
354
359
  const mission = missionId ? loadMission(missionId) : null
355
360