@wrongstack/webui-server 0.296.4 → 0.298.0

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 (200) hide show
  1. package/dist/index.js +2138 -793
  2. package/dist/protocol/client-conversation.d.ts +1 -1
  3. package/dist/protocol/index.js +7 -1
  4. package/dist/protocol/projections.d.ts +5 -1
  5. package/dist/protocol/registry.d.ts +2 -2
  6. package/dist/protocol/server-conversation.d.ts +1 -1
  7. package/dist/server/backend-services.d.ts +8 -2
  8. package/dist/server/codemap-cache.d.ts +1 -0
  9. package/dist/server/collaboration-ws-handler.d.ts +2 -2
  10. package/dist/server/connection-lifecycle.d.ts +13 -0
  11. package/dist/server/connections-health-route.d.ts +7 -1
  12. package/dist/server/context-editor.d.ts +1 -2
  13. package/dist/server/embedded-lifecycle.d.ts +2 -0
  14. package/dist/server/entry.js +1760 -744
  15. package/dist/server/frontend-static-serve.d.ts +7 -0
  16. package/dist/server/goal-ws-handler.d.ts +3 -1
  17. package/dist/server/governance-runtime.d.ts +30 -0
  18. package/dist/server/http-server.d.ts +7 -0
  19. package/dist/server/kanban-supervisor.d.ts +6 -0
  20. package/dist/server/mailbox-handlers.d.ts +2 -1
  21. package/dist/server/mailbox-routes.d.ts +1 -0
  22. package/dist/server/mcp-handlers.d.ts +3 -1
  23. package/dist/server/memory-diagnostics.d.ts +69 -0
  24. package/dist/server/memory-handlers.d.ts +3 -3
  25. package/dist/server/pending-confirms.d.ts +9 -0
  26. package/dist/server/sdd-board-ws-handler.d.ts +15 -8
  27. package/dist/server/sdd-wizard-wiring.d.ts +5 -6
  28. package/dist/server/sdd-wizard-ws-handler.d.ts +18 -6
  29. package/dist/server/specs-ws-handler.d.ts +1 -0
  30. package/dist/server/types.d.ts +5 -1
  31. package/dist/server/worktree-ws-handler.d.ts +30 -2
  32. package/dist/server/ws-auth.d.ts +24 -0
  33. package/dist/server/ws-payload-validation.d.ts +26 -0
  34. package/dist/server/ws-utils.d.ts +12 -1
  35. package/package.json +12 -11
  36. package/dist/index.d.ts.map +0 -1
  37. package/dist/index.js.map +0 -7
  38. package/dist/protocol/client-conversation.d.ts.map +0 -1
  39. package/dist/protocol/client-integrations.d.ts.map +0 -1
  40. package/dist/protocol/client-operations.d.ts.map +0 -1
  41. package/dist/protocol/client-workspace.d.ts.map +0 -1
  42. package/dist/protocol/connection-fsm.d.ts.map +0 -1
  43. package/dist/protocol/decoder.d.ts.map +0 -1
  44. package/dist/protocol/index.d.ts.map +0 -1
  45. package/dist/protocol/index.js.map +0 -7
  46. package/dist/protocol/projections.d.ts.map +0 -1
  47. package/dist/protocol/registry.d.ts.map +0 -1
  48. package/dist/protocol/replay-payload.d.ts.map +0 -1
  49. package/dist/protocol/server-conversation.d.ts.map +0 -1
  50. package/dist/protocol/server-integrations.d.ts.map +0 -1
  51. package/dist/protocol/server-operations.d.ts.map +0 -1
  52. package/dist/protocol/server-workspace.d.ts.map +0 -1
  53. package/dist/protocol/types.d.ts.map +0 -1
  54. package/dist/protocol/version.d.ts.map +0 -1
  55. package/dist/server/agent-roster-handlers.d.ts.map +0 -1
  56. package/dist/server/agent-roster-routes.d.ts.map +0 -1
  57. package/dist/server/autonomy-routes.d.ts.map +0 -1
  58. package/dist/server/backend-services.d.ts.map +0 -1
  59. package/dist/server/boot.d.ts.map +0 -1
  60. package/dist/server/brain-handlers.d.ts.map +0 -1
  61. package/dist/server/brain-routes.d.ts.map +0 -1
  62. package/dist/server/chronicle-routes.d.ts.map +0 -1
  63. package/dist/server/client-presence.d.ts.map +0 -1
  64. package/dist/server/client-transport-routes.d.ts.map +0 -1
  65. package/dist/server/codebase-index-server-control.d.ts.map +0 -1
  66. package/dist/server/codebase-indexing.d.ts.map +0 -1
  67. package/dist/server/codemap-cache.d.ts.map +0 -1
  68. package/dist/server/codemap-handlers.d.ts.map +0 -1
  69. package/dist/server/codemap-telemetry.d.ts.map +0 -1
  70. package/dist/server/collaboration-ws-handler.d.ts.map +0 -1
  71. package/dist/server/completion-handlers.d.ts.map +0 -1
  72. package/dist/server/completion-routes.d.ts.map +0 -1
  73. package/dist/server/config-doctor.d.ts.map +0 -1
  74. package/dist/server/connection-handler.d.ts.map +0 -1
  75. package/dist/server/connection-lifecycle.d.ts.map +0 -1
  76. package/dist/server/connections-health-route.d.ts.map +0 -1
  77. package/dist/server/content-routes.d.ts.map +0 -1
  78. package/dist/server/context-editor.d.ts.map +0 -1
  79. package/dist/server/context-meta.d.ts.map +0 -1
  80. package/dist/server/conversation-operations.d.ts.map +0 -1
  81. package/dist/server/conversation-routes.d.ts.map +0 -1
  82. package/dist/server/custom-context-modes.d.ts.map +0 -1
  83. package/dist/server/deadcode-handlers.d.ts.map +0 -1
  84. package/dist/server/design-handlers.d.ts.map +0 -1
  85. package/dist/server/discover-mailbox-bridge.d.ts.map +0 -1
  86. package/dist/server/embedded-host-adapters.d.ts.map +0 -1
  87. package/dist/server/embedded-lifecycle.d.ts.map +0 -1
  88. package/dist/server/embedded-message-router.d.ts.map +0 -1
  89. package/dist/server/entry.d.ts.map +0 -1
  90. package/dist/server/entry.js.map +0 -7
  91. package/dist/server/eternal-iteration-broadcast.d.ts.map +0 -1
  92. package/dist/server/file-handlers.d.ts.map +0 -1
  93. package/dist/server/file-picker.d.ts.map +0 -1
  94. package/dist/server/frontend-static-serve.d.ts.map +0 -1
  95. package/dist/server/git-handlers.d.ts.map +0 -1
  96. package/dist/server/git-process.d.ts.map +0 -1
  97. package/dist/server/goal-handlers.d.ts.map +0 -1
  98. package/dist/server/goal-routes.d.ts.map +0 -1
  99. package/dist/server/goal-snapshot-routes.d.ts.map +0 -1
  100. package/dist/server/goal-ws-handler.d.ts.map +0 -1
  101. package/dist/server/handlers/index.d.ts.map +0 -1
  102. package/dist/server/handlers/worklist-handlers.d.ts.map +0 -1
  103. package/dist/server/handlers.js.map +0 -7
  104. package/dist/server/host-routes.d.ts.map +0 -1
  105. package/dist/server/http-server/analytics-handler.d.ts.map +0 -1
  106. package/dist/server/http-server/api-handlers.d.ts.map +0 -1
  107. package/dist/server/http-server.d.ts.map +0 -1
  108. package/dist/server/index.d.ts.map +0 -1
  109. package/dist/server/instance-registry.d.ts.map +0 -1
  110. package/dist/server/introspection-routes.d.ts.map +0 -1
  111. package/dist/server/kanban-board-watcher.d.ts.map +0 -1
  112. package/dist/server/kanban-daemon-subscriber.d.ts.map +0 -1
  113. package/dist/server/kanban-decomposition-routes.d.ts.map +0 -1
  114. package/dist/server/kanban-dispatch.d.ts.map +0 -1
  115. package/dist/server/kanban-host-routes.d.ts.map +0 -1
  116. package/dist/server/kanban-route-helpers.d.ts.map +0 -1
  117. package/dist/server/kanban-route-pagination.d.ts.map +0 -1
  118. package/dist/server/kanban-route-protocol.d.ts.map +0 -1
  119. package/dist/server/kanban-routes.d.ts.map +0 -1
  120. package/dist/server/kanban-run-mirror.d.ts.map +0 -1
  121. package/dist/server/kanban-supervisor.d.ts.map +0 -1
  122. package/dist/server/kanban-task-routes.d.ts.map +0 -1
  123. package/dist/server/lifecycle.d.ts.map +0 -1
  124. package/dist/server/mailbox-handlers.d.ts.map +0 -1
  125. package/dist/server/mailbox-routes.d.ts.map +0 -1
  126. package/dist/server/mcp-handlers.d.ts.map +0 -1
  127. package/dist/server/mcp-routes.d.ts.map +0 -1
  128. package/dist/server/memory-handlers.d.ts.map +0 -1
  129. package/dist/server/memory-routes.d.ts.map +0 -1
  130. package/dist/server/message-dispatcher.d.ts.map +0 -1
  131. package/dist/server/mode-handlers.d.ts.map +0 -1
  132. package/dist/server/mode-operations.d.ts.map +0 -1
  133. package/dist/server/mode-routes.d.ts.map +0 -1
  134. package/dist/server/model-auto-discovery.d.ts.map +0 -1
  135. package/dist/server/model-catalog-search.d.ts.map +0 -1
  136. package/dist/server/model-catalog.d.ts.map +0 -1
  137. package/dist/server/model-operations.d.ts.map +0 -1
  138. package/dist/server/network-info.d.ts.map +0 -1
  139. package/dist/server/open-browser.d.ts.map +0 -1
  140. package/dist/server/path-containment.d.ts.map +0 -1
  141. package/dist/server/pending-confirms.d.ts.map +0 -1
  142. package/dist/server/port-utils.d.ts.map +0 -1
  143. package/dist/server/pre-context-services.d.ts.map +0 -1
  144. package/dist/server/pref-helpers.d.ts.map +0 -1
  145. package/dist/server/prefs-handlers.d.ts.map +0 -1
  146. package/dist/server/prefs-routes.d.ts.map +0 -1
  147. package/dist/server/privileged-actions.d.ts.map +0 -1
  148. package/dist/server/process-handlers.d.ts.map +0 -1
  149. package/dist/server/process-routes.d.ts.map +0 -1
  150. package/dist/server/project-handlers.d.ts.map +0 -1
  151. package/dist/server/project-routes.d.ts.map +0 -1
  152. package/dist/server/projects-manifest.d.ts.map +0 -1
  153. package/dist/server/prompts-handlers.d.ts.map +0 -1
  154. package/dist/server/provider-config-io.d.ts.map +0 -1
  155. package/dist/server/provider-config-standalone.d.ts.map +0 -1
  156. package/dist/server/provider-handlers.d.ts.map +0 -1
  157. package/dist/server/provider-keys.d.ts.map +0 -1
  158. package/dist/server/provider-routes.d.ts.map +0 -1
  159. package/dist/server/provider-store.d.ts.map +0 -1
  160. package/dist/server/route-family-dispatcher.d.ts.map +0 -1
  161. package/dist/server/routes.d.ts.map +0 -1
  162. package/dist/server/sdd-board-routes.d.ts.map +0 -1
  163. package/dist/server/sdd-board-ws-handler.d.ts.map +0 -1
  164. package/dist/server/sdd-wizard-routes.d.ts.map +0 -1
  165. package/dist/server/sdd-wizard-wiring.d.ts.map +0 -1
  166. package/dist/server/sdd-wizard-ws-handler.d.ts.map +0 -1
  167. package/dist/server/server-runtime.d.ts.map +0 -1
  168. package/dist/server/session-handlers.d.ts.map +0 -1
  169. package/dist/server/session-history.d.ts.map +0 -1
  170. package/dist/server/session-routes.d.ts.map +0 -1
  171. package/dist/server/setup-event-projection.d.ts.map +0 -1
  172. package/dist/server/setup-events-core-watchers.d.ts.map +0 -1
  173. package/dist/server/setup-events-fleet-broadcaster.d.ts.map +0 -1
  174. package/dist/server/setup-events-provider-handlers.d.ts.map +0 -1
  175. package/dist/server/setup-events-session-helpers.d.ts.map +0 -1
  176. package/dist/server/setup-events-status-watcher.d.ts.map +0 -1
  177. package/dist/server/setup-events-watcher.d.ts.map +0 -1
  178. package/dist/server/setup-events.d.ts.map +0 -1
  179. package/dist/server/setup-screen.d.ts.map +0 -1
  180. package/dist/server/shell-git-routes.d.ts.map +0 -1
  181. package/dist/server/shell-open.d.ts.map +0 -1
  182. package/dist/server/skills-handlers.d.ts.map +0 -1
  183. package/dist/server/specs-routes.d.ts.map +0 -1
  184. package/dist/server/specs-ws-handler.d.ts.map +0 -1
  185. package/dist/server/standalone-session-identity.d.ts.map +0 -1
  186. package/dist/server/start-webui.d.ts.map +0 -1
  187. package/dist/server/stream-coalescer.d.ts.map +0 -1
  188. package/dist/server/techstack-handlers.d.ts.map +0 -1
  189. package/dist/server/terminal-ws-handler.d.ts.map +0 -1
  190. package/dist/server/token-estimator.d.ts.map +0 -1
  191. package/dist/server/types.d.ts.map +0 -1
  192. package/dist/server/usage-cost.d.ts.map +0 -1
  193. package/dist/server/worklist-routes.d.ts.map +0 -1
  194. package/dist/server/worktree-routes.d.ts.map +0 -1
  195. package/dist/server/worktree-ws-handler.d.ts.map +0 -1
  196. package/dist/server/ws-auth.d.ts.map +0 -1
  197. package/dist/server/ws-payload-preferences.d.ts.map +0 -1
  198. package/dist/server/ws-payload-validation.d.ts.map +0 -1
  199. package/dist/server/ws-utils.d.ts.map +0 -1
  200. package/dist/server/zip.d.ts.map +0 -1
@@ -59,6 +59,13 @@ export interface StaticServeOptions {
59
59
  apiToken?: string | undefined;
60
60
  /** Force token auth even when the server binds to loopback. */
61
61
  requireToken?: boolean | undefined;
62
+ /**
63
+ * Extra hostnames the HTTP CSRF/DNS-rebinding guard accepts, for operators
64
+ * fronting the WebUI with a tunnel or reverse proxy. `publicWsUrl`'s hostname
65
+ * is trusted implicitly; set this when the browser-facing HTTP origin differs
66
+ * from it (WS-001).
67
+ */
68
+ allowedHostnames?: readonly string[] | undefined;
62
69
  /**
63
70
  * When true, skip `server.listen()` — the caller is responsible for
64
71
  * calling listen after attaching the WebSocketServer. This prevents
@@ -1,7 +1,7 @@
1
- import type { WebSocket } from 'ws';
2
1
  import type { Agent, Context } from '@wrongstack/core/agent';
3
2
  import type { EventBus } from '@wrongstack/core/kernel';
4
3
  import type { Logger } from '@wrongstack/core/types';
4
+ import type { WebSocket } from 'ws';
5
5
  interface GoalWSMessage {
6
6
  type: string;
7
7
  payload?: Record<string, unknown>;
@@ -66,6 +66,8 @@ export declare class GoalWebSocketHandler {
66
66
  */
67
67
  onBoardState?: ((graphId: string, state: Record<string, unknown>) => void) | undefined);
68
68
  addClient(ws: WebSocket): void;
69
+ /** Release timers, in-flight work, and socket references owned by this host. */
70
+ dispose(): void;
69
71
  handleMessage(ws: WebSocket, msg: GoalWSMessage): Promise<void>;
70
72
  /**
71
73
  * Assess a goal prompt for duration realism. Runs a lightweight LLM call
@@ -0,0 +1,30 @@
1
+ import type { AgentPipelines } from '@wrongstack/core/agent';
2
+ import type { EventBus } from '@wrongstack/core/kernel';
3
+ import type { Logger, WorkspaceCheckpointRef } from '@wrongstack/core/types';
4
+ import type { BootstrapGovernanceRuntimeOptions, GovernanceRuntimeBootstrapCloseResult, GovernanceRuntimeBootstrapResult } from '@wrongstack/runtime/governance-bootstrap';
5
+ import { createGovernanceMutationSnapshotBridge } from '@wrongstack/runtime/governance-mutation-snapshot-bridge';
6
+ export declare const WRONGSTACK_GOVERNANCE_ENV = "WRONGSTACK_GOVERNANCE";
7
+ interface GovernanceBootstrapModule {
8
+ bootstrapGovernanceRuntime(options: BootstrapGovernanceRuntimeOptions): Promise<GovernanceRuntimeBootstrapResult>;
9
+ }
10
+ interface WebUiGovernanceDependencies {
11
+ readonly load: () => Promise<GovernanceBootstrapModule>;
12
+ readonly createMutationBridge: typeof createGovernanceMutationSnapshotBridge;
13
+ }
14
+ export interface WebUiGovernanceRuntimeHandle {
15
+ installToolBoundary(pipelines: AgentPipelines): void;
16
+ close(): Promise<GovernanceRuntimeBootstrapCloseResult>;
17
+ }
18
+ export declare function webUiGovernanceRuntimeEnabled(environment: Readonly<Record<string, string | undefined>>): boolean;
19
+ export declare function setupWebUiGovernance(input: {
20
+ readonly environment: Readonly<Record<string, string | undefined>>;
21
+ readonly projectRoot: string;
22
+ readonly projectId: string;
23
+ readonly sessionId: string;
24
+ readonly contextMeta: Record<string, unknown>;
25
+ readonly events: EventBus;
26
+ readonly logger: Pick<Logger, 'info' | 'warn'>;
27
+ readonly captureWorkspaceCheckpoint: () => Promise<WorkspaceCheckpointRef | undefined>;
28
+ }, dependencies?: WebUiGovernanceDependencies): Promise<WebUiGovernanceRuntimeHandle | undefined>;
29
+ export {};
30
+ //# sourceMappingURL=governance-runtime.d.ts.map
@@ -27,6 +27,13 @@ export interface CreateHttpServerOptions {
27
27
  apiToken?: string | undefined;
28
28
  /** Force HTTP token auth even on loopback binds, useful behind public tunnels. */
29
29
  requireToken?: boolean | undefined;
30
+ /**
31
+ * Extra `Host`/`Origin` hostnames accepted by the CSRF/rebinding guard, for
32
+ * operators fronting the WebUI with a tunnel or reverse proxy. The hostname
33
+ * of `publicWsUrl` is trusted implicitly. Mirrors the WS path's
34
+ * `allowedHostnames` (see ws-auth.ts).
35
+ */
36
+ allowedHostnames?: readonly string[] | undefined;
30
37
  /**
31
38
  * If true, the `/ws-auth` endpoint exchanges a `?token=` query param (or
32
39
  * `X-WS-Token` header) for an `HttpOnly` auth cookie. The cookie is then
@@ -36,6 +36,12 @@ export interface KanbanSupervisorDeps {
36
36
  export interface KanbanSupervisor {
37
37
  getSnapshot(boardId: string): KanbanSupervisorSnapshot | undefined;
38
38
  auditNow(boardId?: string): Promise<KanbanSupervisorSnapshot[]>;
39
+ getStats(): {
40
+ snapshots: number;
41
+ scheduledBoards: number;
42
+ agentCooldowns: number;
43
+ runningAgents: number;
44
+ };
39
45
  dispose(): void;
40
46
  }
41
47
  /**
@@ -8,7 +8,7 @@
8
8
  import { type RemoteMailbox } from '@wrongstack/core/coordination';
9
9
  import type { EventBus } from '@wrongstack/core/kernel';
10
10
  import type { WebSocket } from 'ws';
11
- import type { MailboxSendPayload } from './ws-payload-validation.js';
11
+ import type { MailboxActionPayload, MailboxSendPayload } from './ws-payload-validation.js';
12
12
  export interface MailboxHandlerDeps {
13
13
  /** Absolute project root or a live getter for hosts that can switch projects. */
14
14
  projectRoot: string | (() => string);
@@ -18,6 +18,7 @@ export interface MailboxHandlerDeps {
18
18
  events?: EventBus | undefined;
19
19
  }
20
20
  export declare function getMailboxForDeps(deps: MailboxHandlerDeps): RemoteMailbox | null;
21
+ export declare function handleMailboxAction(ws: WebSocket, deps: MailboxHandlerDeps, payload: MailboxActionPayload): Promise<void>;
21
22
  /** Persist a human-authored WebUI message in the shared project mailbox. */
22
23
  export declare function handleMailboxSend(ws: WebSocket, deps: MailboxHandlerDeps, payload: MailboxSendPayload): Promise<void>;
23
24
  /**
@@ -2,6 +2,7 @@ import type { EventBus } from '@wrongstack/core/kernel';
2
2
  import type { WebSocket } from 'ws';
3
3
  import type { WSClientMessage } from './types.js';
4
4
  export interface MailboxRouteHandlers {
5
+ action: (ws: WebSocket, msg: WSClientMessage) => Promise<void> | void;
5
6
  send: (ws: WebSocket, msg: WSClientMessage) => Promise<void> | void;
6
7
  messages: (ws: WebSocket, msg: WSClientMessage) => Promise<void> | void;
7
8
  agents: (ws: WebSocket, msg: WSClientMessage) => Promise<void> | void;
@@ -8,7 +8,7 @@
8
8
  * config IO, url/header persistence, and live registry start/stop logic lives
9
9
  * there; here we only map structured results to WS events the browser expects.
10
10
  */
11
- import { type MCPRegistry, type MCPServerOperationalHealth } from '@wrongstack/mcp';
11
+ import { type MCPRegistry, type MCPServerOperationalHealth, type McpServerInfo } from '@wrongstack/mcp';
12
12
  import type { WebSocket } from 'ws';
13
13
  import type { WSClientMessage } from './types.js';
14
14
  /** Wire view of a server as the browser MCP panel consumes it. */
@@ -28,6 +28,8 @@ export interface MCPServerView {
28
28
  url?: string;
29
29
  health?: MCPServerOperationalHealth;
30
30
  }
31
+ /** Project the shared {@link McpServerInfo} into the browser wire shape. */
32
+ export declare function toView(info: McpServerInfo, health?: MCPServerOperationalHealth | undefined): MCPServerView;
31
33
  /** mcp.list — configured servers merged with live registry status + tools. */
32
34
  export declare function handleMcpList(ws: WebSocket, _msg: WSClientMessage, globalConfigPath: string, mcpRegistry?: MCPRegistry): Promise<void>;
33
35
  /** mcp.add — persist a new server (incl. url/headers) and start it if enabled. */
@@ -0,0 +1,69 @@
1
+ export interface ProcessMemoryDiagnostic {
2
+ pid: number;
3
+ surface: string;
4
+ sessionId?: string | undefined;
5
+ ts: string;
6
+ memory: {
7
+ rss: number;
8
+ heapUsed: number;
9
+ heapTotal: number;
10
+ retainedHeapUsed?: number | undefined;
11
+ nativeResidual?: number | undefined;
12
+ external?: number | undefined;
13
+ arrayBuffers?: number | undefined;
14
+ };
15
+ signal?: string | undefined;
16
+ heapGrowthBytesPerHour?: number | undefined;
17
+ rssGrowthBytesPerHour?: number | undefined;
18
+ workload: {
19
+ messages?: number | undefined;
20
+ messageEstimatedTokens?: number | undefined;
21
+ historyEntries?: number | undefined;
22
+ historyMountedEntries?: number | undefined;
23
+ historyCachedGroups?: number | undefined;
24
+ appRenders?: number | undefined;
25
+ metricsDroppedObservations?: number | undefined;
26
+ kanbanSyncActive?: boolean | undefined;
27
+ kanbanSyncPendingBoards?: number | undefined;
28
+ kanbanSyncFullRescanPending?: boolean | undefined;
29
+ kanbanSyncRemoteApplyQueued?: boolean | undefined;
30
+ kanbanSyncPendingRemoteBoards?: number | undefined;
31
+ kanbanSyncPublishRuns?: number | undefined;
32
+ kanbanSyncCoalescedRefreshes?: number | undefined;
33
+ kanbanSupervisorSnapshots?: number | undefined;
34
+ kanbanSupervisorScheduledBoards?: number | undefined;
35
+ kanbanSupervisorAgentCooldowns?: number | undefined;
36
+ kanbanSupervisorRunningAgents?: number | undefined;
37
+ };
38
+ resources: {
39
+ active?: number | undefined;
40
+ types?: string | undefined;
41
+ };
42
+ hqQueue?: {
43
+ entries: number;
44
+ bytes: number;
45
+ maxBytes: number;
46
+ droppedFrames: number;
47
+ droppedBytes: number;
48
+ coalescedFrames: number;
49
+ coalescedBytes: number;
50
+ } | undefined;
51
+ hqSnapshot?: {
52
+ inFlight: boolean;
53
+ pending: boolean;
54
+ timerScheduled: boolean;
55
+ eventInFlight: boolean;
56
+ pendingEvents: number;
57
+ coalescedEvents: number;
58
+ droppedEvents: number;
59
+ } | undefined;
60
+ profileTopStack?: string | undefined;
61
+ profileTopStackBytes?: number | undefined;
62
+ }
63
+ /**
64
+ * Read only the tail of the process-wide heap JSONL flight recorder and
65
+ * return the newest sample for each PID. This endpoint must stay cheap even
66
+ * after weeks of uptime, so it never loads the full log into the WebUI server.
67
+ */
68
+ export declare function readRecentProcessMemoryDiagnostics(globalRoot: string | undefined, tailBytes?: number): Promise<ProcessMemoryDiagnostic[]>;
69
+ //# sourceMappingURL=memory-diagnostics.d.ts.map
@@ -67,9 +67,9 @@ export declare function handleSageRemember(ws: WebSocket, msg: unknown, memorySt
67
67
  * Request: { type: 'memory.sage.delete', payload: { id, reason?, force?, neverInject? } }
68
68
  * Response: { type: 'memory.sage.delete', payload: { success, message } }
69
69
  *
70
- * `force` defaults to `true` (backward-compatible). When `false`, the
71
- * permanent-memory guard in `deleteSage` is respected and
72
- * `persistence: 'permanent'` entries cannot be deleted.
70
+ * `force` defaults to `false`. The caller must explicitly pass `force: true`
71
+ * to authorize deletion. The permanent-memory guard in `deleteSage` is
72
+ * always respected when force is not set.
73
73
  *
74
74
  * `neverInject` (default false) marks the memory so context injection
75
75
  * never loads it — used when a memory is factually wrong, not just
@@ -1,6 +1,15 @@
1
1
  export type ConfirmDecision = 'yes' | 'no' | 'always' | 'deny';
2
2
  export interface PendingConfirm {
3
3
  resolve: (decision: ConfirmDecision) => void;
4
+ /**
5
+ * Session this prompt belongs to. `pendingConfirms` is one process-wide map
6
+ * replayed to every connected client, and the resolver keyed only on a
7
+ * client-supplied id — with a session check that no-op'd whenever the client
8
+ * omitted `sessionId`. So any client could answer any session's prompt
9
+ * (WS-082). Recorded here so ownership is checked against the server's own
10
+ * record rather than against whatever the client chose to send.
11
+ */
12
+ sessionId?: string | undefined;
4
13
  decisionSource?: string | undefined;
5
14
  riskTier?: 'safe' | 'standard' | 'destructive' | undefined;
6
15
  boundaryReason?: string | undefined;
@@ -9,6 +9,10 @@ interface SddBoardWSMessage {
9
9
  /** Project paths the handler needs to apply lifecycle ops directly. */
10
10
  export interface SddBoardLifecycleDeps {
11
11
  projectRoot: string;
12
+ /** Explicit compatibility hook for file-codec tests and old integrations. */
13
+ controlTransport?: 'kanban' | 'legacy-file' | undefined;
14
+ /** Explicit compatibility hook for legacy snapshot-file readers. */
15
+ stateTransport?: 'kanban' | 'legacy-file' | undefined;
12
16
  paths: {
13
17
  projectSpecs: string;
14
18
  projectTaskGraphs: string;
@@ -27,28 +31,29 @@ export interface SddBoardSecurityDeps {
27
31
  * Two observe modes (one class, shared by both webui servers):
28
32
  * • in-process (CLI-hosted): subscribe the shared EventBus `sdd.board.snapshot`
29
33
  * for instant updates;
30
- * • standalone (separate process): poll the on-disk snapshot store (the CLI
31
- * run persists JSON every change).
34
+ * • standalone (separate process): poll the project-scoped Kanban workflow
35
+ * state owned by the daemon.
32
36
  *
33
- * Control is uniform + cross-process: every command is appended to the run's
34
- * `<runId>.control.jsonl`, which the CLI run drains and applies — so the run
35
- * stays the single driver and nothing races on shared state.
37
+ * Control is uniform + cross-process: every command is durably enqueued in the
38
+ * Kanban project daemon, which the CLI run drains and applies — so the run stays
39
+ * the single driver and nothing races on shared state.
36
40
  */
37
41
  export declare class SddBoardWebSocketHandler {
38
42
  private readonly store;
39
43
  private readonly clients;
40
44
  private readonly lifecycle?;
41
45
  private readonly security?;
42
- private readonly diskPollingEnabled;
46
+ private readonly standalonePollingEnabled;
43
47
  private latest;
44
48
  private poll;
45
49
  private pollInFlight;
46
50
  private unsub;
51
+ private disposed;
47
52
  constructor(boardsDir: string, events?: EventBus, lifecycle?: SddBoardLifecycleDeps, security?: SddBoardSecurityDeps);
48
53
  addClient(ws: WebSocket): void;
49
54
  handleMessage(msg: SddBoardWSMessage): Promise<void>;
50
55
  /**
51
- * Apply a cleanup/rollback/destroy from disk and broadcast a structured
56
+ * Apply a cleanup/rollback/destroy from durable state and broadcast a structured
52
57
  * `sdd.board.lifecycle_result`. Refuses (no-op) while a run is still active —
53
58
  * the user must stop it first; the UI gates the buttons on `!active` and the
54
59
  * Destroy flow auto-stops then waits before sending `destroy`.
@@ -60,7 +65,9 @@ export declare class SddBoardWebSocketHandler {
60
65
  private stopPolling;
61
66
  private sendCurrent;
62
67
  private broadcastCurrent;
63
- private loadLatestFromDisk;
68
+ private loadLatestSnapshot;
69
+ private listBoardEntries;
70
+ private usesKanbanState;
64
71
  private broadcast;
65
72
  private send;
66
73
  }
@@ -1,7 +1,7 @@
1
- import type { AgentFactory, BrainArbiter } from '@wrongstack/core/coordination';
2
1
  import type { Agent } from '@wrongstack/core/agent';
3
- import { TaskTracker } from '@wrongstack/core/tasking';
2
+ import type { AgentFactory, BrainArbiter } from '@wrongstack/core/coordination';
4
3
  import type { EventBus } from '@wrongstack/core/kernel';
4
+ import { type TaskStore, TaskTracker } from '@wrongstack/core/tasking';
5
5
  import type { TaskGraph } from '@wrongstack/core/types';
6
6
  import { type SddRunHandle, SddRunRegistry } from '@wrongstack/sdd';
7
7
  import type { SddWizardDeps } from './sdd-wizard-ws-handler.js';
@@ -27,6 +27,8 @@ export interface StartSddRunFromGraphDeps {
27
27
  subagentFactory: AgentFactory;
28
28
  brain?: BrainArbiter | undefined;
29
29
  projectSddBoards: string;
30
+ /** Persist task status transitions so a replacement process can resume. */
31
+ taskStore?: TaskStore | undefined;
30
32
  registry?: SddRunRegistry | undefined;
31
33
  /** Isolated read-only LLM turn for the supervisor's auto-split. Omit → no split. */
32
34
  runIsolatedTurn?: ((prompt: string, name: string) => Promise<string>) | undefined;
@@ -61,10 +63,7 @@ export interface SddWizardWiringOptions {
61
63
  projectTaskGraphs: string;
62
64
  projectSddBoards: string;
63
65
  projectDir: string;
64
- /**
65
- * Canonical interview session file (same as wpaths.projectSddSession).
66
- * Destroy + resume share this path so a wipe actually clears the wizard.
67
- */
66
+ /** Legacy interview session file imported into project workflow state. */
68
67
  projectSddSession?: string | undefined;
69
68
  };
70
69
  }
@@ -1,5 +1,5 @@
1
- import type { WebSocket } from 'ws';
2
1
  import type { SddInterviewDriver } from '@wrongstack/sdd';
2
+ import type { WebSocket } from 'ws';
3
3
  interface WizardMessage {
4
4
  type: string;
5
5
  payload?: Record<string, unknown>;
@@ -22,11 +22,11 @@ export interface SddRunStartOpts {
22
22
  * light factory). This keeps the wizard protocol identical across both servers.
23
23
  */
24
24
  export interface SddWizardDeps {
25
- /** Build a fresh interview driver (disk spec/graph stores + session path). */
25
+ /** Build a fresh interview driver (artifact stores + durable session owner). */
26
26
  makeDriver: () => SddInterviewDriver;
27
27
  /**
28
28
  * Await before the first message / client snapshot so project context and any
29
- * disk resume finish loading. Optional for unit tests that inject a ready driver.
29
+ * durable resume finish loading. Optional for unit tests that inject a ready driver.
30
30
  */
31
31
  ensureReady?: (() => Promise<void>) | undefined;
32
32
  /**
@@ -62,7 +62,7 @@ export interface SddWizardDeps {
62
62
  * {@link SddWizardDeps}.
63
63
  *
64
64
  * Continuity: on construction (and before the first client message) the handler
65
- * rehydrates any on-disk interview session so a browser refresh or process
65
+ * rehydrates the durable interview session so a browser refresh or process
66
66
  * restart resumes mid-Q&A / mid-review without re-running the model.
67
67
  */
68
68
  export declare class SddWizardWebSocketHandler {
@@ -73,13 +73,25 @@ export declare class SddWizardWebSocketHandler {
73
73
  private lastAgentText;
74
74
  /** Guards against overlapping interview turns (one in flight at a time). */
75
75
  private busy;
76
- /** Resolves once project-context gather + disk resume finish. */
76
+ /** Set when authoritative session state could not be read during bootstrap. */
77
+ private resumeError;
78
+ /** Resolves once project-context gather + durable resume finish. */
77
79
  private readonly ready;
80
+ /**
81
+ * Single-flight slot for the resume probe. `handleMessage` awaits this
82
+ * when `resumeError` is set, so concurrent frames cannot race the retry
83
+ * — one probe either clears the gate or re-latches it, then every
84
+ * queued frame re-checks and proceeds. `null` when no probe is in
85
+ * flight (or after the probe has settled).
86
+ */
87
+ private resumeProbe;
88
+ private disposed;
78
89
  constructor(deps: SddWizardDeps);
79
90
  private bootstrap;
80
- /** Rehydrate a persisted interview if one exists on disk. */
91
+ /** Rehydrate a persisted interview if one exists. */
81
92
  private tryResume;
82
93
  addClient(ws: WebSocket): void;
94
+ dispose(): void;
83
95
  handleMessage(msg: WizardMessage): Promise<void>;
84
96
  private parseRunOpts;
85
97
  private onStart;
@@ -19,6 +19,7 @@ export declare class SpecsWebSocketHandler {
19
19
  private clients;
20
20
  constructor(specsDir: string, taskGraphsDir: string);
21
21
  addClient(ws: WebSocket): void;
22
+ dispose(): void;
22
23
  handleMessage(msg: SpecsWSMessage): Promise<void>;
23
24
  private buildList;
24
25
  private broadcastList;
@@ -2,7 +2,7 @@ import type { WebSocket } from 'ws';
2
2
  import type { TrustBoundary } from '@wrongstack/core/security';
3
3
  import type { ConfigStore, ModelsRegistry, SecretVault, SessionStore } from '@wrongstack/core/types';
4
4
  import type { JournalEntry } from '@wrongstack/core/storage';
5
- import type { Agent } from '@wrongstack/core/agent';
5
+ import type { Agent, AgentPipelines } from '@wrongstack/core/agent';
6
6
  import type { EventBus } from '@wrongstack/core/kernel';
7
7
  import type { ToolRegistry } from '@wrongstack/core/registry';
8
8
  /** Compatibility envelope; new boundaries should decode to CanonicalServerMessage. */
@@ -16,8 +16,12 @@ export interface WSClientMessage {
16
16
  payload?: unknown | undefined;
17
17
  }
18
18
  export interface WebUIOptions {
19
+ /** Runtime surface label used by instance registration and memory diagnostics. */
20
+ surface?: 'webui' | 'simpleui' | undefined;
19
21
  /** Policy authority for terminal/process/native-shell actions. */
20
22
  trustBoundary?: TrustBoundary | undefined;
23
+ /** Trusted host-only hook installed around leader and light-subagent tools. */
24
+ installToolBoundary?: ((pipelines: AgentPipelines) => void) | undefined;
21
25
  /** HTTP frontend port. Prefer `httpPort`; `port` is kept for compatibility. */
22
26
  port?: number | undefined;
23
27
  /** HTTP frontend port. */
@@ -1,6 +1,6 @@
1
- import type { WebSocket } from 'ws';
2
1
  import type { EventBus } from '@wrongstack/core/kernel';
3
2
  import type { Logger } from '@wrongstack/core/types';
3
+ import type { WebSocket } from 'ws';
4
4
  export interface WorktreeManagementDeps {
5
5
  projectRoot: string;
6
6
  /** Board snapshot dir — powers the cross-process liveness guard on cleanup. */
@@ -22,6 +22,20 @@ export declare class WorktreeWebSocketHandler {
22
22
  private baseBranch;
23
23
  private broadcastInterval;
24
24
  private readonly offs;
25
+ /**
26
+ * Single-flight guard for orphan scans. Two concurrent `scanAndBroadcast()`
27
+ * calls would race: the first call reads `wt.listManaged()`, then the
28
+ * second call's mutation completes (e.g. `removeOne`), then the first call
29
+ * broadcasts a stale orphan list — the user sees a row persist and clicks
30
+ * Remove a second time, hitting "remove failed (not a managed worktree?)".
31
+ *
32
+ * We coalesce concurrent triggers: while a scan is in flight, additional
33
+ * callers are remembered as "need a re-scan" and exactly one follow-up
34
+ * scan runs after the in-flight one finishes. The re-scan picks up any
35
+ * mutations that landed during the previous scan's `listManaged()` window.
36
+ */
37
+ private scanInFlight;
38
+ private scanRescanNeeded;
25
39
  constructor(events: EventBus, logger: Logger, management?: WorktreeManagementDeps | undefined);
26
40
  addClient(ws: WebSocket): void;
27
41
  /** Handle worktree-panel control messages (scan / clean / per-row ops). */
@@ -36,12 +50,26 @@ export declare class WorktreeWebSocketHandler {
36
50
  private underRoot;
37
51
  /** Branches of worktrees a live in-session run currently owns. */
38
52
  private liveActiveBranches;
53
+ /**
54
+ * Coalesced orphan scan entry point. Multiple concurrent callers share one
55
+ * in-flight scan; if a caller arrives during a scan, a single follow-up
56
+ * scan is scheduled to pick up mutations that landed mid-scan. Returns the
57
+ * shared promise so `await this.scanAndBroadcast()` still works for callers
58
+ * that want to chain after the next broadcast.
59
+ */
60
+ private scanAndBroadcast;
61
+ /**
62
+ * Run the actual scan, then drain a pending re-scan request if one came in
63
+ * during the scan. The re-scan is bounded (one follow-up) — additional
64
+ * requests that arrive during the follow-up coalesce into the next one.
65
+ */
66
+ private runScanAndMaybeRescan;
39
67
  /**
40
68
  * Scan the disk for managed worktrees/branches NOT owned by a live in-session
41
69
  * run and broadcast them as orphans, with whether it is safe to clean now.
42
70
  * No-op (empty inventory) when management deps were not wired.
43
71
  */
44
- private scanAndBroadcast;
72
+ private runScanOnce;
45
73
  /**
46
74
  * Force-remove every orphaned worktree + branch. Refused while a run is live —
47
75
  * in this session (active handles) OR another process (the SDD board liveness
@@ -42,6 +42,30 @@ export declare function hostHeaderOk(input: {
42
42
  wsHost: string;
43
43
  allowedHostnames?: readonly string[] | undefined;
44
44
  }): boolean;
45
+ /**
46
+ * CSRF / drive-by guard for the HTTP surface (WS-001).
47
+ *
48
+ * The WebSocket upgrade path has always been Origin- and Host-checked, but the
49
+ * plain HTTP `/api/*` surface was not: it built its URL from a hardcoded base,
50
+ * never read `Origin`, and (on the default loopback bind) required no token. A
51
+ * `POST` with `Content-Type: text/plain` is a CORS *simple request*, so any page
52
+ * the user visits could fire one without a preflight — the response being
53
+ * unreadable does not undo the side effect.
54
+ *
55
+ * Policy, mirroring `hasTrustedBrowserOrigin` in the HQ server:
56
+ * - the `Host` authority must already be trusted (DNS-rebinding guard), and
57
+ * - when the browser sends `Origin`, it must name that same authority.
58
+ *
59
+ * A missing `Origin` is allowed: same-origin top-level navigations and
60
+ * non-browser clients (curl, the CLI, tests) legitimately omit it, and those
61
+ * cannot be driven by a hostile page.
62
+ */
63
+ export declare function httpRequestOriginOk(input: {
64
+ origin: string | undefined;
65
+ hostHeader: string | undefined;
66
+ wsHost: string;
67
+ allowedHostnames?: readonly string[] | undefined;
68
+ }): boolean;
45
69
  export interface VerifyClientInput {
46
70
  /** Browser `Origin` header, or undefined for non-browser clients. */
47
71
  origin?: string | undefined;
@@ -22,8 +22,16 @@ export declare function validateMailboxMessagesPayload(payload: unknown): Payloa
22
22
  interface MailboxAgentsPayload {
23
23
  onlineOnly?: boolean;
24
24
  }
25
+ export interface MailboxActionPayload {
26
+ requestId: string;
27
+ mailId: string;
28
+ action: 'mark-read' | 'acknowledge' | 'reopen' | 'soft-delete';
29
+ readerId: string;
30
+ }
31
+ export declare function validateMailboxActionPayload(payload: unknown): PayloadValidationResult<MailboxActionPayload>;
25
32
  export interface MailboxSendPayload {
26
33
  requestId: string;
34
+ from?: string | undefined;
27
35
  to: string;
28
36
  type: 'note' | 'ask' | 'assign' | 'steer' | 'btw' | 'broadcast' | 'status' | 'result' | 'review';
29
37
  audience: 'all' | 'leaders';
@@ -132,4 +140,22 @@ interface ProjectsSelectPayload {
132
140
  name?: string;
133
141
  }
134
142
  export declare function validateProjectsSelectPayload(payload: unknown): PayloadValidationResult<ProjectsSelectPayload>;
143
+ /**
144
+ * `mcp.add` / `mcp.update` payload.
145
+ *
146
+ * WS-004: these handlers cast the raw WS payload with `as McpServerInput` and
147
+ * passed it straight to `addMcp`, which persists `command`/`args`/`env` to the
148
+ * global config and spawns them — so an unvalidated frame became a persistent
149
+ * child process that respawns on every restart. Validate the shape and the
150
+ * types before anything reaches disk.
151
+ *
152
+ * This checks structure, not policy: `command` is still whatever the operator
153
+ * configures. The spawn itself is hardened separately by
154
+ * `buildWin32CmdShimInvocation` (CMDI-005).
155
+ */
156
+ export interface McpServerPayload {
157
+ name: string;
158
+ [key: string]: unknown;
159
+ }
160
+ export declare function validateMcpServerPayload(payload: unknown, label: string): PayloadValidationResult<McpServerPayload>;
135
161
  //# sourceMappingURL=ws-payload-validation.d.ts.map
@@ -25,7 +25,18 @@ export declare function broadcast(clients: Map<WebSocket, ConnectedClient>, msg:
25
25
  */
26
26
  export declare function sendResult(ws: WebSocket, success: boolean, message: string): void;
27
27
  /**
28
- * Extract a human-readable message from an unknown thrown value.
28
+ * Extract a human-readable message from an unknown thrown value, safe to put
29
+ * in a frame that leaves the process.
30
+ *
31
+ * WS-066: this was `err instanceof Error ? err.message : String(err)` — the
32
+ * verbatim throw. A provider that echoes an `Authorization` header back in its
33
+ * error body, or a connection string with an inline password, went straight to
34
+ * the browser and into whatever the browser logs. {@link scrubErrorDetail}
35
+ * keeps the message (this is a local dev tool; opaque errors would gut it) but
36
+ * removes credentials and rewrites the home directory to `~`.
37
+ *
38
+ * For HTTP `/api/*` JSON bodies — a less authenticated surface — use
39
+ * `sanitizeApiError` instead, which returns a category and nothing else.
29
40
  */
30
41
  export declare function errMessage(err: unknown): string;
31
42
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/webui-server",
3
- "version": "0.296.4",
3
+ "version": "0.298.0",
4
4
  "license": "MIT",
5
5
  "description": "WrongStack WebUI HTTP/WebSocket server module — extracted from @wrongstack/webui in PR #243/244 to remove the CLI -> @wrongstack/webui/server cross-package edge (audit §3.1.1). Pure backend: HTTP routes, WebSocket handlers, MCP tool wrappers, HTML serving. The web frontend lives in @wrongstack/webui; this package is the standalone server it can run on.",
6
6
  "keywords": [
@@ -35,19 +35,20 @@
35
35
  "./package.json": "./package.json"
36
36
  },
37
37
  "files": [
38
- "dist"
38
+ "dist",
39
+ "!dist/**/*.map"
39
40
  ],
40
41
  "dependencies": {
41
42
  "ws": "^8.21.1",
42
- "@wrongstack/core": "0.296.4",
43
- "@wrongstack/runtime": "0.296.4",
44
- "@wrongstack/kanban": "0.296.4",
45
- "@wrongstack/sage": "0.296.4",
46
- "@wrongstack/mcp": "0.296.4",
47
- "@wrongstack/sdd": "0.296.4",
48
- "@wrongstack/techstack": "0.296.4",
49
- "@wrongstack/tools": "0.296.4",
50
- "@wrongstack/providers": "0.296.4"
43
+ "@wrongstack/core": "0.298.0",
44
+ "@wrongstack/runtime": "0.298.0",
45
+ "@wrongstack/sdd": "0.298.0",
46
+ "@wrongstack/kanban": "0.298.0",
47
+ "@wrongstack/mcp": "0.298.0",
48
+ "@wrongstack/techstack": "0.298.0",
49
+ "@wrongstack/providers": "0.298.0",
50
+ "@wrongstack/sage": "0.298.0",
51
+ "@wrongstack/tools": "0.298.0"
51
52
  },
52
53
  "devDependencies": {
53
54
  "@types/node": "^26.1.1",
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC"}