agent-relay-server 0.121.0 → 0.121.1

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 (126) hide show
  1. package/docs/openapi.json +1 -1
  2. package/package.json +5 -4
  3. package/public/assets/{MessageBubble-BaPGFJxq.js → MessageBubble-CLqxCG9b.js} +2 -2
  4. package/public/assets/{MessageBubble-BaPGFJxq.js.map → MessageBubble-CLqxCG9b.js.map} +1 -1
  5. package/public/assets/{PlanGraphPanel-BXCVfmln.js → PlanGraphPanel-BkkToFEb.js} +2 -2
  6. package/public/assets/{PlanGraphPanel-BXCVfmln.js.map → PlanGraphPanel-BkkToFEb.js.map} +1 -1
  7. package/public/assets/{activity-D_shwAZt.js → activity-D-ocGQGs.js} +2 -2
  8. package/public/assets/{activity-D_shwAZt.js.map → activity-D-ocGQGs.js.map} +1 -1
  9. package/public/assets/{agent-profiles-BrfVz5IV.js → agent-profiles-CRPgYTyb.js} +2 -2
  10. package/public/assets/{agent-profiles-BrfVz5IV.js.map → agent-profiles-CRPgYTyb.js.map} +1 -1
  11. package/public/assets/{agents-B5w-Z9Ic.js → agents-Chw5E_2q.js} +2 -2
  12. package/public/assets/{agents-B5w-Z9Ic.js.map → agents-Chw5E_2q.js.map} +1 -1
  13. package/public/assets/{analytics-Bm3D12UN.js → analytics-BmNDG0hR.js} +2 -2
  14. package/public/assets/{analytics-Bm3D12UN.js.map → analytics-BmNDG0hR.js.map} +1 -1
  15. package/public/assets/{automation-DOTL8BFs.js → automation-D7g90kTv.js} +2 -2
  16. package/public/assets/{automation-DOTL8BFs.js.map → automation-D7g90kTv.js.map} +1 -1
  17. package/public/assets/{branch-state-badge-BlqeJZ5-.js → branch-state-badge-Bv7DhLBZ.js} +2 -2
  18. package/public/assets/{branch-state-badge-BlqeJZ5-.js.map → branch-state-badge-Bv7DhLBZ.js.map} +1 -1
  19. package/public/assets/{channels-BoiKfK6Q.js → channels-CLwPeEPi.js} +2 -2
  20. package/public/assets/{channels-BoiKfK6Q.js.map → channels-CLwPeEPi.js.map} +1 -1
  21. package/public/assets/{chat-PeU3iOaa.js → chat-Cgj7VKzc.js} +2 -2
  22. package/public/assets/{chat-PeU3iOaa.js.map → chat-Cgj7VKzc.js.map} +1 -1
  23. package/public/assets/{connectors-BvV8Hee2.js → connectors-Br6fiTny.js} +2 -2
  24. package/public/assets/{connectors-BvV8Hee2.js.map → connectors-Br6fiTny.js.map} +1 -1
  25. package/public/assets/{formatted-body-C1FgzlT1.js → formatted-body-CmkuD23M.js} +3 -3
  26. package/public/assets/{formatted-body-C1FgzlT1.js.map → formatted-body-CmkuD23M.js.map} +1 -1
  27. package/public/assets/{formatted-body-impl-BTpU1xH4.js → formatted-body-impl-DbdFmbwW.js} +2 -2
  28. package/public/assets/{formatted-body-impl-BTpU1xH4.js.map → formatted-body-impl-DbdFmbwW.js.map} +1 -1
  29. package/public/assets/{index-DZEahmyc.js → index-D9OogaB5.js} +6 -6
  30. package/public/assets/{index-DZEahmyc.js.map → index-D9OogaB5.js.map} +1 -1
  31. package/public/assets/{insights-BqmgSpQm.js → insights-qvaPqWCV.js} +2 -2
  32. package/public/assets/{insights-BqmgSpQm.js.map → insights-qvaPqWCV.js.map} +1 -1
  33. package/public/assets/{integrations-CuT92zQi.js → integrations-DYEGSqq_.js} +2 -2
  34. package/public/assets/{integrations-CuT92zQi.js.map → integrations-DYEGSqq_.js.map} +1 -1
  35. package/public/assets/{maintenance-lM7FeXVC.js → maintenance-C0HIEB-J.js} +2 -2
  36. package/public/assets/{maintenance-lM7FeXVC.js.map → maintenance-C0HIEB-J.js.map} +1 -1
  37. package/public/assets/{managed-agents-DaPNv3yY.js → managed-agents-DdS7aI38.js} +2 -2
  38. package/public/assets/{managed-agents-DaPNv3yY.js.map → managed-agents-DdS7aI38.js.map} +1 -1
  39. package/public/assets/{markdown-preview-impl-Wia3Ho1s.js → markdown-preview-impl-BnXMH1z1.js} +2 -2
  40. package/public/assets/{markdown-preview-impl-Wia3Ho1s.js.map → markdown-preview-impl-BnXMH1z1.js.map} +1 -1
  41. package/public/assets/{memory-B1v_zD-M.js → memory-CSwx7bz8.js} +2 -2
  42. package/public/assets/{memory-B1v_zD-M.js.map → memory-CSwx7bz8.js.map} +1 -1
  43. package/public/assets/{messages-BNOx3yK6.js → messages-Be9CmvDv.js} +2 -2
  44. package/public/assets/{messages-BNOx3yK6.js.map → messages-Be9CmvDv.js.map} +1 -1
  45. package/public/assets/{orchestrators-DWFy4asl.js → orchestrators-BgtvvHvo.js} +2 -2
  46. package/public/assets/{orchestrators-DWFy4asl.js.map → orchestrators-BgtvvHvo.js.map} +1 -1
  47. package/public/assets/{overview-C_12cFPM.js → overview-BvhbPWtF.js} +2 -2
  48. package/public/assets/{overview-C_12cFPM.js.map → overview-BvhbPWtF.js.map} +1 -1
  49. package/public/assets/{pairs-DIc7gX83.js → pairs-CpXSOMx0.js} +2 -2
  50. package/public/assets/{pairs-DIc7gX83.js.map → pairs-CpXSOMx0.js.map} +1 -1
  51. package/public/assets/{projects-DPjvJUzT.js → projects-DdPNPFJI.js} +2 -2
  52. package/public/assets/{projects-DPjvJUzT.js.map → projects-DdPNPFJI.js.map} +1 -1
  53. package/public/assets/{prompt-settings-CtumNgG8.js → prompt-settings-BpZse7-X.js} +2 -2
  54. package/public/assets/{prompt-settings-CtumNgG8.js.map → prompt-settings-BpZse7-X.js.map} +1 -1
  55. package/public/assets/{registry-DzUhgMq9.js → registry-pb1gTZEg.js} +2 -2
  56. package/public/assets/{registry-DzUhgMq9.js.map → registry-pb1gTZEg.js.map} +1 -1
  57. package/public/assets/{security-BJ3_u0uY.js → security-BgcX1n9D.js} +2 -2
  58. package/public/assets/{security-BJ3_u0uY.js.map → security-BgcX1n9D.js.map} +1 -1
  59. package/public/assets/{select-AWvlctyQ.js → select-DZPVpKPv.js} +2 -2
  60. package/public/assets/{select-AWvlctyQ.js.map → select-DZPVpKPv.js.map} +1 -1
  61. package/public/assets/{settings-5ofqi-bN.js → settings-hd5kzdRB.js} +2 -2
  62. package/public/assets/{settings-5ofqi-bN.js.map → settings-hd5kzdRB.js.map} +1 -1
  63. package/public/assets/store-DKTLubBT.js +9 -0
  64. package/public/assets/store-DKTLubBT.js.map +1 -0
  65. package/public/assets/{tasks-BEE9bBf7.js → tasks-C5d2LU5E.js} +2 -2
  66. package/public/assets/{tasks-BEE9bBf7.js.map → tasks-C5d2LU5E.js.map} +1 -1
  67. package/public/assets/{teams-C27H_Gwo.js → teams-B8f2pGJe.js} +2 -2
  68. package/public/assets/{teams-C27H_Gwo.js.map → teams-B8f2pGJe.js.map} +1 -1
  69. package/public/assets/{terminal-viewer-impl-WD2TboA7.js → terminal-viewer-impl-CxAscH0U.js} +2 -2
  70. package/public/assets/{terminal-viewer-impl-WD2TboA7.js.map → terminal-viewer-impl-CxAscH0U.js.map} +1 -1
  71. package/public/assets/types-uVOXgvMT.js.map +1 -1
  72. package/public/assets/{work-queue-CtmPK2hK.js → work-queue-9imc7aNK.js} +2 -2
  73. package/public/assets/{work-queue-CtmPK2hK.js.map → work-queue-9imc7aNK.js.map} +1 -1
  74. package/public/assets/{workspaces-B26EKv_H.js → workspaces-CLYc3yF3.js} +2 -2
  75. package/public/assets/{workspaces-B26EKv_H.js.map → workspaces-CLYc3yF3.js.map} +1 -1
  76. package/public/index.html +3 -3
  77. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  78. package/runner/plugins/claude/monitors/relay-monitor.provisioned.mjs +4 -0
  79. package/src/agent-issue-repo.ts +10 -3
  80. package/src/agent-lifecycle-events.ts +23 -1
  81. package/src/agent-ref.ts +18 -3
  82. package/src/bus-outbox.ts +44 -1
  83. package/src/bus.ts +28 -5
  84. package/src/channel-target.ts +4 -0
  85. package/src/cli/steward.ts +15 -14
  86. package/src/commands-db.ts +63 -9
  87. package/src/config.ts +50 -0
  88. package/src/db/claim-sql.ts +16 -0
  89. package/src/db/continuations.ts +44 -3
  90. package/src/db/message-reads.ts +34 -4
  91. package/src/db/messages.ts +128 -4
  92. package/src/db/pairs.ts +1 -0
  93. package/src/db/schema.ts +6 -0
  94. package/src/gate-resolver.ts +124 -0
  95. package/src/lifecycle-manager.ts +41 -10
  96. package/src/lifecycle-signal-registry.ts +87 -0
  97. package/src/maintenance/constants.ts +5 -1
  98. package/src/maintenance/jobs.ts +28 -2
  99. package/src/maintenance/workspaces.ts +75 -44
  100. package/src/managed-policy-escalation.ts +68 -3
  101. package/src/mcp/tools-spawn.ts +16 -6
  102. package/src/mcp-plan-tools.ts +2 -3
  103. package/src/notification-settings.ts +11 -0
  104. package/src/notification-types.ts +4 -0
  105. package/src/operator-alarm.ts +37 -0
  106. package/src/routes/agents.ts +4 -0
  107. package/src/routes/orchestrator-proxy.ts +13 -1
  108. package/src/routes/orchestrator.ts +4 -0
  109. package/src/security.ts +20 -0
  110. package/src/self-resume.ts +185 -21
  111. package/src/server.ts +2 -0
  112. package/src/services/plan-graph.ts +42 -12
  113. package/src/services/register-agent.ts +5 -1
  114. package/src/services/send-message.ts +36 -0
  115. package/src/services/spawn-agent.ts +21 -2
  116. package/src/services/task-semantic-events.ts +17 -1
  117. package/src/spawn-command.ts +3 -0
  118. package/src/spawn-targets.ts +26 -1
  119. package/src/sse.ts +13 -5
  120. package/src/workspace-auto-merge.ts +90 -34
  121. package/src/workspace-escalation.ts +52 -3
  122. package/src/workspace-human-escalation.ts +68 -0
  123. package/src/workspace-orphans.ts +11 -6
  124. package/src/workspace-phase.ts +22 -1
  125. package/public/assets/store-D5et8685.js +0 -9
  126. package/public/assets/store-D5et8685.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-DKTLubBT.js","names":[],"sources":["../../dashboard/node_modules/zustand/esm/vanilla.mjs","../../dashboard/node_modules/zustand/esm/react.mjs","../../dashboard/node_modules/zustand/esm/middleware.mjs","../../sdk/src/speech-text.ts","../../dashboard/src/lib/voice.ts","../../dashboard/src/store/clock.ts","../../sdk/src/provider-catalog.ts","../../dashboard/src/store/helpers.ts","../../dashboard/src/components/chat/chatScrollStore.ts","../../dashboard/src/store/merge.ts","../../dashboard/src/store/slices/notifications.ts","../../dashboard/src/store/slices/voice.ts","../../dashboard/src/store/slices/connection.ts","../../dashboard/src/store/slices/users.ts","../../dashboard/src/store/slices/agents.ts","../../dashboard/src/store/slices/chat.ts","../../dashboard/src/store/slices/plans.ts","../../dashboard/src/store/slices/teams.ts","../../dashboard/src/store/slices/issues.ts","../../dashboard/src/store/slices/work.ts","../../dashboard/src/store/slices/spawn.ts","../../dashboard/src/store/slices/managed.ts","../../dashboard/src/store/slices/operator.ts","../../dashboard/src/store/slices/timeline.ts","../../dashboard/src/store/slices/ops.ts","../../dashboard/src/store/slices/workspaces.ts","../../dashboard/src/store/slices/memory.ts","../../dashboard/src/store/slices/projects.ts","../../dashboard/src/store/index.ts"],"sourcesContent":["const createStoreImpl = (createState) => {\n let state;\n const listeners = /* @__PURE__ */ new Set();\n const setState = (partial, replace) => {\n const nextState = typeof partial === \"function\" ? partial(state) : partial;\n if (!Object.is(nextState, state)) {\n const previousState = state;\n state = (replace != null ? replace : typeof nextState !== \"object\" || nextState === null) ? nextState : Object.assign({}, state, nextState);\n listeners.forEach((listener) => listener(state, previousState));\n }\n };\n const getState = () => state;\n const getInitialState = () => initialState;\n const subscribe = (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n };\n const api = { setState, getState, getInitialState, subscribe };\n const initialState = state = createState(setState, getState, api);\n return api;\n};\nconst createStore = ((createState) => createState ? createStoreImpl(createState) : createStoreImpl);\n\nexport { createStore };\n","import React from 'react';\nimport { createStore } from 'zustand/vanilla';\n\nconst identity = (arg) => arg;\nfunction useStore(api, selector = identity) {\n const slice = React.useSyncExternalStore(\n api.subscribe,\n React.useCallback(() => selector(api.getState()), [api, selector]),\n React.useCallback(() => selector(api.getInitialState()), [api, selector])\n );\n React.useDebugValue(slice);\n return slice;\n}\nconst createImpl = (createState) => {\n const api = createStore(createState);\n const useBoundStore = (selector) => useStore(api, selector);\n Object.assign(useBoundStore, api);\n return useBoundStore;\n};\nconst create = ((createState) => createState ? createImpl(createState) : createImpl);\n\nexport { create, useStore };\n","const reduxImpl = (reducer, initial) => (set, _get, api) => {\n api.dispatch = (action) => {\n set((state) => reducer(state, action), false, action);\n return action;\n };\n api.dispatchFromDevtools = true;\n return { dispatch: (...args) => api.dispatch(...args), ...initial };\n};\nconst redux = reduxImpl;\n\nconst shouldDispatchFromDevtools = (api) => !!api.dispatchFromDevtools && typeof api.dispatch === \"function\";\nconst trackedConnections = /* @__PURE__ */ new Map();\nconst getTrackedConnectionState = (name) => {\n const api = trackedConnections.get(name);\n if (!api) return {};\n return Object.fromEntries(\n Object.entries(api.stores).map(([key, api2]) => [key, api2.getState()])\n );\n};\nconst extractConnectionInformation = (store, extensionConnector, options) => {\n if (store === void 0) {\n return {\n type: \"untracked\",\n connection: extensionConnector.connect(options)\n };\n }\n const existingConnection = trackedConnections.get(options.name);\n if (existingConnection) {\n return { type: \"tracked\", store, ...existingConnection };\n }\n const newConnection = {\n connection: extensionConnector.connect(options),\n stores: {}\n };\n trackedConnections.set(options.name, newConnection);\n return { type: \"tracked\", store, ...newConnection };\n};\nconst removeStoreFromTrackedConnections = (name, store) => {\n if (store === void 0) return;\n const connectionInfo = trackedConnections.get(name);\n if (!connectionInfo) return;\n delete connectionInfo.stores[store];\n if (Object.keys(connectionInfo.stores).length === 0) {\n trackedConnections.delete(name);\n }\n};\nconst v8StackLineRe = /.+ (.+) .+/;\nconst geckoStackLineRe = /^([^@]+)@/;\nfunction findCallerName(stack) {\n var _a, _b, _c;\n if (!stack) return void 0;\n const traceLines = stack.split(\"\\n\");\n const apiSetStateLineIndex = traceLines.findIndex(\n (traceLine) => traceLine.includes(\"api.setState\")\n );\n if (apiSetStateLineIndex < 0) return void 0;\n const callerLine = ((_a = traceLines[apiSetStateLineIndex + 1]) == null ? void 0 : _a.trim()) || \"\";\n return ((_b = v8StackLineRe.exec(callerLine)) == null ? void 0 : _b[1]) || ((_c = geckoStackLineRe.exec(callerLine)) == null ? void 0 : _c[1]);\n}\nconst devtoolsImpl = (fn, devtoolsOptions = {}) => (set, get, api) => {\n const { enabled, anonymousActionType, store, ...options } = devtoolsOptions;\n let extensionConnector;\n try {\n extensionConnector = (enabled != null ? enabled : (import.meta.env ? import.meta.env.MODE : void 0) !== \"production\") && window.__REDUX_DEVTOOLS_EXTENSION__;\n } catch (e) {\n }\n if (!extensionConnector) {\n return fn(set, get, api);\n }\n const { connection, ...connectionInformation } = extractConnectionInformation(store, extensionConnector, options);\n let isRecording = true;\n api.setState = ((state, replace, nameOrAction) => {\n const r = set(state, replace);\n if (!isRecording) return r;\n const action = nameOrAction === void 0 ? {\n type: anonymousActionType || findCallerName(new Error().stack) || \"anonymous\"\n } : typeof nameOrAction === \"string\" ? { type: nameOrAction } : nameOrAction;\n if (store === void 0) {\n connection == null ? void 0 : connection.send(action, get());\n return r;\n }\n connection == null ? void 0 : connection.send(\n {\n ...action,\n type: `${store}/${action.type}`\n },\n {\n ...getTrackedConnectionState(options.name),\n [store]: api.getState()\n }\n );\n return r;\n });\n api.devtools = {\n cleanup: () => {\n if (connection && typeof connection.unsubscribe === \"function\") {\n connection.unsubscribe();\n }\n removeStoreFromTrackedConnections(options.name, store);\n }\n };\n const setStateFromDevtools = (...a) => {\n const originalIsRecording = isRecording;\n isRecording = false;\n set(...a);\n isRecording = originalIsRecording;\n };\n const initialState = fn(api.setState, get, api);\n if (connectionInformation.type === \"untracked\") {\n connection == null ? void 0 : connection.init(initialState);\n } else {\n connectionInformation.stores[connectionInformation.store] = api;\n connection == null ? void 0 : connection.init(\n Object.fromEntries(\n Object.entries(connectionInformation.stores).map(([key, store2]) => [\n key,\n key === connectionInformation.store ? initialState : store2.getState()\n ])\n )\n );\n }\n if (shouldDispatchFromDevtools(api)) {\n let didWarnAboutReservedActionType = false;\n const originalDispatch = api.dispatch;\n api.dispatch = (...args) => {\n if ((import.meta.env ? import.meta.env.MODE : void 0) !== \"production\" && args[0].type === \"__setState\" && !didWarnAboutReservedActionType) {\n console.warn(\n '[zustand devtools middleware] \"__setState\" action type is reserved to set state from the devtools. Avoid using it.'\n );\n didWarnAboutReservedActionType = true;\n }\n originalDispatch(...args);\n };\n }\n connection.subscribe((message) => {\n var _a;\n switch (message.type) {\n case \"ACTION\":\n if (typeof message.payload !== \"string\") {\n console.error(\n \"[zustand devtools middleware] Unsupported action format\"\n );\n return;\n }\n return parseJsonThen(\n message.payload,\n (action) => {\n if (action.type === \"__setState\") {\n if (store === void 0) {\n setStateFromDevtools(action.state);\n return;\n }\n if (Object.keys(action.state).length !== 1) {\n console.error(\n `\n [zustand devtools middleware] Unsupported __setState action format.\n When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(),\n and value of this only key should be a state object. Example: { \"type\": \"__setState\", \"state\": { \"abc123Store\": { \"foo\": \"bar\" } } }\n `\n );\n }\n const stateFromDevtools = action.state[store];\n if (stateFromDevtools === void 0 || stateFromDevtools === null) {\n return;\n }\n if (JSON.stringify(api.getState()) !== JSON.stringify(stateFromDevtools)) {\n setStateFromDevtools(stateFromDevtools);\n }\n return;\n }\n if (shouldDispatchFromDevtools(api)) {\n api.dispatch(action);\n }\n }\n );\n case \"DISPATCH\":\n switch (message.payload.type) {\n case \"RESET\":\n setStateFromDevtools(initialState);\n if (store === void 0) {\n return connection == null ? void 0 : connection.init(api.getState());\n }\n return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));\n case \"COMMIT\":\n if (store === void 0) {\n connection == null ? void 0 : connection.init(api.getState());\n return;\n }\n return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));\n case \"ROLLBACK\":\n return parseJsonThen(message.state, (state) => {\n if (store === void 0) {\n setStateFromDevtools(state);\n connection == null ? void 0 : connection.init(api.getState());\n return;\n }\n setStateFromDevtools(state[store]);\n connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));\n });\n case \"JUMP_TO_STATE\":\n case \"JUMP_TO_ACTION\":\n return parseJsonThen(message.state, (state) => {\n if (store === void 0) {\n setStateFromDevtools(state);\n return;\n }\n if (JSON.stringify(api.getState()) !== JSON.stringify(state[store])) {\n setStateFromDevtools(state[store]);\n }\n });\n case \"IMPORT_STATE\": {\n const { nextLiftedState } = message.payload;\n const lastComputedState = (_a = nextLiftedState.computedStates.slice(-1)[0]) == null ? void 0 : _a.state;\n if (!lastComputedState) return;\n if (store === void 0) {\n setStateFromDevtools(lastComputedState);\n } else {\n setStateFromDevtools(lastComputedState[store]);\n }\n connection == null ? void 0 : connection.send(\n null,\n // FIXME no-any\n nextLiftedState\n );\n return;\n }\n case \"PAUSE_RECORDING\":\n return isRecording = !isRecording;\n }\n return;\n }\n });\n return initialState;\n};\nconst devtools = devtoolsImpl;\nconst parseJsonThen = (stringified, fn) => {\n let parsed;\n try {\n parsed = JSON.parse(stringified);\n } catch (e) {\n console.error(\n \"[zustand devtools middleware] Could not parse the received json\",\n e\n );\n }\n if (parsed !== void 0) fn(parsed);\n};\n\nconst subscribeWithSelectorImpl = (fn) => (set, get, api) => {\n const origSubscribe = api.subscribe;\n api.subscribe = ((selector, optListener, options) => {\n let listener = selector;\n if (optListener) {\n const equalityFn = (options == null ? void 0 : options.equalityFn) || Object.is;\n let currentSlice = selector(api.getState());\n listener = (state) => {\n const nextSlice = selector(state);\n if (!equalityFn(currentSlice, nextSlice)) {\n const previousSlice = currentSlice;\n optListener(currentSlice = nextSlice, previousSlice);\n }\n };\n if (options == null ? void 0 : options.fireImmediately) {\n optListener(currentSlice, currentSlice);\n }\n }\n return origSubscribe(listener);\n });\n const initialState = fn(set, get, api);\n return initialState;\n};\nconst subscribeWithSelector = subscribeWithSelectorImpl;\n\nfunction combine(initialState, create) {\n return (...args) => Object.assign({}, initialState, create(...args));\n}\n\nfunction createJSONStorage(getStorage, options) {\n let storage;\n try {\n storage = getStorage();\n } catch (e) {\n return;\n }\n const persistStorage = {\n getItem: (name) => {\n var _a;\n const parse = (str2) => {\n if (str2 === null) {\n return null;\n }\n return JSON.parse(str2, options == null ? void 0 : options.reviver);\n };\n const str = (_a = storage.getItem(name)) != null ? _a : null;\n if (str instanceof Promise) {\n return str.then(parse);\n }\n return parse(str);\n },\n setItem: (name, newValue) => storage.setItem(name, JSON.stringify(newValue, options == null ? void 0 : options.replacer)),\n removeItem: (name) => storage.removeItem(name)\n };\n return persistStorage;\n}\nconst toThenable = (fn) => (input) => {\n try {\n const result = fn(input);\n if (result instanceof Promise) {\n return result;\n }\n return {\n then(onFulfilled) {\n return toThenable(onFulfilled)(result);\n },\n catch(_onRejected) {\n return this;\n }\n };\n } catch (e) {\n return {\n then(_onFulfilled) {\n return this;\n },\n catch(onRejected) {\n return toThenable(onRejected)(e);\n }\n };\n }\n};\nconst persistImpl = (config, baseOptions) => (set, get, api) => {\n let options = {\n storage: createJSONStorage(() => window.localStorage),\n partialize: (state) => state,\n version: 0,\n merge: (persistedState, currentState) => ({\n ...currentState,\n ...persistedState\n }),\n ...baseOptions\n };\n let hasHydrated = false;\n let hydrationVersion = 0;\n const hydrationListeners = /* @__PURE__ */ new Set();\n const finishHydrationListeners = /* @__PURE__ */ new Set();\n let storage = options.storage;\n if (!storage) {\n return config(\n (...args) => {\n console.warn(\n `[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.`\n );\n set(...args);\n },\n get,\n api\n );\n }\n const setItem = () => {\n const state = options.partialize({ ...get() });\n return storage.setItem(options.name, {\n state,\n version: options.version\n });\n };\n const savedSetState = api.setState;\n api.setState = (state, replace) => {\n savedSetState(state, replace);\n return setItem();\n };\n const configResult = config(\n (...args) => {\n set(...args);\n return setItem();\n },\n get,\n api\n );\n api.getInitialState = () => configResult;\n let stateFromStorage;\n const hydrate = () => {\n var _a, _b;\n if (!storage) return;\n const currentVersion = ++hydrationVersion;\n hasHydrated = false;\n hydrationListeners.forEach((cb) => {\n var _a2;\n return cb((_a2 = get()) != null ? _a2 : configResult);\n });\n const postRehydrationCallback = ((_b = options.onRehydrateStorage) == null ? void 0 : _b.call(options, (_a = get()) != null ? _a : configResult)) || void 0;\n return toThenable(storage.getItem.bind(storage))(options.name).then((deserializedStorageValue) => {\n if (deserializedStorageValue) {\n if (typeof deserializedStorageValue.version === \"number\" && deserializedStorageValue.version !== options.version) {\n if (options.migrate) {\n const migration = options.migrate(\n deserializedStorageValue.state,\n deserializedStorageValue.version\n );\n if (migration instanceof Promise) {\n return migration.then((result) => [true, result]);\n }\n return [true, migration];\n }\n console.error(\n `State loaded from storage couldn't be migrated since no migrate function was provided`\n );\n } else {\n return [false, deserializedStorageValue.state];\n }\n }\n return [false, void 0];\n }).then((migrationResult) => {\n var _a2;\n if (currentVersion !== hydrationVersion) {\n return;\n }\n const [migrated, migratedState] = migrationResult;\n stateFromStorage = options.merge(\n migratedState,\n (_a2 = get()) != null ? _a2 : configResult\n );\n set(stateFromStorage, true);\n if (migrated) {\n return setItem();\n }\n }).then(() => {\n if (currentVersion !== hydrationVersion) {\n return;\n }\n postRehydrationCallback == null ? void 0 : postRehydrationCallback(get(), void 0);\n stateFromStorage = get();\n hasHydrated = true;\n finishHydrationListeners.forEach((cb) => cb(stateFromStorage));\n }).catch((e) => {\n if (currentVersion !== hydrationVersion) {\n return;\n }\n postRehydrationCallback == null ? void 0 : postRehydrationCallback(void 0, e);\n });\n };\n api.persist = {\n setOptions: (newOptions) => {\n options = {\n ...options,\n ...newOptions\n };\n if (newOptions.storage) {\n storage = newOptions.storage;\n }\n },\n clearStorage: () => {\n storage == null ? void 0 : storage.removeItem(options.name);\n },\n getOptions: () => options,\n rehydrate: () => hydrate(),\n hasHydrated: () => hasHydrated,\n onHydrate: (cb) => {\n hydrationListeners.add(cb);\n return () => {\n hydrationListeners.delete(cb);\n };\n },\n onFinishHydration: (cb) => {\n finishHydrationListeners.add(cb);\n return () => {\n finishHydrationListeners.delete(cb);\n };\n }\n };\n if (!options.skipHydration) {\n hydrate();\n }\n return stateFromStorage || configResult;\n};\nconst persist = persistImpl;\n\nfunction ssrSafe(config, isSSR = typeof window === \"undefined\") {\n return (set, get, api) => {\n if (!isSSR) {\n return config(set, get, api);\n }\n const ssrSet = () => {\n throw new Error(\"Cannot set state of Zustand store in SSR\");\n };\n api.setState = ssrSet;\n return config(ssrSet, get, api);\n };\n}\n\nexport { combine, createJSONStorage, devtools, persist, redux, subscribeWithSelector, ssrSafe as unstable_ssrSafe };\n","/**\n * Shared text → speech preparation, imported by both the dashboard\n * (`dashboard/src/lib/voice.ts`) and the voice connector\n * (`connectors/voice/src/text.ts`). Single source of truth — these rules used to\n * live duplicated in both places and drifted.\n *\n * Two stages, applied in order by {@link prepareForSpeech}:\n * 1. {@link speechify} — collapse markdown/code structure into prose worth hearing.\n * 2. {@link normalizeForSpeech} — rule-based normalization of inline tokens\n * (numbers, units, symbols, code identifiers, paths) so the speech engine\n * reads them naturally.\n *\n * Why both, and why here: the server-side Kokoro engine (agent-speech) already\n * normalizes some of this internally (number ranges, decimals, currency) via its\n * phonemizer — but that normalization does NOT run for the browser Web Speech\n * fallback, and the dashboard's own sentence splitter trips on decimals before\n * the engine ever sees them. Normalizing here fixes the browser path and protects\n * the splitter; feeding already-normalized text to Kokoro is harmless (idempotent).\n */\n\n// ─── Stage 1: markdown/code → prose ──────────────────────────────────────────\n\n/** Collapse markdown/code into something worth hearing. */\nexport function speechify(markdown: string): string {\n if (!markdown) return ''\n let text = markdown.replace(/\\r\\n/g, '\\n')\n\n // Fenced code blocks → \"code block, N lines.\"\n text = text.replace(/```[^\\n]*\\n([\\s\\S]*?)```/g, (_m, body: string) => {\n const lines = body.replace(/\\n+$/, '').split('\\n').length\n return ` code block, ${lines} ${lines === 1 ? 'line' : 'lines'}. `\n })\n // Any unterminated fence left over → drop the marker.\n text = text.replace(/```[^\\n]*/g, ' code block. ')\n\n // Markdown tables → a spoken summary (must run before newlines collapse).\n text = collapseTables(text)\n\n // Strip ATX heading markers but keep the heading text.\n text = text.replace(/^#{1,6}\\s+/gm, '')\n // Bullet/numbered list markers → pause.\n text = text.replace(/^\\s*[-*+]\\s+/gm, '. ')\n text = text.replace(/^\\s*\\d+\\.\\s+/gm, '. ')\n // Inline code → keep the contents, drop the backticks.\n text = text.replace(/`([^`]+)`/g, '$1')\n // Markdown links [label](url) → just the label.\n text = text.replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n // Bold/italic emphasis markers.\n text = text.replace(/(\\*\\*|__|\\*|_)(.*?)\\1/g, '$2')\n\n // Collapse whitespace runs and stray punctuation pile-ups.\n text = text.replace(/[ \\t]+/g, ' ')\n text = text.replace(/\\n{2,}/g, '. ')\n text = text.replace(/\\s*\\.\\s*\\.\\s*(\\.\\s*)+/g, '. ')\n return text.trim()\n}\n\n/** A markdown table row: `| a | b |` or `a | b`. The separator row is `|---|:-:|`. */\nfunction isTableRow(line: string): boolean {\n return /\\|/.test(line) && line.trim().length > 0\n}\nfunction isTableSeparator(line: string): boolean {\n return /^\\s*\\|?\\s*:?-{2,}:?\\s*(\\|\\s*:?-{2,}:?\\s*)+\\|?\\s*$/.test(line)\n}\nfunction tableCells(line: string): string[] {\n return line\n .trim()\n .replace(/^\\||\\|$/g, '')\n .split('|')\n .map((c) => c.trim())\n .filter((c) => c.length > 0)\n}\n\n/**\n * Replace contiguous markdown tables with a spoken summary\n * (\"table with columns A, B, C; N rows.\") rather than reading pipes and cells.\n */\nfunction collapseTables(text: string): string {\n const lines = text.split('\\n')\n const out: string[] = []\n let i = 0\n while (i < lines.length) {\n if (\n isTableRow(lines[i]!) &&\n i + 1 < lines.length &&\n isTableSeparator(lines[i + 1]!)\n ) {\n const headers = tableCells(lines[i]!)\n let j = i + 2\n let rows = 0\n while (j < lines.length && isTableRow(lines[j]!) && !isTableSeparator(lines[j]!)) {\n rows++\n j++\n }\n const cols = headers.length ? `with columns ${headers.join(', ')}` : ''\n const rowText = `${rows} ${rows === 1 ? 'row' : 'rows'}`\n out.push(`table ${cols ? `${cols}, ` : ''}${rowText}.`.replace(/\\s+/g, ' ').trim())\n i = j\n continue\n }\n out.push(lines[i]!)\n i++\n }\n return out.join('\\n')\n}\n\n// ─── Stage 2: inline token normalization ─────────────────────────────────────\n\n/**\n * One normalization rule. Each is a single regex + replacement — add a new one\n * by appending an entry. Order matters: earlier rules see the raw text, later\n * rules see the output of earlier ones (see the ordering notes in SPEECH_RULES).\n */\ninterface SpeechRule {\n readonly name: string\n readonly pattern: RegExp\n readonly replace: string | ((substring: string, ...args: string[]) => string)\n}\n\nconst UNIT_WORDS: Record<string, string> = {\n ms: 'milliseconds',\n s: 'seconds',\n kb: 'kilobytes',\n mb: 'megabytes',\n gb: 'gigabytes',\n tb: 'terabytes',\n}\n\n/** Spell out a fractional part digit-by-digit: \"14\" → \"1 4\" (so \"3.14\" → \"3 point 1 4\"). */\nfunction spaceDigits(frac: string): string {\n return frac.split('').join(' ')\n}\n\n/**\n * Spell a token one character at a time so the engine reads each symbol on its\n * own: digits stay as digits, letters are upper-cased so they're heard as the\n * letter name (\"e\" → \"E\" = \"ee\") rather than a syllable. Used for commit hashes\n * (\"834543e\" → \"8 3 4 5 4 3 E\") and acronyms (\"id\" → \"I D\").\n */\nfunction spellOut(token: string): string {\n return token\n .split('')\n .map((c) => (/[0-9]/.test(c) ? c : c.toUpperCase()))\n .join(' ')\n}\n\n/**\n * Ordered normalization rules. ORDER IS LOAD-BEARING:\n * - URLs/paths first, before anything mangles their slashes/dots.\n * - `~` → \"approximately\" before number rules consume the digits after it.\n * - number ranges (\"5-7\") before unit expansion, so the unit attaches once: \"5 to 7 seconds\".\n * - versions (\"0.27.0\") before decimals, so all dots become \"dot\" uniformly\n * instead of the decimal rule eating only the first pair and stranding \".0\".\n * - decimals before unit expansion and before the sentence splitter sees the dot.\n * - commit-hash spelling before nothing in particular, but after decimals so a\n * real number is never mistaken for a hash.\n * - \"id\" acronym expansion (camelCase suffix before the standalone word).\n * - unit expansion among the number rules.\n * - interior-dot LAST, so it only mops up dots the structured rules left behind\n * (identifiers like \"branch.landed\", \"router.ts\") — never a decimal or version.\n */\nexport const SPEECH_RULES: readonly SpeechRule[] = [\n // Bare URLs read character-by-character → say \"link\".\n { name: 'url', pattern: /\\bhttps?:\\/\\/\\S+/gi, replace: ' link ' },\n // Absolute-ish paths (\"/api/connectors/voice\") → spoken words.\n {\n name: 'path',\n pattern: /(?:\\/[A-Za-z0-9._-]+){2,}\\/?/g,\n replace: (m) => ' ' + m.replace(/[/._-]+/g, ' ').trim() + ' ',\n },\n // A lone slash between words (\"src/router.ts\", \"TCP/IP\") → a space, so the\n // engine doesn't read \"slash\". Absolute multi-segment paths are already gone.\n { name: 'slash', pattern: /(?<=[A-Za-z0-9])\\/(?=[A-Za-z0-9])/g, replace: ' ' },\n // Empty-paren code identifiers (\"fetchSpeech()\") → drop the parens.\n { name: 'func-call', pattern: /\\b([A-Za-z_$][\\w$]*)\\(\\)/g, replace: '$1' },\n // Approximation tilde (\"~0.1\", \"~5\") → \"approximately\".\n { name: 'approx', pattern: /~(?=\\s*[\\d.])/g, replace: 'approximately ' },\n // Numeric ranges (\"5-7\", \"100–200\") → \"X to Y\".\n { name: 'range', pattern: /(\\d)\\s*[-–—]\\s*(?=\\d)/g, replace: '$1 to ' },\n // Dotted versions (\"0.27.0\", \"v1.2.3\", IPs) → every dot spoken as \"dot\".\n // Runs BEFORE decimal so 3+-part versions don't get half-eaten into \"point\".\n // A leading v/V is voiced as \"version\" (unambiguous here — v then digits then ≥2 dots).\n {\n name: 'version',\n pattern: /\\b(v?)(\\d+(?:\\.\\d+){2,})\\b/gi,\n replace: (_m, v: string, nums: string) =>\n `${v ? 'version ' : ''}${nums.replace(/\\./g, ' dot ')}`,\n },\n // Decimals (\"0.1\", \"3.14\") → \"0 point 1\" so the sentence splitter keeps them whole.\n { name: 'decimal', pattern: /\\b(\\d+)\\.(\\d+)\\b/g, replace: (_m, i: string, f: string) => `${i} point ${spaceDigits(f)}` },\n // Commit hashes (\"834543e\", \"3e0c66f85bd5\") → spell each char so the engine\n // doesn't read them as one giant number. Heuristic: 7–40 hex chars containing\n // BOTH a letter and a digit — excludes plain numbers and plain words.\n {\n name: 'hash',\n pattern: /\\b(?=[0-9a-f]{7,40}\\b)(?=[0-9a-f]*[a-f])(?=[0-9a-f]*\\d)[0-9a-f]{7,40}\\b/gi,\n replace: (m: string) => spellOut(m),\n },\n // camelCase \"Id\" suffix (\"messageId\", \"threadIds\") → \"message I D\", \"thread I Ds\".\n {\n name: 'id-camel',\n pattern: /([a-z])(Id|ID)(s)?\\b/g,\n replace: (_m, pre: string, _acr: string, plural: string) => `${pre} I D${plural ? 's' : ''}`,\n },\n // Standalone \"id\"/\"Id\"/\"ID\" word → \"I D\" (so it's not read as \"iiid\").\n {\n name: 'id-word',\n pattern: /\\b(?:id|Id|ID)(s)?\\b/g,\n replace: (_m, plural: string) => `I D${plural ? 's' : ''}`,\n },\n // Byte units (\"165KB\", \"2 MB\") → spelled out (case-insensitive).\n {\n name: 'byte-unit',\n pattern: /\\b(\\d+)\\s?(kb|mb|gb|tb)\\b/gi,\n replace: (_m, n: string, u: string) => `${n} ${UNIT_WORDS[u.toLowerCase()]}`,\n },\n // Time units (\"500ms\", \"5s\") → spelled out. Guard against 4-digit years for \"s\".\n { name: 'ms-unit', pattern: /\\b(\\d{1,5})\\s?ms\\b/g, replace: (_m, n: string) => `${n} ${UNIT_WORDS.ms}` },\n { name: 's-unit', pattern: /\\b(\\d{1,3})\\s?s\\b/g, replace: (_m, n: string) => `${n} ${UNIT_WORDS.s}` },\n // Heteronym \"live\": the engine defaults to the verb /lɪv/ (\"I want to live\"),\n // but in deployment chatter it's the adjective /laɪv/ (\"the version is live\").\n // Respell → \"lyve\" ONLY after a high-confidence adjective cue, so the verb is\n // never touched. NOTE: heteronym sense can't be fully disambiguated by regex —\n // this deliberately covers only the predicate/\"go live\" phrasings agents use.\n {\n name: 'live-adjective',\n pattern:\n /\\b(is|are|was|were|be|been|being|now|and|stays?|staying|went|go|goes|going|deployed|it's|that's|here's|there's|we're|you're|they're)\\s+live\\b/gi,\n replace: (_m, cue: string) => `${cue} lyve`,\n },\n // Interior dot — any dot glued directly to a following alphanumeric (\"branch.landed\",\n // \"router.ts\", \"Node.js\") → \" dot \". Runs LAST: real sentence-ending dots (followed by\n // a space or end-of-string) are untouched, and versions/decimals were already consumed.\n { name: 'interior-dot', pattern: /\\.(?=[A-Za-z0-9])/g, replace: ' dot ' },\n]\n\n/**\n * Apply the inline normalization rules in order. Pure and idempotent enough to\n * feed straight into either the Kokoro engine or the browser Web Speech API.\n */\nexport function normalizeForSpeech(text: string): string {\n if (!text) return ''\n let out = text\n for (const rule of SPEECH_RULES) {\n out = out.replace(rule.pattern, rule.replace as string)\n }\n // Tidy any double spaces the rules introduced.\n return out.replace(/[ \\t]{2,}/g, ' ').trim()\n}\n\n/** Full pipeline: markdown → prose → normalized speech text. */\nexport function prepareForSpeech(markdown: string): string {\n return normalizeForSpeech(speechify(markdown))\n}\n","import { apiPostAudio, apiPostJsonForBlob } from './api'\nimport { prepareForSpeech } from 'agent-relay-sdk/speech-text'\n\n/**\n * Browser voice I/O for chat.\n *\n * TTS (output) is 100% browser-native via the Web Speech API — the store\n * already receives every agent response turn, so there is no backend round-trip.\n * STT (input) records the mic and posts the clip to the voice connector's\n * whisper endpoint through the relay proxy (single-origin, auth-gated).\n *\n * Speaker policy (\"active chat owns the speaker\"):\n * - One utterance at a time. Disabled => silent.\n * - Only the active chat speaks, and only responses that arrive while it is\n * active (no backlog replay).\n * - Switching away lets the current utterance finish but drops the now-background\n * chat's queued + future responses.\n * - The active chat preempts: if it speaks while a previous chat's audio lingers,\n * that audio is cancelled.\n */\n\n// --- text → speech sanitation -------------------------------------------------\n// The markdown-collapsing + normalization rules live once in the SDK\n// (agent-relay-sdk/speech-text), shared with the voice connector. Re-exported so\n// existing imports (and tests) keep resolving against this module.\nexport { speechify, normalizeForSpeech, prepareForSpeech } from 'agent-relay-sdk/speech-text'\n\n// Curated Kokoro voices (af/am = US female/male, bf/bm = British). Full set is\n// 28; these are the most natural for all-day listening. Shared by the Settings\n// voice picker (and any other UI) — declare once here, never re-list.\nexport const KOKORO_VOICES: { id: string; label: string }[] = [\n { id: 'am_michael', label: 'Michael (US ♂)' },\n { id: 'am_adam', label: 'Adam (US ♂)' },\n { id: 'af_heart', label: 'Heart (US ♀)' },\n { id: 'af_bella', label: 'Bella (US ♀)' },\n { id: 'af_nicole', label: 'Nicole (US ♀)' },\n { id: 'af_sarah', label: 'Sarah (US ♀)' },\n { id: 'bm_george', label: 'George (UK ♂)' },\n { id: 'bf_emma', label: 'Emma (UK ♀)' },\n]\n\nconst MAX_CHUNK = 220\n\n/** Split into utterance-sized chunks (sentence boundaries; hard-split very long runs). */\nfunction chunkForSpeech(text: string): string[] {\n const sentences = text.match(/[^.!?]+[.!?]*\\s*/g)?.map((s) => s.trim()).filter(Boolean) ?? [text]\n const out: string[] = []\n for (const s of sentences) {\n if (s.length <= MAX_CHUNK) { out.push(s); continue }\n let rest = s\n while (rest.length > MAX_CHUNK) {\n let cut = rest.lastIndexOf(' ', MAX_CHUNK)\n if (cut <= 0) cut = MAX_CHUNK\n out.push(rest.slice(0, cut).trim())\n rest = rest.slice(cut).trim()\n }\n if (rest) out.push(rest)\n }\n return out\n}\n\n// --- TTS controller -----------------------------------------------------------\n\nconst synthAvailable = typeof window !== 'undefined' && 'speechSynthesis' in window\n\nclass VoiceTts {\n private enabled = false\n // BCP-47 tag for the spoken voice. Defaults to English rather than the browser\n // UI locale: agent responses are overwhelmingly English, and keying off\n // navigator.language made a Swedish-locale browser read English text in a Swedish\n // voice. Empty string = follow the browser locale (opt-in to the old behavior).\n private lang = 'en-US'\n // 'kokoro' = high-quality server TTS (M1) with automatic browser fallback;\n // 'browser' = Web Speech API only. Kokoro is preferred for all-day listening.\n private mode: 'kokoro' | 'browser' = 'kokoro'\n private kokoroVoice = 'am_michael'\n // Selected browser voice by voiceURI. Empty = let the engine pick the default\n // voice for `lang`. A chosen voice carries its own locale, so it overrides lang.\n private browserVoice = ''\n private active: string | null = null\n private queue: { chatId: string; text: string }[] = []\n private currentChat: string | null = null\n private speaking = false\n private gen = 0\n private audioEl: HTMLAudioElement | null = null\n private audioUrl: string | null = null\n // Manual (play-button) playback bypasses the active-chat gate. `playingKey` is\n // the id of the bubble being played on demand, surfaced to the UI so it can\n // mark the active bubble; null when nothing manual is playing.\n private playingKey: string | null = null\n private manual = false\n private listeners = new Set<() => void>()\n\n // Available when either the browser engine or the server path can speak.\n get available() { return synthAvailable || typeof Audio !== 'undefined' }\n isEnabled() { return this.enabled }\n\n /** Subscribe to manual-playback changes (useSyncExternalStore). */\n subscribe(fn: () => void): () => void {\n this.listeners.add(fn)\n return () => { this.listeners.delete(fn) }\n }\n\n /** Id of the bubble currently played on demand, or null. */\n getPlayingKey(): string | null { return this.playingKey }\n\n private setPlayingKey(key: string | null): void {\n if (key === this.playingKey) return\n this.playingKey = key\n for (const fn of this.listeners) fn()\n }\n\n setEnabled(on: boolean): void {\n if (on === this.enabled) return\n this.enabled = on\n if (!on) this.reset()\n }\n\n /** Set the spoken-voice language (BCP-47, e.g. \"en-US\"). Empty = browser locale. */\n setLang(lang: string): void {\n this.lang = lang\n }\n\n setMode(mode: 'kokoro' | 'browser'): void {\n if (mode === this.mode) return\n this.mode = mode\n this.reset()\n }\n\n setKokoroVoice(voice: string): void {\n this.kokoroVoice = voice\n }\n\n /** Set the browser engine's voice by voiceURI. Empty = default for the language. */\n setBrowserVoice(uri: string): void {\n this.browserVoice = uri\n }\n\n setActiveChat(chatId: string | null): void {\n if (chatId === this.active) return\n this.active = chatId\n // Drop queued items that don't belong to the new active chat; the utterance\n // currently playing is allowed to finish.\n this.queue = this.queue.filter((q) => q.chatId === chatId)\n }\n\n /** A captured agent response turn arrived for `chatId`. */\n onResponse(chatId: string, rawText: string): void {\n if (!this.enabled || !this.available || !chatId || chatId !== this.active) return\n const text = prepareForSpeech(rawText)\n if (!text) return\n // An incoming auto response preempts lingering audio from a different chat or\n // any on-demand (manual) playback in progress.\n if (this.speaking && (this.manual || (this.currentChat && this.currentChat !== chatId))) {\n this.queue = []\n this.cancel()\n }\n this.queue.push({ chatId, text })\n this.pump()\n }\n\n /**\n * Play an arbitrary response on demand (chat-bubble play button), independent\n * of which chat is active. Preempts whatever is currently playing.\n */\n speak(rawText: string, key?: string): void {\n if (!this.available) return\n const text = prepareForSpeech(rawText)\n if (!text) return\n this.reset()\n this.manual = true\n this.speaking = true\n this.currentChat = null\n this.setPlayingKey(key ?? null)\n const gen = ++this.gen\n const chunks = chunkForSpeech(text)\n const done = (): void => {\n if (gen !== this.gen) return\n this.speaking = false\n this.manual = false\n this.setPlayingKey(null)\n }\n if (this.mode === 'kokoro') this.speakKokoro(chunks, 0, gen, done)\n else this.speakBrowser(chunks, 0, gen, done)\n }\n\n /** Cut all speech immediately (e.g. when the user starts talking). */\n bargeIn(): void { this.reset() }\n\n private reset(): void {\n this.queue = []\n this.cancel()\n }\n\n private cancel(): void {\n this.gen++\n this.currentChat = null\n this.speaking = false\n this.manual = false\n this.setPlayingKey(null)\n try { window.speechSynthesis.cancel() } catch { /* ignore */ }\n this.stopAudio()\n }\n\n private stopAudio(): void {\n if (this.audioEl) { try { this.audioEl.pause(); this.audioEl.src = '' } catch { /* */ } }\n if (this.audioUrl) { try { URL.revokeObjectURL(this.audioUrl) } catch { /* */ } this.audioUrl = null }\n }\n\n private pump(): void {\n if (this.speaking) return\n const item = this.queue.shift()\n if (!item) return\n this.speaking = true\n this.manual = false\n this.currentChat = item.chatId\n const gen = ++this.gen\n const chunks = chunkForSpeech(item.text)\n const done = (): void => {\n if (gen !== this.gen) return\n this.speaking = false\n this.currentChat = null\n this.pump()\n }\n if (this.mode === 'kokoro') this.speakKokoro(chunks, 0, gen, done)\n else this.speakBrowser(chunks, 0, gen, done)\n }\n\n // Web Speech API: one utterance per chunk, sequential.\n private speakBrowser(chunks: string[], i: number, gen: number, done: () => void): void {\n if (gen !== this.gen) return\n if (!synthAvailable || i >= chunks.length) return done()\n const u = new SpeechSynthesisUtterance(chunks[i])\n const picked = this.browserVoice ? window.speechSynthesis.getVoices().find((v) => v.voiceURI === this.browserVoice) : undefined\n if (picked) { u.voice = picked; u.lang = picked.lang } else { u.lang = this.lang || navigator.language || 'en-US' }\n u.onend = () => this.speakBrowser(chunks, i + 1, gen, done)\n u.onerror = () => this.speakBrowser(chunks, i + 1, gen, done)\n window.speechSynthesis.speak(u)\n }\n\n // Server (Kokoro) TTS: fetch each chunk's audio, play it, and prefetch the\n // next chunk while the current one plays so playback stays continuous. Any\n // fetch/playback failure falls back to the browser engine for the rest.\n private speakKokoro(chunks: string[], i: number, gen: number, done: () => void, prefetched?: Promise<Blob>): void {\n if (gen !== this.gen) return\n const text = chunks[i]\n if (text === undefined) return done()\n const cur = prefetched ?? this.fetchSpeech(text)\n const nextText = chunks[i + 1]\n const next = nextText !== undefined ? this.fetchSpeech(nextText) : undefined\n cur.then((blob) => {\n if (gen !== this.gen) return\n this.playBlob(blob, gen, () => this.speakKokoro(chunks, i + 1, gen, done, next), () => {\n // playback failed → browser fallback for the remainder\n this.speakBrowser(chunks, i, gen, done)\n })\n }).catch(() => {\n if (gen !== this.gen) return\n // server unreachable / errored → browser fallback for the remainder\n this.speakBrowser(chunks, i, gen, done)\n })\n }\n\n private fetchSpeech(text: string): Promise<Blob> {\n return apiPostJsonForBlob('/connectors/voice/call/speak', { text, voice: this.kokoroVoice })\n }\n\n private playBlob(blob: Blob, gen: number, onend: () => void, onerror: () => void): void {\n if (gen !== this.gen) return\n if (typeof Audio === 'undefined') return onerror()\n this.stopAudio()\n if (!this.audioEl) this.audioEl = new Audio()\n const url = URL.createObjectURL(blob)\n this.audioUrl = url\n const el = this.audioEl\n el.src = url\n el.onended = () => { if (gen === this.gen) onend() }\n el.onerror = () => { if (gen === this.gen) onerror() }\n el.play().catch(() => { if (gen === this.gen) onerror() })\n }\n}\n\nexport const voiceTts = new VoiceTts()\n\n/** Sorted unique BCP-47 languages the browser's speech engine can speak. May be empty\n * until the engine finishes loading voices (listen for `voiceschanged` and re-read). */\nfunction availableSpeechLangs(): string[] {\n if (!synthAvailable) return []\n try {\n const langs = new Set(window.speechSynthesis.getVoices().map((v) => v.lang).filter(Boolean))\n return [...langs].sort()\n } catch {\n return []\n }\n}\n\n/** The browser's individual speech voices ({uri, label, lang}), sorted by language\n * then name. May be empty until the engine loads (listen for `voiceschanged`). */\nexport function availableSpeechVoices(): { uri: string; label: string; lang: string }[] {\n if (!synthAvailable) return []\n try {\n return window.speechSynthesis.getVoices()\n .map((v) => ({ uri: v.voiceURI, label: `${v.name} (${v.lang})`, lang: v.lang }))\n .sort((a, b) => a.lang.localeCompare(b.lang) || a.label.localeCompare(b.label))\n } catch {\n return []\n }\n}\n\n// --- STT (push-to-talk) -------------------------------------------------------\n\nexport const micAvailable = typeof navigator !== 'undefined'\n && !!navigator.mediaDevices?.getUserMedia\n && typeof window !== 'undefined' && 'MediaRecorder' in window\n\nfunction pickMimeType(): string | undefined {\n const candidates = ['audio/webm;codecs=opus', 'audio/webm', 'audio/ogg;codecs=opus', 'audio/mp4']\n for (const m of candidates) {\n if (typeof MediaRecorder !== 'undefined' && MediaRecorder.isTypeSupported?.(m)) return m\n }\n return undefined\n}\n\n/** Hold-to-talk mic recorder. start() on press, stop() on release returns the clip. */\nexport class PttRecorder {\n private rec: MediaRecorder | null = null\n private chunks: BlobPart[] = []\n private stream: MediaStream | null = null\n\n async start(): Promise<void> {\n this.stream = await navigator.mediaDevices.getUserMedia({ audio: true })\n this.chunks = []\n const mime = pickMimeType()\n this.rec = new MediaRecorder(this.stream, mime ? { mimeType: mime } : undefined)\n this.rec.ondataavailable = (e) => { if (e.data.size) this.chunks.push(e.data) }\n this.rec.start()\n }\n\n async stop(): Promise<Blob | null> {\n const rec = this.rec\n if (!rec) { this.teardown(); return null }\n const stopped = new Promise<void>((resolve) => { rec.onstop = () => resolve() })\n try { rec.stop() } catch { /* already stopped */ }\n await stopped\n const type = rec.mimeType || 'audio/webm'\n const blob = new Blob(this.chunks, { type })\n this.teardown()\n return blob.size ? blob : null\n }\n\n cancel(): void {\n try { this.rec?.stop() } catch { /* ignore */ }\n this.teardown()\n }\n\n private teardown(): void {\n this.stream?.getTracks().forEach((t) => t.stop())\n this.rec = null\n this.stream = null\n this.chunks = []\n }\n}\n\n/** Send a recorded clip through the relay proxy to the voice connector's whisper STT. */\nexport async function transcribeClip(blob: Blob): Promise<string> {\n const result = await apiPostAudio<{ text?: string }>('/connectors/voice/call/transcribe', blob)\n return (result?.text ?? '').trim()\n}\n","import { create } from 'zustand'\n\n// Dedicated, non-persisted clock store. Kept separate from the main relay store\n// so the 1s tick never re-serializes persisted state to localStorage and never\n// churns selectors that don't actually depend on wall-clock time.\ninterface ClockStore {\n now: number\n _timer: ReturnType<typeof setInterval> | null\n start: () => void\n stop: () => void\n}\n\nexport const useClock = create<ClockStore>((set, get) => ({\n now: Date.now(),\n _timer: null,\n start() {\n if (get()._timer) return\n set({ _timer: setInterval(() => set({ now: Date.now() }), 1000) })\n },\n stop() {\n const timer = get()._timer\n if (timer) {\n clearInterval(timer)\n set({ _timer: null })\n }\n },\n}))\n\n// Live, 1s-granularity clock for relative timestamps (\"2s ago\").\nexport function useNow(): number {\n return useClock((s) => s.now)\n}\n\n// Coarse 30s bucket for expensive recomputations (e.g. staleness-aware sorting)\n// that must react to time passing but don't need per-second precision.\nexport function useNowBucket(bucketMs = 30_000): number {\n return useClock((s) => Math.floor(s.now / bucketMs))\n}\n","import { getAllManifests, getManifest } from \"agent-relay-providers\";\nimport type { ProviderModelEntry } from \"agent-relay-providers\";\nexport type { ProviderContextThreshold } from \"agent-relay-providers\";\nimport type { SpawnProvider } from \"./types.js\";\n\n/** Built-in reasoning-effort levels used when no runtime catalog is available. */\nexport const VALID_EFFORTS = [\"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const;\nexport type ProviderEffort = string;\n\nexport interface ResolvedProviderContextThreshold {\n selfCompactUtilization: number;\n warnUtilization: number;\n hardBackstopUtilization: number;\n}\n\nexport type ProviderModelCatalogEntry = ProviderModelEntry;\n\nexport interface ProviderCatalogEntry {\n provider: SpawnProvider;\n label: string;\n defaultModel?: string;\n models: ProviderModelCatalogEntry[];\n}\n\ninterface ProviderSelection {\n provider: SpawnProvider;\n model?: string;\n effort?: string;\n}\n\ninterface ResolvedProviderSelection {\n provider: SpawnProvider;\n modelAlias?: string;\n providerModel?: string;\n effort?: string;\n model?: ProviderModelCatalogEntry;\n}\n\nconst DEFAULT_WARN_OFFSET = 0.1;\nconst DEFAULT_HARD_BACKSTOP_UTILIZATION = 0.9;\nconst COMPACT_256K_OR_SMALLER = 0.8;\nconst COMPACT_1M_OR_LARGER = 0.4;\nconst LOW_WINDOW_TOKENS = 256_000;\nconst HIGH_WINDOW_TOKENS = 1_000_000;\n\nfunction manifestToCatalogEntry(manifest: { id: string; label: string; catalog: { models: ProviderModelEntry[]; defaultModel?: string } }): ProviderCatalogEntry {\n return {\n provider: manifest.id,\n label: manifest.label,\n defaultModel: manifest.catalog.defaultModel,\n models: manifest.catalog.models,\n };\n}\n\nexport const PROVIDER_CATALOG: Record<string, ProviderCatalogEntry> = Object.fromEntries(\n getAllManifests().map(manifest => [manifest.id, manifestToCatalogEntry(manifest)]),\n);\n\nexport function providerCatalogList(): ProviderCatalogEntry[] {\n return getAllManifests().map(manifestToCatalogEntry);\n}\n\nexport function deriveContextThresholdFromWindowTokens(windowTokens: number | undefined): ResolvedProviderContextThreshold {\n const selfCompactUtilization = deriveSelfCompactUtilization(windowTokens);\n return {\n selfCompactUtilization,\n warnUtilization: warnUtilizationFor(selfCompactUtilization),\n hardBackstopUtilization: Math.max(DEFAULT_HARD_BACKSTOP_UTILIZATION, selfCompactUtilization),\n };\n}\n\nexport function resolveProviderContextThreshold(\n model: Pick<ProviderModelCatalogEntry, \"contextThreshold\" | \"limits\"> | undefined,\n): ResolvedProviderContextThreshold {\n const derived = deriveContextThresholdFromWindowTokens(model?.limits?.contextWindowTokens?.value);\n const selfCompactUtilization = model?.contextThreshold?.selfCompactUtilization ?? derived.selfCompactUtilization;\n return {\n selfCompactUtilization,\n warnUtilization: model?.contextThreshold?.warnUtilization ?? warnUtilizationFor(selfCompactUtilization),\n hardBackstopUtilization: model?.contextThreshold?.hardBackstopUtilization ?? defaultHardBackstopFor(selfCompactUtilization, derived.hardBackstopUtilization),\n };\n}\n\nfunction deriveSelfCompactUtilization(windowTokens: number | undefined): number {\n if (!Number.isFinite(windowTokens) || !windowTokens || windowTokens <= LOW_WINDOW_TOKENS) return COMPACT_256K_OR_SMALLER;\n if (windowTokens >= HIGH_WINDOW_TOKENS) return COMPACT_1M_OR_LARGER;\n const t = (windowTokens - LOW_WINDOW_TOKENS) / (HIGH_WINDOW_TOKENS - LOW_WINDOW_TOKENS);\n return roundUtilization(COMPACT_256K_OR_SMALLER - t * (COMPACT_256K_OR_SMALLER - COMPACT_1M_OR_LARGER));\n}\n\nfunction warnUtilizationFor(selfCompactUtilization: number): number {\n return roundUtilization(Math.max(0, selfCompactUtilization - DEFAULT_WARN_OFFSET));\n}\n\nfunction defaultHardBackstopFor(selfCompactUtilization: number, fallback: number): number {\n return fallback > selfCompactUtilization ? fallback : roundUtilization(Math.min(1, selfCompactUtilization + DEFAULT_WARN_OFFSET));\n}\n\nfunction roundUtilization(value: number): number {\n return Math.round(value * 1000) / 1000;\n}\n\nexport function getProviderCatalog(provider: SpawnProvider, catalog?: ProviderCatalogEntry[]): ProviderCatalogEntry {\n const entry = catalog?.find((catalogEntry) => catalogEntry.provider === provider) ?? PROVIDER_CATALOG[provider];\n if (!entry) throw new Error(`unknown provider catalog: ${provider}`);\n return entry;\n}\n\ninterface FindProviderModelOptions {\n observedWindowTokens?: number;\n}\n\nexport function findProviderCatalogModel(\n catalog: ProviderCatalogEntry,\n input: string | readonly string[] | undefined,\n options: FindProviderModelOptions = {},\n): ProviderModelCatalogEntry | undefined {\n const inputs = (Array.isArray(input) ? input : input ? [input] : [])\n .map((value) => value.trim())\n .filter(Boolean);\n if (!inputs.length) return undefined;\n for (const value of inputs) {\n const exact = exactModelMatch(catalog, value);\n const observed = observedWindowModel(catalog, exact, inputs, options.observedWindowTokens);\n if (observed) return observed;\n if (exact) return exact;\n }\n for (const value of inputs) {\n const prefixed = catalog.models.find((e) => e.alias.startsWith(value + \"-\"));\n if (prefixed) return prefixed;\n }\n return undefined;\n}\n\nfunction exactModelMatch(catalog: ProviderCatalogEntry, input: string): ProviderModelCatalogEntry | undefined {\n const lower = input.toLowerCase();\n return catalog.models.find((e) => e.alias === input)\n ?? catalog.models.find((e) => e.providerModel === input)\n ?? catalog.models.find((e) => e.alias.toLowerCase() === lower || e.providerModel.toLowerCase() === lower);\n}\n\nfunction observedWindowModel(\n catalog: ProviderCatalogEntry,\n exact: ProviderModelCatalogEntry | undefined,\n inputs: readonly string[],\n observedWindowTokens: number | undefined,\n): ProviderModelCatalogEntry | undefined {\n if (!Number.isFinite(observedWindowTokens) || !observedWindowTokens) return undefined;\n if (exact?.limits?.contextWindowTokens?.value === observedWindowTokens) return exact;\n const familyIds = new Set([\n ...(exact ? [exact.alias, exact.providerModel] : []),\n ...inputs,\n ].map(modelFamilyId));\n return catalog.models.find((model) =>\n model.limits?.contextWindowTokens?.value === observedWindowTokens &&\n (familyIds.has(modelFamilyId(model.alias)) || familyIds.has(modelFamilyId(model.providerModel)))\n );\n}\n\nfunction modelFamilyId(value: string): string {\n return value.trim().toLowerCase().replace(/\\[[^\\]]+\\]$/, \"\");\n}\n\n/** Outcome of resolving a provider's default model against its live-enabled model set. */\nexport interface DefaultModelResolution {\n /** The chosen enabled model alias, or undefined when the provider has no enabled models. */\n alias?: string;\n /** The provider's designated default alias (may be disabled, unavailable, or absent). */\n preferred?: string;\n /** True when the returned alias is NOT the designated default (it was disabled/unavailable/absent). */\n fellBack: boolean;\n}\n\n/** A model is spawnable when it is neither operator-disabled nor provider-side unavailable. */\nfunction isEnabledModel(model: ProviderModelCatalogEntry): boolean {\n return !model.disabled && !model.unavailable;\n}\n\n/**\n * #932 — resolve a provider's default model against its LIVE-ENABLED model set. This is the\n * single seam both the spawn path (resolveProviderSelection, when no model is given) and the\n * spawn_targets reporting matrix use, so the advertised default and the actually-spawned model\n * can never drift and neither can return a disabled model.\n *\n * Ranked preference: prefer the designated `defaultModel` when it is enabled; otherwise fall\n * DOWN the catalog's declared model order to the first enabled model — never blindly `models[0]`,\n * since the designated default is always tried first. Enabled = not disabled and not unavailable.\n */\nexport function resolveDefaultModel(catalog: ProviderCatalogEntry): DefaultModelResolution {\n const preferred = catalog.defaultModel;\n const designated = preferred ? catalog.models.find((m) => m.alias === preferred) : undefined;\n if (designated && isEnabledModel(designated)) {\n return { alias: designated.alias, preferred, fellBack: false };\n }\n const fallback = catalog.models.find(isEnabledModel);\n return { alias: fallback?.alias, preferred, fellBack: fallback?.alias !== preferred };\n}\n\nexport function resolveProviderSelection(selection: ProviderSelection, catalog?: ProviderCatalogEntry[]): ResolvedProviderSelection {\n const catalogEntry = getProviderCatalog(selection.provider, catalog);\n // #932 — when no model is requested, resolve the default dynamically against the enabled set\n // (deterministic ranked preference) so a bare spawn can never resolve to a disabled model.\n // An explicitly-requested model bypasses this and is validated as-is below (disabled → reject).\n let alias = selection.model;\n if (!alias) {\n const resolution = resolveDefaultModel(catalogEntry);\n alias = resolution.alias;\n if (resolution.fellBack && alias) {\n const reason = resolution.preferred\n ? `designated default '${resolution.preferred}' is disabled/unavailable`\n : \"no designated default configured\";\n console.warn(`[provider-catalog] ${selection.provider} default resolved to '${alias}' (${reason}).`);\n }\n }\n const model = findProviderCatalogModel(catalogEntry, alias);\n if (alias && !model) {\n const known = catalogEntry.models.map((e) => e.alias).join(\", \");\n throw new Error(`unknown ${selection.provider} model: ${alias} (available: ${known})`);\n }\n if (model?.disabled) {\n throw new Error(`${selection.provider} model ${model.alias} is disabled`);\n }\n if (model?.unavailable) {\n throw new Error(`${selection.provider} model ${model.alias} is unavailable: ${model.unavailable}`);\n }\n if (selection.effort && !model) {\n throw new Error(`effort requires a ${selection.provider} model`);\n }\n if (selection.effort && model && !model.efforts.includes(selection.effort)) {\n throw new Error(`${selection.provider} model ${model.alias} does not support effort ${selection.effort}`);\n }\n return {\n provider: selection.provider,\n modelAlias: model?.alias,\n providerModel: model?.providerModel,\n effort: selection.effort,\n model,\n };\n}\n","// Provider/orchestrator selection helpers shared by the spawn, managed and\n// connection (file-open) slices.\nimport { getProviderCatalog, resolveDefaultModel } from '../../../sdk/src/provider-catalog'\nimport type { ProviderCatalogEntry, ProviderModelCatalogEntry } from 'agent-relay-sdk/provider-catalog'\nimport type { Agent, Orchestrator, SpawnProvider } from '@/types'\nimport { normalizePathForCompare, pathWithinBase } from '@/lib/display'\nimport { getKnownProviderIds } from '@/lib/provider-display'\n\nexport interface ConfigEntry<T> {\n value: T\n}\n\nexport function firstAvailableProvider(orchestrator: Orchestrator | undefined): SpawnProvider {\n return orchestrator?.providers[0] ?? getKnownProviderIds()[0] ?? ''\n}\n\n// Models a user may actually launch: the effective catalogue (static ⊕ DB\n// overrides via GET /providers) with disabled entries dropped. Pass the store's\n// `providerCatalog`; when it hasn't loaded yet `getProviderCatalog` falls back\n// to the static bundle (where nothing is disabled), so pickers never go empty.\nexport function availableModelsFor(provider: string, catalog?: ProviderCatalogEntry[]): ProviderModelCatalogEntry[] {\n return getProviderCatalog(provider as SpawnProvider, catalog).models.filter((model) => !model.disabled)\n}\n\nexport function defaultModelFor(provider: string, catalog?: ProviderCatalogEntry[]): string {\n // #932 — reuse the single default-resolution seam (honour the catalogue default only\n // when it's still enabled, else fall down the ranked enabled set) so the UI's auto-selected\n // default matches what the server would actually spawn and never picks a disabled/unavailable model.\n const entry = getProviderCatalog(provider as SpawnProvider, catalog)\n return resolveDefaultModel(entry).alias ?? ''\n}\n\nexport function orchestratorForPath(orchestrators: Orchestrator[], path: string): Orchestrator | undefined {\n return orchestrators\n .filter((orch) => orch.status === 'online' && pathWithinBase(path, orch.baseDir))\n .sort((a, b) => normalizePathForCompare(b.baseDir).length - normalizePathForCompare(a.baseDir).length)[0]\n}\n\nexport function orchestratorForAgent(orchestrators: Orchestrator[], agent: Agent): Orchestrator | undefined {\n const managed = orchestrators.find((orch) => orch.managedAgents.some((managedAgent) => managedAgent.agentId === agent.id))\n if (managed) return managed\n const cwd = typeof agent.meta?.cwd === 'string' ? agent.meta.cwd : ''\n return cwd ? orchestratorForPath(orchestrators, cwd) : undefined\n}\n","// Per-conversation chat scroll memory with server-side persistence (#759).\n//\n// This replaces the old module-level `agentScrollMemory` Map that lived only in VirtualTimeline:\n// that map survived an agent-switch remount but was WIPED on a full page reload / new tab /\n// different device, so a reading position never came back after a reload and could not follow a\n// user across devices (the \"re-entry roulette\" and \"not per-user\" halves of #759). It also let\n// the \"Latest\" button path and the manual-scroll-to-bottom path diverge: only the latter happened\n// to write the sticky flag through (via the scroll-event side channel), so \"Latest\" did not\n// reliably register sticky-bottom.\n//\n// The seam is now ONE place every state transition writes through, and it is backed by the\n// existing per-user settings store (tier:\"user\" descriptor `chat`/`scroll`, src/settings/registry.ts):\n// - hydrate once at boot from GET /api/settings/chat/scroll (so a fresh tab/device starts warm),\n// - keep a live in-memory map for synchronous reads during render,\n// - debounce writes back to PUT /api/settings/chat/scroll.\n// The value is a single map keyed by agent/conversation id; each entry is a LOGICAL anchor\n// (`anchorMessageId` = durable message id range + `topKey` = compatibility timeline key +\n// within-row `offset`) plus the `stick` intent — never a raw pixel scrollTop, so it resolves to\n// the same message on any device.\nimport { api } from '@/lib/api'\n\nexport interface ChatScrollEntry {\n // Follow-the-bottom intent. When true the conversation re-opens pinned to the latest message.\n stick: boolean\n // Timeline entry key of the row at the top of the viewport (`msg-<id>` etc.), or null when sticky.\n topKey: string | null\n // Durable message-id anchor for the row. Activity rows can contain multiple session messages, so\n // anchorEndMessageId preserves the row range when reload/slicing suppresses or splits steps.\n anchorMessageId?: number\n anchorEndMessageId?: number\n // Pixels of that row scrolled above the fold, so restore lands on the exact within-row pixel.\n offset: number\n}\n\nconst SETTINGS_PATH = '/settings/chat/scroll'\n// Bound the persisted map so a long-lived user with thousands of conversations can't grow the\n// row past the descriptor's maxProperties (200). We evict the oldest-touched locally well under it.\nconst MAX_CONVERSATIONS = 150\nconst PERSIST_DEBOUNCE_MS = 1_000\n\n// Insertion-ordered map: re-setting a key moves it to the end, so the first keys are oldest-touched\n// and are the ones we evict. Reads during render must be synchronous, hence a plain module map.\nlet memory = new Map<string, ChatScrollEntry>()\nlet persistTimer: ReturnType<typeof setTimeout> | null = null\nlet hydratePromise: Promise<void> | null = null\n\ninterface ChatScrollSettingResponse {\n value?: Record<string, ChatScrollEntry> | null\n}\n\n// Narrow an untrusted server/JSON value to a ChatScrollEntry. Server schema already validates on\n// write, but hydration must tolerate a malformed/old row without throwing during boot.\nfunction asEntry(value: unknown): ChatScrollEntry | null {\n if (!value || typeof value !== 'object') return null\n const record = value as Record<string, unknown>\n if (typeof record.stick !== 'boolean') return null\n if (record.topKey !== null && typeof record.topKey !== 'string') return null\n if (typeof record.offset !== 'number' || !Number.isFinite(record.offset)) return null\n const anchorMessageId = typeof record.anchorMessageId === 'number' && Number.isFinite(record.anchorMessageId)\n ? record.anchorMessageId\n : undefined\n const anchorEndMessageId = typeof record.anchorEndMessageId === 'number' && Number.isFinite(record.anchorEndMessageId)\n ? record.anchorEndMessageId\n : undefined\n return {\n stick: record.stick,\n topKey: record.topKey,\n ...(anchorMessageId !== undefined ? { anchorMessageId } : {}),\n ...(anchorEndMessageId !== undefined ? { anchorEndMessageId } : {}),\n offset: Math.max(0, record.offset),\n }\n}\n\n/** Synchronous read for render-time restore. Undefined → never-seen conversation (defaults apply). */\nexport function getChatScroll(agentId: string): ChatScrollEntry | undefined {\n return memory.get(agentId)\n}\n\n/**\n * Write a conversation's scroll state through the one seam: updates the live map (so the next\n * synchronous read sees it) and schedules a debounced server persist. Every transition — manual\n * scroll-to-bottom, the \"Latest\" button, and reading-position capture — calls this, so they all\n * converge on the same stored state.\n */\nexport function setChatScroll(agentId: string, entry: ChatScrollEntry): void {\n // Move-to-end keeps insertion order == recency, so eviction drops the oldest-touched.\n memory.delete(agentId)\n memory.set(agentId, entry)\n evictOverflow()\n schedulePersist()\n}\n\nfunction evictOverflow(): void {\n while (memory.size > MAX_CONVERSATIONS) {\n const oldest = memory.keys().next().value\n if (oldest === undefined) break\n memory.delete(oldest)\n }\n}\n\nfunction serialize(): Record<string, ChatScrollEntry> {\n const out: Record<string, ChatScrollEntry> = {}\n for (const [agentId, entry] of memory) out[agentId] = entry\n return out\n}\n\nfunction schedulePersist(): void {\n if (persistTimer !== null) clearTimeout(persistTimer)\n persistTimer = setTimeout(() => { void flushChatScroll() }, PERSIST_DEBOUNCE_MS)\n}\n\n/** Persist immediately (cancels any pending debounce). Used by the debounce timer and by tests. */\nexport async function flushChatScroll(): Promise<void> {\n if (persistTimer !== null) {\n clearTimeout(persistTimer)\n persistTimer = null\n }\n try {\n await api('PUT', SETTINGS_PATH, { value: serialize() })\n } catch {\n // View state — a failed write is non-fatal; the next transition reschedules a persist.\n }\n}\n\n/**\n * Hydrate the in-memory map from the user's persisted settings, ONCE per session. Called at boot\n * (connection.init) so a fresh tab/device has the map populated before the user opens a chat and\n * VirtualTimeline reads it synchronously on mount. Server values do NOT clobber entries already\n * written locally this session (a live read position wins over a stale server snapshot).\n */\nexport function hydrateChatScroll(): Promise<void> {\n if (hydratePromise) return hydratePromise\n hydratePromise = (async () => {\n try {\n const res = await api<ChatScrollSettingResponse>('GET', SETTINGS_PATH)\n const value = res.value\n if (!value || typeof value !== 'object') return\n for (const [agentId, raw] of Object.entries(value)) {\n if (memory.has(agentId)) continue\n const entry = asEntry(raw)\n if (entry) memory.set(agentId, entry)\n }\n evictOverflow()\n } catch {\n // Boot-time best-effort; absence of persisted state just means everything starts at default.\n }\n })()\n return hydratePromise\n}\n\n/** Test-only: reset all module state so each test starts from a clean slate. */\nexport function _resetChatScrollStoreForTest(): void {\n if (persistTimer !== null) {\n clearTimeout(persistTimer)\n persistTimer = null\n }\n memory = new Map()\n hydratePromise = null\n}\n","// Correctness-sensitive pure helpers shared across slices. These are the ONE\n// home for poll-merge identity preservation, the active-visible-chat predicate,\n// and the compact/clear status derivation — keeping a single implementation is\n// the whole point (per-slice copies would silently drift, issue #292).\nimport type { Agent, Message, RelayNotification } from '@/types'\nimport { isRecord } from 'agent-relay-sdk/types'\nimport { HUMAN_AGENT_ID } from '@/lib/constants'\nimport { isBadgeWorthy } from '@/lib/display'\nimport type { ChatStatusEvent, RelayStore, TimelineCommand } from './types'\n\n// `agent.status` is a live status projection, not the full REST card. Static/heavy fields\n// may be omitted, so retain the previous cached card for anything the status tick does not\n// send. Incoming values still win, including explicit nulls inside meta.\nexport function mergeAgentStatusUpdate(prev: Agent | undefined, incoming: Agent): Agent {\n const base = prev ? { ...prev, ...incoming } : incoming\n const meta = prev?.meta || incoming.meta\n ? { ...(prev?.meta ?? {}), ...(incoming.meta ?? {}) }\n : undefined\n // Deep-merge `meta.workspace` one level. The status projection strips heavy workspace\n // subfields (deps/probe/symlinks, #1012), so a shallow meta merge lets each slim tick's\n // `workspace` object REPLACE the cached full one and drop them — the #560 flicker class\n // one level down (#1017). Incoming workspace fields still win; subfields the tick omitted\n // survive from the cache.\n if (meta && isRecord(prev?.meta?.workspace) && isRecord(incoming.meta?.workspace)) {\n meta.workspace = { ...prev.meta.workspace, ...incoming.meta.workspace }\n }\n return {\n ...base,\n ...(meta ? { meta } : {}),\n ...(!incoming.issueRepo && prev?.issueRepo ? { issueRepo: prev.issueRepo } : {}),\n }\n}\n\nexport function indexAgents(agents: Agent[]): Record<string, Agent> {\n const byId: Record<string, Agent> = {}\n for (const agent of agents) byId[agent.id] = agent\n return byId\n}\n\nexport function upsertById<T extends { id: string | number }>(list: T[], item: T): T[] {\n const idx = list.findIndex((e) => e.id === item.id)\n const next = [...list]\n if (idx >= 0) next[idx] = item\n else next.push(item)\n return next\n}\n\nconst CHAT_STATUS_DEDUPE_WINDOW_MS = 3000\n\nexport function appendChatStatusEvent(\n eventsByAgent: Record<string, ChatStatusEvent[]>,\n agentId: string | undefined,\n status: string | undefined,\n timestamp = Date.now(),\n id?: string,\n): Record<string, ChatStatusEvent[]> {\n if (!agentId || !status) return eventsByAgent\n const evts = [...(eventsByAgent[agentId] || [])]\n const eventId = id || `s-${status}-${timestamp}`\n if (evts.some((event) => event.id === eventId)) return eventsByAgent\n if (evts.some((event) => event.status === status && Math.abs(event.timestamp - timestamp) <= CHAT_STATUS_DEDUPE_WINDOW_MS)) return eventsByAgent\n evts.push({ id: eventId, status, timestamp })\n if (evts.length > 200) evts.splice(0, evts.length - 200)\n return { ...eventsByAgent, [agentId]: evts }\n}\n\nexport function statusTimelineEvent(prev: Agent | undefined, next: Agent): string | undefined {\n if (!prev || prev.status === next.status) return undefined\n const cameOnline = (prev.status === 'offline' || prev.status === 'stale') && (next.status === 'online' || next.status === 'idle' || next.status === 'busy')\n if (cameOnline) return 'online'\n if (next.status === 'offline') return 'offline'\n return undefined\n}\n\n// Real provider-hook statuses that prove a compact/clear actually began — any\n// of these retracts a pending stall warning for that agent.\nexport const COMMAND_STALL_CLEAR_STATUSES = new Set(['compacting', 'compacted', 'clearing-context', 'context-cleared'])\n\nexport function agentTimelineEvent(agent: Agent): { status: string; id?: string; timestamp?: number } | undefined {\n const event = agent.meta?.timelineEvent\n if (!event || typeof event !== 'object' || Array.isArray(event)) return undefined\n const status = (event as Record<string, unknown>).status\n if (typeof status !== 'string' || !status) return undefined\n const id = (event as Record<string, unknown>).id\n const timestamp = (event as Record<string, unknown>).timestamp\n return {\n status,\n ...(typeof id === 'string' ? { id } : {}),\n ...(typeof timestamp === 'number' && Number.isFinite(timestamp) ? { timestamp } : {}),\n }\n}\n\nexport function commandAgentId(command: TimelineCommand): string | undefined {\n const paramAgentId = command.params?.agentId\n if (typeof paramAgentId === 'string' && paramAgentId) return paramAgentId\n return typeof command.target === 'string' && command.target ? command.target : undefined\n}\n\nexport function commandTimelineStatus(command: TimelineCommand, event: string): string | undefined {\n if (event === 'command.dispatched') {\n // \"We asked\" — not \"it happened\". The honest started/finished states for\n // compact/clear come from the real provider hooks (PreCompact, SessionStart)\n // via the agent's timelineEvent, not from the command lifecycle.\n if (command.type === 'agent.compact') return 'compaction-requested'\n if (command.type === 'agent.clearContext') return 'clear-requested'\n if (command.type === 'agent.shutdown') return 'shutting-down'\n return undefined\n }\n if (event === 'command.succeeded') {\n // compact/clear \"succeeded\" only means keystrokes were delivered into the\n // tmux session — say nothing here and let the hooks report the real result.\n if (command.type === 'agent.shutdown') return 'shutdown'\n if (command.type === 'agent.restart') return 'restarted'\n }\n return undefined\n}\n\nfunction messageFingerprint(message: Message): string {\n return JSON.stringify(message)\n}\n\nexport function mergeFetchedMessages(current: Message[], incoming: Message[]): Message[] {\n const byId = new Map(current.map((message) => [message.id, message]))\n const sameOrder = current.length === incoming.length && current.every((message, index) => message.id === incoming[index]?.id)\n let changed = !sameOrder\n const next = incoming.map((message) => {\n const existing = byId.get(message.id)\n if (existing && messageFingerprint(existing) === messageFingerprint(message)) return existing\n changed = true\n return message\n })\n return changed ? next : current\n}\n\n// Identity-preserving merge for polled lists: when a poll returns data that is\n// byte-identical to what we hold, return the SAME array reference so selectors\n// subscribed to it don't recompute. Reuses per-item refs that are unchanged.\nexport function mergeListById<T extends { id: string | number }>(current: T[], incoming: T[]): T[] {\n const dedupedIncoming = [...new Map(incoming.map((item) => [item.id, item])).values()]\n const byId = new Map(current.map((item) => [item.id, item]))\n const sameOrder = current.length === dedupedIncoming.length && current.every((item, index) => item.id === dedupedIncoming[index]?.id)\n let changed = !sameOrder\n const next = dedupedIncoming.map((item) => {\n const existing = byId.get(item.id)\n if (existing === item) return existing\n if (existing && JSON.stringify(existing) === JSON.stringify(item)) return existing\n changed = true\n return item\n })\n return changed ? next : current\n}\n\nexport function isDashboardHidden(): boolean {\n return typeof document === 'undefined' ? true : document.hidden\n}\n\n// True when `peer`'s thread is the one the user is actively looking at: on the\n// chat view, selected, and the tab visible. Incoming messages for such a thread\n// should never raise an unread badge — they are read on arrival.\nexport function isActiveVisibleChat(peer: string, state: RelayStore): boolean {\n return Boolean(peer && state.view === 'chat' && state.selectedInboxThread === peer && !isDashboardHidden())\n}\n\nfunction notificationPeer(notification: RelayNotification): string {\n return notification.threadPeer || notification.agentId || ''\n}\n\nexport function notificationTargetsActiveChat(notification: RelayNotification, state: RelayStore): boolean {\n return isActiveVisibleChat(notificationPeer(notification), state)\n}\n\nexport function lastInboundMessageId(messages: Message[]): number {\n return messages\n .filter((m) => m.to === HUMAN_AGENT_ID && m.from !== HUMAN_AGENT_ID && isBadgeWorthy(m))\n .reduce((max, m) => Math.max(max, m.id), 0)\n}\n","import type { Message, RelayNotification, NotificationSettings, ViewName } from '@/types'\nimport { HUMAN_AGENT_ID } from '@/lib/constants'\nimport { isDashboardHidden, notificationTargetsActiveChat } from '../merge'\nimport type { NotificationsSlice, RelayStore, StoreSlice } from '../types'\n\n// A message is human-facing when the human is the sender or recipient, or it's a\n// channel-inbound message (e.g. Telegram) routed to the human via a policy target.\n// Used to suppress notifications for purely inter-agent traffic (#506).\nfunction messageInvolvesHuman(msg: Message): boolean {\n if (msg.from === HUMAN_AGENT_ID || msg.to === HUMAN_AGENT_ID) return true\n const direction = typeof msg.payload?.direction === 'string' ? msg.payload.direction : undefined\n return msg.kind === 'channel.event' && direction === 'inbound'\n}\n\nconst DEFAULT_NOTIFICATION_SETTINGS: NotificationSettings = {\n enabled: true,\n browserEnabled: false,\n messages: true,\n replies: true,\n errors: true,\n}\n\nfunction notificationCategoryEnabled(notification: RelayNotification, settings: NotificationSettings): boolean {\n if (!settings.enabled) return false\n if (notification.kind === 'message') return settings.messages\n if (notification.kind === 'reply') return settings.replies\n return settings.errors\n}\n\nfunction browserNotificationsSupported(): boolean {\n return typeof window !== 'undefined' && 'Notification' in window\n}\n\nexport function browserNotificationPermission(): NotificationPermission | 'unsupported' {\n if (!browserNotificationsSupported()) return 'unsupported'\n return window.Notification.permission\n}\n\nfunction shouldShowBrowserNotification(notification: RelayNotification, state: RelayStore): boolean {\n if (!notificationCategoryEnabled(notification, state.notificationSettings)) return false\n if (!state.notificationSettings.browserEnabled) return false\n if (browserNotificationPermission() !== 'granted') return false\n if (notificationTargetsActiveChat(notification, state)) return false\n return isDashboardHidden() || state.view !== 'chat'\n}\n\nfunction showBrowserNotification(notification: RelayNotification, onClick: () => void | Promise<void>) {\n if (browserNotificationPermission() !== 'granted') return\n try {\n const n = new window.Notification(notification.title, {\n body: notification.body,\n tag: notification.id,\n icon: 'icons/agent-relay-192.png',\n data: {\n view: notification.view,\n threadPeer: notification.threadPeer,\n agentId: notification.agentId,\n messageId: notification.messageId,\n },\n })\n n.onclick = () => {\n window.focus()\n void onClick()\n n.close()\n }\n } catch {}\n}\n\nexport const createNotificationsSlice: StoreSlice<NotificationsSlice> = (set, get) => ({\n notification: null,\n toastNotification: null,\n notifications: [],\n notificationUnread: 0,\n notificationSettings: { ...DEFAULT_NOTIFICATION_SETTINGS },\n notificationPermission: browserNotificationPermission(),\n confirmModal: { show: false, title: '', message: '', action: null },\n errorModal: { show: false, title: '', message: '' },\n\n openConfirm(title, message, action) { set({ confirmModal: { show: true, title, message, action } }) },\n showError(title, message) { set({ errorModal: { show: true, title, message } }) },\n showNotification(message) {\n // Plain status toast — not actionable, so clear any pending actionable one.\n set({ notification: message, toastNotification: null })\n setTimeout(() => set({ notification: null }), 3000)\n },\n handleRelayNotification(relayNotification) {\n const s = get()\n if (!notificationCategoryEnabled(relayNotification, s.notificationSettings)) return\n if (s.notifications.some((item) => item.id === relayNotification.id)) return\n // #506: agent→agent messages must never notify. The server already withholds\n // these, but guard the client too — if the referenced message is in the store\n // and involves no human, drop it before any toast/unread/browser notification.\n if (relayNotification.messageId && (relayNotification.kind === 'message' || relayNotification.kind === 'reply')) {\n const msg = s.messages.find((m) => m.id === relayNotification.messageId)\n if (msg && !messageInvolvesHuman(msg)) return\n }\n const activeChatNotification = notificationTargetsActiveChat(relayNotification, s)\n const toast = `${relayNotification.title}: ${relayNotification.body}`\n const notifications = [relayNotification, ...s.notifications].slice(0, 100)\n set({\n notifications,\n notificationUnread: activeChatNotification ? s.notificationUnread : Math.min(99, s.notificationUnread + 1),\n notification: activeChatNotification ? s.notification : toast,\n // Keep the actionable notification so the in-app toast can route to it on tap.\n toastNotification: activeChatNotification ? s.toastNotification : relayNotification,\n })\n if (!activeChatNotification) {\n setTimeout(() => {\n if (get().toastNotification?.id === relayNotification.id) set({ notification: null, toastNotification: null })\n }, 4500)\n }\n if (shouldShowBrowserNotification(relayNotification, s)) {\n showBrowserNotification(relayNotification, () => get().openNotification(relayNotification))\n }\n if (activeChatNotification && relayNotification.threadPeer && relayNotification.messageId) {\n get().markInboxThreadReadTo(relayNotification.threadPeer, relayNotification.messageId)\n }\n },\n async openNotification(relayNotification) {\n set({ notificationUnread: 0 })\n if (relayNotification.threadPeer) {\n get().openInboxThread(relayNotification.threadPeer)\n if (relayNotification.messageId) get().markInboxThreadReadTo(relayNotification.threadPeer, relayNotification.messageId)\n }\n if (relayNotification.view) await get().switchView(relayNotification.view as ViewName)\n if (relayNotification.agentId && relayNotification.view === 'agents') {\n const agent = get().agentsById[relayNotification.agentId]\n if (agent) get().openAgentDetail(agent)\n }\n },\n dismissToast() { set({ notification: null, toastNotification: null }) },\n markNotificationsRead() { set({ notificationUnread: 0 }) },\n clearNotifications() { set({ notifications: [], notificationUnread: 0 }) },\n async requestBrowserNotifications() {\n if (!browserNotificationsSupported()) {\n set({ notificationPermission: 'unsupported', notificationSettings: { ...get().notificationSettings, browserEnabled: false } })\n get().showNotification('Browser notifications are not supported here')\n return\n }\n const permission = await window.Notification.requestPermission()\n set({\n notificationPermission: permission,\n notificationSettings: { ...get().notificationSettings, browserEnabled: permission === 'granted' },\n })\n get().showNotification(permission === 'granted' ? 'Browser notifications enabled' : 'Browser notifications not enabled')\n },\n})\n","import { voiceTts } from '@/lib/voice'\nimport type { RelayStore, StoreSlice, VoiceSlice } from '../types'\n\nexport const createVoiceSlice: StoreSlice<VoiceSlice> = (set) => ({\n voiceTtsEnabled: false,\n voiceTtsLang: 'en-US',\n voiceTtsMode: 'kokoro',\n voiceTtsKokoroVoice: 'am_michael',\n voiceTtsBrowserVoice: '',\n voiceInputMode: 'compose',\n\n setVoiceTtsEnabled(on: boolean) {\n // The module-scope subscription syncs the controller's active chat from\n // (view, selectedInboxThread); here we only flip the enabled flag.\n voiceTts.setEnabled(on)\n set({ voiceTtsEnabled: on })\n },\n\n setVoiceTtsLang(lang: string) {\n voiceTts.setLang(lang)\n set({ voiceTtsLang: lang })\n },\n\n setVoiceTtsMode(mode: 'kokoro' | 'browser') {\n voiceTts.setMode(mode)\n set({ voiceTtsMode: mode })\n },\n\n setVoiceTtsKokoroVoice(voice: string) {\n voiceTts.setKokoroVoice(voice)\n set({ voiceTtsKokoroVoice: voice })\n },\n\n setVoiceTtsBrowserVoice(uri: string) {\n voiceTts.setBrowserVoice(uri)\n set({ voiceTtsBrowserVoice: uri })\n },\n\n setVoiceInputMode(mode: 'compose' | 'autosend' | 'handsfree') {\n set({ voiceInputMode: mode })\n },\n})\n\n// Keep the browser TTS controller's \"active chat\" in sync with navigation. This\n// is an external-system subscription (the speechSynthesis engine), not derived\n// React state — so it lives here, driven by the store, not by a component effect.\n// Guarded: it early-returns unless the derived active chat actually changes, so\n// it does NOT fire a side-effect on every poll merge.\nlet _voiceActiveChat: string | null = null\nexport function syncVoiceActiveChat(s: RelayStore): void {\n const active = s.voiceTtsEnabled && s.view === 'chat' ? (s.selectedInboxThread || null) : null\n if (active === _voiceActiveChat) return\n _voiceActiveChat = active\n voiceTts.setActiveChat(active)\n}\n","import { api, setAuthToken, setUnauthorizedHandler, openRelayEventStream } from '@/lib/api'\nimport { setDashboardView } from '@/lib/dashboard-attribution'\nimport { voiceTts } from '@/lib/voice'\nimport { useClock } from '@/store/clock'\nimport { LIVE_REFRESH_MS, MESSAGE_BUFFER_CAP } from '@/lib/constants'\nimport { inboxPeer, isBadgeWorthy, isHumanInboundMessage } from '@/lib/display'\nimport type { Agent, Message, Orchestrator, Plan, RelayNotification, ActivityEvent, Stats, Task, Team, ThemeId, ViewName } from '@/types'\nimport { orchestratorForPath, orchestratorForAgent } from '../helpers'\nimport { hydrateChatScroll } from '@/components/chat/chatScrollStore'\nimport { isRecord, stringValue } from 'agent-relay-sdk/types'\nimport {\n appendChatStatusEvent, agentTimelineEvent, statusTimelineEvent, commandAgentId,\n commandTimelineStatus, COMMAND_STALL_CLEAR_STATUSES, isActiveVisibleChat, upsertById,\n mergeAgentStatusUpdate,\n} from '../merge'\nimport type { ConnectionSlice, PresenceViewer, StoreSlice, TimelineCommand } from '../types'\nimport { browserNotificationPermission } from './notifications'\nimport { syncVoiceActiveChat } from './voice'\n\ninterface ConnectionSliceDeps {\n waitForHydration: () => Promise<void>\n}\n\nfunction spawnFailureReason(command: TimelineCommand): string {\n const result = isRecord(command.result) ? command.result : undefined\n return stringValue(command.error)\n ?? stringValue(result?.error)\n ?? stringValue(result?.reason)\n ?? stringValue(result?.message)\n ?? 'spawn command failed'\n}\n\nfunction localCommandActivityId(command: TimelineCommand): number {\n const key = command.id || command.correlationId || `${command.type}:${command.updatedAt ?? command.createdAt ?? Date.now()}`\n let hash = 0\n for (let i = 0; i < key.length; i++) hash = ((hash * 31) + key.charCodeAt(i)) >>> 0\n return -Math.max(1, hash)\n}\n\nfunction upsertActivityByClientId(events: ActivityEvent[], item: ActivityEvent): ActivityEvent[] {\n if (item.clientId) {\n const idx = events.findIndex((event) => event.clientId === item.clientId)\n if (idx >= 0) {\n const next = [...events]\n next[idx] = item\n return next\n }\n }\n return upsertById(events, item)\n}\n\nexport const createConnectionSlice = (deps: ConnectionSliceDeps): StoreSlice<ConnectionSlice> => (set, get) => ({\n theme: 'dark' as ThemeId,\n view: 'overview' as ViewName,\n autoRefresh: true,\n mobileMenuOpen: false,\n commandPaletteOpen: false,\n commandQuery: '',\n connected: false,\n connectionError: false,\n quotaSeq: 0,\n authNeeded: false,\n authToken: '',\n stats: {},\n fileOrchestratorId: '',\n filePath: '',\n fileSelectedPath: '',\n fileSelectedLine: 0,\n fileOverlayOpen: false,\n _es: null,\n _refreshTimer: null,\n _refreshInFlight: false,\n\n set: (partial) => set(partial),\n\n async init() {\n await deps.waitForHydration()\n const token = get().authToken\n if (token) setAuthToken(token)\n setDashboardView(get().view)\n // Restore the persisted speaker toggle + language into the TTS controller.\n voiceTts.setEnabled(get().voiceTtsEnabled)\n voiceTts.setLang(get().voiceTtsLang)\n voiceTts.setMode(get().voiceTtsMode)\n voiceTts.setKokoroVoice(get().voiceTtsKokoroVoice)\n voiceTts.setBrowserVoice(get().voiceTtsBrowserVoice)\n syncVoiceActiveChat(get())\n setUnauthorizedHandler(() => { if (!get().authNeeded) set({ authNeeded: true, connectionError: false }) })\n // Warm the per-user chat scroll memory from the server (#759) so a fresh tab/device has each\n // conversation's saved reading position before the user opens a chat. Fire-and-forget: the\n // store falls back to \"open at latest\" if it hasn't landed by first mount.\n void hydrateChatScroll()\n set({ notificationPermission: browserNotificationPermission() })\n get().startClock()\n try {\n const stats = await api<Stats>('GET', '/stats')\n set({ stats })\n } catch (e: unknown) {\n if ((e as { status?: number }).status === 401) { set({ authNeeded: true }); return }\n }\n await get().refresh()\n void get().fetchAllIssueRefs()\n get().connectSSE()\n get().startAutoRefresh()\n },\n\n async switchView(view: ViewName) {\n setDashboardView(view)\n set({ view, mobileMenuOpen: false })\n const s = get()\n if (['chat', 'messages'].includes(view)) await Promise.all([s.fetchMessages(), s.fetchChatHistoryImports(), s.fetchDeliverableTasks({ unfiltered: true })])\n if (view === 'activity') await Promise.all([s.fetchMessages(), s.fetchPairs(), s.fetchTasks(), s.fetchActivityEvents()])\n if (view === 'work') await Promise.all([s.fetchMessages(), s.fetchTasks()])\n if (view === 'pairs') await s.fetchPairs()\n if (view === 'channels') await Promise.all([s.fetchChannels(), s.fetchIntegrations()])\n if (view === 'connectors') await s.fetchConnectors()\n if (view === 'integrations') await s.fetchIntegrations()\n if (view === 'memory') await s.refreshMemory()\n if (view === 'tasks') await Promise.all([s.fetchTasks(), s.fetchDeliverableTasks()])\n if (view === 'teams') await Promise.all([s.fetchTeams(), s.fetchTeamOutcomeAggregates(), s.refreshIssueRefs()])\n if (view === 'automation') await Promise.all([s.fetchAutomations(), s.fetchAutomationRuns()])\n if (view === 'maintenance') await s.fetchMaintenanceJobs()\n if (view === 'workspaces') await Promise.all([s.fetchWorkspaces(), s.fetchOrchestrators(), s.fetchAgents()])\n if (view === 'files') await s.fetchOrchestrators()\n if (view === 'analytics') await s.fetchAnalytics()\n },\n\n startClock() {\n useClock.getState().start()\n },\n\n stopClock() {\n useClock.getState().stop()\n },\n\n startAutoRefresh() {\n get().stopAutoRefresh()\n if (!get().autoRefresh) return\n const timer = setInterval(() => get().refreshLiveData(), LIVE_REFRESH_MS)\n set({ _refreshTimer: timer })\n },\n\n stopAutoRefresh() {\n const s = get()\n if (s._refreshTimer) { clearInterval(s._refreshTimer); set({ _refreshTimer: null }) }\n },\n\n async apiCall<T = unknown>(method: string, path: string, body?: unknown): Promise<T> {\n return api<T>(method, path, body)\n },\n\n async openFilesAt(input) {\n const s = get()\n const orchestratorId = input.orchestratorId || orchestratorForPath(s.orchestrators, input.selectedPath || input.path)?.id || s.orchestrators.find((orch) => orch.status === 'online')?.id || ''\n set({\n fileOrchestratorId: orchestratorId,\n filePath: input.path,\n fileSelectedPath: input.selectedPath || '',\n fileSelectedLine: input.line || 0,\n fileOverlayOpen: input.overlay === true,\n })\n if (!input.overlay) await get().switchView('files')\n },\n\n async openFilesForAgent(agent, overlay = false) {\n const cwd = typeof agent.meta?.cwd === 'string' ? agent.meta.cwd : ''\n if (!cwd) {\n get().showNotification('Agent has no cwd')\n return\n }\n const orch = orchestratorForAgent(get().orchestrators, agent)\n if (!orch) {\n get().showNotification('No online orchestrator covers that cwd')\n return\n }\n await get().openFilesAt({ orchestratorId: orch.id, path: cwd, overlay })\n },\n\n closeFileOverlay() {\n set({ fileOverlayOpen: false })\n },\n\n async refresh() {\n const s = get()\n await Promise.all([\n s.fetchStats(), s.fetchHealth(), s.fetchAgents(), s.fetchUsers(), s.fetchOrchestrators(),\n s.fetchPairs(), s.fetchMessages(), s.fetchTasks(), s.fetchDeliverableTasks({ unfiltered: true }), s.fetchChannels(),\n s.fetchConnectors(), s.fetchIntegrations(), s.fetchAutomations(), s.fetchAutomationRuns(),\n s.fetchManagedPolicies(), s.fetchAgentProfiles(), s.fetchToolGroupCatalog(), s.fetchMaintenanceJobs(), s.fetchWorkspaces(), s.fetchInboxState(), s.fetchActivityEvents(), s.fetchChatHistoryImports(),\n s.fetchProviderCatalog(),\n ])\n },\n\n // Periodic reconciliation poll. SSE is the live channel; this is the\n // safety net. Polls only the globally-visible \"core\" data plus whatever\n // the active view shows — not all ~19 endpoints every tick. A cheap\n // /stats canary detects auth/connection state and short-circuits the\n // poll when the relay is unreachable instead of failing silently.\n async refreshLiveData() {\n if (get()._refreshInFlight || get().authNeeded) return\n set({ _refreshInFlight: true })\n try {\n try {\n const stats = await api<Stats>('GET', '/stats')\n set(get().connectionError ? { stats, connectionError: false } : { stats })\n } catch (e) {\n if ((e as { status?: number }).status === 401) return\n if (!get().connectionError) set({ connectionError: true })\n return\n }\n const s = get()\n // Core: drives sidebar badges, attention counts, and SSE reconciliation.\n const work: Array<Promise<unknown>> = [\n s.fetchHealth(), s.fetchAgents(), s.fetchOrchestrators(),\n s.fetchPairs(), s.fetchMessages(), s.fetchTasks(),\n s.fetchManagedPolicies(), s.fetchInboxState(),\n ]\n // View-scoped: only poll what the open view actually renders.\n const view = s.view\n if (['chat', 'messages'].includes(view)) work.push(s.fetchChatHistoryImports(), s.fetchDeliverableTasks({ unfiltered: true }))\n if (view === 'activity') work.push(s.fetchActivityEvents())\n if (view === 'memory') work.push(s.refreshMemory())\n if (view === 'automation') work.push(s.fetchAutomations(), s.fetchAutomationRuns())\n if (view === 'channels') work.push(s.fetchChannels(), s.fetchIntegrations())\n if (view === 'connectors') work.push(s.fetchConnectors())\n if (view === 'integrations') work.push(s.fetchIntegrations())\n if (view === 'maintenance') work.push(s.fetchMaintenanceJobs())\n if (view === 'profiles') work.push(s.fetchAgentProfiles())\n if (view === 'workspaces') work.push(s.fetchWorkspaces())\n if (view === 'analytics') work.push(s.fetchAnalytics())\n if (view === 'teams') work.push(s.fetchTeams(), s.fetchTeamOutcomeAggregates(), s.refreshIssueRefs())\n if (s.agentDetailOpen && s.agentDetailId) work.push(s.fetchAgentIssueActivity(s.agentDetailId))\n if (view === 'tasks') work.push(s.fetchDeliverableTasks())\n if (s.taskDetailOpen && s.taskDetailId) work.push(s.fetchTaskDetail(s.taskDetailId))\n await Promise.all(work)\n } finally { set({ _refreshInFlight: false }) }\n },\n\n async fetchStats() { try { set({ stats: await api('GET', '/stats') }) } catch {} },\n\n // --- SSE ---\n connectSSE() {\n get().disconnectSSE()\n const stream = openRelayEventStream(get().authToken, {\n connected: (info = {}) => {\n set(get().connectionError ? { connected: true, connectionError: false } : { connected: true })\n if (!info.replay?.available) {\n void get().refreshLiveData()\n const loadedAgentIds = Object.entries(get().agentChatByAgent)\n .filter(([, window]) => window.loadedInitial)\n .map(([agentId]) => agentId)\n void Promise.all(loadedAgentIds.map((agentId) => get().gapFillAgentChat(agentId)))\n void get().refreshPlans()\n void get().refreshIssueRefs()\n }\n },\n disconnected: () => set({ connected: false }),\n message: (event, data) => {\n if (event === 'connected') return\n if (event === 'replay.gap') {\n void get().refreshLiveData()\n const loadedAgentIds = Object.entries(get().agentChatByAgent)\n .filter(([, window]) => window.loadedInitial)\n .map(([agentId]) => agentId)\n void Promise.all(loadedAgentIds.map((agentId) => get().gapFillAgentChat(agentId)))\n void get().refreshPlans()\n void get().refreshIssueRefs()\n return\n }\n if (event === 'message.new') {\n const msg = JSON.parse(data) as Message\n // TTS trigger runs BEFORE dedup: the 5s fetchMessages poll can win the\n // race against SSE in busy sessions (many reasoning/tool events queued),\n // adding the response to the store first. The dedup then skips the SSE\n // event entirely — including the TTS call. Moving it here ensures TTS\n // fires exactly once (SSE delivers each message once).\n if (msg.kind === 'session' && msg.from !== 'user') {\n const sess = (msg.payload as { session?: { type?: string; origin?: string } } | undefined)?.session\n if (sess?.type === 'response' && sess?.origin === 'provider') {\n voiceTts.onResponse(inboxPeer(msg), msg.body)\n }\n }\n const s = get()\n const globalDuplicate = s.messages.some((m) => m.id === msg.id)\n if (!globalDuplicate) {\n const msgs = [...s.messages, msg]\n if (msgs.length > MESSAGE_BUFFER_CAP) msgs.splice(0, msgs.length - MESSAGE_BUFFER_CAP)\n set({ messages: msgs })\n }\n // If the user is actively viewing this thread, keep it read so no\n // stale unread badge appears while the chat is open and focused.\n const peer = inboxPeer(msg)\n // Fan-out: deliver to every loaded agent-chat window that involves this message.\n // appendChatTail uses messageMatchesAgentChat internally, so each call only\n // appends when the message actually belongs in that agent's window.\n for (const agentId of Object.keys(get().agentChatByAgent)) {\n get().appendChatTail(agentId, [msg])\n }\n if (isHumanInboundMessage(msg) && isBadgeWorthy(msg) && isActiveVisibleChat(peer, s)) {\n get().markInboxThreadReadTo(peer, msg.id)\n }\n return\n }\n if (event === 'message.queued' || event === 'message.expired' || event === 'message.delivery_updated' || event === 'message.reaction_updated') {\n const msg = JSON.parse(data) as Message\n set({ messages: upsertById(get().messages, msg) })\n get().applyChatMessageUpdate(msg)\n return\n }\n if (event === 'notification.created') {\n get().handleRelayNotification(JSON.parse(data) as RelayNotification)\n return\n }\n if (event === 'plan.created' || event === 'plan.changed') {\n const payload = JSON.parse(data) as { plan?: Plan; data?: { plan?: Plan } }\n const plan = payload.plan ?? payload.data?.plan\n if (plan) get().upsertPlan(plan)\n return\n }\n if (event === 'plan.deleted') {\n const payload = JSON.parse(data) as { plan?: Plan; planId?: string; id?: string; data?: { plan?: Plan; planId?: string; id?: string } }\n const plan = payload.plan ?? payload.data?.plan\n const planId = plan?.id ?? payload.planId ?? payload.id ?? payload.data?.planId ?? payload.data?.id\n if (plan || planId) get().removePlan(plan ?? planId!)\n return\n }\n if (event === 'team.dissolved') {\n const payload = JSON.parse(data) as { teamId?: string; outcomeStatus?: Team['outcomeStatus'] | null; reason?: string | null }\n if (payload.teamId) get().markTeamDissolved({ teamId: payload.teamId, outcomeStatus: payload.outcomeStatus, reason: payload.reason })\n void Promise.all([get().fetchTeams(), get().fetchTeamOutcomeAggregates()])\n return\n }\n if (event === 'activity.created') {\n const item = JSON.parse(data) as ActivityEvent & { issueRefId?: string; issueAction?: string }\n set({ activityEvents: upsertById(get().activityEvents, item) })\n if (item.issueRefId) void get().fetchIssueRef(item.issueRefId)\n if (item.agentId && get().agentDetailOpen && get().agentDetailId === item.agentId) void get().fetchAgentIssueActivity(item.agentId)\n return\n }\n if (event === 'agent.status') {\n const incoming = JSON.parse(data) as Agent\n const s = get()\n const prev = s.agentsById[incoming.id]\n // Retain the immutable, server-derived issueRepo if this update omits it (#560).\n const agent = mergeAgentStatusUpdate(prev, incoming)\n set({ agents: upsertById(s.agents, agent), agentsById: { ...s.agentsById, [agent.id]: agent } })\n if (s.livenessMismatches[agent.id] && agent.status !== 'offline') {\n const next = { ...s.livenessMismatches }\n delete next[agent.id]\n set({ livenessMismatches: next })\n }\n // A real provider hook (compacting/compacted/context-cleared) means\n // the command landed after all — retract any stall warning. But\n // timelineEvent is latched, so only a hook fresher than the warning\n // counts; a stale \"compacted\" from a prior compaction must not clear it.\n const stall = s.commandStalls[agent.id]\n if (stall) {\n const te = agentTimelineEvent(agent)\n if (te?.status && COMMAND_STALL_CLEAR_STATUSES.has(te.status) && (te.timestamp ?? 0) > stall.ts) {\n const next = { ...s.commandStalls }\n delete next[agent.id]\n set({ commandStalls: next })\n }\n }\n const timelineEvent = agentTimelineEvent(agent)\n const status = timelineEvent?.status ?? statusTimelineEvent(prev, agent)\n if (status) {\n const timestamp = timelineEvent?.timestamp ?? Date.now()\n const id = timelineEvent?.id || `agent-status:${agent.id}:${status}:${timestamp}`\n set({ chatStatusEvents: appendChatStatusEvent(s.chatStatusEvents, agent.id, status, timestamp, id) })\n }\n return\n }\n if (event === 'dashboard.presence') {\n const d = JSON.parse(data) as { agentId?: string; viewers?: PresenceViewer[] }\n if (d.agentId) get().applyPresence(d.agentId, d.viewers ?? [])\n return\n }\n if (event === 'liveness.mismatch') {\n const d = JSON.parse(data) as { agentId: string; detail?: string; policyName?: string; spawnRequestId?: string }\n if (d.agentId) {\n set({ livenessMismatches: { ...get().livenessMismatches, [d.agentId]: { agentId: d.agentId, detail: d.detail ?? 'Process reported running but relay marked it offline', ts: Date.now(), policyName: d.policyName, spawnRequestId: d.spawnRequestId } } })\n }\n return\n }\n if (event === 'command.stalled') {\n const d = JSON.parse(data) as { agentId: string; kind?: 'compact' | 'clear'; detail?: string; commandId?: string }\n if (d.agentId) {\n const ts = Date.now()\n const kind = d.kind ?? 'compact'\n const stallStatus = kind === 'clear' ? 'clear-stalled' : 'compaction-stalled'\n set({\n commandStalls: { ...get().commandStalls, [d.agentId]: { agentId: d.agentId, kind, detail: d.detail ?? 'A context command was requested but never started — the session may need manual intervention.', ts, commandId: d.commandId } },\n // Surface it in the chat timeline too, where \"compaction requested\" lives.\n chatStatusEvents: appendChatStatusEvent(get().chatStatusEvents, d.agentId, stallStatus, ts, `command-stalled:${d.agentId}:${d.commandId ?? ts}`),\n })\n }\n return\n }\n if (event === 'agent.removed') {\n const eventData = JSON.parse(data) as { id: string }\n const s = get()\n const byId = { ...s.agentsById }\n delete byId[eventData.id]\n const liveness = { ...s.livenessMismatches }\n delete liveness[eventData.id]\n const stalls = { ...s.commandStalls }\n delete stalls[eventData.id]\n set({ livenessMismatches: liveness, commandStalls: stalls })\n set({ agents: s.agents.filter((a) => a.id !== eventData.id), agentsById: byId })\n return\n }\n if (event === 'provider-quota.updated') {\n set({ quotaSeq: get().quotaSeq + 1 })\n return\n }\n if (event === 'message.claimed') {\n const eventData = JSON.parse(data) as { messageId: number; claimedBy: string; claimExpiresAt?: number }\n set({ messages: get().messages.map((m) => m.id === eventData.messageId ? { ...m, claimedBy: eventData.claimedBy, claimExpiresAt: eventData.claimExpiresAt } : m) })\n return\n }\n if (event === 'message.claim_released') {\n const eventData = JSON.parse(data) as { messageId: number }\n set({ messages: get().messages.map((m) => m.id === eventData.messageId ? { ...m, claimedBy: undefined, claimedAt: undefined, claimExpiresAt: undefined } : m) })\n return\n }\n if (event === 'message.deleted') {\n const eventData = JSON.parse(data) as { messageId: number }\n set({ messages: get().messages.filter((m) => m.id !== eventData.messageId) })\n return\n }\n if (event === 'orchestrator.status') {\n const orch = JSON.parse(data) as Orchestrator\n set({ orchestrators: upsertById(get().orchestrators, orch) })\n return\n }\n if (event === 'orchestrator.removed') {\n const eventData = JSON.parse(data) as { id: string }\n set({ orchestrators: get().orchestrators.filter((o) => o.id !== eventData.id) })\n return\n }\n if (event === 'policy.state.changed' || event === 'config.changed') {\n void get().fetchManagedPolicies()\n return\n }\n if (event === 'command.failed') {\n const eventData = JSON.parse(data) as { command?: TimelineCommand }\n const command = eventData.command\n if (command?.type === 'agent.spawn') {\n const reason = spawnFailureReason(command)\n const timestamp = typeof command.updatedAt === 'number' ? command.updatedAt : typeof command.createdAt === 'number' ? command.createdAt : Date.now()\n const params = isRecord(command.params) ? command.params : {}\n const spawnRequestId = stringValue(params.spawnRequestId) ?? command.correlationId\n const provider = stringValue(params.provider)\n const clientId = `command:${command.id ?? spawnRequestId ?? timestamp}:agent-spawn-failed`\n const activity: ActivityEvent = {\n id: localCommandActivityId(command),\n clientId,\n kind: 'state',\n title: 'agent.spawn · failed',\n body: reason,\n meta: [provider, spawnRequestId].filter(Boolean).join(' · ') || command.target,\n icon: 'ti-alert-triangle',\n view: 'agents',\n createdAt: timestamp,\n ts: timestamp,\n metadata: {\n commandId: command.id,\n commandType: command.type,\n status: command.status ?? 'failed',\n target: command.target,\n provider,\n spawnRequestId,\n },\n }\n set({ activityEvents: upsertActivityByClientId(get().activityEvents, activity) })\n get().showNotification(`Spawn failed: ${reason}`)\n }\n return\n }\n if (event === 'command.dispatched' || event === 'command.succeeded') {\n const eventData = JSON.parse(data) as { command?: TimelineCommand }\n const command = eventData.command\n if (command) {\n const status = commandTimelineStatus(command, event)\n if (status) {\n const timestamp = typeof command.updatedAt === 'number' ? command.updatedAt : typeof command.createdAt === 'number' ? command.createdAt : Date.now()\n set({ chatStatusEvents: appendChatStatusEvent(get().chatStatusEvents, commandAgentId(command), status, timestamp, command.id ? `command:${command.id}:${status}` : undefined) })\n }\n }\n return\n }\n if (event.startsWith('task.')) {\n const task = JSON.parse(data) as Task\n if (task.kind === 'deliverable') {\n const s = get()\n // Upsert first, then evict tasks that no longer match the active filters\n // so a status/stage change immediately disappears from a filtered list.\n const upserted = upsertById(s.deliverableTasks, task)\n const statusOk = !s.deliverableStatusFilter || task.status === s.deliverableStatusFilter\n // Normalize: the server omits deliverableStage when it is \"none\" (mappers.ts:303),\n // so treat undefined as \"none\" before comparing against the active filter.\n const effectiveStage = task.deliverableStage ?? 'none'\n const stageOk = !s.deliverableStageFilter || effectiveStage === s.deliverableStageFilter\n const projectOk = !s.deliverableProjectFilter || task.projectId === s.deliverableProjectFilter\n const filtered = (statusOk && stageOk && projectOk) ? upserted : upserted.filter((t) => t.id !== task.id)\n set({ deliverableTasks: filtered })\n if (s.taskDetailById[task.id]) {\n set({ taskDetailById: { ...s.taskDetailById, [task.id]: task as import('@/types').TaskDetail } })\n }\n } else {\n set({ tasks: upsertById(get().tasks, task) })\n }\n }\n },\n })\n set({ _es: stream })\n },\n\n disconnectSSE() {\n const es = get()._es\n if (es) { es.close(); set({ _es: null }) }\n },\n\n // --- Command palette ---\n openCommandPalette() { set({ commandQuery: '', commandPaletteOpen: true }) },\n closeCommandPalette() { set({ commandPaletteOpen: false, commandQuery: '' }) },\n async runCommand(command) {\n const { action, payload } = command\n get().closeCommandPalette()\n if (action === 'openView') await get().switchView(payload.view as ViewName)\n else if (action === 'agentPreset') { set({ showOffline: true, agentPresetFilter: (payload.preset as string) || '' }); await get().switchView('agents') }\n else if (action === 'copy') try { await navigator.clipboard?.writeText((payload.value as string) || '') } catch {}\n else if (action === 'messageAgent') { const agent = get().agentsById[payload.agentId as string]; if (agent) get().openComposeToAgent(agent) }\n else if (action === 'pairAgent') get().openPairInvite(payload.agentId as string)\n else if (action === 'filterTag') { set({ agentTagFilter: (payload.tag as string) || '', tagFilter: (payload.tag as string) || '' }); await get().switchView('agents') }\n else if (action === 'exportActivity') get().exportActivity((payload.format as string) || 'markdown')\n },\n\n // --- Auth ---\n async saveTokenAndRefresh() {\n const token = get().authToken\n setAuthToken(token)\n try {\n await api('GET', '/stats')\n } catch (e: unknown) {\n if ((e as { status?: number }).status === 401) {\n set({ authNeeded: true })\n return\n }\n }\n set({ authNeeded: false, connectionError: false })\n get().connectSSE()\n await get().refresh()\n get().startAutoRefresh()\n },\n\n // --- Cleanup ---\n destroy() {\n setUnauthorizedHandler(null)\n get().disconnectSSE()\n get().stopAutoRefresh()\n get().stopClock()\n },\n})\n","// Human users directory + viewer presence (#390). Two related responsibilities:\n// - `usersById`: resolves a `user:<id>` message address to a name/avatar (keystone #393),\n// fed once at boot from GET /api/users and refreshed by the reconciliation poll.\n// - `presenceByAgent`: who is currently viewing/typing in each agent's chat, fed live by the\n// `dashboard.presence` SSE event and our own heartbeat POSTs.\n// Until auth (#389) there is exactly one user (`user:default`), so both maps stay tiny.\nimport { api } from '@/lib/api'\nimport type { User } from '@/types'\nimport { mergeListById } from '../merge'\nimport type { PresenceViewer, StoreSlice, UsersSlice } from '../types'\n\nexport const createUsersSlice: StoreSlice<UsersSlice> = (set, get) => ({\n users: [],\n usersById: {},\n presenceByAgent: {},\n\n async fetchUsers() {\n try {\n const incoming = await api<User[]>('GET', '/users')\n const users = mergeListById(get().users, incoming)\n if (users === get().users) return\n const usersById: Record<string, User> = {}\n for (const u of users) usersById[u.id] = u\n set({ users, usersById })\n } catch {}\n },\n\n applyPresence(agentId, viewers) {\n set({ presenceByAgent: { ...get().presenceByAgent, [agentId]: viewers } })\n },\n\n // Heartbeat our own presence on an agent thread. `leaving:true` removes us immediately\n // (navigated away) rather than waiting for the server TTL. The response carries the live\n // viewer set, so we reflect it without waiting for the SSE round-trip.\n async announcePresence(agentId, typing, leaving) {\n try {\n const res = await api<{ viewers: PresenceViewer[] }>('POST', '/agents/' + encodeURIComponent(agentId) + '/presence', { typing, ...(leaving ? { leaving: true } : {}) })\n get().applyPresence(agentId, res.viewers ?? [])\n } catch {}\n },\n})\n","import { api } from '@/lib/api'\nimport { displayName } from '@/lib/display'\nimport type { Agent } from '@/types'\nimport { indexAgents, mergeListById } from '../merge'\nimport type { AgentsSlice, StoreSlice } from '../types'\n\nexport const createAgentsSlice: StoreSlice<AgentsSlice> = (set, get) => ({\n agents: [],\n agentsById: {},\n showOffline: false,\n showBuiltIns: false,\n agentSort: 'status',\n agentSortDir: 'asc',\n agentPresetFilter: '',\n agentStatusFilter: '',\n agentTagFilter: '',\n agentHostFilter: '',\n selectedAgent: '',\n agentDetailOpen: false,\n agentDetailId: '',\n livenessMismatches: {},\n commandStalls: {},\n channelDetailOpen: false,\n channelDetailId: '',\n renameModal: { show: false, agentId: '', label: '' },\n tagFilter: '',\n\n async fetchAgents() {\n try {\n const incoming = await api<Agent[]>('GET', '/agents')\n const agents = mergeListById(get().agents, incoming)\n if (agents === get().agents) return\n set({ agents, agentsById: indexAgents(agents) })\n } catch {}\n },\n openAgentDetail(agent) {\n set({ agentDetailId: agent.id, agentDetailOpen: true })\n // The cached card may have first materialized from a slim `agent.status` tick (heavy\n // profile-projection meta omitted, #1012) — e.g. an agent spawned after the dashboard\n // connected — which renders a blank profile panel until the next poll (#1017). Pull the\n // full REST card now so the drawer opens with complete data. This lives in the user-action\n // handler, not a useEffect (docs/dashboard.md discipline); fetchAgents' identity-preserving\n // merge no-ops when nothing changed, so a warm open costs nothing.\n void get().fetchAgents()\n },\n closeAgentDetail() { set({ agentDetailOpen: false }) },\n openChannelDetail(channel) { set({ channelDetailId: channel.id, channelDetailOpen: true }) },\n closeChannelDetail() { set({ channelDetailOpen: false }) },\n openRename(agent) { set({ renameModal: { show: true, agentId: agent.id, label: agent.label || '' } }) },\n async doRename() {\n const s = get()\n const label = s.renameModal.label.trim() || null\n try {\n await api('PATCH', '/agents/' + s.renameModal.agentId + '/label', { label })\n set({ renameModal: { ...s.renameModal, show: false } })\n } catch (e) { get().showError('Rename Failed', (e as Error).message) }\n },\n async doDeleteAgent(id) {\n try {\n await api('DELETE', '/agents/' + id)\n const s = get()\n const byId = { ...s.agentsById }\n delete byId[id]\n set({ agents: s.agents.filter((a) => a.id !== id), agentsById: byId, selectedAgent: s.selectedAgent === id ? '' : s.selectedAgent })\n } catch (e) { get().showError('Delete Failed', (e as Error).message) }\n },\n async doAgentAction(agent, action, component) {\n try {\n await api('POST', '/agents/' + encodeURIComponent(agent.id) + '/actions', { action, ...(component ? { surface: { component } } : {}) })\n const label = action === 'restart' ? 'Restart' : action === 'resume' ? 'Resume' : action === 'shutdown' ? 'Shutdown' : action === 'compact' ? 'Compact' : action === 'clearContext' ? 'Clear context' : action\n get().showNotification(`${label} command sent to ${displayName(agent)}`)\n await Promise.all([get().fetchMessages(), get().fetchActivityEvents()])\n } catch (e) { get().showError('Action Failed', (e as Error).message) }\n },\n})\n","import { api } from '@/lib/api'\nimport { HUMAN_AGENT_ID, INBOX_OPERATOR_ID, DEFAULT_COMPOSE, DEFAULT_INBOX_COMPOSE } from '@/lib/constants'\nimport { inboxPeer, isBadgeWorthy, isHumanInboundMessage, isReactionEventMessage, messageMatchesAgentChat } from '@/lib/display'\nimport type { ChatHistoryImport, InboxState, Message } from '@/types'\nimport { isActiveVisibleChat, lastInboundMessageId, mergeFetchedMessages, mergeListById, upsertById } from '../merge'\nimport type { AgentChatWindow } from '../types'\nimport type { ChatSlice, StoreSlice } from '../types'\n\nconst AGENT_CHAT_PAGE_LIMIT = 300\n\nconst EMPTY_AGENT_CHAT_WINDOW: AgentChatWindow = {\n messages: [],\n oldestLoadedId: null,\n hasMore: true,\n loading: false,\n loadedInitial: false,\n}\n\nfunction agentChatPath(agentId: string, params: Record<string, string | number>): string {\n const search = new URLSearchParams()\n for (const [key, value] of Object.entries(params)) search.set(key, String(value))\n return `/agents/${encodeURIComponent(agentId)}/chat?${search.toString()}`\n}\n\nfunction messagesForAgent(agentId: string, messages: Message[]): Message[] {\n return messages\n .filter((message) => messageMatchesAgentChat(agentId, message) && !isReactionEventMessage(message))\n .sort((a, b) => a.id - b.id)\n}\n\nfunction mergeAgentChatMessages(current: Message[], incoming: Message[]): Message[] {\n return mergeListById(current, [...current, ...incoming].sort((a, b) => a.id - b.id))\n}\n\n// When a real outbound message from the human arrives (SSE echo or background poll), strip any\n// negative-id optimistic placeholder from `base` so it doesn't double-render alongside the real\n// message. Centralised here so all reconcile paths (appendChatTail, loadAgentChat) stay in sync.\nfunction dropOptimisticOutbound(base: Message[], incoming: Message[]): Message[] {\n if (!incoming.some((m) => m.from === HUMAN_AGENT_ID)) return base\n return base.filter((m) => m.id >= 0 || m.from !== HUMAN_AGENT_ID)\n}\n\nfunction oldestLoadedId(messages: Message[]): number | null {\n return messages[0]?.id ?? null\n}\n\n// Bound the number of fully-loaded (300-message) agent chat windows kept in memory so reopening\n// a recently-viewed chat renders instantly from cache while older windows are evicted (#516).\nconst MAX_CACHED_CHATS = 12\n\n// LRU of recently-OPENED agent chats, most-recent first. Module-level (recency tracking needs no\n// reactivity); the actual eviction mutates agentChatByAgent through set(), which is reactive.\nconst chatAccessOrder: string[] = []\nfunction touchChatAccess(agentId: string): void {\n const existing = chatAccessOrder.indexOf(agentId)\n if (existing !== -1) chatAccessOrder.splice(existing, 1)\n chatAccessOrder.unshift(agentId)\n}\n\n// Keep the MAX_CACHED_CHATS most-recently-opened fully-loaded windows plus any forced ids (the\n// chat in view, the open inbox thread) — evict the rest. Partial windows (live-tail only, never\n// fully opened) are left in place: they are tiny and dropping them would lose an un-opened SSE\n// tail. This is what makes the cache bounded instead of growing forever.\nfunction evictChatWindows(byAgent: Record<string, AgentChatWindow>, keepIds: string[]): Record<string, AgentChatWindow> {\n const keep = new Set(keepIds.filter(Boolean))\n let kept = 0\n for (const id of chatAccessOrder) {\n if (kept >= MAX_CACHED_CHATS) break\n if (byAgent[id]?.loadedInitial) {\n keep.add(id)\n kept++\n }\n }\n const next: Record<string, AgentChatWindow> = {}\n for (const [id, win] of Object.entries(byAgent)) {\n if (!win.loadedInitial || keep.has(id)) next[id] = win\n }\n return next\n}\n\nexport const createChatSlice: StoreSlice<ChatSlice> = (set, get) => ({\n messages: [],\n agentChatByAgent: {},\n threadHistory: {},\n threadMessages: [],\n threadOpen: false,\n chatHistoryImports: [],\n selectedInboxThread: '',\n replyTo: null,\n composeOpen: false,\n showReasoning: false,\n chatAgentSearch: '',\n chatAgentProviderFilter: '',\n chatAgentStatusFilter: '',\n chatAgentTagFilter: '',\n chatAgentCapFilter: '',\n chatAgentHostFilter: '',\n chatAgentSort: 'status',\n chatAgentSortDir: 'asc',\n chatAgentGroupBy: '',\n chatAgentFiltersCollapsed: true,\n chatCollapsedChildren: [],\n feedObserveAll: false,\n coordinatorFollows: [],\n coordinatorMutes: [],\n chatStatusEvents: {},\n chatStickToBottom: true,\n chatHasNewItems: false,\n inboxReadCursors: {},\n inboxArchivedThreads: {},\n inboxDrafts: {},\n chatSending: false,\n inboxSearch: '',\n inboxSort: 'attention',\n inboxSortDir: 'desc',\n inboxShowArchived: false,\n channelFilter: '',\n compose: { ...DEFAULT_COMPOSE },\n inboxCompose: { ...DEFAULT_INBOX_COMPOSE },\n\n async fetchMessages() {\n try {\n const s = get()\n let path = '/messages?limit=100'\n if (s.view === 'messages' && s.selectedAgent) path += '&for=' + encodeURIComponent(s.selectedAgent)\n if (s.view === 'messages' && s.channelFilter) path += '&channel=' + encodeURIComponent(s.channelFilter)\n const messages = await api<Message[]>('GET', path)\n const merged = mergeFetchedMessages(get().messages, messages)\n set({ messages: merged })\n // Auto-read on the poll path too. The 5s poll frequently beats SSE\n // (and on mobile is often the *only* live path, since browsers suspend\n // SSE on a backgrounded tab) — so the SSE-side auto-read alone leaves\n // an unread badge on the very thread the user is reading. Advance the\n // cursor past the latest inbound message for the active visible thread.\n const after = get()\n const peer = after.selectedInboxThread\n if (isActiveVisibleChat(peer, after)) {\n let lastId = 0\n for (const m of merged) {\n if (m.id > lastId && inboxPeer(m) === peer && isHumanInboundMessage(m) && isBadgeWorthy(m)) lastId = m.id\n }\n if (lastId) get().markInboxThreadReadTo(peer, lastId)\n }\n } catch {}\n },\n async loadAgentChat(agentId) {\n if (!agentId) return\n touchChatAccess(agentId)\n const current = get().agentChatByAgent[agentId]\n if (current?.loading) return\n // A cached window renders instantly from its existing messages — only flip the loading flag\n // (which drives the spinner/placeholder) when there is nothing to show yet. The fetch still\n // runs and merges in the background so the cached view refreshes (#516).\n const hasCache = !!current?.loadedInitial && current.messages.length > 0\n if (!hasCache) {\n set((state) => ({\n agentChatByAgent: {\n ...state.agentChatByAgent,\n [agentId]: { ...(state.agentChatByAgent[agentId] ?? EMPTY_AGENT_CHAT_WINDOW), loading: true },\n },\n }))\n }\n try {\n const page = await api<Message[]>('GET', agentChatPath(agentId, { limit: AGENT_CHAT_PAGE_LIMIT }))\n const incoming = messagesForAgent(agentId, page)\n const latest = get().agentChatByAgent[agentId] ?? EMPTY_AGENT_CHAT_WINDOW\n const messages = latest.loadedInitial\n ? mergeAgentChatMessages(dropOptimisticOutbound(latest.messages, incoming), incoming)\n : mergeListById([], incoming)\n // On a fresh open the latest full page implies older messages exist. On a reopen-merge,\n // the latest page only covers the NEWEST 300 and says nothing about the old end we may\n // already have paged to — so preserve the window's hasMore instead of resetting it true,\n // which otherwise forced a needless loadOlder round-trip on every cached reopen (#519).\n const hasMore = latest.loadedInitial ? latest.hasMore : page.length === AGENT_CHAT_PAGE_LIMIT\n set((state) => ({\n agentChatByAgent: evictChatWindows({\n ...state.agentChatByAgent,\n [agentId]: {\n messages,\n oldestLoadedId: oldestLoadedId(messages),\n hasMore,\n loading: false,\n loadedInitial: true,\n },\n }, [agentId, get().selectedAgent, get().selectedInboxThread]),\n }))\n } catch (e) {\n set((state) => ({\n agentChatByAgent: {\n ...state.agentChatByAgent,\n [agentId]: { ...(state.agentChatByAgent[agentId] ?? EMPTY_AGENT_CHAT_WINDOW), loading: false },\n },\n }))\n get().showError('Chat Load Failed', (e as Error).message)\n }\n },\n async loadOlder(agentId) {\n if (!agentId) return\n const current = get().agentChatByAgent[agentId]\n if (!current?.loadedInitial) { await get().loadAgentChat(agentId); return }\n if (current.loading || !current.hasMore || current.oldestLoadedId === null) return\n set((state) => ({\n agentChatByAgent: {\n ...state.agentChatByAgent,\n [agentId]: { ...current, loading: true },\n },\n }))\n try {\n const page = await api<Message[]>('GET', agentChatPath(agentId, { before: current.oldestLoadedId, limit: AGENT_CHAT_PAGE_LIMIT }))\n const incoming = messagesForAgent(agentId, page)\n const latest = get().agentChatByAgent[agentId] ?? current\n const messages = mergeListById(latest.messages, [...incoming, ...latest.messages].sort((a, b) => a.id - b.id))\n set((state) => ({\n agentChatByAgent: {\n ...state.agentChatByAgent,\n [agentId]: {\n messages,\n oldestLoadedId: oldestLoadedId(messages),\n hasMore: page.length === AGENT_CHAT_PAGE_LIMIT,\n loading: false,\n loadedInitial: true,\n },\n },\n }))\n } catch (e) {\n set((state) => ({\n agentChatByAgent: {\n ...state.agentChatByAgent,\n [agentId]: { ...(state.agentChatByAgent[agentId] ?? current), loading: false },\n },\n }))\n get().showError('Chat Load Failed', (e as Error).message)\n }\n },\n async gapFillAgentChat(agentId) {\n if (!agentId) return\n let current = get().agentChatByAgent[agentId]\n if (!current?.loadedInitial || current.messages.length === 0) return\n let cursor = current.messages[current.messages.length - 1]!.id\n try {\n for (;;) {\n const since = cursor\n const page = await api<Message[]>('GET', agentChatPath(agentId, { since: cursor, limit: AGENT_CHAT_PAGE_LIMIT }))\n if (page.length === 0) return\n get().appendChatTail(agentId, page.filter((message) => message.id > since))\n cursor = page[page.length - 1]!.id\n if (cursor <= since) return\n if (page.length < AGENT_CHAT_PAGE_LIMIT) return\n current = get().agentChatByAgent[agentId]\n const newest = current?.messages[current.messages.length - 1]?.id\n if (newest && newest > cursor) cursor = newest\n }\n } catch (e) {\n // Gap-fill is a background reconnect refresh (connection.ts), not a user action.\n // The keyed agent may be offline/unknown/evicted by the time we re-fetch, so the\n // server answers \"Agent not found\". Degrade silently — never raise a blocking modal\n // that would tear down the open chat over a recoverable background miss (#540).\n console.warn(`[chat] gap-fill skipped for ${agentId}: ${(e as Error).message}`)\n }\n },\n appendChatTail(agentId, msgs) {\n if (!agentId || msgs.length === 0) return\n const incoming = messagesForAgent(agentId, msgs)\n if (incoming.length === 0) return\n const current = get().agentChatByAgent[agentId] ?? EMPTY_AGENT_CHAT_WINDOW\n const messages = mergeAgentChatMessages(dropOptimisticOutbound(current.messages, incoming), incoming)\n set({\n agentChatByAgent: {\n ...get().agentChatByAgent,\n [agentId]: {\n ...current,\n messages,\n oldestLoadedId: oldestLoadedId(messages),\n },\n },\n })\n },\n async fetchThreadHistory(peer) {\n if (!peer) return\n try {\n const history = await api<Message[]>('GET', '/messages?for=' + encodeURIComponent(peer) + '&limit=500')\n set({ threadHistory: { ...get().threadHistory, [peer]: history } })\n } catch {}\n },\n async fetchChatHistoryImports() {\n try {\n set({ chatHistoryImports: await api<ChatHistoryImport[]>('GET', '/chat/history-imports?limit=500') })\n } catch {}\n },\n async fetchInboxState() {\n try {\n const state = await api<InboxState>('GET', '/inbox/state?operatorId=' + encodeURIComponent('user'))\n const readCursors: Record<string, number> = {}\n const archivedThreads: Record<string, number> = {}\n const drafts: Record<string, string> = {}\n for (const t of state?.threads || []) {\n if (t.readCursorMessageId) readCursors[t.peerId] = t.readCursorMessageId\n if (t.archivedAtMessageId) archivedThreads[t.peerId] = t.archivedAtMessageId\n }\n for (const d of state?.drafts || []) { if (d.body) drafts[d.peerId] = d.body }\n const localDrafts = get().inboxDrafts\n const hasLocalDrafts = Object.keys(localDrafts).length > 0\n set({ inboxReadCursors: readCursors, inboxArchivedThreads: archivedThreads, ...(hasLocalDrafts ? {} : { inboxDrafts: drafts }) })\n } catch {}\n },\n\n openCompose() { set({ composeOpen: true, compose: { ...DEFAULT_COMPOSE, from: HUMAN_AGENT_ID } }) },\n openComposeToAgent(agent) {\n set({ replyTo: null, compose: { ...DEFAULT_COMPOSE, from: HUMAN_AGENT_ID, to: agent.id }, composeOpen: true })\n },\n async doSend() {\n const s = get()\n if (!s.compose.from || !s.compose.to || !s.compose.body) { get().showError('Validation', 'From, To, and Message are required.'); return }\n try {\n const payload: Record<string, unknown> = { from: s.compose.from, to: s.compose.to, body: s.compose.body }\n if (s.compose.channel) payload.channel = s.compose.channel\n if (s.compose.subject) payload.subject = s.compose.subject\n if (s.replyTo) payload.replyTo = s.replyTo.id\n if (s.compose.claimable) { payload.claimable = true; payload.kind = 'task'; payload.payload = { title: s.compose.subject || 'Claimable task' } }\n await api('POST', '/messages', payload)\n set({ composeOpen: false, replyTo: null, compose: { ...DEFAULT_COMPOSE, from: HUMAN_AGENT_ID } })\n await get().fetchMessages()\n } catch (e) { get().showError('Send Failed', (e as Error).message) }\n },\n async doClaim(msgId) {\n const s = get()\n const agentId = s.compose.from || s.selectedAgent\n if (!agentId) { get().showError('Validation', 'Select an agent first.'); return }\n try {\n const result = await api<{ ok: boolean; error?: string }>('POST', '/messages/' + msgId + '/claim', { agentId })\n if (!result.ok) get().showError('Claim Failed', result.error || 'unknown')\n if (result.ok) await Promise.all([get().fetchMessages(), get().fetchTasks()])\n } catch (e) { get().showError('Claim Failed', (e as Error).message) }\n },\n async doDeliveryAction(msgId, action, reason) {\n try {\n await api('POST', '/messages/' + msgId + '/delivery/actions', { action, reason, agentId: get().compose.from || get().selectedAgent || HUMAN_AGENT_ID })\n await get().fetchMessages()\n } catch (e) { get().showError('Delivery Update Failed', (e as Error).message) }\n },\n async doDeleteMessage(id) {\n try {\n await api('DELETE', '/messages/' + id)\n set({ messages: get().messages.filter((m) => m.id !== id) })\n } catch (e) { get().showError('Delete Failed', (e as Error).message) }\n },\n // Apply an in-place update to a message (reactions, delivery status, claim) inside the\n // per-agent chat window so the open bubble reflects it immediately — the chat view reads\n // from agentChatByAgent, not the global `messages` feed. Only touches windows that already\n // hold the message (no new window for a never-opened conversation).\n applyChatMessageUpdate(updated) {\n const byAgent = get().agentChatByAgent\n const patches: typeof byAgent = {}\n for (const [agentId, win] of Object.entries(byAgent)) {\n if (win.messages.some((m) => m.id === updated.id)) {\n patches[agentId] = { ...win, messages: win.messages.map((m) => (m.id === updated.id ? updated : m)) }\n }\n }\n if (Object.keys(patches).length === 0) return\n set({ agentChatByAgent: { ...byAgent, ...patches } })\n },\n async reactToMessage(msg, emoji) {\n try {\n const mine = (msg.reactions || []).some((reaction) => reaction.actorId === HUMAN_AGENT_ID && reaction.emoji === emoji)\n const updated = await api<Message>('POST', '/messages/' + msg.id + '/reactions', {\n actorId: HUMAN_AGENT_ID,\n emoji,\n action: mine ? 'remove' : 'add',\n })\n set({ messages: upsertById(get().messages, updated) })\n get().applyChatMessageUpdate(updated)\n } catch (e) { get().showError('Reaction Failed', (e as Error).message) }\n },\n startReply(msg) {\n const target = msg.from === HUMAN_AGENT_ID ? msg.to : msg.from\n set({ replyTo: { id: msg.id, from: msg.from }, compose: { ...DEFAULT_COMPOSE, from: HUMAN_AGENT_ID, to: target, channel: msg.channel || '' }, composeOpen: true })\n },\n cancelReply() { set({ replyTo: null }) },\n async openThread(threadId) {\n set({ threadMessages: [], threadOpen: true })\n try { set({ threadMessages: await api('GET', '/messages/' + threadId + '/thread') }) }\n catch (e) { get().showError('Thread Load Failed', (e as Error).message) }\n },\n\n openInboxThread(peer, messages = []) {\n set({ selectedInboxThread: peer })\n if (messages.length) get().markInboxThreadRead(peer, messages)\n void get().loadAgentChat(peer)\n },\n async sendChatMessage(body, thread, attachments = []) {\n if ((!body.trim() && attachments.length === 0) || get().chatSending) return\n const peer = thread.peer\n set({ chatSending: true })\n\n // (B) Optimistic draft clear — save and clear BEFORE the await so the composer empties\n // immediately. On failure the saved value is restored so unfailed text isn't lost.\n const savedDraft = get().inboxDrafts[peer]\n const nextDrafts = { ...get().inboxDrafts }\n delete nextDrafts[peer]\n set({ inboxDrafts: nextDrafts })\n\n // (A) Optimistic message insert — appears instantly; reconciled when POST resolves.\n // appendChatTail's hasRealOutbound guard (SSE path) will drop this negative-id placeholder\n // when the server echo arrives, preventing double-render.\n const optimisticId = -Date.now()\n const optimisticMsg: Message = {\n id: optimisticId,\n from: HUMAN_AGENT_ID,\n to: peer,\n kind: 'chat',\n body: body.trim() || 'Attachment',\n ...(thread.lastMessage?.channel ? { channel: thread.lastMessage.channel } : {}),\n payload: attachments.length ? { attachments } : {},\n readBy: [HUMAN_AGENT_ID],\n createdAt: Date.now(),\n }\n // Insert directly (not via appendChatTail) to bypass the messagesForAgent filter and keep\n // the message at the tail without disrupting the sort order of real messages.\n set((state) => {\n const win = state.agentChatByAgent[peer] ?? EMPTY_AGENT_CHAT_WINDOW\n const messages = [...win.messages, optimisticMsg]\n return { agentChatByAgent: { ...state.agentChatByAgent, [peer]: { ...win, messages, oldestLoadedId: oldestLoadedId(messages) } } }\n })\n\n try {\n const peerAgent = get().agentsById[peer]\n const useLiveSession = peerAgent?.providerCapabilities?.liveSession?.inject\n let sentMsg: Message | null = null\n if (useLiveSession) {\n await api('POST', '/agents/' + encodeURIComponent(peer) + '/prompt', { body: body.trim() || 'Attachment', ...(attachments.length ? { attachments } : {}) })\n } else {\n const payload: Record<string, unknown> = { from: HUMAN_AGENT_ID, to: peer, body: body.trim() || 'Attachment' }\n if (thread.lastMessage?.channel) payload.channel = thread.lastMessage.channel\n if (thread.lastMessage?.id) payload.replyTo = thread.lastMessage.id\n if (attachments.length) payload.payload = { attachments }\n sentMsg = await api<Message>('POST', '/messages', payload)\n }\n\n // Reconcile optimistic: replace with real message, or drop if SSE already delivered it.\n set((state) => {\n const win = state.agentChatByAgent[peer]\n if (!win) return {}\n const withoutOptimistic = win.messages.filter((m) => m.id !== optimisticId)\n if (sentMsg) {\n const alreadyPresent = withoutOptimistic.some((m) => m.id === sentMsg!.id)\n const messages = alreadyPresent ? withoutOptimistic : mergeAgentChatMessages(withoutOptimistic, [sentMsg])\n return { agentChatByAgent: { ...state.agentChatByAgent, [peer]: { ...win, messages, oldestLoadedId: oldestLoadedId(messages) } } }\n }\n // liveSession: drop optimistic, SSE will deliver the real message\n return { agentChatByAgent: { ...state.agentChatByAgent, [peer]: { ...win, messages: withoutOptimistic, oldestLoadedId: oldestLoadedId(withoutOptimistic) } } }\n })\n\n api('DELETE', '/inbox/drafts?operatorId=' + encodeURIComponent(INBOX_OPERATOR_ID) + '&peerId=' + encodeURIComponent(peer)).catch(() => {})\n // (A2) Fire-and-forget — do not gate chatSending:false on the refetch\n void Promise.all([get().fetchMessages(), get().loadAgentChat(peer)])\n } catch (e) {\n // Restore draft only if the user hasn't typed new text since the optimistic clear (B).\n // If inboxDrafts[peer] is now non-empty, new content was typed — don't clobber it.\n if (savedDraft !== undefined) {\n set((state) => {\n if (state.inboxDrafts[peer] !== undefined) return {}\n return { inboxDrafts: { ...state.inboxDrafts, [peer]: savedDraft } }\n })\n }\n // Remove optimistic message so it doesn't linger on failure\n set((state) => {\n const win = state.agentChatByAgent[peer]\n if (!win) return {}\n const messages = win.messages.filter((m) => m.id !== optimisticId)\n return { agentChatByAgent: { ...state.agentChatByAgent, [peer]: { ...win, messages, oldestLoadedId: oldestLoadedId(messages) } } }\n })\n get().showError('Send Failed', (e as Error).message)\n }\n set({ chatSending: false })\n },\n async sendPermissionDecision(agentId, approvalId, decision, answers) {\n try {\n await api('POST', '/agents/' + encodeURIComponent(agentId) + '/permission-decision', { approvalId, decision, ...(answers ? { answers } : {}) })\n const label = decision === 'approve' ? 'Approved' : decision === 'approve-session' ? 'Approved for session' : decision === 'abort' ? 'Aborted' : decision === 'answer' ? 'Answer sent for' : 'Denied'\n get().showNotification(`${label} ${decision === 'answer' ? \"Claude's question\" : 'permission request'}`)\n await get().fetchAgents()\n } catch (e) { get().showError('Permission Failed', (e as Error).message) }\n },\n markInboxThreadReadTo(peer, lastId) {\n if (!peer || lastId <= (get().inboxReadCursors[peer] || 0)) return\n set({ inboxReadCursors: { ...get().inboxReadCursors, [peer]: lastId } })\n api('PATCH', '/inbox/threads', { operatorId: INBOX_OPERATOR_ID, peerId: peer, readCursorMessageId: lastId }).catch(() => {})\n },\n markInboxThreadRead(peer, messages) {\n if (!peer || !messages.length) return\n get().markInboxThreadReadTo(peer, lastInboundMessageId(messages))\n },\n markInboxThreadUnread(peer) {\n const next = { ...get().inboxReadCursors }\n delete next[peer]\n set({ inboxReadCursors: next })\n api('PATCH', '/inbox/threads', { operatorId: INBOX_OPERATOR_ID, peerId: peer, readCursorMessageId: null }).catch(() => {})\n },\n archiveInboxThread(peer, lastMessageId) {\n set({ inboxArchivedThreads: { ...get().inboxArchivedThreads, [peer]: lastMessageId } })\n if (get().selectedInboxThread === peer) set({ selectedInboxThread: '' })\n api('PATCH', '/inbox/threads', { operatorId: INBOX_OPERATOR_ID, peerId: peer, archivedAtMessageId: lastMessageId }).catch(() => {})\n },\n unarchiveInboxThread(peer) {\n const next = { ...get().inboxArchivedThreads }\n delete next[peer]\n set({ inboxArchivedThreads: next })\n api('PATCH', '/inbox/threads', { operatorId: INBOX_OPERATOR_ID, peerId: peer, archivedAtMessageId: null }).catch(() => {})\n },\n setReplyDraft(peer, value) {\n const next = { ...get().inboxDrafts }\n if (value) next[peer] = value\n else delete next[peer]\n set({ inboxDrafts: next })\n if (value) api('PUT', '/inbox/drafts', { operatorId: INBOX_OPERATOR_ID, peerId: peer, body: value }).catch(() => {})\n else api('DELETE', '/inbox/drafts?operatorId=' + encodeURIComponent(INBOX_OPERATOR_ID) + '&peerId=' + encodeURIComponent(peer)).catch(() => {})\n },\n async sendInboxReply(peer, lastMessage) {\n const body = (get().inboxDrafts[peer] || '').trim()\n if (!body || get().chatSending) { if (!body) get().showError('Validation', 'Reply body is required.'); return }\n set({ chatSending: true })\n try {\n const payload: Record<string, unknown> = { from: HUMAN_AGENT_ID, to: peer, body }\n if (lastMessage?.channel) payload.channel = lastMessage.channel\n if (lastMessage?.id) payload.replyTo = lastMessage.id\n await api('POST', '/messages', payload)\n const next = { ...get().inboxDrafts }\n delete next[peer]\n set({ inboxDrafts: next })\n api('DELETE', '/inbox/drafts?operatorId=' + encodeURIComponent(INBOX_OPERATOR_ID) + '&peerId=' + encodeURIComponent(peer)).catch(() => {})\n await get().fetchMessages()\n } catch (e) { get().showError('Reply Failed', (e as Error).message) }\n set({ chatSending: false })\n },\n // Fold/unfold a parent's spawned-children group in the conversations list (#612).\n // Persisted via partialize so the fold survives a reload, like the other chat prefs.\n toggleChatChildrenCollapsed(parentId) {\n const current = get().chatCollapsedChildren\n set({\n chatCollapsedChildren: current.includes(parentId)\n ? current.filter((id) => id !== parentId)\n : [...current, parentId],\n })\n },\n setFeedObserveAll(all) { set({ feedObserveAll: all }) },\n toggleCoordinatorFollow(agentId) {\n const follows = get().coordinatorFollows\n const mutes = get().coordinatorMutes\n if (follows.includes(agentId)) {\n set({ coordinatorFollows: follows.filter((id) => id !== agentId) })\n } else {\n set({ coordinatorFollows: [...follows, agentId], coordinatorMutes: mutes.filter((id) => id !== agentId) })\n }\n },\n toggleCoordinatorMute(agentId) {\n const mutes = get().coordinatorMutes\n const follows = get().coordinatorFollows\n if (mutes.includes(agentId)) {\n set({ coordinatorMutes: mutes.filter((id) => id !== agentId) })\n } else {\n set({ coordinatorMutes: [...mutes, agentId], coordinatorFollows: follows.filter((id) => id !== agentId) })\n }\n },\n async doSendInboxCompose() {\n const s = get()\n const ic = s.inboxCompose\n let target = ic.to\n if (!target) return\n if (ic.toMode === 'tag') target = 'tag:' + target\n else if (ic.toMode === 'cap') target = 'cap:' + target\n if (!ic.body) { get().showError('Validation', 'Message is required.'); return }\n try {\n const payload: Record<string, unknown> = { from: HUMAN_AGENT_ID, to: target, body: ic.body }\n if (ic.channel) payload.channel = ic.channel\n if (ic.subject) payload.subject = ic.subject\n if (ic.claimable) { payload.claimable = true; payload.kind = 'task'; payload.payload = { title: ic.subject || 'Claimable task' } }\n await api('POST', '/messages', payload)\n set({ inboxCompose: { ...DEFAULT_INBOX_COMPOSE, toMode: ic.toMode, to: ic.to } })\n await get().fetchMessages()\n } catch (e) { get().showError('Send Failed', (e as Error).message) }\n },\n})\n","import { api } from '@/lib/api'\nimport type { Plan } from '@/types'\nimport type { PlansSlice, StoreSlice } from '../types'\n\nexport function planLookupKey(threadId: string | number, channel?: string): string {\n return JSON.stringify([String(threadId), channel || ''])\n}\n\nexport function planTeamLookupKey(teamId: string, channel?: string): string {\n return JSON.stringify(['team', teamId, channel || ''])\n}\n\nfunction planLookup(plan: Plan): { threadId: string; channel?: string } {\n return { threadId: String(plan.threadId), ...(plan.channel ? { channel: plan.channel } : {}) }\n}\n\nfunction planTeamLookup(plan: Plan): { teamId: string; channel?: string } | null {\n return plan.teamId ? { teamId: plan.teamId, ...(plan.channel ? { channel: plan.channel } : {}) } : null\n}\n\nfunction lookupKey(input: { threadId: string; channel?: string } | { teamId: string; channel?: string }): string {\n return 'teamId' in input ? planTeamLookupKey(input.teamId, input.channel) : planLookupKey(input.threadId, input.channel)\n}\n\nfunction planPath(input: { threadId: string | number; channel?: string } | { teamId: string; channel?: string }): string {\n const search = new URLSearchParams('teamId' in input ? { teamId: input.teamId } : { threadId: String(input.threadId) })\n if (input.channel) search.set('channel', input.channel)\n return `/plans?${search.toString()}`\n}\n\nexport const createPlansSlice: StoreSlice<PlansSlice> = (set, get) => ({\n plansById: {},\n plansByThread: {},\n plansByTeam: {},\n planLookups: {},\n planLoading: {},\n planCollapsed: null,\n sessionTasksCollapsed: false,\n\n setPlanCollapsed(collapsed) {\n set({ planCollapsed: collapsed })\n },\n\n setSessionTasksCollapsed(collapsed) {\n set({ sessionTasksCollapsed: collapsed })\n },\n\n async fetchPlan(input) {\n const lookup = 'teamId' in input\n ? { teamId: input.teamId, ...(input.channel ? { channel: input.channel } : {}) }\n : { threadId: String(input.threadId), ...(input.channel ? { channel: input.channel } : {}) }\n const key = lookupKey(lookup)\n set({\n planLookups: { ...get().planLookups, [key]: lookup },\n planLoading: { ...get().planLoading, [key]: true },\n })\n try {\n const plan = await api<Plan>('GET', planPath(lookup))\n const nextLookup = planLookup(plan)\n const nextKey = planLookupKey(nextLookup.threadId, nextLookup.channel)\n const nextTeamLookup = planTeamLookup(plan)\n const nextTeamKey = nextTeamLookup ? planTeamLookupKey(nextTeamLookup.teamId, nextTeamLookup.channel) : ''\n const requestedThread = !('teamId' in lookup)\n const requestedTeam = 'teamId' in lookup\n set({\n plansById: { ...get().plansById, [plan.id]: plan },\n plansByThread: { ...get().plansByThread, ...(requestedThread ? { [key]: plan } : {}), [nextKey]: plan },\n plansByTeam: nextTeamLookup ? { ...get().plansByTeam, ...(requestedTeam ? { [key]: plan } : {}), [nextTeamKey]: plan } : get().plansByTeam,\n planLookups: {\n ...get().planLookups,\n [key]: lookup,\n [nextKey]: nextLookup,\n ...(nextTeamLookup ? { [nextTeamKey]: nextTeamLookup } : {}),\n },\n planLoading: { ...get().planLoading, [key]: false, [nextKey]: false, ...(nextTeamLookup ? { [nextTeamKey]: false } : {}) },\n })\n void get().hydrateIssueRefsForPlan(plan)\n } catch (e) {\n const status = typeof (e as { status?: unknown }).status === 'number' ? (e as { status: number }).status : 0\n set({\n plansByThread: status === 404 && !('teamId' in lookup) ? { ...get().plansByThread, [key]: null } : get().plansByThread,\n plansByTeam: status === 404 && 'teamId' in lookup ? { ...get().plansByTeam, [key]: null } : get().plansByTeam,\n planLoading: { ...get().planLoading, [key]: false },\n })\n if (status && status !== 404) get().showError('Plan Load Failed', (e as Error).message)\n }\n },\n\n async refreshPlans() {\n const lookups = Object.values(get().planLookups)\n await Promise.all(lookups.map((lookup) => get().fetchPlan(lookup)))\n },\n\n async setPlanArchived(id, archived) {\n try {\n const plan = await api<Plan>('PATCH', `/plans/${encodeURIComponent(id)}/archive`, { archived })\n get().upsertPlan(plan)\n } catch (e) {\n get().showError(archived ? 'Plan Archive Failed' : 'Plan Unarchive Failed', (e as Error).message)\n }\n },\n\n upsertPlan(plan) {\n const lookup = planLookup(plan)\n const key = planLookupKey(lookup.threadId, lookup.channel)\n const teamLookup = planTeamLookup(plan)\n const teamKey = teamLookup ? planTeamLookupKey(teamLookup.teamId, teamLookup.channel) : ''\n set({\n plansById: { ...get().plansById, [plan.id]: plan },\n plansByThread: { ...get().plansByThread, [key]: plan },\n plansByTeam: teamLookup ? { ...get().plansByTeam, [teamKey]: plan } : get().plansByTeam,\n planLookups: { ...get().planLookups, [key]: lookup, ...(teamLookup ? { [teamKey]: teamLookup } : {}) },\n planLoading: { ...get().planLoading, [key]: false, ...(teamLookup ? { [teamKey]: false } : {}) },\n })\n void get().hydrateIssueRefsForPlan(plan)\n },\n\n removePlan(planOrId) {\n const id = typeof planOrId === 'string' ? planOrId : planOrId.id\n const plansById = { ...get().plansById }\n delete plansById[id]\n const plansByThread = Object.fromEntries(Object.entries(get().plansByThread).filter(([, plan]) => plan?.id !== id))\n const plansByTeam = Object.fromEntries(Object.entries(get().plansByTeam).filter(([, plan]) => plan?.id !== id))\n const planLookups = Object.fromEntries(Object.entries(get().planLookups).filter(([key]) => (\n get().plansByThread[key]?.id !== id && get().plansByTeam[key]?.id !== id\n )))\n set({ plansById, plansByThread, plansByTeam, planLookups })\n },\n})\n","import { errMessage } from 'agent-relay-sdk/types'\nimport { api } from '@/lib/api'\nimport type { Team } from '@/types'\nimport { mergeListById, upsertById } from '../merge'\nimport type { StoreSlice, TeamOutcomeAggregates, TeamsSlice } from '../types'\n\ninterface TeamsResponse {\n teams: Team[]\n}\n\nexport const createTeamsSlice: StoreSlice<TeamsSlice> = (set, get) => ({\n teams: [],\n teamsLoading: false,\n teamsError: null,\n teamOutcomeAggregates: null,\n teamOutcomeAggregatesLoading: false,\n teamOutcomeAggregatesError: null,\n\n async fetchTeams() {\n set({ teamsLoading: true, teamsError: null })\n try {\n const payload = await api<TeamsResponse>('GET', '/teams')\n const teams = mergeListById(get().teams, payload.teams)\n set({ teams, teamsLoading: false, teamsError: null })\n return teams\n } catch (e) {\n set({ teamsLoading: false, teamsError: errMessage(e) })\n return get().teams\n }\n },\n\n async fetchTeamOutcomeAggregates() {\n set({ teamOutcomeAggregatesLoading: true, teamOutcomeAggregatesError: null })\n try {\n const payload = await api<TeamOutcomeAggregates>('GET', '/teams/outcomes')\n set({ teamOutcomeAggregates: payload, teamOutcomeAggregatesLoading: false, teamOutcomeAggregatesError: null })\n return payload\n } catch (e) {\n set({ teamOutcomeAggregatesLoading: false, teamOutcomeAggregatesError: errMessage(e) })\n return get().teamOutcomeAggregates\n }\n },\n\n upsertTeam(team) {\n const teams = upsertById(get().teams, team)\n if (teams !== get().teams) set({ teams })\n },\n\n markTeamDissolved(input) {\n const now = Date.now()\n set({\n teams: get().teams.map((team) => team.id === input.teamId\n ? {\n ...team,\n status: 'dissolved',\n updatedAt: now,\n dissolvedAt: team.dissolvedAt ?? now,\n zeroMembersSince: undefined,\n ...(input.outcomeStatus ? { outcomeStatus: input.outcomeStatus } : {}),\n ...(input.reason ? { outcomeSummary: input.reason } : {}),\n }\n : team),\n })\n },\n\n async dissolveTeam(teamId) {\n try {\n const team = await api<Team>('POST', `/teams/${encodeURIComponent(teamId)}/dissolve`, {})\n get().upsertTeam(team)\n get().showNotification('Team dissolved')\n await Promise.all([get().fetchTeams(), get().fetchTeamOutcomeAggregates(), get().fetchActivityEvents()])\n } catch (e) {\n get().showError('Dissolve Failed', errMessage(e))\n }\n },\n})\n","import { errMessage } from 'agent-relay-sdk/types'\nimport { api } from '@/lib/api'\nimport type { Plan } from '@/types'\nimport type { AgentIssueActivitySummary, IssueAssociationEntityType, IssueAssociationIssue, IssueRef } from '@/lib/issue-types'\nimport type { IssuesSlice, StoreSlice } from '../types'\n\ninterface IssueRefResponse {\n issueRef: IssueRef\n}\n\ninterface EntityIssuesResponse {\n issues: IssueAssociationIssue[]\n}\n\ninterface AgentIssueActivityResponse {\n agentId: string\n issues: AgentIssueActivitySummary[]\n}\n\nexport function issueEntityKey(entityType: IssueAssociationEntityType, entityId: string): string {\n return JSON.stringify([entityType, entityId])\n}\n\nfunction planIssueRefIds(plan: Plan): string[] {\n return [...new Set(plan.nodes.map((node) => node.refs?.issueId).filter((id): id is string => Boolean(id)))]\n}\n\nfunction upsertIssueRefs(current: Record<string, IssueRef>, issueRefs: IssueRef[]): Record<string, IssueRef> {\n let changed = false\n const next = { ...current }\n for (const issueRef of issueRefs) {\n const existing = current[issueRef.id]\n if (!existing || JSON.stringify(existing) !== JSON.stringify(issueRef)) {\n next[issueRef.id] = issueRef\n changed = true\n }\n }\n return changed ? next : current\n}\n\nexport const createIssuesSlice: StoreSlice<IssuesSlice> = (set, get) => ({\n issueRefsById: {},\n issuesByEntity: {},\n issueActivityByAgent: {},\n issueLoading: {},\n issueErrors: {},\n\n async fetchAllIssueRefs() {\n try {\n const payload = await api<{ issueRefs: IssueRef[] }>('GET', '/issue-refs?limit=500')\n set({ issueRefsById: upsertIssueRefs(get().issueRefsById, payload.issueRefs) })\n } catch {}\n },\n\n async fetchIssueRef(id) {\n const key = `ref:${id}`\n set({ issueLoading: { ...get().issueLoading, [key]: true }, issueErrors: { ...get().issueErrors, [key]: null } })\n try {\n const payload = await api<IssueRefResponse>('GET', `/issue-refs/${encodeURIComponent(id)}`)\n set({\n issueRefsById: upsertIssueRefs(get().issueRefsById, [payload.issueRef]),\n issueLoading: { ...get().issueLoading, [key]: false },\n })\n return payload.issueRef\n } catch (e) {\n set({ issueLoading: { ...get().issueLoading, [key]: false }, issueErrors: { ...get().issueErrors, [key]: errMessage(e) } })\n return get().issueRefsById[id] ?? null\n }\n },\n\n async fetchIssuesForEntity(entityType, entityId) {\n const key = issueEntityKey(entityType, entityId)\n set({ issueLoading: { ...get().issueLoading, [key]: true }, issueErrors: { ...get().issueErrors, [key]: null } })\n try {\n const payload = await api<EntityIssuesResponse>('GET', `/issue-associations/entities/${encodeURIComponent(entityType)}/${encodeURIComponent(entityId)}/issues`)\n set({\n issuesByEntity: { ...get().issuesByEntity, [key]: payload.issues },\n issueRefsById: upsertIssueRefs(get().issueRefsById, payload.issues.map((item) => item.issueRef)),\n issueLoading: { ...get().issueLoading, [key]: false },\n })\n return payload.issues\n } catch (e) {\n set({ issueLoading: { ...get().issueLoading, [key]: false }, issueErrors: { ...get().issueErrors, [key]: errMessage(e) } })\n return get().issuesByEntity[key] ?? []\n }\n },\n\n async fetchAgentIssueActivity(agentId) {\n const key = `agent:${agentId}`\n set({ issueLoading: { ...get().issueLoading, [key]: true }, issueErrors: { ...get().issueErrors, [key]: null } })\n try {\n const payload = await api<AgentIssueActivityResponse>('GET', `/issue-activity/agents/${encodeURIComponent(agentId)}`)\n set({\n issueActivityByAgent: { ...get().issueActivityByAgent, [agentId]: payload.issues },\n issueRefsById: upsertIssueRefs(get().issueRefsById, payload.issues.map((item) => item.issueRef)),\n issueLoading: { ...get().issueLoading, [key]: false },\n })\n return payload.issues\n } catch (e) {\n set({ issueLoading: { ...get().issueLoading, [key]: false }, issueErrors: { ...get().issueErrors, [key]: errMessage(e) } })\n return get().issueActivityByAgent[agentId] ?? []\n }\n },\n\n async hydrateIssueRefsForPlan(plan) {\n const missing = planIssueRefIds(plan).filter((id) => !get().issueRefsById[id])\n await Promise.all(missing.map((id) => get().fetchIssueRef(id)))\n },\n\n async refreshIssueRefs() {\n const refs = Object.keys(get().issueRefsById).map((id) => get().fetchIssueRef(id))\n const entities = Object.keys(get().issuesByEntity).map((key) => {\n try {\n const [entityType, entityId] = JSON.parse(key) as [IssueAssociationEntityType, string]\n return get().fetchIssuesForEntity(entityType, entityId)\n } catch {\n return Promise.resolve([])\n }\n })\n await Promise.all([...refs, ...entities])\n },\n\n upsertIssueRef(issueRef) {\n const issueRefsById = upsertIssueRefs(get().issueRefsById, [issueRef])\n if (issueRefsById !== get().issueRefsById) set({ issueRefsById })\n },\n})\n","import { api } from '@/lib/api'\nimport { DEFAULT_PAIR_MESSAGE, DEFAULT_PAIR_INVITE } from '@/lib/constants'\nimport type { Memory, PairSession, Task, TaskDetail, TaskMemory, TaskEvent } from '@/types'\nimport { mergeListById, upsertById } from '../merge'\nimport type { StoreSlice, TaskCreateForm, TaskDispatchResult, WorkSlice } from '../types'\n\nconst DEFAULT_TASK_CREATE_FORM: TaskCreateForm = {\n title: '',\n intent: '',\n doneWhen: '',\n issueRefs: '',\n dependsOn: '',\n dispatchComplexity: '',\n dispatchType: '',\n dispatchFootprint: '',\n dispatchCriticality: '',\n}\n\nexport const createWorkSlice: StoreSlice<WorkSlice> = (set, get) => ({\n pairs: [],\n tasks: [],\n taskEvents: [],\n taskEventCache: {},\n taskEventsOpen: false,\n pairInviteOpen: false,\n pairMessageOpen: false,\n pairInvite: { ...DEFAULT_PAIR_INVITE },\n deliverableTasks: [],\n deliverableStatusFilter: '',\n deliverableStageFilter: '',\n deliverableProjectFilter: '',\n taskDetailById: {},\n taskMemoryById: {},\n taskHistoryById: {},\n taskDetailOpen: false,\n taskDetailId: null,\n taskCreateOpen: false,\n taskCreateForm: { ...DEFAULT_TASK_CREATE_FORM },\n taskDispatchResultById: {},\n taskDispatchLoadingId: null,\n pairMessage: { ...DEFAULT_PAIR_MESSAGE },\n pairStatusFilter: 'open',\n taskStatusFilter: '',\n taskSourceFilter: '',\n\n async fetchPairs() {\n try {\n const s = get()\n let pairs: PairSession[]\n if (s.view === 'activity') {\n pairs = await api('GET', '/pairs')\n } else if (s.pairStatusFilter === 'open') {\n const [active, pending] = await Promise.all([\n api<PairSession[]>('GET', '/pairs?status=active'),\n api<PairSession[]>('GET', '/pairs?status=pending'),\n ])\n pairs = [...active, ...pending]\n } else if (s.pairStatusFilter) {\n pairs = await api('GET', '/pairs?status=' + encodeURIComponent(s.pairStatusFilter))\n } else {\n pairs = await api('GET', '/pairs')\n }\n const sorted = pairs.sort((a, b) => new Date(b.updatedAt as string).getTime() - new Date(a.updatedAt as string).getTime())\n const merged = mergeListById(get().pairs, sorted)\n if (merged !== get().pairs) set({ pairs: merged })\n } catch {}\n },\n async fetchTasks() {\n try {\n const s = get()\n const params = new URLSearchParams({ limit: '100' })\n if (s.taskStatusFilter) params.set('status', s.taskStatusFilter)\n if (s.taskSourceFilter) params.set('source', s.taskSourceFilter)\n const tasks = mergeListById(get().tasks, await api<Task[]>('GET', '/tasks?' + params.toString()))\n if (tasks !== get().tasks) set({ tasks })\n } catch {}\n },\n async fetchDeliverableTasks(opts) {\n try {\n const s = get()\n const params = new URLSearchParams({ kind: 'deliverable', limit: '200' })\n if (!opts?.unfiltered) {\n if (s.deliverableStatusFilter) params.set('status', s.deliverableStatusFilter)\n if (s.deliverableStageFilter) params.set('deliverableStage', s.deliverableStageFilter)\n if (s.deliverableProjectFilter) params.set('projectId', s.deliverableProjectFilter)\n }\n const incoming = await api<Task[]>('GET', '/tasks?' + params.toString())\n const next = mergeListById(get().deliverableTasks, incoming)\n if (next !== get().deliverableTasks) set({ deliverableTasks: next })\n } catch {}\n },\n async fetchTaskDetail(id) {\n try {\n const detail = await api<TaskDetail>('GET', '/tasks/' + id + '/detail')\n set({ taskDetailById: { ...get().taskDetailById, [id]: detail } })\n } catch {}\n },\n async fetchTaskMemory(id) {\n try {\n const memory = await api<TaskMemory>('GET', '/tasks/' + id + '/memory')\n set({ taskMemoryById: { ...get().taskMemoryById, [id]: memory } })\n } catch {}\n },\n async fetchTaskHistory(id) {\n try {\n const history = await api<TaskEvent[]>('GET', '/tasks/' + id + '/history')\n set({ taskHistoryById: { ...get().taskHistoryById, [id]: history } })\n } catch {}\n },\n openTaskDetail(task) {\n set({ taskDetailOpen: true, taskDetailId: task.id })\n void get().fetchTaskDetail(task.id)\n void get().fetchTaskMemory(task.id)\n void get().fetchTaskHistory(task.id)\n },\n openTaskDetailById(id) {\n set({ taskDetailOpen: true, taskDetailId: id })\n void get().fetchTaskDetail(id)\n void get().fetchTaskMemory(id)\n void get().fetchTaskHistory(id)\n },\n closeTaskDetail() {\n set({ taskDetailOpen: false, taskDetailId: null })\n },\n async doClaimTask(taskId) {\n const s = get()\n const agentId = s.compose.from || s.selectedAgent\n if (!agentId) { get().showError('Validation', 'Select an agent first.'); return }\n try {\n const result = await api<{ ok: boolean; error?: string }>('POST', '/tasks/' + taskId + '/claim', { agentId })\n if (!result.ok) get().showError('Claim Failed', result.error || 'unknown')\n else await Promise.all([get().fetchTasks(), get().fetchMessages()])\n } catch (e) { get().showError('Claim Failed', (e as Error).message) }\n },\n async doUpdateTaskStatus(task, status) {\n if (!task || !status || status === task.status) return\n try {\n const body: Record<string, unknown> = { status }\n const agentId = task.claimedBy || get().compose.from || get().selectedAgent\n if (agentId) body.agentId = agentId\n const result = await api<{ task: Task; memory?: Memory; memoryEvent?: TaskEvent }>('PATCH', '/tasks/' + task.id + '/status', body)\n const updated = result.task || result\n set({ tasks: get().tasks.map((t) => t.id === task.id ? updated : t) })\n if (result.memoryEvent) {\n const cached = get().taskEventCache[task.id] || []\n set({\n taskEventCache: { ...get().taskEventCache, [task.id]: [...cached, result.memoryEvent] },\n ...(get().taskEventsOpen ? { taskEvents: [...get().taskEvents, result.memoryEvent] } : {}),\n })\n }\n if (result.memory) get().showNotification(`Memory captured: ${result.memory.title}`)\n } catch (e) { get().showError('Status Update Failed', (e as Error).message); await get().fetchTasks() }\n },\n async openTaskEvents(task) {\n set({ taskEvents: [], taskEventsOpen: true })\n try {\n const events = await api<TaskEvent[]>('GET', '/tasks/' + task.id + '/events')\n set({ taskEvents: events, taskEventCache: { ...get().taskEventCache, [task.id]: events } })\n } catch (e) { get().showError('Task Events Failed', (e as Error).message) }\n },\n\n openTaskCreate() { set({ taskCreateOpen: true, taskCreateForm: { ...DEFAULT_TASK_CREATE_FORM } }) },\n closeTaskCreate() { set({ taskCreateOpen: false }) },\n setTaskCreateForm(patch) { set({ taskCreateForm: { ...get().taskCreateForm, ...patch } }) },\n\n async doCreateTask() {\n const { taskCreateForm: f } = get()\n const title = f.title.trim()\n if (!title) { get().showError('Validation', 'Title is required.'); return }\n const intent = f.intent.trim() || title\n const dispatch: Record<string, string> = {}\n if (f.dispatchComplexity) dispatch.complexity = f.dispatchComplexity\n if (f.dispatchType) dispatch.type = f.dispatchType\n if (f.dispatchFootprint) dispatch.contextFootprint = f.dispatchFootprint\n if (f.dispatchCriticality) dispatch.criticality = f.dispatchCriticality\n const issueRefs = f.issueRefs.split(',').map((s) => s.trim()).filter(Boolean)\n const dependsOn = f.dependsOn.split(',').map((s) => Number(s.trim())).filter((n) => Number.isInteger(n) && n > 0)\n try {\n await api<Task>('POST', '/tasks', {\n title,\n intent,\n ...(f.doneWhen.trim() ? { doneWhen: f.doneWhen.trim() } : {}),\n ...(issueRefs.length ? { issueRefs } : {}),\n ...(dependsOn.length ? { dependsOn } : {}),\n ...(Object.keys(dispatch).length ? { dispatch } : {}),\n })\n set({ taskCreateOpen: false, taskCreateForm: { ...DEFAULT_TASK_CREATE_FORM } })\n await get().fetchDeliverableTasks()\n } catch (e) { get().showError('Create Task Failed', (e as Error).message) }\n },\n\n async doEditTask(id, patch) {\n try {\n const detail = await api<TaskDetail>('PATCH', '/tasks/' + id, patch)\n set({\n taskDetailById: { ...get().taskDetailById, [id]: detail },\n deliverableTasks: get().deliverableTasks.map((t) => t.id === id ? { ...t, ...detail } : t),\n })\n } catch (e) { get().showError('Edit Task Failed', (e as Error).message) }\n },\n\n async doDispatchTask(id, pin) {\n set({ taskDispatchLoadingId: id })\n try {\n const body: Record<string, unknown> = {}\n if (pin && Object.keys(pin).length) body.pin = pin\n const result = await api<TaskDispatchResult>('POST', '/tasks/' + id + '/dispatch', body)\n set({ taskDispatchResultById: { ...get().taskDispatchResultById, [id]: result }, taskDispatchLoadingId: null })\n } catch (e) {\n set({ taskDispatchLoadingId: null })\n get().showError('Dispatch Failed', (e as Error).message)\n }\n },\n\n async doSpawnWithTask(id, target) {\n const orchId = get().spawnOrchId\n try {\n const body: Record<string, unknown> = { provider: target.provider, taskId: id }\n if (target.model) body.model = target.model\n if (target.effort) body.effort = target.effort\n if (orchId) body.orchestratorId = orchId\n await api('POST', '/agents/spawn', body)\n get().showNotification(`Worker spawning for T${id} (${target.provider})`)\n } catch (e) { get().showError('Spawn Failed', (e as Error).message) }\n },\n\n openPairInvite(requesterId) { set({ pairInvite: { ...DEFAULT_PAIR_INVITE, requesterId: requesterId || get().selectedAgent || '' }, pairInviteOpen: true }) },\n closePairInvite() { set({ pairInviteOpen: false, pairInvite: { ...DEFAULT_PAIR_INVITE } }) },\n async doCreatePair() {\n const s = get()\n if (!s.pairInvite.requesterId || !s.pairInvite.targetId) { get().showError('Validation', 'Requester and Target are required.'); return }\n if (s.pairInvite.requesterId === s.pairInvite.targetId) { get().showError('Validation', 'Requester and Target must be different.'); return }\n try {\n const payload: Record<string, unknown> = { from: s.pairInvite.requesterId, target: s.pairInvite.targetId }\n if (s.pairInvite.objective) payload.objective = s.pairInvite.objective\n await api('POST', '/pairs', payload)\n set({ pairInviteOpen: false, pairInvite: { ...DEFAULT_PAIR_INVITE } })\n await Promise.all([get().fetchPairs(), get().fetchMessages()])\n } catch (e) { get().showError('Pair Invite Failed', (e as Error).message) }\n },\n openPairMessage(pair, fromId) {\n set({ pairMessage: { ...DEFAULT_PAIR_MESSAGE, pairId: pair.id, from: fromId || pair.requesterId }, pairMessageOpen: true })\n },\n closePairMessage() { set({ pairMessageOpen: false, pairMessage: { ...DEFAULT_PAIR_MESSAGE } }) },\n async doSendPairMessage() {\n const s = get()\n if (!s.pairMessage.pairId || !s.pairMessage.from || !s.pairMessage.body) { get().showError('Validation', 'Pair, From, and Message are required.'); return }\n try {\n const payload: Record<string, unknown> = { from: s.pairMessage.from, body: s.pairMessage.body }\n if (s.pairMessage.subject) payload.subject = s.pairMessage.subject\n await api('POST', '/pairs/' + encodeURIComponent(s.pairMessage.pairId) + '/messages', payload)\n set({ pairMessageOpen: false, pairMessage: { ...DEFAULT_PAIR_MESSAGE } })\n await Promise.all([get().fetchPairs(), get().fetchMessages()])\n } catch (e) { get().showError('Pair Message Failed', (e as Error).message) }\n },\n async doAcceptPair(pair) {\n try {\n await api('POST', '/pairs/' + encodeURIComponent(pair.id) + '/accept', { agentId: pair.targetId })\n await Promise.all([get().fetchPairs(), get().fetchMessages()])\n } catch (e) { get().showError('Accept Failed', (e as Error).message) }\n },\n async doRejectPair(pair) {\n try {\n await api('POST', '/pairs/' + encodeURIComponent(pair.id) + '/reject', { agentId: pair.targetId })\n await Promise.all([get().fetchPairs(), get().fetchMessages()])\n } catch (e) { get().showError('Reject Failed', (e as Error).message) }\n },\n async doHangupPair(pair, agentId) {\n try {\n await api('POST', '/pairs/' + encodeURIComponent(pair.id) + '/hangup', { agentId: agentId || pair.requesterId })\n await Promise.all([get().fetchPairs(), get().fetchMessages()])\n } catch (e) { get().showError('Hang Up Failed', (e as Error).message) }\n },\n})\n","import { api } from '@/lib/api'\nimport { HUMAN_AGENT_ID, INBOX_OPERATOR_ID } from '@/lib/constants'\nimport { messageBody, displayName, pathWithinBase } from '@/lib/display'\nimport { getProviderCatalog } from 'agent-relay-sdk/provider-catalog'\nimport type { ProviderCatalogEntry } from 'agent-relay-sdk/provider-catalog'\nimport { isRecord, stringValue } from 'agent-relay-sdk/types'\nimport type { Orchestrator, SpawnProvider } from '@/types'\nimport { firstAvailableProvider, defaultModelFor, availableModelsFor } from '../helpers'\nimport { mergeListById } from '../merge'\nimport type { SpawnSlice, StoreSlice } from '../types'\n\nexport const createSpawnSlice: StoreSlice<SpawnSlice> = (set, get) => ({\n orchestrators: [],\n providerCatalog: [],\n orchestratorSpawnOpen: false,\n orchestratorInstallOpen: false,\n spawnOrchId: '',\n spawnProvider: firstAvailableProvider(undefined),\n spawnModel: defaultModelFor(firstAvailableProvider(undefined)),\n spawnEffort: '',\n spawnProfile: 'default-relay',\n spawnCwd: '',\n spawnLabel: '',\n spawnApproval: 'guarded',\n spawnWorkspaceMode: 'inherit',\n spawnPrompt: '',\n spawnSystemPromptAppend: '',\n spawnCount: 1,\n spawnCwdHistory: [],\n isSpawning: false,\n spawnDirBrowser: { open: false, loading: false, path: '', parent: '', baseDir: '', entries: [], error: '' },\n pendingForkImport: null,\n\n async fetchOrchestrators() {\n try {\n const orchestrators = mergeListById(get().orchestrators, await api<Orchestrator[]>('GET', '/orchestrators'))\n if (orchestrators !== get().orchestrators) set({ orchestrators })\n } catch {}\n },\n async fetchProviderCatalog() {\n try {\n const res = await api<{ catalog: ProviderCatalogEntry[] }>('GET', '/providers')\n set({ providerCatalog: res.catalog })\n } catch {}\n },\n async refreshOrchestratorProviders(orchId) {\n try {\n await api('GET', '/orchestrators/' + encodeURIComponent(orchId) + '/providers?refresh=1')\n await get().fetchOrchestrators()\n get().showNotification('Provider status refreshed')\n } catch (e) { get().showError('Provider Refresh Failed', (e as Error).message) }\n },\n\n openAgentSpawn() { get().openOrchestratorSpawn() },\n closeAgentSpawn() { set({ orchestratorSpawnOpen: false }) },\n async doSpawnAgent() { await get().submitOrchestratorSpawn() },\n openOrchestratorSpawn() {\n get().openOrchestratorSpawnAt()\n },\n openOrchestratorSpawnAt(input = {}) {\n const online = get().orchestrators.filter((o) => o.status === 'online')\n if (!online.length) { get().showError('No Orchestrators', 'No orchestrators are currently online.'); return }\n void get().fetchProviderCatalog()\n const s = get()\n const requested = input.orchestratorId ? online.find((o) => o.id === input.orchestratorId) : undefined\n if (input.orchestratorId && !requested) { get().showError('Orchestrator Offline', 'The selected orchestrator is not online.'); return }\n const cwdHint = input.cwd?.trim() || s.spawnCwd\n const cwdOrch = cwdHint ? online.find((o) => pathWithinBase(cwdHint, o.baseDir)) : undefined\n const persisted = s.spawnOrchId ? online.find((o) => o.id === s.spawnOrchId) : undefined\n const orchId = requested?.id || cwdOrch?.id || persisted?.id || online[0]!.id\n const orch = online.find((o) => o.id === orchId)!\n const provider = s.spawnProvider && orch.providers.includes(s.spawnProvider as any) ? s.spawnProvider : firstAvailableProvider(orch)\n // Keep the persisted model only if it's still an enabled choice — otherwise a\n // model disabled in Settings would survive in the dialog. Falls back to default.\n const keepModel = s.spawnProvider === provider && s.spawnModel\n && availableModelsFor(provider, s.providerCatalog).some((m) => m.alias === s.spawnModel)\n const model = keepModel ? s.spawnModel : defaultModelFor(provider, s.providerCatalog)\n const cwd = cwdHint && pathWithinBase(cwdHint, orch.baseDir) ? cwdHint : orch.baseDir || ''\n const effort = s.spawnProvider === provider && s.spawnModel === model ? s.spawnEffort : ''\n set({ spawnOrchId: orchId, spawnProvider: provider, spawnModel: model, spawnEffort: effort, spawnProfile: s.spawnProfile || 'default-relay', spawnCwd: cwd, spawnLabel: '', spawnWorkspaceMode: 'inherit', spawnPrompt: '', spawnSystemPromptAppend: '', spawnCount: 1, pendingForkImport: null, orchestratorSpawnOpen: true })\n },\n forkFromAgent(agentId, messages) {\n const s = get()\n const agent = s.agentsById[agentId]\n if (!agent) { get().showError('Fork', 'Agent not found.'); return }\n const online = s.orchestrators.filter((o) => o.status === 'online')\n if (!online.length) { get().showError('No Orchestrators', 'No orchestrators are currently online.'); return }\n void get().fetchProviderCatalog()\n const provCaps = agent.providerCapabilities\n const provider = provCaps?.model?.provider || (typeof agent.meta?.provider === 'string' ? agent.meta.provider as string : '')\n const reportedModel = provCaps?.model?.alias || provCaps?.model?.id || ''\n const catalog = provider ? getProviderCatalog(provider as SpawnProvider, s.providerCatalog) : undefined\n const catalogEntry = catalog?.models.find((m) => m.alias === reportedModel || (m as any).providerModel === reportedModel)\n const model = catalogEntry?.alias || ''\n const effort = provCaps?.model?.effort || ''\n // A branch agent's meta.cwd is its isolated WORKTREE, not the project repo. Anchoring a\n // fork there made it squat inside the source's worktree (shared mode, no workspace row of\n // its own): it rode that workspace's branch + auto-land/reaper lifecycle and had its cwd\n // deleted out from under it when the source landed. Anchor to the real repo root instead\n // (the precedence agentProjectName already uses) and mirror the source's effective workspace\n // mode — so forking a branch agent yields its OWN branch agent, and forking a shared/main\n // agent stays shared in the repo.\n const wsRaw = agent.meta?.workspace\n const workspace: Record<string, unknown> = isRecord(wsRaw) ? wsRaw : {}\n const cwd = stringValue(workspace.repoRoot) || stringValue(workspace.sourceCwd) || (typeof agent.meta?.cwd === 'string' ? agent.meta.cwd as string : '')\n const sourceWorkspaceMode = stringValue(workspace.mode)\n const forkWorkspaceMode = sourceWorkspaceMode === 'isolated' ? 'isolated' : sourceWorkspaceMode === 'shared' ? 'shared' : 'inherit'\n const approval = provCaps?.session?.approvalMode || (typeof agent.meta?.approvalMode === 'string' ? agent.meta.approvalMode as string : 'guarded')\n const label = agent.label ? `${agent.label} (fork)` : ''\n const orch = online.find((o) => o.hostname === agent.machine) || online.find((o) => pathWithinBase(cwd, o.baseDir)) || online[0]!\n\n const agentName = displayName(agent)\n const lines: string[] = []\n for (const msg of messages) {\n const body = messageBody(msg).trim()\n if (!body) continue\n const role = msg.from === HUMAN_AGENT_ID ? 'user' : 'agent'\n lines.push(`[${role}]: ${body}`)\n }\n const history = lines.join('\\n')\n const maxHistory = 14000\n const trimmed = history.length > maxHistory ? '...\\n' + history.slice(history.length - maxHistory) : history\n const systemPromptAppend = `You are continuing work from a previous agent session (${agentName}). Below is the conversation history. Treat it as context for your work, not as your own actions.\\n\\n${trimmed}`\n const messageIds = messages\n .filter((msg) => typeof msg.id === 'number' && msg.id > 0)\n .map((msg) => msg.id)\n\n set({\n spawnOrchId: orch.id,\n spawnProvider: provider || firstAvailableProvider(orch),\n spawnModel: model || defaultModelFor(provider || firstAvailableProvider(orch)),\n spawnEffort: effort,\n spawnProfile: s.spawnProfile || 'default-relay',\n spawnCwd: cwd || orch.baseDir || '',\n spawnLabel: label,\n spawnApproval: approval,\n spawnWorkspaceMode: forkWorkspaceMode,\n spawnPrompt: 'Continue from this previous session.',\n spawnSystemPromptAppend: systemPromptAppend,\n spawnCount: 1,\n pendingForkImport: {\n sourcePeerId: agent.id,\n sourceAgentId: agent.id,\n sourceThreadId: agent.id,\n sourceAgentLabel: agentName,\n messageIds,\n },\n orchestratorSpawnOpen: true,\n })\n },\n openOrchestratorInstall() {\n set({ orchestratorInstallOpen: true })\n },\n openOrchestratorSpawnFor(orchId) {\n const s = get()\n const orch = s.orchestrators.find((o) => o.id === orchId)\n const provider = firstAvailableProvider(orch)\n const model = defaultModelFor(provider, s.providerCatalog)\n const effort = s.spawnProvider === provider && s.spawnModel === model ? s.spawnEffort : ''\n const cwd = s.spawnCwd && pathWithinBase(s.spawnCwd, orch?.baseDir) ? s.spawnCwd : orch?.baseDir || ''\n set({ spawnOrchId: orchId, spawnProvider: provider, spawnModel: model, spawnEffort: effort, spawnProfile: s.spawnProfile || 'default-relay', spawnCwd: cwd, spawnLabel: '', spawnWorkspaceMode: 'inherit', spawnPrompt: '', spawnSystemPromptAppend: '', spawnCount: 1, pendingForkImport: null, orchestratorSpawnOpen: true })\n },\n selectSpawnHistory(orchId, cwd) {\n const s = get()\n const orch = s.orchestrators.find((o) => o.id === orchId && o.status === 'online')\n if (!orch) { get().showError('Unavailable', 'That orchestrator is no longer online.'); return }\n if (orchId === s.spawnOrchId) { set({ spawnCwd: cwd }); return }\n const provider = s.spawnProvider && orch.providers.includes(s.spawnProvider as any) ? s.spawnProvider : firstAvailableProvider(orch)\n const keepModel = s.spawnProvider === provider && s.spawnModel\n && availableModelsFor(provider, s.providerCatalog).some((m) => m.alias === s.spawnModel)\n const model = keepModel ? s.spawnModel : defaultModelFor(provider, s.providerCatalog)\n const effort = s.spawnProvider === provider && s.spawnModel === model ? s.spawnEffort : ''\n set({ spawnOrchId: orchId, spawnProvider: provider, spawnModel: model, spawnEffort: effort, spawnCwd: cwd })\n },\n async submitOrchestratorSpawn() {\n const s = get()\n if (s.isSpawning) { return }\n if (!s.spawnOrchId) { get().showError('Validation', 'Select an orchestrator.'); return }\n const orch = s.orchestrators.find((o) => o.id === s.spawnOrchId)\n if (!orch) { get().showError('Validation', 'Selected orchestrator is not available.'); return }\n const cwd = s.spawnCwd.trim()\n if (!cwd) { get().showError('Validation', 'Select a working directory.'); return }\n if (!pathWithinBase(cwd, orch.baseDir)) { get().showError('Validation', `Working directory must be under ${orch.baseDir}.`); return }\n const pendingForkImport = s.pendingForkImport\n // A fork imports one source session into one target — never fan it out.\n const count = pendingForkImport ? 1 : Math.max(1, Math.min(10, Math.round(s.spawnCount) || 1))\n set({ isSpawning: true })\n try {\n for (let i = 0; i < count; i++) {\n const payload: Record<string, unknown> = { provider: s.spawnProvider, orchestratorId: s.spawnOrchId, approvalMode: s.spawnApproval, workspaceMode: s.spawnWorkspaceMode || 'inherit' }\n if (s.spawnModel) payload.model = s.spawnModel\n if (s.spawnEffort) payload.effort = s.spawnEffort\n if (s.spawnProfile) payload.profile = s.spawnProfile\n payload.cwd = cwd\n if (s.spawnLabel) payload.label = count > 1 ? `${s.spawnLabel} ${i + 1}` : s.spawnLabel\n if (s.spawnPrompt) payload.prompt = s.spawnPrompt\n if (s.spawnSystemPromptAppend) payload.systemPromptAppend = s.spawnSystemPromptAppend\n const response = await api<{ command?: { params?: { spawnRequestId?: string } } }>('POST', '/agents/spawn', payload)\n const targetSpawnRequestId = response.command?.params?.spawnRequestId\n if (pendingForkImport && targetSpawnRequestId && pendingForkImport.messageIds.length > 0) {\n await api('POST', '/chat/history-imports', {\n targetSpawnRequestId,\n sourcePeerId: pendingForkImport.sourcePeerId,\n sourceAgentId: pendingForkImport.sourceAgentId,\n sourceThreadId: pendingForkImport.sourceThreadId,\n sourceAgentLabel: pendingForkImport.sourceAgentLabel,\n importedBy: INBOX_OPERATOR_ID,\n messageIds: pendingForkImport.messageIds,\n })\n }\n }\n const history = [{ orchId: s.spawnOrchId, cwd }, ...s.spawnCwdHistory.filter((h) => !(h.orchId === s.spawnOrchId && h.cwd === cwd))].slice(0, 5)\n set({ orchestratorSpawnOpen: false, pendingForkImport: null, isSpawning: false, spawnCwdHistory: history })\n await Promise.all([get().fetchAgents(), get().fetchOrchestrators(), get().fetchActivityEvents(), get().fetchChatHistoryImports()])\n } catch (e) { set({ isSpawning: false }); get().showError('Spawn Failed', (e as Error).message) }\n },\n orchestratorAction(orchId, action, agentId, component) {\n const label = action === 'restart' ? 'Restart' : 'Shutdown'\n const target = agentId ? `agent \"${agentId}\"` : 'all agents'\n get().openConfirm(`${label} Agent`, `${label} ${target} on orchestrator \"${orchId}\"?`, async () => {\n try {\n await api('POST', '/orchestrators/' + encodeURIComponent(orchId) + '/actions', { action, agentId, ...(component ? { surface: { component } } : {}) })\n await Promise.all([get().fetchOrchestrators(), get().fetchActivityEvents()])\n } catch (e) { get().showError(`${label} Failed`, (e as Error).message) }\n })\n },\n upgradeOrchestrator(orchId, targetVersion) {\n const to = targetVersion ? ` to v${targetVersion}` : ' to match the relay'\n get().openConfirm('Upgrade Orchestrator', `Upgrade \"${orchId}\"${to}? It will install the new version and restart itself.`, async () => {\n try {\n await api('POST', '/orchestrators/' + encodeURIComponent(orchId) + '/actions', { action: 'upgrade', ...(targetVersion ? { targetVersion } : {}) })\n await Promise.all([get().fetchOrchestrators(), get().fetchActivityEvents()])\n } catch (e) { get().showError('Upgrade Failed', (e as Error).message) }\n })\n },\n deleteOrchestrator(orchId) {\n get().openConfirm('Remove Orchestrator', `Remove orchestrator \"${orchId}\"?`, async () => {\n try {\n await api('DELETE', '/orchestrators/' + encodeURIComponent(orchId))\n await Promise.all([get().fetchOrchestrators(), get().fetchActivityEvents()])\n } catch (e) { get().showError('Delete Failed', (e as Error).message) }\n })\n },\n async browseSpawnDirectory(path) {\n const orchId = get().spawnOrchId\n if (!orchId) return\n const browser = get().spawnDirBrowser\n set({ spawnDirBrowser: { ...browser, loading: true, error: '' } })\n try {\n const q = path ? '?path=' + encodeURIComponent(path) : ''\n const listing = await api<{ path: string; parent?: string; baseDir: string; entries: Array<{ name: string; path: string }> }>('GET', '/orchestrators/' + encodeURIComponent(orchId) + '/directories' + q)\n set({\n spawnDirBrowser: { open: true, loading: false, path: listing.path || '', parent: listing.parent || '', baseDir: listing.baseDir || '', entries: listing.entries || [], error: '' },\n spawnCwd: listing.path || get().spawnCwd,\n })\n } catch (e) { set({ spawnDirBrowser: { ...get().spawnDirBrowser, loading: false, error: (e as Error).message } }) }\n },\n selectSpawnDirectory(path) {\n set({ spawnCwd: path || get().spawnDirBrowser.path, spawnDirBrowser: { ...get().spawnDirBrowser, open: false } })\n },\n})\n","import { api } from '@/lib/api'\nimport { errMessage } from 'agent-relay-sdk/types'\nimport type { AgentProfile, SpawnPolicy } from '@/types'\nimport { firstAvailableProvider, defaultModelFor } from '../helpers'\nimport type { ConfigEntry } from '../helpers'\nimport type { ManagedSlice, StoreSlice, ToolGroupCatalogEntry } from '../types'\n\nexport const createManagedSlice: StoreSlice<ManagedSlice> = (set, get) => ({\n managedPolicies: [],\n policyModal: {\n open: false, editing: '', name: '', description: '', orchestratorId: '', cwd: '',\n enabled: undefined,\n provider: firstAvailableProvider(undefined), rig: '', model: defaultModelFor(firstAvailableProvider(undefined)), effort: '', profile: 'default-relay', providerArgs: '', prompt: '', tags: '', capabilities: '',\n label: '', mode: 'always-on' as const, permissionMode: 'guarded' as const,\n restartOnUpdate: true, scheduledDailyRestart: false,\n keepaliveSeconds: '300', backoffSchedule: '5,15,60,300', backoffResetAfterSeconds: '600',\n channelId: '',\n },\n policyDirBrowser: { open: false, loading: false, path: '', parent: '', baseDir: '', entries: [], error: '' },\n agentProfiles: [],\n toolGroupCatalog: [],\n toolGroupCatalogStatus: { loading: false, loaded: false, error: null },\n profileModal: {\n open: false, mode: 'create' as const,\n profile: {\n name: '', description: '', provider: 'any' as const, base: 'host' as const,\n instructions: { system: '', append: [], repoInstructions: 'allow' as const, globalInstructions: 'allow' as const },\n relay: { context: true, skills: true, plugins: true, statusLine: true, mcp: true },\n skills: [], plugins: [],\n mcp: { mode: 'host' as const }, hooks: { mode: 'host' as const },\n permissions: { filesystem: 'host' as const },\n env: {}, providerOptions: {},\n },\n relayToolsMode: 'full' as const,\n },\n\n async fetchManagedPolicies() { try { set({ managedPolicies: await api('GET', '/spawn-policies/health') }) } catch {} },\n async fetchAgentProfiles() {\n try {\n const entries = await api<ConfigEntry<AgentProfile>[]>('GET', '/agent-profiles')\n set({ agentProfiles: entries.map((entry) => entry.value) })\n } catch {}\n },\n async fetchToolGroupCatalog() {\n set({ toolGroupCatalogStatus: { loading: true, loaded: get().toolGroupCatalogStatus.loaded, error: null } })\n try {\n const catalog = await api<ToolGroupCatalogEntry[]>('GET', '/agent-profiles/tool-groups')\n set({ toolGroupCatalog: catalog, toolGroupCatalogStatus: { loading: false, loaded: true, error: null } })\n } catch (e) {\n set({ toolGroupCatalog: [], toolGroupCatalogStatus: { loading: false, loaded: true, error: errMessage(e) } })\n }\n },\n\n openPolicyCreate() {\n const online = get().orchestrators.filter((o) => o.status === 'online')\n if (!online.length) { get().showError('No Orchestrators', 'No orchestrators are currently online.'); return }\n void get().fetchProviderCatalog()\n set({\n policyModal: {\n open: true, editing: '', name: '', description: '', orchestratorId: online[0]!.id, cwd: '',\n enabled: undefined,\n provider: firstAvailableProvider(online[0]), rig: '', model: defaultModelFor(firstAvailableProvider(online[0]), get().providerCatalog), effort: '', profile: 'default-relay', providerArgs: '', prompt: '', tags: '', capabilities: '',\n label: '', mode: 'always-on', permissionMode: 'guarded',\n restartOnUpdate: true, scheduledDailyRestart: false,\n keepaliveSeconds: '300', backoffSchedule: '5,15,60,300', backoffResetAfterSeconds: '600',\n channelId: '',\n },\n policyDirBrowser: { open: false, loading: false, path: '', parent: '', baseDir: '', entries: [], error: '' },\n })\n },\n openPolicyEdit(policy) {\n void get().fetchProviderCatalog()\n set({\n policyModal: {\n open: true, editing: policy.name, name: policy.name,\n enabled: policy.enabled,\n description: policy.description || '', orchestratorId: policy.orchestratorId,\n cwd: policy.cwd, provider: policy.provider,\n rig: policy.rig || '',\n model: policy.model || defaultModelFor(policy.provider, get().providerCatalog),\n effort: policy.effort || '',\n profile: policy.profile || 'default-relay',\n providerArgs: (policy.providerArgs || []).join(' '),\n prompt: policy.prompt || '',\n tags: (policy.tags || []).join(', '),\n capabilities: (policy.capabilities || []).join(', '),\n label: policy.label || '', mode: policy.mode,\n permissionMode: policy.permissionMode,\n restartOnUpdate: policy.restartOnUpdate,\n scheduledDailyRestart: policy.scheduledDailyRestart,\n keepaliveSeconds: String(policy.onDemand?.keepaliveSeconds ?? 300),\n backoffSchedule: (policy.backoff?.schedule || [5, 15, 60, 300]).join(', '),\n backoffResetAfterSeconds: String(policy.backoff?.resetAfterSeconds ?? 600),\n channelId: policy.binding?.channelId || '',\n },\n policyDirBrowser: { open: false, loading: false, path: '', parent: '', baseDir: '', entries: [], error: '' },\n })\n },\n async submitPolicy() {\n const m = get().policyModal\n if (!m.name.trim()) { get().showError('Validation', 'Policy name is required.'); return }\n if (!m.orchestratorId) { get().showError('Validation', 'Select an orchestrator.'); return }\n if (!m.cwd.trim()) { get().showError('Validation', 'Working directory is required.'); return }\n const csvSplit = (s: string) => s.split(/[,\\s]+/).map((v) => v.trim()).filter(Boolean)\n const policy: SpawnPolicy = {\n name: m.name.trim(),\n ...(m.description.trim() ? { description: m.description.trim() } : {}),\n ...(m.enabled !== undefined ? { enabled: m.enabled } : {}),\n orchestratorId: m.orchestratorId,\n cwd: m.cwd.trim(),\n provider: m.provider,\n ...(m.rig.trim() ? { rig: m.rig.trim() } : {}),\n ...(m.model ? { model: m.model } : {}),\n ...(m.effort ? { effort: m.effort as any } : {}),\n ...(m.profile ? { profile: m.profile } : {}),\n providerArgs: m.providerArgs.trim() ? m.providerArgs.trim().split(/\\s+/) : [],\n ...(m.prompt.trim() ? { prompt: m.prompt.trim() } : {}),\n tags: csvSplit(m.tags),\n capabilities: csvSplit(m.capabilities),\n ...(m.label.trim() ? { label: m.label.trim() } : {}),\n mode: m.mode,\n permissionMode: m.permissionMode,\n restartOnUpdate: m.restartOnUpdate,\n scheduledDailyRestart: m.scheduledDailyRestart,\n ...(m.mode === 'on-demand' ? { onDemand: { keepaliveSeconds: Number(m.keepaliveSeconds) || 300, idleDefinition: 'no-activity' as const } } : {}),\n backoff: {\n schedule: csvSplit(m.backoffSchedule).map(Number).filter((n) => n > 0),\n resetAfterSeconds: Number(m.backoffResetAfterSeconds) || 600,\n },\n ...(m.channelId.trim() ? { binding: { type: 'channel' as const, channelId: m.channelId.trim() } } : {}),\n }\n if (!policy.backoff.schedule.length) policy.backoff.schedule = [5, 15, 60, 300]\n try {\n await api('PUT', '/spawn-policy/' + encodeURIComponent(policy.name), policy)\n set({ policyModal: { ...m, open: false } })\n await get().fetchManagedPolicies()\n } catch (e) { get().showError('Save Failed', (e as Error).message) }\n },\n deletePolicy(name) {\n get().openConfirm('Delete Policy', `Delete managed agent policy \"${name}\"? This will stop any running agent.`, async () => {\n try {\n await api('DELETE', '/spawn-policy/' + encodeURIComponent(name))\n await get().fetchManagedPolicies()\n } catch (e) { get().showError('Delete Failed', (e as Error).message) }\n })\n },\n async browsePolicyDirectory(path) {\n const orchId = get().policyModal.orchestratorId\n if (!orchId) return\n const browser = get().policyDirBrowser\n set({ policyDirBrowser: { ...browser, loading: true, error: '' } })\n try {\n const q = path ? '?path=' + encodeURIComponent(path) : ''\n const listing = await api<{ path: string; parent?: string; baseDir: string; entries: Array<{ name: string; path: string }> }>('GET', '/orchestrators/' + encodeURIComponent(orchId) + '/directories' + q)\n set({\n policyDirBrowser: { open: true, loading: false, path: listing.path || '', parent: listing.parent || '', baseDir: listing.baseDir || '', entries: listing.entries || [], error: '' },\n policyModal: { ...get().policyModal, cwd: listing.path || get().policyModal.cwd },\n })\n } catch (e) { set({ policyDirBrowser: { ...get().policyDirBrowser, loading: false, error: (e as Error).message } }) }\n },\n selectPolicyDirectory(path) {\n set({ policyModal: { ...get().policyModal, cwd: path || get().policyDirBrowser.path }, policyDirBrowser: { ...get().policyDirBrowser, open: false } })\n },\n\n openProfileModal(mode, profile) {\n void get().fetchToolGroupCatalog()\n const blank: AgentProfile = {\n name: '', description: '', provider: 'any', base: 'host',\n instructions: { system: '', append: [], repoInstructions: 'allow', globalInstructions: 'allow' },\n relay: { context: true, skills: true, plugins: true, statusLine: true, mcp: true },\n skills: [], plugins: [],\n mcp: { mode: 'host' }, hooks: { mode: 'host' },\n permissions: { filesystem: 'host' },\n env: {}, providerOptions: {},\n }\n let p: AgentProfile\n if (profile && mode === 'duplicate') {\n p = JSON.parse(JSON.stringify(profile))\n p.name = profile.name + '-copy'\n delete (p as any).builtIn\n } else if (profile && (mode === 'edit' || mode === 'view')) {\n p = JSON.parse(JSON.stringify(profile))\n } else {\n p = blank\n }\n const relayToolsMode = (p.relay.tools?.groups?.length ?? 0) > 0 ? 'custom' : 'full'\n set({ profileModal: { open: true, mode, profile: p, relayToolsMode } })\n },\n closeProfileModal() {\n set({ profileModal: { ...get().profileModal, open: false } })\n },\n updateProfileModal(partial) {\n const current = get().profileModal.profile\n set({ profileModal: { ...get().profileModal, profile: { ...current, ...partial } } })\n },\n setRelayToolsMode(mode) {\n set({ profileModal: { ...get().profileModal, relayToolsMode: mode } })\n // Switching to full access clears any explicit group list immediately — round-trips as\n // full surface (relay.tools undefined) and never leaves a stale custom list behind (#970).\n if (mode === 'full') {\n const { profile } = get().profileModal\n set({ profileModal: { ...get().profileModal, profile: { ...profile, relay: { ...profile.relay, tools: undefined } } } })\n }\n },\n async saveProfile() {\n const { mode, profile } = get().profileModal\n if (!profile.name.trim()) { get().showError('Validation', 'Profile name is required.'); return }\n if (mode === 'view') return\n try {\n await api('PUT', '/agent-profiles/' + encodeURIComponent(profile.name.trim()), profile)\n set({ profileModal: { ...get().profileModal, open: false } })\n await get().fetchAgentProfiles()\n get().showNotification(`Profile \"${profile.name}\" saved`)\n } catch (e) { get().showError('Save Failed', (e as Error).message) }\n },\n deleteProfile(input) {\n const profile = typeof input === 'string' ? get().agentProfiles.find((p) => p.name === input) : input\n const name = typeof input === 'string' ? input : input.name\n const isReset = profile?.builtIn === true\n get().openConfirm(isReset ? 'Reset Profile' : 'Delete Profile', isReset ? `Reset built-in profile \"${name}\" to its default?` : `Delete agent profile \"${name}\"?`, async () => {\n try {\n await api('DELETE', '/agent-profiles/' + encodeURIComponent(name))\n await get().fetchAgentProfiles()\n get().showNotification(isReset ? `Profile \"${name}\" reset to default` : `Profile \"${name}\" deleted`)\n } catch (e) { get().showError('Delete Failed', (e as Error).message) }\n })\n },\n})\n","import { api } from '@/lib/api'\nimport { INBOX_OPERATOR_ID } from '@/lib/constants'\nimport type { ActivityEvent, ViewName } from '@/types'\nimport type { OperatorSlice, StoreSlice } from '../types'\n\nexport const createOperatorSlice: StoreSlice<OperatorSlice> = (set, get) => ({\n operatorActivity: [],\n\n recordOperatorActivity(input) {\n const item: ActivityEvent = {\n id: Date.now(),\n kind: 'operator',\n title: input.title || 'Activity',\n body: input.body,\n meta: input.meta,\n icon: input.icon,\n view: input.view,\n peer: input.peer,\n messageId: input.messageId,\n pairId: input.pairId,\n taskId: input.taskId,\n agentId: input.agentId,\n ts: Date.now(),\n clientId: 'op-' + Date.now(),\n createdAt: Date.now(),\n }\n set({ operatorActivity: [item, ...get().operatorActivity.slice(0, 79)] })\n api('POST', '/activity', {\n operatorId: INBOX_OPERATOR_ID, clientId: item.clientId, kind: item.kind,\n title: item.title, body: item.body, meta: item.meta, icon: item.icon,\n view: item.view, peer: item.peer, messageId: item.messageId,\n pairId: item.pairId, taskId: item.taskId, agentId: item.agentId,\n }).catch(() => {})\n },\n async openActivityItem(item) {\n if (item.view) await get().switchView((item.view === 'inbox' ? 'chat' : item.view) as ViewName)\n if (item.peer) get().openInboxThread(item.peer)\n if (item.agentId) {\n const agent = get().agentsById[item.agentId]\n if (agent) get().openAgentDetail(agent)\n }\n if (item.taskId) {\n get().openTaskDetailById(item.taskId)\n }\n },\n})\n","import { api } from '@/lib/api'\nimport { safeFilename, downloadText, toTimestamp } from '@/lib/display'\nimport type { ActivityEvent, TaskEvent } from '@/types'\nimport type { StoreSlice, TimelineSlice } from '../types'\n\nfunction timelineEventTime(event: ActivityEvent): number {\n return toTimestamp(event.createdAt ?? event.ts)\n}\n\nfunction renderTimelineMarkdown(events: ActivityEvent[], exportedAt: string): string {\n const lines = ['# Agent Relay Timeline', '', `Exported: ${exportedAt}`, `Events: ${events.length}`, '']\n for (const event of events) {\n const when = new Date(timelineEventTime(event)).toISOString()\n lines.push(`## ${event.title || event.kind || 'Event'}`)\n lines.push(`- When: ${when}`)\n if (event.kind) lines.push(`- Kind: ${event.kind}`)\n if (event.agentId) lines.push(`- Agent: ${event.agentId}`)\n if (event.peer) lines.push(`- Peer: ${event.peer}`)\n if (event.body) lines.push('', event.body)\n lines.push('')\n }\n return lines.join('\\n')\n}\n\nexport const createTimelineSlice: StoreSlice<TimelineSlice> = (set, get) => ({\n activityEvents: [],\n activityFilter: '',\n\n async fetchActivityEvents() {\n try {\n const events = await api<ActivityEvent[]>('GET', '/activity?limit=200')\n set({ activityEvents: events })\n } catch {}\n },\n\n exportActivity(format) {\n const s = get()\n const events = [...s.operatorActivity, ...s.activityEvents].sort((a, b) => timelineEventTime(b) - timelineEventTime(a))\n const exportedAt = new Date().toISOString()\n const filename = `agent-relay-timeline-${exportedAt.slice(0, 10)}.${format === 'json' ? 'json' : 'md'}`\n if (format === 'json') {\n downloadText(filename, JSON.stringify({ title: 'Agent Relay Timeline', exportedAt, events }, null, 2), 'application/json')\n } else {\n downloadText(filename, renderTimelineMarkdown(events, exportedAt), 'text/markdown')\n }\n },\n exportThread(thread, format) {\n const filename = `agent-relay-thread-${safeFilename(thread.peer)}.${format === 'json' ? 'json' : 'md'}`\n const data = { title: 'Thread: ' + thread.peer, importedHistory: thread.importedHistory || [], messages: thread.messages, exportedAt: new Date().toISOString() }\n downloadText(filename, JSON.stringify(data, null, 2), format === 'json' ? 'application/json' : 'text/markdown')\n },\n exportPair(pair, format) {\n const filename = `agent-relay-pair-${safeFilename(pair.id)}.${format === 'json' ? 'json' : 'md'}`\n const data = { title: 'Pair: ' + pair.id, pair, exportedAt: new Date().toISOString() }\n downloadText(filename, JSON.stringify(data, null, 2), format === 'json' ? 'application/json' : 'text/markdown')\n },\n async exportTask(task, format) {\n let events = get().taskEventCache[task.id] || []\n if (!events.length) {\n try { events = await api<TaskEvent[]>('GET', '/tasks/' + task.id + '/events'); set({ taskEventCache: { ...get().taskEventCache, [task.id]: events } }) } catch {}\n }\n const filename = `agent-relay-task-${task.id}.${format === 'json' ? 'json' : 'md'}`\n const data = { title: 'Task: ' + (task.title || 'T' + task.id), task, events, exportedAt: new Date().toISOString() }\n downloadText(filename, JSON.stringify(data, null, 2), format === 'json' ? 'application/json' : 'text/markdown')\n },\n})\n","import { api } from '@/lib/api'\nimport type { AnalyticsPeriod, MaintenanceJob, MaintenanceJobRun, ViewName } from '@/types'\nimport type { OpsSlice, StoreSlice } from '../types'\n\nexport const createOpsSlice: StoreSlice<OpsSlice> = (set, get) => ({\n channels: [],\n connectors: [],\n integrations: [],\n automations: [],\n automationRuns: [],\n health: null,\n maintenanceJobs: [],\n analyticsPeriod: '24h' as AnalyticsPeriod,\n analyticsData: null,\n\n async fetchChannels() { try { set({ channels: await api('GET', '/channels') }) } catch {} },\n async fetchConnectors() { try { set({ connectors: await api('GET', '/connectors') }) } catch {} },\n async fetchIntegrations() { try { set({ integrations: await api('GET', '/integrations') }) } catch {} },\n async fetchAutomations() { try { set({ automations: await api('GET', '/automations') }) } catch {} },\n async fetchAutomationRuns(automationId) {\n try {\n const params = new URLSearchParams({ limit: '100' })\n if (automationId) params.set('automationId', automationId)\n set({ automationRuns: await api('GET', '/automation-runs?' + params.toString()) })\n } catch {}\n },\n async fetchHealth() { try { set({ health: await api('GET', '/health') }) } catch {} },\n async fetchMaintenanceJobs() { try { set({ maintenanceJobs: await api('GET', '/maintenance/jobs') }) } catch {} },\n async runMaintenanceJob(id) {\n try {\n const result = await api<{ run: MaintenanceJobRun; jobs: MaintenanceJob[] }>('POST', '/maintenance/jobs/' + encodeURIComponent(id) + '/run', {})\n set({ maintenanceJobs: result.jobs })\n get().showNotification(result.run.status === 'succeeded' ? 'Maintenance job completed' : 'Maintenance job skipped')\n return result.run\n } catch (e) {\n get().showError('Maintenance Job Failed', (e as Error).message)\n await get().fetchMaintenanceJobs()\n return null\n }\n },\n async runHealthAction(action) {\n if (action.preset) set({ agentPresetFilter: action.preset, showOffline: true })\n if (action.api && action.path) { await api(action.api, action.path); await get().refreshLiveData() }\n if (action.view) await get().switchView(action.view as ViewName)\n if (action.copy) try { await navigator.clipboard?.writeText(action.copy) } catch {}\n },\n async runConnectorAction(connectorId, action) {\n try {\n const result = await api<{ ok: boolean; stderr?: string; parsed?: unknown }>('POST', '/connectors/' + encodeURIComponent(connectorId) + '/actions', { action })\n await get().fetchConnectors()\n if (!result.ok) throw new Error(result.stderr || `${action} failed`)\n return result\n } catch (e) {\n get().showError('Connector Action Failed', (e as Error).message)\n throw e\n }\n },\n async fetchAnalytics() {\n try { set({ analyticsData: await api('GET', '/stats/analytics?period=' + get().analyticsPeriod) }) } catch {}\n },\n setAnalyticsPeriod(period) {\n set({ analyticsPeriod: period })\n void get().fetchAnalytics()\n },\n})\n","import { api } from '@/lib/api'\nimport type { WorkspaceRecord, WorkspaceGitStateResult, WorkspaceMergePreviewResult, WorkspaceDiffResult } from '@/types'\nimport type { StoreSlice, WorkspacesSlice } from '../types'\n\nexport const createWorkspacesSlice: StoreSlice<WorkspacesSlice> = (set, get) => ({\n workspaces: [],\n workspaceFocusId: null,\n workspaceGitState: {},\n workspaceMergePreview: {},\n workspaceDiff: {},\n workspaceOrphans: [],\n\n // Branch-state badge click-through (#236): jump to the Workspaces panel and\n // tell it which workspace to surface/highlight, solving \"can't find it among many\".\n async openWorkspaceFocus(workspaceId) {\n set({ workspaceFocusId: workspaceId })\n await get().switchView('workspaces')\n },\n async fetchWorkspaces() { try { set({ workspaces: await api<WorkspaceRecord[]>('GET', '/workspaces') }) } catch {} },\n async fetchWorkspaceGitState(workspaceId) {\n try {\n const state = await api<WorkspaceGitStateResult>('GET', '/workspaces/' + encodeURIComponent(workspaceId) + '/git-state')\n set((s) => ({ workspaceGitState: { ...s.workspaceGitState, [workspaceId]: state } }))\n } catch (e) {\n set((s) => ({ workspaceGitState: { ...s.workspaceGitState, [workspaceId]: { available: false, reason: (e as Error).message } } }))\n }\n },\n async fetchWorkspaceMergePreview(workspaceId) {\n try {\n const preview = await api<WorkspaceMergePreviewResult>('GET', '/workspaces/' + encodeURIComponent(workspaceId) + '/merge-preview')\n set((s) => ({ workspaceMergePreview: { ...s.workspaceMergePreview, [workspaceId]: preview } }))\n } catch (e) {\n set((s) => ({ workspaceMergePreview: { ...s.workspaceMergePreview, [workspaceId]: { available: false, reason: (e as Error).message } } }))\n }\n },\n async fetchWorkspaceDiff(workspaceId) {\n try {\n const diff = await api<WorkspaceDiffResult>('GET', '/workspaces/' + encodeURIComponent(workspaceId) + '/diff')\n set((s) => ({ workspaceDiff: { ...s.workspaceDiff, [workspaceId]: diff } }))\n } catch (e) {\n set((s) => ({ workspaceDiff: { ...s.workspaceDiff, [workspaceId]: { available: false, reason: (e as Error).message } } }))\n }\n },\n async fetchWorkspaceOrphans() {\n try {\n const res = await api<{ orphans: import('@/types').WorkspaceOrphan[] }>('GET', '/workspaces/orphans')\n set({ workspaceOrphans: res.orphans ?? [] })\n } catch { /* best-effort; panel just stays empty */ }\n },\n async reclaimWorkspaceOrphan(orphan) {\n get().openConfirm('Reclaim Orphan Worktree', `Remove the orphaned worktree \"${orphan.branch || orphan.worktreePath}\" from disk? It has no live agent.`, async () => {\n try {\n await api('POST', '/workspaces/orphans/reclaim', { worktreePath: orphan.worktreePath, repoRoot: orphan.repoRoot, branch: orphan.branch })\n get().showNotification('Orphan reclaim dispatched')\n await Promise.all([get().fetchWorkspaceOrphans(), get().fetchActivityEvents()])\n } catch (e) { get().showError('Reclaim Failed', (e as Error).message) }\n })\n },\n async workspaceAction(workspaceId, action) {\n const label = action === 'ready'\n ? 'Mark ready'\n : action === 'request-review'\n ? 'Request review'\n : action === 'merge-plan'\n ? 'Mark merge planned'\n : action === 'merge'\n ? 'Merge'\n : action === 'abandon'\n ? 'Abandon'\n : 'Cleanup'\n const run = async () => {\n try {\n await api('POST', '/workspaces/' + encodeURIComponent(workspaceId) + '/actions', { action })\n get().showNotification(action === 'cleanup' ? 'Workspace cleanup requested' : action === 'merge' ? 'Workspace merge dispatched' : `Workspace updated: ${label}`)\n await Promise.all([get().fetchWorkspaces(), get().fetchOrchestrators(), get().fetchActivityEvents()])\n } catch (e) { get().showError('Workspace Action Failed', (e as Error).message) }\n }\n if (action === 'cleanup' || action === 'abandon' || action === 'merge') {\n const workspace = get().workspaces.find((item) => item.id === workspaceId)\n const target = workspace?.branch || workspace?.worktreePath || workspaceId\n if (action === 'merge') {\n const preview = get().workspaceMergePreview[workspaceId]\n const mergeDetail = preview && preview.available !== false\n ? ` — ${preview.strategy === 'pr' ? 'opens a PR' : `rebases & fast-forwards into ${preview.baseRef || 'base'}, then deletes the branch`}.`\n : ' — lands the work and deletes the branch.'\n get().openConfirm('Merge Workspace', `Merge workspace \"${target}\"?${mergeDetail}`, run)\n return\n }\n if (action === 'cleanup') {\n // Destroy-guard: fetch live git state so the user sees what cleanup\n // will throw away before confirming the force-remove.\n await get().fetchWorkspaceGitState(workspaceId)\n const gs = get().workspaceGitState[workspaceId]\n let warn = ''\n if (gs && gs.available !== false && !gs.missing) {\n const ahead = gs.ahead ?? 0\n const dirty = gs.dirtyCount ?? 0\n if (ahead > 0 || dirty > 0) {\n const parts = [ahead > 0 ? `${ahead} unmerged commit(s)` : '', dirty > 0 ? `${dirty} uncommitted change(s)` : ''].filter(Boolean)\n warn = ` ⚠ This destroys ${parts.join(' and ')}.`\n }\n }\n get().openConfirm('Cleanup Workspace', `Remove the worktree for \"${target}\" on the host?${warn}`, run)\n return\n }\n get().openConfirm('Abandon Workspace', `Abandon workspace \"${target}\"?`, run)\n return\n }\n await run()\n },\n async purgeWorkspace(workspaceId) {\n const workspace = get().workspaces.find((item) => item.id === workspaceId)\n const target = workspace?.branch || workspace?.worktreePath || workspaceId\n get().openConfirm('Purge Workspace Record', `Permanently remove the record for \"${target}\"? This only clears the dashboard row — it does not touch disk.`, async () => {\n try {\n await api('DELETE', '/workspaces/' + encodeURIComponent(workspaceId))\n get().showNotification('Workspace record purged')\n await Promise.all([get().fetchWorkspaces(), get().fetchActivityEvents()])\n } catch (e) { get().showError('Purge Failed', (e as Error).message) }\n })\n },\n})\n","import { api } from '@/lib/api'\nimport type { Memory, MemoryInjectResult, MemoryQuery, MemorySearchResult } from '@/types'\nimport type { MemorySlice, StoreSlice } from '../types'\n\nexport const createMemorySlice: StoreSlice<MemorySlice> = (set, get) => ({\n memories: [],\n memoryTotal: 0,\n memoryBrokerInfo: null,\n memoryStats: null,\n memoryFilters: { search: '', type: '', scope: '', tags: '', visibility: '', minRelevance: '0' },\n selectedMemoryId: '',\n memoryLoading: false,\n\n async refreshMemory() {\n await Promise.all([get().fetchMemoryBroker(), get().fetchMemoryStats(), get().fetchMemories()])\n },\n async fetchMemoryBroker() {\n try { set({ memoryBrokerInfo: await api('GET', '/memories/broker') }) }\n catch (e) { get().showError('Memory Broker Failed', (e as Error).message) }\n },\n async fetchMemoryStats() {\n try { set({ memoryStats: await api('GET', '/memories/stats') }) }\n catch {}\n },\n async fetchMemories() {\n const filters = get().memoryFilters\n const query: MemoryQuery = { limit: 100 }\n if (filters.type) query.type = filters.type as MemoryQuery['type']\n if (filters.scope.trim()) query.scope = filters.scope.trim()\n if (filters.visibility) query.visibility = filters.visibility as MemoryQuery['visibility']\n if (filters.tags.trim()) query.tags = filters.tags.split(',').map((tag) => tag.trim()).filter(Boolean)\n if (filters.minRelevance.trim() !== '') {\n const relevance = Number(filters.minRelevance)\n if (Number.isFinite(relevance)) query.minRelevance = Math.max(0, Math.min(1, relevance))\n }\n set({ memoryLoading: true })\n try {\n const result = await api<MemorySearchResult>('POST', '/memories/search', query)\n const selectedMemoryId = result.memories.some((m) => m.id === get().selectedMemoryId) ? get().selectedMemoryId : (result.memories[0]?.id || '')\n set({ memories: result.memories, memoryTotal: result.total, selectedMemoryId })\n } catch (e) {\n get().showError('Memory Search Failed', (e as Error).message)\n } finally {\n set({ memoryLoading: false })\n }\n },\n async saveMemory(input, id) {\n try {\n const saved = id\n ? await api<Memory>('PATCH', '/memories/' + encodeURIComponent(id), input)\n : await api<Memory>('POST', '/memories', input)\n set({ selectedMemoryId: saved.id })\n await Promise.all([get().fetchMemories(), get().fetchMemoryStats()])\n get().showNotification(id ? 'Memory updated' : 'Memory created')\n } catch (e) { get().showError('Memory Save Failed', (e as Error).message) }\n },\n deleteMemory(id) {\n get().openConfirm('Delete Memory', `Delete memory \"${id}\"?`, async () => {\n try {\n await api('DELETE', '/memories/' + encodeURIComponent(id))\n set({ selectedMemoryId: '' })\n await Promise.all([get().fetchMemories(), get().fetchMemoryStats()])\n get().showNotification('Memory deleted')\n } catch (e) { get().showError('Delete Failed', (e as Error).message) }\n })\n },\n async injectMemory(input) {\n try {\n const result = await api<MemoryInjectResult>('POST', '/memories/inject', input)\n get().showNotification(`Memory inject command sent to ${input.agentId}`)\n return result\n } catch (e) {\n get().showError('Memory Inject Failed', (e as Error).message)\n return null\n }\n },\n})\n","import { api } from '@/lib/api'\nimport type { Project } from '@/types'\nimport type { StoreSlice, ProjectsSlice } from '../types'\n\nexport const createProjectsSlice: StoreSlice<ProjectsSlice> = (set, get) => ({\n projectFocusId: null,\n projectByCwd: {},\n\n // Agent→project deep link: stash the target project (id or slug) and jump to the\n // Projects view, which selects it on arrival via consumeProjectFocus.\n async openProjectFocus(projectId) {\n set({ projectFocusId: projectId })\n await get().switchView('projects')\n },\n consumeProjectFocus() {\n if (get().projectFocusId !== null) set({ projectFocusId: null })\n },\n\n // Resolve cwd → project through the server (longest-root-match lives there, #405).\n // Cache by cwd so repeated card/drawer renders don't refetch; a resolved-but-no-match\n // is cached as null so we don't retry a known miss every render.\n async fetchProjectForCwd(cwd) {\n if (!cwd || cwd in get().projectByCwd) return\n try {\n const res = await api<{ project: Project | null }>('GET', '/projects/resolve?cwd=' + encodeURIComponent(cwd))\n set((s) => ({ projectByCwd: { ...s.projectByCwd, [cwd]: res.project ?? null } }))\n } catch {\n // Best-effort enrichment — leave uncached so a later attempt can retry.\n }\n },\n})\n","import { create } from 'zustand'\nimport { persist } from 'zustand/middleware'\nimport type { RelayStore } from './types'\nimport { createConnectionSlice } from './slices/connection'\nimport { createUsersSlice } from './slices/users'\nimport { createAgentsSlice } from './slices/agents'\nimport { createChatSlice } from './slices/chat'\nimport { createPlansSlice } from './slices/plans'\nimport { createTeamsSlice } from './slices/teams'\nimport { createIssuesSlice } from './slices/issues'\nimport { createWorkSlice } from './slices/work'\nimport { createSpawnSlice } from './slices/spawn'\nimport { createManagedSlice } from './slices/managed'\nimport { createNotificationsSlice } from './slices/notifications'\nimport { createOperatorSlice } from './slices/operator'\nimport { createTimelineSlice } from './slices/timeline'\nimport { createVoiceSlice, syncVoiceActiveChat } from './slices/voice'\nimport { createOpsSlice } from './slices/ops'\nimport { createWorkspacesSlice } from './slices/workspaces'\nimport { createMemorySlice } from './slices/memory'\nimport { createProjectsSlice } from './slices/projects'\n\n// Re-exports preserve the public `@/store` surface used across the dashboard.\nexport { useClock, useNow, useNowBucket } from '@/store/clock'\nexport type { ChatStatusEvent, RelayStore } from './types'\n\nfunction waitForStoreHydration(): Promise<void> {\n if (useRelayStore.persist.hasHydrated()) return Promise.resolve()\n return new Promise((resolve) => {\n const unsub = useRelayStore.persist.onFinishHydration(() => { unsub(); resolve() })\n })\n}\n\n// The store is assembled from per-domain slices (store/slices/*). Each slice is a\n// standard Zustand StateCreator `(set, get) => ({...})`; they all share the one\n// RelayStore type, so any slice can `get()` the whole store and `set()` any field.\n// Correctness-sensitive pure helpers (poll-merge identity, active-chat predicate,\n// compact/clear status derivation) live once in store/merge.ts — never per-slice.\nexport const useRelayStore = create<RelayStore>()(\n persist(\n (...a) => ({\n ...createConnectionSlice({ waitForHydration: waitForStoreHydration })(...a),\n ...createUsersSlice(...a),\n ...createAgentsSlice(...a),\n ...createChatSlice(...a),\n ...createPlansSlice(...a),\n ...createTeamsSlice(...a),\n ...createIssuesSlice(...a),\n ...createWorkSlice(...a),\n ...createSpawnSlice(...a),\n ...createManagedSlice(...a),\n ...createNotificationsSlice(...a),\n ...createOperatorSlice(...a),\n ...createTimelineSlice(...a),\n ...createVoiceSlice(...a),\n ...createOpsSlice(...a),\n ...createWorkspacesSlice(...a),\n ...createMemorySlice(...a),\n ...createProjectsSlice(...a),\n }),\n {\n name: 'agent-relay-dashboard',\n partialize: (state) => ({\n theme: state.theme,\n view: state.view,\n showOffline: state.showOffline,\n showBuiltIns: state.showBuiltIns,\n showReasoning: state.showReasoning,\n autoRefresh: state.autoRefresh,\n voiceTtsEnabled: state.voiceTtsEnabled,\n voiceTtsLang: state.voiceTtsLang,\n voiceTtsMode: state.voiceTtsMode,\n voiceTtsKokoroVoice: state.voiceTtsKokoroVoice,\n voiceTtsBrowserVoice: state.voiceTtsBrowserVoice,\n voiceInputMode: state.voiceInputMode,\n agentSort: state.agentSort,\n agentSortDir: state.agentSortDir,\n agentPresetFilter: state.agentPresetFilter,\n agentStatusFilter: state.agentStatusFilter,\n agentTagFilter: state.agentTagFilter,\n agentHostFilter: state.agentHostFilter,\n pairStatusFilter: state.pairStatusFilter,\n inboxSort: state.inboxSort,\n inboxSortDir: state.inboxSortDir,\n inboxShowArchived: state.inboxShowArchived,\n chatAgentProviderFilter: state.chatAgentProviderFilter,\n chatAgentStatusFilter: state.chatAgentStatusFilter,\n chatAgentTagFilter: state.chatAgentTagFilter,\n chatAgentCapFilter: state.chatAgentCapFilter,\n chatAgentHostFilter: state.chatAgentHostFilter,\n chatAgentSort: state.chatAgentSort,\n chatAgentSortDir: state.chatAgentSortDir,\n chatAgentGroupBy: state.chatAgentGroupBy,\n chatAgentFiltersCollapsed: state.chatAgentFiltersCollapsed,\n chatCollapsedChildren: state.chatCollapsedChildren,\n feedObserveAll: state.feedObserveAll,\n coordinatorFollows: state.coordinatorFollows,\n coordinatorMutes: state.coordinatorMutes,\n planCollapsed: state.planCollapsed,\n sessionTasksCollapsed: state.sessionTasksCollapsed,\n activityFilter: state.activityFilter,\n analyticsPeriod: state.analyticsPeriod,\n memoryFilters: state.memoryFilters,\n authToken: state.authToken,\n inboxReadCursors: state.inboxReadCursors,\n inboxArchivedThreads: state.inboxArchivedThreads,\n inboxDrafts: state.inboxDrafts,\n chatStatusEvents: state.chatStatusEvents,\n operatorActivity: state.operatorActivity,\n notifications: state.notifications,\n notificationUnread: state.notificationUnread,\n notificationSettings: state.notificationSettings,\n spawnOrchId: state.spawnOrchId,\n spawnProvider: state.spawnProvider,\n spawnModel: state.spawnModel,\n spawnEffort: state.spawnEffort,\n spawnProfile: state.spawnProfile,\n spawnCwd: state.spawnCwd,\n spawnApproval: state.spawnApproval,\n spawnWorkspaceMode: state.spawnWorkspaceMode,\n spawnCwdHistory: state.spawnCwdHistory,\n }),\n },\n ),\n)\n\n// Keep the browser TTS controller's \"active chat\" in sync with navigation. This\n// is an external-system subscription (the speechSynthesis engine), not derived\n// React state — so it lives here, driven by the store, not by a component effect.\nuseRelayStore.subscribe(syncVoiceActiveChat)\n"],"x_google_ignoreList":[0,1,2],"mappings":"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"}