@swarmclawai/swarmclaw 1.2.4 → 1.2.6

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 (262) hide show
  1. package/README.md +14 -0
  2. package/bin/daemon-cmd.js +169 -0
  3. package/bin/server-cmd.js +3 -0
  4. package/bin/swarmclaw.js +11 -0
  5. package/package.json +17 -16
  6. package/src/app/api/agents/[id]/clone/route.ts +3 -32
  7. package/src/app/api/agents/[id]/route.ts +6 -158
  8. package/src/app/api/agents/[id]/status/route.ts +2 -3
  9. package/src/app/api/agents/[id]/thread/route.ts +4 -17
  10. package/src/app/api/agents/bulk/route.ts +5 -47
  11. package/src/app/api/agents/route.ts +5 -119
  12. package/src/app/api/agents/trash/route.ts +13 -24
  13. package/src/app/api/auth/route.ts +3 -9
  14. package/src/app/api/autonomy/estop/route.ts +5 -5
  15. package/src/app/api/chatrooms/[id]/chat/route.ts +11 -5
  16. package/src/app/api/chatrooms/[id]/route.ts +23 -2
  17. package/src/app/api/chatrooms/route.ts +13 -2
  18. package/src/app/api/chats/[id]/clear/route.ts +2 -13
  19. package/src/app/api/chats/[id]/deploy/route.ts +2 -3
  20. package/src/app/api/chats/[id]/edit-resend/route.ts +7 -13
  21. package/src/app/api/chats/[id]/mailbox/route.ts +6 -8
  22. package/src/app/api/chats/[id]/queue/route.ts +17 -64
  23. package/src/app/api/chats/[id]/retry/route.ts +4 -22
  24. package/src/app/api/chats/[id]/route.ts +10 -138
  25. package/src/app/api/chats/heartbeat/route.ts +2 -1
  26. package/src/app/api/chats/migrate-messages/route.ts +7 -0
  27. package/src/app/api/chats/route.ts +13 -134
  28. package/src/app/api/connectors/[id]/access/route.ts +12 -229
  29. package/src/app/api/connectors/[id]/doctor/route.ts +1 -1
  30. package/src/app/api/connectors/[id]/health/route.ts +12 -39
  31. package/src/app/api/connectors/[id]/route.ts +14 -122
  32. package/src/app/api/connectors/[id]/webhook/route.ts +1 -1
  33. package/src/app/api/connectors/doctor/route.ts +1 -1
  34. package/src/app/api/connectors/route.ts +12 -70
  35. package/src/app/api/credentials/[id]/route.ts +2 -4
  36. package/src/app/api/credentials/route.ts +10 -19
  37. package/src/app/api/daemon/health-check/route.ts +3 -4
  38. package/src/app/api/daemon/route.ts +10 -8
  39. package/src/app/api/documents/route.ts +11 -10
  40. package/src/app/api/external-agents/route.ts +3 -3
  41. package/src/app/api/gateways/[id]/health/route.ts +2 -3
  42. package/src/app/api/gateways/[id]/route.ts +7 -122
  43. package/src/app/api/gateways/route.ts +3 -103
  44. package/src/app/api/mcp-servers/[id]/tools/route.ts +5 -5
  45. package/src/app/api/openclaw/dashboard-url/route.ts +8 -16
  46. package/src/app/api/openclaw/directory/route.ts +2 -2
  47. package/src/app/api/openclaw/history/route.ts +3 -5
  48. package/src/app/api/providers/[id]/route.test.ts +49 -0
  49. package/src/app/api/providers/ollama/route.ts +6 -5
  50. package/src/app/api/schedules/[id]/route.ts +14 -108
  51. package/src/app/api/schedules/[id]/run/route.ts +6 -67
  52. package/src/app/api/schedules/route.ts +9 -51
  53. package/src/app/api/settings/route.ts +4 -3
  54. package/src/app/api/setup/check-provider/route.ts +23 -1
  55. package/src/app/api/setup/openclaw-device/route.ts +2 -2
  56. package/src/app/api/system/status/route.ts +2 -2
  57. package/src/app/api/tasks/[id]/route.ts +16 -202
  58. package/src/app/api/tasks/bulk/route.ts +5 -86
  59. package/src/app/api/tasks/metrics/route.ts +2 -1
  60. package/src/app/api/tasks/route.ts +11 -171
  61. package/src/app/api/upload/route.ts +1 -1
  62. package/src/app/api/uploads/[filename]/route.ts +1 -1
  63. package/src/app/api/uploads/route.ts +1 -1
  64. package/src/app/api/webhooks/[id]/history/route.ts +2 -2
  65. package/src/app/layout.tsx +9 -6
  66. package/src/app/protocols/page.tsx +71 -89
  67. package/src/app/tasks/page.tsx +32 -32
  68. package/src/cli/index.js +1 -0
  69. package/src/cli/spec.js +1 -0
  70. package/src/components/agents/agent-sheet.tsx +5 -5
  71. package/src/components/auth/setup-wizard/index.tsx +4 -4
  72. package/src/components/auth/setup-wizard/step-agents.tsx +1 -1
  73. package/src/components/auth/setup-wizard/step-connect.tsx +1 -1
  74. package/src/components/auth/setup-wizard/utils.ts +1 -1
  75. package/src/components/chatrooms/chatroom-sheet.tsx +16 -276
  76. package/src/components/connectors/connector-list.tsx +26 -40
  77. package/src/components/connectors/connector-sheet.tsx +95 -149
  78. package/src/components/gateways/gateway-sheet.tsx +61 -110
  79. package/src/components/layout/live-query-sync.tsx +121 -0
  80. package/src/components/protocols/structured-session-launcher.tsx +24 -45
  81. package/src/components/providers/app-query-provider.tsx +17 -0
  82. package/src/components/providers/provider-list.tsx +60 -61
  83. package/src/components/providers/provider-sheet.tsx +74 -56
  84. package/src/components/skills/skill-list.tsx +5 -18
  85. package/src/components/skills/skill-sheet.tsx +21 -20
  86. package/src/components/skills/skills-workspace.tsx +48 -87
  87. package/src/components/tasks/task-card.tsx +20 -13
  88. package/src/components/tasks/task-column.tsx +22 -7
  89. package/src/components/tasks/task-list.tsx +8 -11
  90. package/src/components/tasks/task-sheet.tsx +111 -103
  91. package/src/features/agents/queries.ts +20 -0
  92. package/src/features/chatrooms/queries.ts +20 -0
  93. package/src/features/chats/queries.ts +27 -0
  94. package/src/features/connectors/queries.ts +145 -0
  95. package/src/features/credentials/queries.ts +37 -0
  96. package/src/features/extensions/queries.ts +26 -0
  97. package/src/features/external-agents/queries.ts +36 -0
  98. package/src/features/gateways/queries.ts +274 -0
  99. package/src/features/missions/queries.ts +23 -0
  100. package/src/features/projects/queries.ts +20 -0
  101. package/src/features/protocols/queries.ts +149 -0
  102. package/src/features/providers/queries.ts +142 -0
  103. package/src/features/settings/queries.ts +20 -0
  104. package/src/features/skills/queries.ts +182 -0
  105. package/src/features/tasks/queries.ts +189 -0
  106. package/src/hooks/use-ws.ts +3 -2
  107. package/src/lib/app/api-client.ts +2 -2
  108. package/src/lib/providers/index.test.ts +108 -0
  109. package/src/lib/providers/index.ts +38 -15
  110. package/src/lib/query/client.ts +17 -0
  111. package/src/lib/server/agents/agent-runtime-config.ts +1 -1
  112. package/src/lib/server/agents/agent-service.ts +429 -0
  113. package/src/lib/server/agents/agent-thread-session.ts +6 -5
  114. package/src/lib/server/agents/autonomy-contract.ts +1 -4
  115. package/src/lib/server/agents/delegation-advisory.test.ts +206 -0
  116. package/src/lib/server/agents/delegation-advisory.ts +251 -0
  117. package/src/lib/server/agents/main-agent-loop.ts +98 -40
  118. package/src/lib/server/agents/subagent-runtime.ts +12 -0
  119. package/src/lib/server/autonomy/supervisor-reflection.test.ts +20 -1
  120. package/src/lib/server/autonomy/supervisor-reflection.ts +39 -19
  121. package/src/lib/server/build-llm.ts +7 -15
  122. package/src/lib/server/capability-router.test.ts +70 -1
  123. package/src/lib/server/capability-router.ts +24 -99
  124. package/src/lib/server/chat-execution/chat-execution-utils.ts +0 -15
  125. package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -4
  126. package/src/lib/server/chat-execution/chat-turn-finalization.ts +77 -12
  127. package/src/lib/server/chat-execution/chat-turn-partial-persistence.ts +4 -4
  128. package/src/lib/server/chat-execution/chat-turn-preflight.ts +2 -2
  129. package/src/lib/server/chat-execution/chat-turn-preparation.ts +41 -17
  130. package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -2
  131. package/src/lib/server/chat-execution/chat-turn-tool-routing.test.ts +45 -0
  132. package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +48 -17
  133. package/src/lib/server/chat-execution/continuation-evaluator.ts +4 -1
  134. package/src/lib/server/chat-execution/direct-memory-intent.test.ts +9 -0
  135. package/src/lib/server/chat-execution/direct-memory-intent.ts +12 -2
  136. package/src/lib/server/chat-execution/message-classifier.test.ts +35 -23
  137. package/src/lib/server/chat-execution/message-classifier.ts +74 -32
  138. package/src/lib/server/chat-execution/prompt-builder.test.ts +29 -0
  139. package/src/lib/server/chat-execution/prompt-builder.ts +37 -2
  140. package/src/lib/server/chat-execution/prompt-sections.test.ts +56 -0
  141. package/src/lib/server/chat-execution/prompt-sections.ts +193 -0
  142. package/src/lib/server/chat-execution/stream-agent-chat.ts +63 -7
  143. package/src/lib/server/chat-execution/stream-continuation.test.ts +36 -0
  144. package/src/lib/server/chat-execution/stream-continuation.ts +28 -13
  145. package/src/lib/server/chatrooms/chatroom-agent-signals.ts +26 -18
  146. package/src/lib/server/chatrooms/chatroom-helpers.ts +19 -18
  147. package/src/lib/server/chatrooms/chatroom-repository.ts +16 -0
  148. package/src/lib/server/chatrooms/chatroom-routing.test.ts +96 -0
  149. package/src/lib/server/chatrooms/chatroom-routing.ts +207 -53
  150. package/src/lib/server/chatrooms/mailbox-utils.ts +4 -2
  151. package/src/lib/server/chatrooms/session-mailbox.ts +50 -40
  152. package/src/lib/server/chats/chat-session-service.ts +410 -0
  153. package/src/lib/server/connectors/access.ts +1 -1
  154. package/src/lib/server/connectors/commands.ts +7 -6
  155. package/src/lib/server/connectors/connector-inbound.ts +14 -7
  156. package/src/lib/server/connectors/connector-outbound.ts +16 -11
  157. package/src/lib/server/connectors/connector-service.ts +453 -0
  158. package/src/lib/server/connectors/delivery.ts +17 -12
  159. package/src/lib/server/connectors/inbound-audio-transcription.ts +5 -14
  160. package/src/lib/server/connectors/media.ts +1 -1
  161. package/src/lib/server/connectors/response-media.ts +1 -1
  162. package/src/lib/server/connectors/session-consolidation.ts +11 -7
  163. package/src/lib/server/connectors/session.ts +9 -7
  164. package/src/lib/server/connectors/voice-note.ts +2 -1
  165. package/src/lib/server/context-manager.ts +20 -1
  166. package/src/lib/server/cost.ts +2 -3
  167. package/src/lib/server/credentials/credential-repository.ts +43 -4
  168. package/src/lib/server/credentials/credential-service.ts +112 -0
  169. package/src/lib/server/daemon/admin-metadata.ts +64 -0
  170. package/src/lib/server/daemon/controller.ts +577 -0
  171. package/src/lib/server/daemon/daemon-runtime.ts +352 -0
  172. package/src/lib/server/daemon/daemon-status-repository.ts +63 -0
  173. package/src/lib/server/daemon/types.ts +101 -0
  174. package/src/lib/server/embeddings.ts +3 -9
  175. package/src/lib/server/eval/agent-regression.ts +3 -2
  176. package/src/lib/server/eval/runner.ts +2 -2
  177. package/src/lib/server/execution-brief.test.ts +167 -0
  178. package/src/lib/server/execution-brief.ts +295 -0
  179. package/src/lib/server/execution-engine/chat-turn.ts +9 -0
  180. package/src/lib/server/execution-engine/import-boundary.test.ts +44 -0
  181. package/src/lib/server/execution-engine/index.ts +35 -0
  182. package/src/lib/server/execution-engine/task-attempt.ts +303 -0
  183. package/src/lib/server/execution-engine/types.ts +33 -0
  184. package/src/lib/server/gateways/gateway-profile-repository.ts +47 -3
  185. package/src/lib/server/gateways/gateway-profile-service.ts +200 -0
  186. package/src/lib/server/memory/session-archive-memory.ts +12 -10
  187. package/src/lib/server/messages/message-repository.ts +330 -0
  188. package/src/lib/server/missions/mission-service/core.ts +8 -6
  189. package/src/lib/server/openclaw/agent-resolver.ts +2 -3
  190. package/src/lib/server/openclaw/doctor.ts +1 -1
  191. package/src/lib/server/openclaw/gateway.test.ts +10 -1
  192. package/src/lib/server/openclaw/gateway.ts +5 -14
  193. package/src/lib/server/openclaw/health.ts +3 -11
  194. package/src/lib/server/openclaw/sync.ts +8 -6
  195. package/src/lib/server/persistence/storage-context.ts +3 -0
  196. package/src/lib/server/protocols/protocol-agent-turn.ts +25 -17
  197. package/src/lib/server/protocols/protocol-normalization.ts +1 -1
  198. package/src/lib/server/protocols/protocol-queries.ts +13 -7
  199. package/src/lib/server/protocols/protocol-run-lifecycle.ts +16 -20
  200. package/src/lib/server/protocols/protocol-run-repository.ts +81 -0
  201. package/src/lib/server/protocols/protocol-step-processors.ts +23 -31
  202. package/src/lib/server/protocols/protocol-swarm.ts +8 -8
  203. package/src/lib/server/protocols/protocol-template-repository.ts +42 -0
  204. package/src/lib/server/protocols/protocol-templates.ts +4 -2
  205. package/src/lib/server/protocols/protocol-types.ts +10 -7
  206. package/src/lib/server/provider-endpoint.ts +7 -12
  207. package/src/lib/server/provider-model-discovery.ts +2 -11
  208. package/src/lib/server/query-expansion.ts +5 -6
  209. package/src/lib/server/run-context.test.ts +365 -0
  210. package/src/lib/server/run-context.ts +367 -0
  211. package/src/lib/server/runtime/heartbeat-service.ts +7 -5
  212. package/src/lib/server/runtime/queue/core.ts +61 -190
  213. package/src/lib/server/runtime/run-ledger.ts +8 -0
  214. package/src/lib/server/runtime/session-run-manager/drain.ts +2 -2
  215. package/src/lib/server/runtime/session-run-manager/enqueue.ts +6 -0
  216. package/src/lib/server/runtime/session-run-manager/state.ts +4 -0
  217. package/src/lib/server/schedules/schedule-route-service.ts +230 -0
  218. package/src/lib/server/service-result.ts +16 -0
  219. package/src/lib/server/session-note.ts +2 -3
  220. package/src/lib/server/session-reset-policy.ts +4 -3
  221. package/src/lib/server/session-tools/connector.ts +9 -6
  222. package/src/lib/server/session-tools/context-mgmt.ts +58 -9
  223. package/src/lib/server/session-tools/crud.ts +162 -10
  224. package/src/lib/server/session-tools/delegate.ts +1 -1
  225. package/src/lib/server/session-tools/manage-tasks.test.ts +152 -0
  226. package/src/lib/server/session-tools/memory.ts +6 -4
  227. package/src/lib/server/session-tools/session-info.test.ts +56 -0
  228. package/src/lib/server/session-tools/session-info.ts +119 -12
  229. package/src/lib/server/session-tools/skill-runtime.ts +3 -1
  230. package/src/lib/server/session-tools/skills.ts +15 -15
  231. package/src/lib/server/session-tools/subagent.test.ts +115 -1
  232. package/src/lib/server/session-tools/subagent.ts +125 -7
  233. package/src/lib/server/session-tools/team-context.ts +4 -3
  234. package/src/lib/server/session-tools/wallet.ts +0 -58
  235. package/src/lib/server/sessions/session-lineage.ts +55 -0
  236. package/src/lib/server/sessions/session-repository.ts +2 -2
  237. package/src/lib/server/skills/learned-skills.ts +24 -23
  238. package/src/lib/server/skills/runtime-skill-resolver.ts +2 -1
  239. package/src/lib/server/skills/skill-repository.ts +136 -13
  240. package/src/lib/server/skills/skill-suggestions.ts +25 -28
  241. package/src/lib/server/storage-normalization.test.ts +44 -267
  242. package/src/lib/server/storage-normalization.ts +75 -0
  243. package/src/lib/server/storage.ts +19 -0
  244. package/src/lib/server/structured-extract.ts +3 -14
  245. package/src/lib/server/tasks/task-followups.ts +16 -11
  246. package/src/lib/server/tasks/task-result.test.ts +25 -29
  247. package/src/lib/server/tasks/task-result.ts +5 -9
  248. package/src/lib/server/tasks/task-route-service.ts +449 -0
  249. package/src/lib/server/text-normalization.ts +41 -0
  250. package/src/lib/server/tool-planning.ts +6 -42
  251. package/src/lib/server/upload-path.ts +5 -0
  252. package/src/lib/server/working-state/extraction.ts +614 -0
  253. package/src/lib/server/working-state/normalization.ts +866 -0
  254. package/src/lib/server/working-state/prompt.ts +60 -0
  255. package/src/lib/server/working-state/repository.ts +38 -0
  256. package/src/lib/server/working-state/service.test.ts +253 -0
  257. package/src/lib/server/working-state/service.ts +293 -0
  258. package/src/lib/validation/schemas.ts +1 -0
  259. package/src/lib/ws-client.ts +3 -3
  260. package/src/stores/slices/task-slice.ts +1 -4
  261. package/src/stores/use-chatroom-store.ts +2 -2
  262. package/src/types/index.ts +277 -12
@@ -0,0 +1,60 @@
1
+ import { cleanText } from '@/lib/server/text-normalization'
2
+ import type { SessionWorkingState } from '@/types'
3
+
4
+ // ---------------------------------------------------------------------------
5
+ // buildWorkingStatePromptBlock
6
+ // ---------------------------------------------------------------------------
7
+
8
+ function buildListSection(title: string, values: string[]): string | null {
9
+ if (values.length === 0) return null
10
+ return [title, ...values.map((value) => `- ${value}`)].join('\n')
11
+ }
12
+
13
+ export function buildWorkingStatePromptBlockFromState(
14
+ state: SessionWorkingState | null,
15
+ ): string {
16
+ if (!state) return ''
17
+ const activePlan = state.planSteps
18
+ .filter((item) => item.status === 'active')
19
+ .map((item) => item.text)
20
+ .slice(0, 8)
21
+ const confirmedFacts = state.confirmedFacts
22
+ .filter((item) => item.status === 'active')
23
+ .map((item) => item.statement)
24
+ .slice(0, 8)
25
+ const blockers = state.blockers
26
+ .filter((item) => item.status === 'active')
27
+ .map((item) => item.nextAction ? `${item.summary} | next: ${item.nextAction}` : item.summary)
28
+ .slice(0, 6)
29
+ const questions = state.openQuestions
30
+ .filter((item) => item.status === 'active')
31
+ .map((item) => item.question)
32
+ .slice(0, 6)
33
+ const hypotheses = state.hypotheses
34
+ .filter((item) => item.status === 'active')
35
+ .map((item) => item.confidence ? `${item.statement} (${item.confidence})` : item.statement)
36
+ .slice(0, 6)
37
+ const artifacts = state.artifacts
38
+ .filter((item) => item.status === 'active')
39
+ .map((item) => cleanText(item.path || item.url || item.label, 220))
40
+ .slice(0, 6)
41
+
42
+ const sections = [
43
+ '## Active Working State',
44
+ state.objective ? `Objective: ${state.objective}` : '',
45
+ state.summary ? `Summary: ${state.summary}` : '',
46
+ `Status: ${state.status}`,
47
+ state.nextAction ? `Next action: ${state.nextAction}` : '',
48
+ state.successCriteria.length > 0 ? `Success criteria: ${state.successCriteria.join(' | ')}` : '',
49
+ state.constraints.length > 0 ? `Constraints: ${state.constraints.join(' | ')}` : '',
50
+ buildListSection('Plan', activePlan),
51
+ buildListSection('Confirmed facts', confirmedFacts),
52
+ buildListSection('Blockers', blockers),
53
+ buildListSection('Open questions', questions),
54
+ buildListSection('Hypotheses', hypotheses),
55
+ buildListSection('Artifacts', artifacts),
56
+ 'Trust this structured state before reconstructing status from the raw transcript.',
57
+ ].filter(Boolean)
58
+
59
+ return sections.join('\n')
60
+ }
@@ -0,0 +1,38 @@
1
+ import { perf } from '@/lib/server/runtime/perf'
2
+ import {
3
+ deletePersistedWorkingState as deleteStoredWorkingState,
4
+ loadPersistedWorkingState as loadStoredWorkingState,
5
+ upsertPersistedWorkingState as upsertStoredWorkingState,
6
+ } from '@/lib/server/storage'
7
+
8
+ export type PersistedWorkingState = Record<string, unknown>
9
+
10
+ export function loadPersistedWorkingState(sessionId: string): PersistedWorkingState | null {
11
+ return perf.measureSync(
12
+ 'repository',
13
+ 'working-state.get',
14
+ () => loadStoredWorkingState(sessionId) as PersistedWorkingState | null,
15
+ { sessionId },
16
+ )
17
+ }
18
+
19
+ export function upsertPersistedWorkingState(
20
+ sessionId: string,
21
+ value: PersistedWorkingState,
22
+ ): void {
23
+ perf.measureSync(
24
+ 'repository',
25
+ 'working-state.upsert',
26
+ () => upsertStoredWorkingState(sessionId, value),
27
+ { sessionId },
28
+ )
29
+ }
30
+
31
+ export function deletePersistedWorkingState(sessionId: string): void {
32
+ perf.measureSync(
33
+ 'repository',
34
+ 'working-state.delete',
35
+ () => deleteStoredWorkingState(sessionId),
36
+ { sessionId },
37
+ )
38
+ }
@@ -0,0 +1,253 @@
1
+ import assert from 'node:assert/strict'
2
+ import fs from 'node:fs'
3
+ import os from 'node:os'
4
+ import path from 'node:path'
5
+ import { spawnSync } from 'node:child_process'
6
+ import { describe, it } from 'node:test'
7
+
8
+ const repoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '../../..')
9
+
10
+ function runWithTempDataDir(script: string) {
11
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-working-state-'))
12
+ try {
13
+ const result = spawnSync(
14
+ process.execPath,
15
+ ['--import', 'tsx', '--input-type=module', '--eval', script],
16
+ {
17
+ cwd: repoRoot,
18
+ env: {
19
+ ...process.env,
20
+ DATA_DIR: tempDir,
21
+ WORKSPACE_DIR: path.join(tempDir, 'workspace'),
22
+ SWARMCLAW_BUILD_MODE: '1',
23
+ },
24
+ encoding: 'utf-8',
25
+ timeout: 20000,
26
+ },
27
+ )
28
+ assert.equal(result.status, 0, result.stderr || result.stdout || 'subprocess failed')
29
+ const lines = (result.stdout || '')
30
+ .trim()
31
+ .split('\n')
32
+ .map((line) => line.trim())
33
+ .filter(Boolean)
34
+ const jsonLine = [...lines].reverse().find((line) => line.startsWith('{'))
35
+ return JSON.parse(jsonLine || '{}') as Record<string, unknown>
36
+ } finally {
37
+ fs.rmSync(tempDir, { recursive: true, force: true })
38
+ }
39
+ }
40
+
41
+ describe('working-state service', () => {
42
+ it('merges deterministic evidence with structured extraction and renders a prompt block', () => {
43
+ const output = runWithTempDataDir(`
44
+ const storageMod = await import('@/lib/server/storage')
45
+ const storage = storageMod.default || storageMod['module.exports'] || storageMod
46
+ const serviceMod = await import('@/lib/server/working-state/service')
47
+ const service = serviceMod.default || serviceMod['module.exports'] || serviceMod
48
+
49
+ storage.saveAgents({
50
+ 'agent-a': {
51
+ id: 'agent-a',
52
+ name: 'Agent A',
53
+ provider: 'openai',
54
+ model: 'gpt-test',
55
+ },
56
+ })
57
+
58
+ storage.saveSessions({
59
+ main: {
60
+ id: 'main',
61
+ name: 'Main Thread',
62
+ shortcutForAgentId: 'agent-a',
63
+ cwd: process.cwd(),
64
+ user: 'tester',
65
+ provider: 'openai',
66
+ model: 'gpt-test',
67
+ claudeSessionId: null,
68
+ messages: [
69
+ { role: 'user', text: 'Please fix the migration and write the summary to docs/migration.md.', time: 1 },
70
+ ],
71
+ createdAt: 1,
72
+ lastActiveAt: 1,
73
+ sessionType: 'human',
74
+ agentId: 'agent-a',
75
+ heartbeatEnabled: true,
76
+ },
77
+ })
78
+
79
+ const mission = {
80
+ id: 'mission-1',
81
+ source: 'chat',
82
+ objective: 'Ship the migration fix safely.',
83
+ successCriteria: ['docs written', 'type-check green'],
84
+ status: 'active',
85
+ phase: 'executing',
86
+ currentStep: 'Run deploy after approval',
87
+ plannerSummary: 'Fix the migration, verify it, then deploy.',
88
+ verifierSummary: null,
89
+ blockerSummary: null,
90
+ createdAt: 1,
91
+ updatedAt: 2,
92
+ }
93
+
94
+ const state = await service.synchronizeWorkingStateForTurn({
95
+ sessionId: 'main',
96
+ agentId: 'agent-a',
97
+ mission,
98
+ source: 'chat',
99
+ runId: 'run-1',
100
+ message: 'Please fix the migration and write the summary to docs/migration.md.',
101
+ assistantText: 'I fixed the migration and wrote docs/migration.md.',
102
+ toolEvents: [
103
+ {
104
+ name: 'files',
105
+ input: JSON.stringify({ action: 'write', files: [{ path: 'docs/migration.md', content: '# migration' }] }),
106
+ output: JSON.stringify({ ok: true, files: [{ path: 'docs/migration.md' }] }),
107
+ },
108
+ {
109
+ name: 'shell',
110
+ input: 'npm run type-check',
111
+ output: 'Type check passed. Evidence file: /api/uploads/typecheck.txt',
112
+ },
113
+ {
114
+ name: 'deploy_release',
115
+ input: JSON.stringify({ env: 'prod' }),
116
+ output: JSON.stringify({ requiresApproval: true, approvalId: 'apr-123' }),
117
+ },
118
+ ],
119
+ }, {
120
+ generateText: async () => JSON.stringify({
121
+ status: 'blocked',
122
+ nextAction: 'Wait for approval apr-123, then run the deploy step.',
123
+ planSteps: [
124
+ { text: 'Fix the migration build issue', status: 'resolved' },
125
+ { text: 'Wait for approval apr-123', status: 'active' },
126
+ ],
127
+ factsUpsert: [
128
+ { statement: 'Type check passed after the migration update.', source: 'tool' },
129
+ ],
130
+ decisionsAppend: [
131
+ { summary: 'Capture the migration summary in docs/migration.md.', rationale: 'Keep the fix durable.' },
132
+ ],
133
+ blockersUpsert: [
134
+ { summary: 'Approval apr-123 is pending.', kind: 'approval', nextAction: 'Wait for approval apr-123', status: 'active' },
135
+ ],
136
+ hypothesesUpsert: [
137
+ { statement: 'After approval, deploy should be the only remaining step.', confidence: 'medium', status: 'active' },
138
+ ],
139
+ }),
140
+ })
141
+
142
+ const prompt = service.buildWorkingStatePromptBlock('main', { mission })
143
+ console.log(JSON.stringify({
144
+ status: state.status,
145
+ nextAction: state.nextAction,
146
+ planSteps: state.planSteps.map((step) => ({ text: step.text, status: step.status })),
147
+ facts: state.confirmedFacts.map((fact) => fact.statement),
148
+ blockers: state.blockers.map((blocker) => blocker.summary),
149
+ artifacts: state.artifacts.map((artifact) => artifact.path || artifact.url || artifact.label),
150
+ prompt,
151
+ }))
152
+ `)
153
+
154
+ assert.equal(output.status, 'blocked')
155
+ assert.match(String(output.nextAction), /approval apr-123/i)
156
+ assert.ok(Array.isArray(output.planSteps) && output.planSteps.some((step) => /wait for approval apr-123/i.test(String((step as Record<string, unknown>).text))))
157
+ assert.ok(Array.isArray(output.facts) && output.facts.some((fact) => /type check passed/i.test(String(fact))))
158
+ assert.ok(Array.isArray(output.blockers) && output.blockers.some((blocker) => /approval apr-123 is pending/i.test(String(blocker))))
159
+ assert.ok(Array.isArray(output.artifacts) && output.artifacts.some((artifact) => /docs\/migration\.md/i.test(String(artifact))))
160
+ assert.match(String(output.prompt), /Active Working State/)
161
+ assert.match(String(output.prompt), /docs\/migration\.md/)
162
+ assert.match(String(output.prompt), /approval apr-123/i)
163
+ })
164
+
165
+ it('keeps the main loop hydrated from working state and mirrors main-loop updates back', () => {
166
+ const output = runWithTempDataDir(`
167
+ const storageMod = await import('@/lib/server/storage')
168
+ const storage = storageMod.default || storageMod['module.exports'] || storageMod
169
+ const mainLoopMod = await import('@/lib/server/agents/main-agent-loop')
170
+ const mainLoop = mainLoopMod.default || mainLoopMod['module.exports'] || mainLoopMod
171
+ const serviceMod = await import('@/lib/server/working-state/service')
172
+ const service = serviceMod.default || serviceMod['module.exports'] || serviceMod
173
+
174
+ storage.saveAgents({
175
+ 'agent-a': {
176
+ id: 'agent-a',
177
+ name: 'Agent A',
178
+ provider: 'openai',
179
+ model: 'gpt-test',
180
+ },
181
+ })
182
+
183
+ storage.saveSessions({
184
+ main: {
185
+ id: 'main',
186
+ name: 'Main Thread',
187
+ shortcutForAgentId: 'agent-a',
188
+ cwd: process.cwd(),
189
+ user: 'tester',
190
+ provider: 'openai',
191
+ model: 'gpt-test',
192
+ claudeSessionId: null,
193
+ messages: [
194
+ { role: 'user', text: 'Ship the release checklist.', time: 1 },
195
+ ],
196
+ createdAt: 1,
197
+ lastActiveAt: 1,
198
+ sessionType: 'human',
199
+ agentId: 'agent-a',
200
+ heartbeatEnabled: true,
201
+ },
202
+ })
203
+
204
+ service.applyWorkingStatePatch('main', {
205
+ objective: 'Ship the release checklist.',
206
+ status: 'progress',
207
+ nextAction: 'Verify the changelog output.',
208
+ planSteps: [
209
+ { text: 'Verify the changelog output.', status: 'active' },
210
+ { text: 'Publish the release notes.', status: 'resolved' },
211
+ ],
212
+ blockersUpsert: [
213
+ { summary: 'Waiting on release manager approval.', kind: 'approval', status: 'active' },
214
+ ],
215
+ })
216
+
217
+ const before = mainLoop.getMainLoopStateForSession('main')
218
+ const prompt = mainLoop.buildMainLoopHeartbeatPrompt(storage.loadSessions().main, 'fallback heartbeat')
219
+
220
+ mainLoop.handleMainLoopRunResult({
221
+ sessionId: 'main',
222
+ message: 'Continue the release checklist.',
223
+ internal: true,
224
+ source: 'heartbeat',
225
+ resultText: [
226
+ 'Updated the changelog and prepared the release notes.',
227
+ '[MAIN_LOOP_PLAN]{"steps":["publish the release notes"],"current_step":"publish the release notes","completed_steps":["verify the changelog output"]}',
228
+ '[MAIN_LOOP_REVIEW]{"note":"changelog verified","confidence":0.91,"needs_replan":false}',
229
+ '[AGENT_HEARTBEAT_META]{"goal":"Ship the release checklist","status":"progress","next_action":"publish the release notes"}',
230
+ ].join('\\n'),
231
+ toolEvents: [{ name: 'shell', input: 'npm run type-check', output: 'ok' }],
232
+ })
233
+
234
+ const after = mainLoop.getMainLoopStateForSession('main')
235
+ const working = service.loadSessionWorkingState('main')
236
+
237
+ console.log(JSON.stringify({
238
+ beforeNextAction: before?.nextAction || null,
239
+ prompt,
240
+ afterNextAction: after?.nextAction || null,
241
+ workingNextAction: working?.nextAction || null,
242
+ workingPlanSteps: working?.planSteps?.map((step) => ({ text: step.text, status: step.status })) || [],
243
+ }))
244
+ `)
245
+
246
+ assert.match(String(output.beforeNextAction), /verify the changelog output/i)
247
+ assert.match(String(output.prompt), /verify the changelog output/i)
248
+ assert.match(String(output.prompt), /waiting on release manager approval/i)
249
+ assert.match(String(output.afterNextAction), /publish the release notes/i)
250
+ assert.match(String(output.workingNextAction), /publish the release notes/i)
251
+ assert.ok(Array.isArray(output.workingPlanSteps) && output.workingPlanSteps.some((step) => /publish the release notes/i.test(String((step as Record<string, unknown>).text))))
252
+ })
253
+ })
@@ -0,0 +1,293 @@
1
+ import { genId } from '@/lib/id'
2
+ import { cleanText, cleanMultiline, normalizeList } from '@/lib/server/text-normalization'
3
+ import type {
4
+ Mission,
5
+ SessionWorkingState,
6
+ WorkingBlocker,
7
+ WorkingPlanStepPatch,
8
+ WorkingStatePatch,
9
+ WorkingStateItemStatus,
10
+ WorkingStateStatus,
11
+ } from '@/types'
12
+
13
+ import {
14
+ deletePersistedWorkingState,
15
+ loadPersistedWorkingState,
16
+ upsertPersistedWorkingState,
17
+ } from './repository'
18
+
19
+ import {
20
+ normalizeWorkingState,
21
+ defaultWorkingState,
22
+ compactWorkingStateObject,
23
+ syncWorkingStateWithMission,
24
+ normalizeItemStatus,
25
+ normalizeStateStatus,
26
+ upsertItems,
27
+ factUpsertConfig,
28
+ artifactUpsertConfig,
29
+ decisionUpsertConfig,
30
+ blockerUpsertConfig,
31
+ questionUpsertConfig,
32
+ hypothesisUpsertConfig,
33
+ appendEvidenceRefs,
34
+ markSuperseded,
35
+ now,
36
+ MAX_PLAN_STEPS,
37
+ compactPlanSteps,
38
+ } from '@/lib/server/working-state/normalization'
39
+ import type {
40
+ WorkingStateDeterministicUpdateInput,
41
+ SynchronizeWorkingStateForTurnInput,
42
+ } from '@/lib/server/working-state/normalization'
43
+
44
+ import {
45
+ deterministicEvidencePatch,
46
+ extractWorkingStatePatch,
47
+ shouldExtractStructuredPatch,
48
+ } from '@/lib/server/working-state/extraction'
49
+
50
+ import { buildWorkingStatePromptBlockFromState } from '@/lib/server/working-state/prompt'
51
+
52
+ // ---------------------------------------------------------------------------
53
+ // Re-exports for consumer compatibility
54
+ // ---------------------------------------------------------------------------
55
+
56
+ // normalization.ts re-exports
57
+ export {
58
+ // Constants
59
+ MAX_PLAN_STEPS,
60
+ MAX_CONFIRMED_FACTS,
61
+ MAX_ARTIFACTS,
62
+ MAX_DECISIONS,
63
+ MAX_BLOCKERS,
64
+ MAX_OPEN_QUESTIONS,
65
+ MAX_HYPOTHESES,
66
+ MAX_EVIDENCE_REFS,
67
+ EXTRACTION_TIMEOUT_MS,
68
+ ACTIVE_STATUS,
69
+ // Schemas
70
+ WorkingPlanStepPatchSchema,
71
+ WorkingFactPatchSchema,
72
+ WorkingArtifactPatchSchema,
73
+ WorkingDecisionPatchSchema,
74
+ WorkingBlockerPatchSchema,
75
+ WorkingQuestionPatchSchema,
76
+ WorkingHypothesisPatchSchema,
77
+ WorkingStatePatchSchema,
78
+ // Normalize functions
79
+ normalizeItemStatus,
80
+ normalizeStateStatus,
81
+ normalizeEvidenceIds,
82
+ normalizeEvidenceRef,
83
+ normalizePlanStep,
84
+ normalizeFact,
85
+ normalizeArtifact,
86
+ normalizeDecision,
87
+ normalizeBlocker,
88
+ normalizeQuestion,
89
+ normalizeHypothesis,
90
+ normalizeWorkingState,
91
+ normalizeMatchKey,
92
+ // Utility helpers
93
+ now,
94
+ itemSortRank,
95
+ genericCompact,
96
+ compactPlanSteps,
97
+ defaultWorkingState,
98
+ compactWorkingStateObject,
99
+ missionStatusToWorkingStateStatus,
100
+ syncWorkingStateWithMission,
101
+ // Upsert
102
+ upsertItems,
103
+ factUpsertConfig,
104
+ artifactUpsertConfig,
105
+ decisionUpsertConfig,
106
+ blockerUpsertConfig,
107
+ questionUpsertConfig,
108
+ hypothesisUpsertConfig,
109
+ appendEvidenceRefs,
110
+ markSuperseded,
111
+ } from '@/lib/server/working-state/normalization'
112
+ export type {
113
+ TimedWorkingItem,
114
+ UpsertConfig,
115
+ WorkingStateDeterministicUpdateInput,
116
+ WorkingStateExtractionInput,
117
+ SynchronizeWorkingStateForTurnInput,
118
+ } from '@/lib/server/working-state/normalization'
119
+
120
+ // extraction.ts re-exports
121
+ export {
122
+ parseStructuredObject,
123
+ extractFirstJsonObject,
124
+ parseWorkingStatePatchResponse,
125
+ renderStateForExtraction,
126
+ summarizeToolEvents,
127
+ buildWorkingStatePatchPrompt,
128
+ collectJsonCandidates,
129
+ uniqueByKey,
130
+ looksLikeUrl,
131
+ looksLikeFilePath,
132
+ extractPlainTextArtifacts,
133
+ deterministicEvidencePatch,
134
+ extractWorkingStatePatch,
135
+ shouldExtractStructuredPatch,
136
+ } from '@/lib/server/working-state/extraction'
137
+
138
+ // prompt.ts re-exports
139
+ export { buildWorkingStatePromptBlockFromState } from '@/lib/server/working-state/prompt'
140
+
141
+ // ---------------------------------------------------------------------------
142
+ // CRUD / coordination layer
143
+ // ---------------------------------------------------------------------------
144
+
145
+ export function loadSessionWorkingState(sessionId: string, options?: { mission?: Mission | null }): SessionWorkingState | null {
146
+ const stored = loadPersistedWorkingState(sessionId)
147
+ if (!stored && !options?.mission) return null
148
+ return normalizeWorkingState(stored, sessionId, options?.mission || null)
149
+ }
150
+
151
+ export function getOrCreateSessionWorkingState(sessionId: string, options?: { mission?: Mission | null }): SessionWorkingState {
152
+ return loadSessionWorkingState(sessionId, options) || defaultWorkingState(sessionId, options?.mission || null)
153
+ }
154
+
155
+ export function saveSessionWorkingState(state: SessionWorkingState): SessionWorkingState {
156
+ const normalized = compactWorkingStateObject(normalizeWorkingState(state, state.sessionId))
157
+ upsertPersistedWorkingState(normalized.sessionId, normalized as unknown as Record<string, unknown>)
158
+ return normalized
159
+ }
160
+
161
+ export function deleteSessionWorkingState(sessionId: string): void {
162
+ deletePersistedWorkingState(sessionId)
163
+ }
164
+
165
+ export function applyWorkingStatePatch(
166
+ sessionId: string,
167
+ patch: WorkingStatePatch,
168
+ options?: { mission?: Mission | null },
169
+ ): SessionWorkingState {
170
+ const current = getOrCreateSessionWorkingState(sessionId, options)
171
+ const next: SessionWorkingState = {
172
+ ...current,
173
+ missionId: options?.mission?.id || current.missionId || null,
174
+ objective: patch.objective !== undefined ? (cleanMultiline(patch.objective, 900) || null) : current.objective,
175
+ summary: patch.summary !== undefined ? (cleanMultiline(patch.summary, 600) || null) : current.summary,
176
+ constraints: patch.constraints !== undefined ? normalizeList(patch.constraints, 12, 240) : current.constraints,
177
+ successCriteria: patch.successCriteria !== undefined ? normalizeList(patch.successCriteria, 12, 240) : current.successCriteria,
178
+ status: patch.status !== undefined && patch.status !== null ? normalizeStateStatus(patch.status, current.status) : current.status,
179
+ nextAction: patch.nextAction !== undefined ? (cleanText(patch.nextAction, 240) || null) : current.nextAction,
180
+ planSteps: upsertItems(current.planSteps, patch.planSteps, {
181
+ max: MAX_PLAN_STEPS,
182
+ getPatchId: (item) => cleanText(item.id, 120) || null,
183
+ getPatchKey: (item) => cleanText(item.text, 240),
184
+ getItemKey: (item) => item.text,
185
+ create: (item, nowTs) => ({
186
+ id: cleanText(item.id, 120) || genId(12),
187
+ text: cleanText(item.text, 240),
188
+ status: normalizeItemStatus(item.status),
189
+ createdAt: nowTs,
190
+ updatedAt: nowTs,
191
+ }),
192
+ merge: (item, patchItem, nowTs) => ({
193
+ ...item,
194
+ text: cleanText(patchItem.text, 240) || item.text,
195
+ status: normalizeItemStatus(patchItem.status, item.status),
196
+ updatedAt: nowTs,
197
+ }),
198
+ compact: compactPlanSteps,
199
+ }),
200
+ confirmedFacts: upsertItems(current.confirmedFacts, patch.factsUpsert, factUpsertConfig()),
201
+ artifacts: upsertItems(current.artifacts, patch.artifactsUpsert, artifactUpsertConfig()),
202
+ decisions: upsertItems(current.decisions, patch.decisionsAppend, decisionUpsertConfig()),
203
+ blockers: upsertItems(current.blockers, patch.blockersUpsert, blockerUpsertConfig()),
204
+ openQuestions: upsertItems(current.openQuestions, patch.questionsUpsert, questionUpsertConfig()),
205
+ hypotheses: upsertItems(current.hypotheses, patch.hypothesesUpsert, hypothesisUpsertConfig()),
206
+ evidenceRefs: appendEvidenceRefs(current.evidenceRefs, patch.evidenceAppend),
207
+ updatedAt: now(),
208
+ }
209
+
210
+ next.planSteps = markSuperseded(next.planSteps, patch.supersedeIds)
211
+ next.confirmedFacts = markSuperseded(next.confirmedFacts, patch.supersedeIds)
212
+ next.artifacts = markSuperseded(next.artifacts, patch.supersedeIds)
213
+ next.decisions = markSuperseded(next.decisions, patch.supersedeIds)
214
+ next.blockers = markSuperseded(next.blockers, patch.supersedeIds)
215
+ next.openQuestions = markSuperseded(next.openQuestions, patch.supersedeIds)
216
+ next.hypotheses = markSuperseded(next.hypotheses, patch.supersedeIds)
217
+
218
+ const synced = compactWorkingStateObject(syncWorkingStateWithMission(next, options?.mission || null))
219
+ upsertPersistedWorkingState(sessionId, synced as unknown as Record<string, unknown>)
220
+ return synced
221
+ }
222
+
223
+ export function recordWorkingStateEvidence(input: WorkingStateDeterministicUpdateInput): SessionWorkingState {
224
+ return applyWorkingStatePatch(
225
+ input.sessionId,
226
+ deterministicEvidencePatch(input),
227
+ { mission: input.mission || null },
228
+ )
229
+ }
230
+
231
+ export async function synchronizeWorkingStateForTurn(
232
+ input: SynchronizeWorkingStateForTurnInput,
233
+ options?: {
234
+ generateText?: (prompt: string) => Promise<string>
235
+ },
236
+ ): Promise<SessionWorkingState> {
237
+ const deterministic = recordWorkingStateEvidence(input)
238
+ if (!shouldExtractStructuredPatch(input)) return deterministic
239
+ const patch = await extractWorkingStatePatch({
240
+ ...input,
241
+ currentState: deterministic,
242
+ }, options)
243
+ if (!patch) return deterministic
244
+ return applyWorkingStatePatch(input.sessionId, patch, { mission: input.mission || null })
245
+ }
246
+
247
+ export function syncWorkingStateFromMainLoopState(input: {
248
+ sessionId: string
249
+ mission?: Mission | null
250
+ goal?: string | null
251
+ summary?: string | null
252
+ status?: WorkingStateStatus | null
253
+ nextAction?: string | null
254
+ planSteps?: string[]
255
+ blockers?: Array<{ summary: string; kind?: WorkingBlocker['kind'] | null }>
256
+ facts?: string[]
257
+ }): SessionWorkingState {
258
+ const planSteps = Array.isArray(input.planSteps)
259
+ ? input.planSteps.map((step, index) => ({
260
+ text: step,
261
+ status: index === 0 && input.status !== 'completed' ? 'active' : (input.status === 'completed' ? 'resolved' : 'resolved'),
262
+ } satisfies WorkingPlanStepPatch))
263
+ : undefined
264
+ return applyWorkingStatePatch(input.sessionId, {
265
+ objective: cleanMultiline(input.goal, 900) || undefined,
266
+ summary: cleanMultiline(input.summary, 600) || undefined,
267
+ status: input.status || undefined,
268
+ nextAction: cleanText(input.nextAction, 240) || undefined,
269
+ planSteps,
270
+ blockersUpsert: Array.isArray(input.blockers)
271
+ ? input.blockers.map((blocker) => ({
272
+ summary: cleanText(blocker.summary, 280),
273
+ kind: blocker.kind || undefined,
274
+ status: (input.status === 'completed' ? 'resolved' : 'active') as WorkingStateItemStatus,
275
+ })).filter((blocker) => blocker.summary)
276
+ : undefined,
277
+ factsUpsert: Array.isArray(input.facts)
278
+ ? input.facts.map((fact) => ({
279
+ statement: cleanText(fact, 280),
280
+ source: 'system' as const,
281
+ status: (input.status === 'completed' ? 'resolved' : 'active') as WorkingStateItemStatus,
282
+ })).filter((fact) => fact.statement)
283
+ : undefined,
284
+ }, { mission: input.mission || null })
285
+ }
286
+
287
+ export function buildWorkingStatePromptBlock(
288
+ sessionId: string,
289
+ options?: { mission?: Mission | null },
290
+ ): string {
291
+ const state = loadSessionWorkingState(sessionId, options)
292
+ return buildWorkingStatePromptBlockFromState(state)
293
+ }
@@ -189,6 +189,7 @@ export const ChatroomCreateSchema = z.object({
189
189
  description: z.string().optional().default(''),
190
190
  chatMode: z.enum(['sequential', 'parallel']).optional(),
191
191
  autoAddress: z.boolean().optional(),
192
+ routingGuidance: z.string().nullable().optional(),
192
193
  routingRules: z.array(z.object({
193
194
  id: z.string(),
194
195
  type: z.enum(['keyword', 'capability']),
@@ -1,4 +1,4 @@
1
- import { jitteredBackoff } from '@/lib/shared-utils'
1
+ import { jitteredBackoff, hmrSingleton } from '@/lib/shared-utils'
2
2
 
3
3
  type WsCallback = () => void
4
4
 
@@ -7,9 +7,9 @@ let wsEnabled = false
7
7
  let reconnectTimer: ReturnType<typeof setTimeout> | null = null
8
8
  let reconnectAttempt = 0
9
9
  const MAX_RECONNECT_DELAY = 30_000
10
- const listeners = new Map<string, Set<WsCallback>>()
10
+ const listeners = hmrSingleton('wsClient_listeners', () => new Map<string, Set<WsCallback>>())
11
11
  let connected = false
12
- const connectionStateListeners = new Set<() => void>()
12
+ const connectionStateListeners = hmrSingleton('wsClient_connectionStateListeners', () => new Set<() => void>())
13
13
 
14
14
  function getWsUrl(): string {
15
15
  if (typeof window === 'undefined') return 'ws://localhost:3457/ws'
@@ -56,8 +56,5 @@ export const createTaskSlice: StateCreator<AppState, [], [], TaskSlice> = (set,
56
56
  }
57
57
  },
58
58
  showArchivedTasks: false,
59
- setShowArchivedTasks: (show) => {
60
- set({ showArchivedTasks: show })
61
- get().loadTasks(show)
62
- }
59
+ setShowArchivedTasks: (show) => set({ showArchivedTasks: show })
63
60
  })