@wrongstack/webui-server 0.287.0 → 0.289.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 (54) hide show
  1. package/dist/index.js +1951 -457
  2. package/dist/index.js.map +4 -4
  3. package/dist/server/backend-services.d.ts +2 -2
  4. package/dist/server/backend-services.d.ts.map +1 -1
  5. package/dist/server/codebase-indexing.d.ts +2 -1
  6. package/dist/server/codebase-indexing.d.ts.map +1 -1
  7. package/dist/server/codemap-handlers.d.ts +26 -0
  8. package/dist/server/codemap-handlers.d.ts.map +1 -0
  9. package/dist/server/codemap-telemetry.d.ts +15 -0
  10. package/dist/server/codemap-telemetry.d.ts.map +1 -0
  11. package/dist/server/collaboration-ws-handler.d.ts +1 -1
  12. package/dist/server/connection-handler.d.ts +2 -2
  13. package/dist/server/connection-handler.d.ts.map +1 -1
  14. package/dist/server/context-meta.d.ts.map +1 -1
  15. package/dist/server/entry.js +1940 -452
  16. package/dist/server/entry.js.map +4 -4
  17. package/dist/server/goal-handlers.d.ts +1 -1
  18. package/dist/server/goal-routes.d.ts +10 -0
  19. package/dist/server/goal-routes.d.ts.map +1 -0
  20. package/dist/server/{autophase-ws-handler.d.ts → goal-ws-handler.d.ts} +18 -18
  21. package/dist/server/goal-ws-handler.d.ts.map +1 -0
  22. package/dist/server/handlers.js +1 -0
  23. package/dist/server/handlers.js.map +2 -2
  24. package/dist/server/http-server.d.ts +20 -0
  25. package/dist/server/http-server.d.ts.map +1 -1
  26. package/dist/server/index.d.ts +9 -8
  27. package/dist/server/index.d.ts.map +1 -1
  28. package/dist/server/instance-registry.d.ts +2 -2
  29. package/dist/server/instance-registry.d.ts.map +1 -1
  30. package/dist/server/kanban-routes.d.ts.map +1 -1
  31. package/dist/server/mcp-handlers.d.ts +1 -1
  32. package/dist/server/memory-handlers.d.ts +37 -0
  33. package/dist/server/memory-handlers.d.ts.map +1 -1
  34. package/dist/server/message-dispatcher.d.ts.map +1 -1
  35. package/dist/server/mode-handlers.d.ts.map +1 -1
  36. package/dist/server/pref-helpers.d.ts +1 -1
  37. package/dist/server/pref-helpers.d.ts.map +1 -1
  38. package/dist/server/provider-keys.d.ts.map +1 -1
  39. package/dist/server/routes.d.ts +5 -5
  40. package/dist/server/routes.d.ts.map +1 -1
  41. package/dist/server/server-runtime.d.ts +7 -1
  42. package/dist/server/server-runtime.d.ts.map +1 -1
  43. package/dist/server/session-handlers.d.ts.map +1 -1
  44. package/dist/server/setup-events.d.ts +1 -1
  45. package/dist/server/setup-events.d.ts.map +1 -1
  46. package/dist/server/start-webui.d.ts.map +1 -1
  47. package/dist/server/techstack-handlers.d.ts +101 -0
  48. package/dist/server/techstack-handlers.d.ts.map +1 -0
  49. package/dist/server/worktree-ws-handler.d.ts +1 -1
  50. package/dist/server/ws-payload-validation.d.ts.map +1 -1
  51. package/package.json +10 -12
  52. package/dist/server/autophase-routes.d.ts +0 -10
  53. package/dist/server/autophase-routes.d.ts.map +0 -1
  54. package/dist/server/autophase-ws-handler.d.ts.map +0 -1
@@ -9,7 +9,7 @@
9
9
  * unparseable file broadcasts `null` so clients clear stale goal state.
10
10
  */
11
11
  /**
12
- * Read `goal.json` for `projectRoot` and broadcast a `goal.updated` message.
12
+ * Read `goal.json` for `projectRoot` and broadcast a `goal-state.updated` message.
13
13
  * The path must match /goal, the autonomy engines, and TUI F9, which all
14
14
  * resolve via `resolveWstackPaths().projectGoal`
15
15
  * (`~/.wrongstack/projects/<slug>/goal.json`) — NOT the repo-local
@@ -0,0 +1,10 @@
1
+ import type { WebSocket } from 'ws';
2
+ import type { WSClientMessage } from './types.js';
3
+ export interface GoalRouteHandlers {
4
+ handleMessage: (msg: {
5
+ type: string;
6
+ payload?: Record<string, unknown>;
7
+ }) => Promise<void>;
8
+ }
9
+ export declare function handleGoalRoute(_ws: WebSocket, msg: WSClientMessage, handlers: GoalRouteHandlers): Promise<boolean>;
10
+ //# sourceMappingURL=goal-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal-routes.d.ts","sourceRoot":"","sources":["../../src/server/goal-routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,CAAC,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5F;AAED,wBAAsB,eAAe,CACnC,GAAG,EAAE,SAAS,EACd,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,iBAAiB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAIlB"}
@@ -1,22 +1,22 @@
1
1
  import type { WebSocket } from 'ws';
2
2
  import type { Agent, Context, EventBus, Logger } from '@wrongstack/core';
3
- interface AutoPhaseWSMessage {
3
+ interface GoalWSMessage {
4
4
  type: string;
5
5
  payload?: Record<string, unknown>;
6
6
  }
7
7
  /**
8
- * AutoPhaseWebSocketHandler — WebSocket-based AutoPhase control.
8
+ * GoalWebSocketHandler — WebSocket-based goal-driven phase execution.
9
9
  *
10
10
  * Message types:
11
- * autophase.start → { title, phases?, autonomous? }
12
- * autophase.pause → {}
13
- * autophase.resume → {}
14
- * autophase.stop → {}
15
- * autophase.status → {}
16
- * autophase.selectPhase → { phaseId }
17
- * autophase.taskStatus → { taskId, status }
11
+ * goal.start → { title, phases?, autonomous? }
12
+ * goal.pause → {}
13
+ * goal.resume → {}
14
+ * goal.stop → {}
15
+ * goal.status → {}
16
+ * goal.selectPhase → { phaseId }
17
+ * goal.taskStatus → { taskId, status }
18
18
  */
19
- export declare class AutoPhaseWebSocketHandler {
19
+ export declare class GoalWebSocketHandler {
20
20
  private agent;
21
21
  private context;
22
22
  private logger;
@@ -24,10 +24,10 @@ export declare class AutoPhaseWebSocketHandler {
24
24
  private projectRoot?;
25
25
  /**
26
26
  * Optional tap invoked on every board-state broadcast with the live
27
- * `buildState()` projection. Lets a KanbanRunMirror project the AutoPhase
27
+ * `buildState()` projection. Lets a KanbanRunMirror project the Goal
28
28
  * run into a kanban board without this handler knowing about kanban. The
29
29
  * WebUI orchestrator does NOT emit PhaseEventMap on the shared bus, so this
30
- * callback is the mirror's only live signal for AutoPhase.
30
+ * callback is the mirror's only live signal for Goal.
31
31
  */
32
32
  private onBoardState?;
33
33
  private orchestrator;
@@ -51,26 +51,26 @@ export declare class AutoPhaseWebSocketHandler {
51
51
  constructor(agent: Agent, context: Context, logger: Logger, storeDir: string, events?: EventBus | undefined, projectRoot?: string | undefined,
52
52
  /**
53
53
  * Optional tap invoked on every board-state broadcast with the live
54
- * `buildState()` projection. Lets a KanbanRunMirror project the AutoPhase
54
+ * `buildState()` projection. Lets a KanbanRunMirror project the Goal
55
55
  * run into a kanban board without this handler knowing about kanban. The
56
56
  * WebUI orchestrator does NOT emit PhaseEventMap on the shared bus, so this
57
- * callback is the mirror's only live signal for AutoPhase.
57
+ * callback is the mirror's only live signal for Goal.
58
58
  */
59
59
  onBoardState?: ((graphId: string, state: Record<string, unknown>) => void) | undefined);
60
60
  addClient(ws: WebSocket): void;
61
- handleMessage(msg: AutoPhaseWSMessage): Promise<void>;
61
+ handleMessage(msg: GoalWSMessage): Promise<void>;
62
62
  private handleStart;
63
63
  /**
64
64
  * Halt the run NOW — at any phase. Sets `stopping` (so a planning turn that
65
65
  * resolves afterwards bails), aborts in-flight agents, stops the orchestrator
66
66
  * tick, and ends the live broadcast. The board is kept for review; use
67
- * `autophase.clear` to reset or `autophase.revert` to undo the changes.
67
+ * `goal.clear` to reset or `goal.revert` to undo the changes.
68
68
  */
69
69
  private handleStop;
70
70
  /**
71
71
  * Stop + wipe: tear down phase worktrees and reset to an empty board so the UI
72
72
  * returns to the start screen ("new one"). Does NOT touch already-merged commits
73
- * on the base branch — that is `autophase.revert`.
73
+ * on the base branch — that is `goal.revert`.
74
74
  */
75
75
  private handleClear;
76
76
  /**
@@ -100,4 +100,4 @@ export declare class AutoPhaseWebSocketHandler {
100
100
  private send;
101
101
  }
102
102
  export {};
103
- //# sourceMappingURL=autophase-ws-handler.d.ts.map
103
+ //# sourceMappingURL=goal-ws-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal-ws-handler.d.ts","sourceRoot":"","sources":["../../src/server/goal-ws-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAYpC,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAoDzE,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,oBAAoB;IAqB7B,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,MAAM;IAEd,OAAO,CAAC,MAAM,CAAC;IACf,OAAO,CAAC,WAAW,CAAC;IACpB;;;;;;OAMG;IACH,OAAO,CAAC,YAAY,CAAC;IAjCvB,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,iBAAiB,CAA+C;IACxE,kFAAkF;IAClF,OAAO,CAAC,KAAK,CAAgC;IAC7C;;uFAEmF;IACnF,OAAO,CAAC,QAAQ,CAAS;IACzB,2EAA2E;IAC3E,OAAO,CAAC,SAAS,CAAgC;IACjD;oFACgF;IAChF,OAAO,CAAC,OAAO,CAAgD;IAC/D,wEAAwE;IACxE,OAAO,CAAC,aAAa,CAAqB;IAE1C,YACU,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EACR,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,EAC7B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS;IACxC;;;;;;OAMG;IACK,YAAY,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,EAG/F;IAED,SAAS,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI,CAS7B;IAEK,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAyGrD;YAEa,WAAW;IAwIzB;;;;;OAKG;YACW,UAAU;IASxB;;;;OAIG;YACW,WAAW;IAYzB;;;;;OAKG;YACW,YAAY;IAsB1B,4EAA4E;IAC5E,OAAO,CAAC,aAAa;IAUrB;;;4BAGwB;YACV,UAAU;YA2BV,oBAAoB;IA+BlC,+DAA+D;IAC/D,OAAO,CAAC,kBAAkB;YAKZ,sBAAsB;IAcpC,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,cAAc;IAkBtB,OAAO,CAAC,UAAU;IAqGlB,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,IAAI;CAKb"}
@@ -1,4 +1,5 @@
1
1
  // src/server/ws-payload-validation.ts
2
+ import { FORBIDDEN_PROTO_KEYS } from "@wrongstack/core/utils";
2
3
  function isRecord(value) {
3
4
  return typeof value === "object" && value !== null && !Array.isArray(value);
4
5
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/server/ws-payload-validation.ts", "../../src/server/handlers/worklist-handlers.ts"],
4
- "sourcesContent": ["export type PayloadValidationResult<T> = { ok: true; value: T } | { ok: false; message: string };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nexport interface ModelSwitchPayload {\n provider: string;\n model: string;\n}\n\nexport function validateModelSwitchPayload(\n payload: unknown,\n): PayloadValidationResult<ModelSwitchPayload> {\n if (!isRecord(payload)) {\n return {\n ok: false,\n message: 'model.switch payload must be an object with string provider and model',\n };\n }\n const provider = payload['provider'];\n const model = payload['model'];\n if (typeof provider !== 'string' || provider.trim().length === 0) {\n return { ok: false, message: 'model.switch payload.provider must be a non-empty string' };\n }\n if (typeof model !== 'string' || model.trim().length === 0) {\n return { ok: false, message: 'model.switch payload.model must be a non-empty string' };\n }\n return { ok: true, value: { provider, model } };\n}\n\nexport interface PrefsUpdatePayload {\n prefs: Record<string, unknown>;\n}\n\nconst AUTONOMY_VALUES = new Set(['off', 'suggest', 'auto', 'eternal', 'eternal-parallel']);\n\nexport interface MailboxMessagesPayload {\n limit?: number;\n agentId?: string;\n unreadOnly?: boolean;\n incompleteOnly?: boolean;\n}\n\nexport function validateMailboxMessagesPayload(\n payload: unknown,\n): PayloadValidationResult<MailboxMessagesPayload | undefined> {\n if (payload === undefined) return { ok: true, value: undefined };\n if (!isRecord(payload)) {\n return { ok: false, message: 'mailbox.messages payload must be an object when provided' };\n }\n const limit = payload['limit'];\n const agentId = payload['agentId'];\n const unreadOnly = payload['unreadOnly'];\n const incompleteOnly = payload['incompleteOnly'];\n if (limit !== undefined && (typeof limit !== 'number' || !Number.isFinite(limit) || limit < 1)) {\n return {\n ok: false,\n message: 'mailbox.messages payload.limit must be a positive number when provided',\n };\n }\n if (agentId !== undefined && typeof agentId !== 'string') {\n return {\n ok: false,\n message: 'mailbox.messages payload.agentId must be a string when provided',\n };\n }\n if (unreadOnly !== undefined && typeof unreadOnly !== 'boolean') {\n return {\n ok: false,\n message: 'mailbox.messages payload.unreadOnly must be a boolean when provided',\n };\n }\n if (incompleteOnly !== undefined && typeof incompleteOnly !== 'boolean') {\n return {\n ok: false,\n message: 'mailbox.messages payload.incompleteOnly must be a boolean when provided',\n };\n }\n return {\n ok: true,\n value: {\n ...(limit !== undefined ? { limit: limit as number } : {}),\n ...(agentId !== undefined ? { agentId: agentId as string } : {}),\n ...(unreadOnly !== undefined ? { unreadOnly: unreadOnly as boolean } : {}),\n ...(incompleteOnly !== undefined ? { incompleteOnly: incompleteOnly as boolean } : {}),\n },\n };\n}\n\nexport interface MailboxAgentsPayload {\n onlineOnly?: boolean;\n}\n\nexport function validateMailboxAgentsPayload(\n payload: unknown,\n): PayloadValidationResult<MailboxAgentsPayload | undefined> {\n if (payload === undefined) return { ok: true, value: undefined };\n if (!isRecord(payload)) {\n return { ok: false, message: 'mailbox.agents payload must be an object when provided' };\n }\n const onlineOnly = payload['onlineOnly'];\n if (onlineOnly !== undefined && typeof onlineOnly !== 'boolean') {\n return {\n ok: false,\n message: 'mailbox.agents payload.onlineOnly must be a boolean when provided',\n };\n }\n return {\n ok: true,\n value: { ...(onlineOnly !== undefined ? { onlineOnly: onlineOnly as boolean } : {}) },\n };\n}\n\nexport interface MailboxPurgePayload {\n completedMaxAgeMs?: number;\n incompleteMaxAgeMs?: number;\n}\n\nexport function validateMailboxPurgePayload(\n payload: unknown,\n): PayloadValidationResult<MailboxPurgePayload | undefined> {\n if (payload === undefined) return { ok: true, value: undefined };\n if (!isRecord(payload)) {\n return { ok: false, message: 'mailbox.purge payload must be an object when provided' };\n }\n const completedMaxAgeMs = payload['completedMaxAgeMs'];\n const incompleteMaxAgeMs = payload['incompleteMaxAgeMs'];\n if (\n completedMaxAgeMs !== undefined &&\n (typeof completedMaxAgeMs !== 'number' ||\n !Number.isFinite(completedMaxAgeMs) ||\n completedMaxAgeMs < 0)\n ) {\n return {\n ok: false,\n message:\n 'mailbox.purge payload.completedMaxAgeMs must be a non-negative number when provided',\n };\n }\n if (\n incompleteMaxAgeMs !== undefined &&\n (typeof incompleteMaxAgeMs !== 'number' ||\n !Number.isFinite(incompleteMaxAgeMs) ||\n incompleteMaxAgeMs < 0)\n ) {\n return {\n ok: false,\n message:\n 'mailbox.purge payload.incompleteMaxAgeMs must be a non-negative number when provided',\n };\n }\n return {\n ok: true,\n value: {\n ...(completedMaxAgeMs !== undefined ? { completedMaxAgeMs: completedMaxAgeMs as number } : {}),\n ...(incompleteMaxAgeMs !== undefined\n ? { incompleteMaxAgeMs: incompleteMaxAgeMs as number }\n : {}),\n },\n };\n}\n\nexport interface BrainRiskPayload {\n level: string;\n}\n\nconst BRAIN_RISK_VALUES = new Set(['off', 'low', 'medium', 'high', 'all']);\n\nexport function validateBrainRiskPayload(\n payload: unknown,\n): PayloadValidationResult<BrainRiskPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'brain.risk payload must be an object with string level' };\n }\n const level = payload['level'];\n if (typeof level !== 'string' || !BRAIN_RISK_VALUES.has(level)) {\n return {\n ok: false,\n message: 'brain.risk payload.level must be one of off, low, medium, high, all',\n };\n }\n return { ok: true, value: { level } };\n}\n\nexport interface BrainAskPayload {\n question: string;\n}\n\nexport function validateBrainAskPayload(\n payload: unknown,\n): PayloadValidationResult<BrainAskPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'brain.ask payload must be an object with string question' };\n }\n const question = payload['question'];\n if (typeof question !== 'string' || question.trim().length === 0) {\n return { ok: false, message: 'brain.ask payload.question must be a non-empty string' };\n }\n return { ok: true, value: { question: question.trim() } };\n}\n\nexport interface BrainConfigSetPayload {\n patch: Record<string, unknown>;\n}\n\nexport function validateBrainConfigSetPayload(\n payload: unknown,\n): PayloadValidationResult<BrainConfigSetPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'brain.config.set payload must be an object with a patch object' };\n }\n const patch = payload['patch'];\n if (!isRecord(patch)) {\n // Field-level validation happens in BrainRuntime.apply(), which throws\n // BEFORE any live state changes \u2014 this only guards the envelope shape.\n return { ok: false, message: 'brain.config.set payload.patch must be an object' };\n }\n return { ok: true, value: { patch } };\n}\n\nexport interface AutonomySwitchPayload {\n mode: string;\n}\n\nexport function validateAutonomySwitchPayload(\n payload: unknown,\n): PayloadValidationResult<AutonomySwitchPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'autonomy.switch payload must be an object with string mode' };\n }\n const mode = payload['mode'];\n if (typeof mode !== 'string' || !AUTONOMY_VALUES.has(mode)) {\n return { ok: false, message: 'autonomy.switch payload.mode must be a valid autonomy mode' };\n }\n return { ok: true, value: { mode } };\n}\n\nexport interface PlanTemplateUsePayload {\n template: string;\n}\n\nexport function validatePlanTemplateUsePayload(\n payload: unknown,\n): PayloadValidationResult<PlanTemplateUsePayload> {\n if (!isRecord(payload)) {\n return {\n ok: false,\n message: 'plan.template_use payload must be an object with string template',\n };\n }\n const template = payload['template'];\n if (typeof template !== 'string' || template.trim().length === 0) {\n return { ok: false, message: 'plan.template_use payload.template must be a non-empty string' };\n }\n return { ok: true, value: { template } };\n}\nconst CONTEXT_STRATEGY_VALUES = new Set(['hybrid', 'intelligent', 'selective']);\nconst CONTEXT_MODE_VALUES = new Set(['balanced', 'frugal', 'deep', 'archival']);\nconst TOKEN_SAVING_TIER_VALUES = new Set(['off', 'minimal', 'light', 'medium', 'aggressive']);\nconst ENHANCE_LANGUAGE_VALUES = new Set(['original', 'english']);\nconst LOG_LEVEL_VALUES = new Set(['debug', 'info', 'warn', 'error']);\nconst AUDIT_LEVEL_VALUES = new Set(['minimal', 'standard', 'full']);\nconst REASONING_MODE_VALUES = new Set(['auto', 'on', 'off']);\nconst REASONING_EFFORT_VALUES = new Set([\n 'none',\n 'minimal',\n 'low',\n 'medium',\n 'high',\n 'xhigh',\n 'max',\n]);\nconst CACHE_TTL_VALUES = new Set(['default', '5m', '1h']);\n\nconst BOOLEAN_PREF_KEYS = new Set([\n 'yolo',\n 'chime',\n 'confirmExit',\n 'streamFleet',\n 'nextPrediction',\n 'titleAnimation',\n 'enhanceEnabled',\n 'featureMcp',\n 'featurePlugins',\n 'featureMemory',\n 'featureSkills',\n 'featureModelsRegistry',\n 'indexOnStart',\n 'contextAutoCompact',\n 'tgSessionEnd',\n 'tgDelegate',\n 'reasoningPreserve',\n 'hqEnabled',\n 'hqRawContent',\n 'fallbackAuto',\n 'favoriteModelsOnly',\n 'breakerEnabled',\n 'debugStream',\n]);\n\n/** Keys whose value must be an array of strings (e.g. an ordered model list). */\nconst STRING_ARRAY_PREF_KEYS = new Set(['fallbackModels', 'favoriteModels']);\nconst STRING_ARRAY_RECORD_PREF_KEYS = new Set(['fallbackProfiles']);\nconst MODEL_MATRIX_PREF_KEYS = new Set(['modelMatrix']);\n\nconst NUMBER_PREF_KEYS = new Set([\n 'autonomyDelayMs',\n 'autoProceedMaxIterations',\n 'maxIterations',\n 'maxConcurrent',\n 'enhanceDelayMs',\n 'tgLongToolMs',\n 'breakerAutoKillResetMs',\n]);\n\nconst STRING_PREF_KEYS = new Set([\n 'hqUrl',\n 'hqToken',\n 'uiLocale',\n 'thinkingWord',\n 'refinerProvider',\n 'refinerModel',\n 'refinerFallbackProfile',\n]);\n\nconst ENUM_PREF_KEYS: Record<string, Set<string>> = {\n autonomy: AUTONOMY_VALUES,\n contextStrategy: CONTEXT_STRATEGY_VALUES,\n contextMode: CONTEXT_MODE_VALUES,\n tokenSavingTier: TOKEN_SAVING_TIER_VALUES,\n enhanceLanguage: ENHANCE_LANGUAGE_VALUES,\n logLevel: LOG_LEVEL_VALUES,\n auditLevel: AUDIT_LEVEL_VALUES,\n reasoningMode: REASONING_MODE_VALUES,\n reasoningEffort: REASONING_EFFORT_VALUES,\n cacheTtl: CACHE_TTL_VALUES,\n statuslineMode: new Set(['minimum', 'detailed', 'no-color']),\n animationStyle: new Set(['rainbow', 'wave', 'pulse', 'dots', 'breathe', 'cycle']),\n fsAccess: new Set(['unrestricted', 'project']),\n};\n\nfunction validateModelRuntimeValue(modelRuntime: Record<string, unknown>, path: string): string | null {\n const reasoning = modelRuntime['reasoning'];\n if (reasoning !== undefined) {\n if (!isRecord(reasoning)) return `${path}.reasoning must be an object when provided`;\n const mode = reasoning['mode'];\n const effort = reasoning['effort'];\n const preserve = reasoning['preserve'];\n if (mode !== undefined && (typeof mode !== 'string' || !REASONING_MODE_VALUES.has(mode))) {\n return `${path}.reasoning.mode must be one of: ${Array.from(REASONING_MODE_VALUES).join(', ')}`;\n }\n if (effort !== undefined && (typeof effort !== 'string' || !REASONING_EFFORT_VALUES.has(effort))) {\n return `${path}.reasoning.effort must be one of: ${Array.from(REASONING_EFFORT_VALUES).join(', ')}`;\n }\n if (preserve !== undefined && typeof preserve !== 'boolean') {\n return `${path}.reasoning.preserve must be a boolean when provided`;\n }\n }\n\n const cache = modelRuntime['cache'];\n if (cache !== undefined) {\n if (!isRecord(cache)) return `${path}.cache must be an object when provided`;\n const ttl = cache['ttl'];\n if (ttl !== undefined && (typeof ttl !== 'string' || !CACHE_TTL_VALUES.has(ttl) || ttl === 'default')) {\n return `${path}.cache.ttl must be one of: 5m, 1h`;\n }\n }\n\n const parameters = modelRuntime['parameters'];\n if (parameters !== undefined && !isRecord(parameters)) {\n return `${path}.parameters must be an object when provided`;\n }\n\n return null;\n}\n\nfunction validatePreferenceValue(key: string, value: unknown): string | null {\n if (BOOLEAN_PREF_KEYS.has(key)) {\n return typeof value === 'boolean' ? null : `prefs.update payload.${key} must be a boolean`;\n }\n if (NUMBER_PREF_KEYS.has(key)) {\n return typeof value === 'number' && Number.isFinite(value)\n ? null\n : `prefs.update payload.${key} must be a finite number`;\n }\n if (STRING_PREF_KEYS.has(key)) {\n return typeof value === 'string' ? null : `prefs.update payload.${key} must be a string`;\n }\n if (STRING_ARRAY_PREF_KEYS.has(key)) {\n return Array.isArray(value) && value.every((v) => typeof v === 'string')\n ? null\n : `prefs.update payload.${key} must be an array of strings`;\n }\n if (STRING_ARRAY_RECORD_PREF_KEYS.has(key)) {\n return isRecord(value) &&\n Object.values(value).every(\n (v) => Array.isArray(v) && v.every((item) => typeof item === 'string'),\n )\n ? null\n : `prefs.update payload.${key} must be an object of string arrays`;\n }\n if (MODEL_MATRIX_PREF_KEYS.has(key)) {\n if (!isRecord(value)) return `prefs.update payload.${key} must be an object`;\n for (const entry of Object.values(value)) {\n if (!isRecord(entry)) return `prefs.update payload.${key} entries must be objects`;\n const provider = entry['provider'];\n const model = entry['model'];\n const fallbackProfile = entry['fallbackProfile'];\n const modelRuntime = entry['modelRuntime'];\n if (provider !== undefined && typeof provider !== 'string') {\n return `prefs.update payload.${key}.provider must be a string when provided`;\n }\n if (model !== undefined && typeof model !== 'string') {\n return `prefs.update payload.${key}.model must be a string when provided`;\n }\n if (fallbackProfile !== undefined && typeof fallbackProfile !== 'string') {\n return `prefs.update payload.${key}.fallbackProfile must be a string when provided`;\n }\n if (modelRuntime !== undefined && !isRecord(modelRuntime)) {\n return `prefs.update payload.${key}.modelRuntime must be an object when provided`;\n }\n if (isRecord(modelRuntime)) {\n const runtimeError = validateModelRuntimeValue(modelRuntime, `prefs.update payload.${key}.modelRuntime`);\n if (runtimeError) return runtimeError;\n }\n if (model === undefined && fallbackProfile === undefined && modelRuntime === undefined) {\n return `prefs.update payload.${key} entries require model, fallbackProfile, or modelRuntime`;\n }\n }\n return null;\n }\n const allowed = ENUM_PREF_KEYS[key];\n if (allowed) {\n return typeof value === 'string' && allowed.has(value)\n ? null\n : `prefs.update payload.${key} must be one of: ${Array.from(allowed).join(', ')}`;\n }\n return `prefs.update payload contains unknown preference key: ${key}`;\n}\n\nexport function validatePrefsUpdatePayload(\n payload: unknown,\n): PayloadValidationResult<PrefsUpdatePayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'prefs.update payload must be an object' };\n }\n for (const [key, value] of Object.entries(payload)) {\n const error = validatePreferenceValue(key, value);\n if (error) return { ok: false, message: error };\n }\n return { ok: true, value: { prefs: payload } };\n}\n\nexport interface SkillsCreatePayload {\n name: string;\n description: string;\n scope: 'project' | 'global';\n}\n\nexport function validateSkillsCreatePayload(\n payload: unknown,\n): PayloadValidationResult<SkillsCreatePayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'skills.create payload must be an object' };\n }\n const name = payload['name'];\n const description = payload['description'];\n const scope = payload['scope'];\n if (typeof name !== 'string' || name.trim().length === 0) {\n return { ok: false, message: 'Skill name is required' };\n }\n if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(name.trim())) {\n return { ok: false, message: 'Skill name must be kebab-case (e.g. my-new-skill)' };\n }\n if (typeof description !== 'string' || description.trim().length === 0) {\n return { ok: false, message: 'Description/trigger is required' };\n }\n if (scope !== 'project' && scope !== 'global') {\n return { ok: false, message: 'skills.create payload.scope must be project or global' };\n }\n return { ok: true, value: { name, description, scope } };\n}\n\nexport interface SkillsEditPayload {\n name: string;\n body: string;\n}\n\nexport function validateSkillsEditPayload(\n payload: unknown,\n): PayloadValidationResult<SkillsEditPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'skills.edit payload must be an object' };\n }\n const name = payload['name'];\n const body = payload['body'];\n if (typeof name !== 'string' || name.trim().length === 0) {\n return { ok: false, message: 'Skill name is required' };\n }\n if (typeof body !== 'string' || body.length === 0) {\n return { ok: false, message: 'Skill body is required' };\n }\n return { ok: true, value: { name, body } };\n}\n\nexport interface ProcessKillPayload {\n pid: number;\n}\n\nexport function validateProcessKillPayload(\n payload: unknown,\n): PayloadValidationResult<ProcessKillPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'process.kill payload must be an object with numeric pid' };\n }\n const pid = payload['pid'];\n if (typeof pid !== 'number' || !Number.isInteger(pid) || pid <= 0) {\n return { ok: false, message: 'process.kill payload.pid must be a positive integer' };\n }\n return { ok: true, value: { pid } };\n}\n\nexport interface WorkingDirSetPayload {\n path: string;\n}\n\nexport function validateWorkingDirSetPayload(\n payload: unknown,\n): PayloadValidationResult<WorkingDirSetPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'working_dir.set payload must be an object with string path' };\n }\n const newPath = payload['path'];\n if (typeof newPath !== 'string' || newPath.trim().length === 0) {\n return { ok: false, message: 'working_dir.set payload.path must be a non-empty string' };\n }\n return { ok: true, value: { path: newPath } };\n}\n\nexport interface ModeSwitchPayload {\n id: string;\n}\n\nexport function validateModeSwitchPayload(\n payload: unknown,\n): PayloadValidationResult<ModeSwitchPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'mode.switch payload must be an object with string id' };\n }\n const id = payload['id'];\n if (typeof id !== 'string' || id.trim().length === 0) {\n return { ok: false, message: 'mode.switch payload.id must be a non-empty string' };\n }\n return { ok: true, value: { id } };\n}\n\nexport interface ContextModeIdPayload {\n id: string;\n}\n\nfunction validateContextModeIdPayload(\n payload: unknown,\n type: 'context.mode.switch' | 'context.mode.delete',\n): PayloadValidationResult<ContextModeIdPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: `${type} payload must be an object with string id` };\n }\n const id = payload['id'];\n if (typeof id !== 'string' || id.trim().length === 0) {\n return { ok: false, message: `${type} payload.id must be a non-empty string` };\n }\n return { ok: true, value: { id } };\n}\n\nexport function validateContextModeSwitchPayload(\n payload: unknown,\n): PayloadValidationResult<ContextModeIdPayload> {\n return validateContextModeIdPayload(payload, 'context.mode.switch');\n}\n\nexport function validateContextModeDeletePayload(\n payload: unknown,\n): PayloadValidationResult<ContextModeIdPayload> {\n return validateContextModeIdPayload(payload, 'context.mode.delete');\n}\n\nexport interface ContextModeCreatePayload {\n id: string;\n name: string;\n description: string;\n thresholds: { warn: number; soft: number; hard: number };\n preserveK: number;\n eliseThreshold: number;\n}\n\nfunction isFiniteNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value);\n}\n\nexport function validateContextModeCreatePayload(\n payload: unknown,\n): PayloadValidationResult<ContextModeCreatePayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'context.mode.create payload must be an object' };\n }\n const id = payload['id'];\n const name = payload['name'];\n const description = payload['description'];\n const thresholds = payload['thresholds'];\n const preserveK = payload['preserveK'];\n const eliseThreshold = payload['eliseThreshold'];\n\n if (typeof id !== 'string' || id.trim().length === 0) {\n return { ok: false, message: 'context.mode.create payload.id must be a non-empty string' };\n }\n if (typeof name !== 'string' || name.trim().length === 0) {\n return { ok: false, message: 'context.mode.create payload.name must be a non-empty string' };\n }\n if (typeof description !== 'string') {\n return { ok: false, message: 'context.mode.create payload.description must be a string' };\n }\n if (!isRecord(thresholds)) {\n return {\n ok: false,\n message:\n 'context.mode.create payload.thresholds must be an object with warn/soft/hard numbers',\n };\n }\n if (\n !isFiniteNumber(thresholds['warn']) ||\n !isFiniteNumber(thresholds['soft']) ||\n !isFiniteNumber(thresholds['hard'])\n ) {\n return {\n ok: false,\n message: 'context.mode.create payload.thresholds.warn/soft/hard must be finite numbers',\n };\n }\n if (!isFiniteNumber(preserveK)) {\n return { ok: false, message: 'context.mode.create payload.preserveK must be a finite number' };\n }\n if (!isFiniteNumber(eliseThreshold)) {\n return {\n ok: false,\n message: 'context.mode.create payload.eliseThreshold must be a finite number',\n };\n }\n return {\n ok: true,\n value: {\n id,\n name,\n description,\n thresholds: { warn: thresholds['warn'], soft: thresholds['soft'], hard: thresholds['hard'] },\n preserveK,\n eliseThreshold,\n },\n };\n}\n\nexport interface ContextModeUpdatePayload {\n id: string;\n name?: string;\n description?: string;\n thresholds?: { warn?: number; soft?: number; hard?: number };\n preserveK?: number;\n eliseThreshold?: number;\n}\n\nexport function validateContextModeUpdatePayload(\n payload: unknown,\n): PayloadValidationResult<ContextModeUpdatePayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'context.mode.update payload must be an object' };\n }\n const id = payload['id'];\n if (typeof id !== 'string' || id.trim().length === 0) {\n return { ok: false, message: 'context.mode.update payload.id must be a non-empty string' };\n }\n\n const name = payload['name'];\n if (name !== undefined && typeof name !== 'string') {\n return {\n ok: false,\n message: 'context.mode.update payload.name must be a string when provided',\n };\n }\n\n const description = payload['description'];\n if (description !== undefined && typeof description !== 'string') {\n return {\n ok: false,\n message: 'context.mode.update payload.description must be a string when provided',\n };\n }\n\n const thresholds = payload['thresholds'];\n let validatedThresholds: ContextModeUpdatePayload['thresholds'];\n if (thresholds !== undefined) {\n if (!isRecord(thresholds)) {\n return {\n ok: false,\n message: 'context.mode.update payload.thresholds must be an object when provided',\n };\n }\n for (const key of ['warn', 'soft', 'hard'] as const) {\n const val = thresholds[key];\n if (val !== undefined && !isFiniteNumber(val)) {\n return {\n ok: false,\n message: `context.mode.update payload.thresholds.${key} must be a finite number when provided`,\n };\n }\n }\n validatedThresholds = {\n ...(typeof thresholds['warn'] === 'number' ? { warn: thresholds['warn'] } : {}),\n ...(typeof thresholds['soft'] === 'number' ? { soft: thresholds['soft'] } : {}),\n ...(typeof thresholds['hard'] === 'number' ? { hard: thresholds['hard'] } : {}),\n };\n }\n\n const preserveK = payload['preserveK'];\n if (preserveK !== undefined && !isFiniteNumber(preserveK)) {\n return {\n ok: false,\n message: 'context.mode.update payload.preserveK must be a finite number when provided',\n };\n }\n\n const eliseThreshold = payload['eliseThreshold'];\n if (eliseThreshold !== undefined && !isFiniteNumber(eliseThreshold)) {\n return {\n ok: false,\n message: 'context.mode.update payload.eliseThreshold must be a finite number when provided',\n };\n }\n\n return {\n ok: true,\n value: {\n id,\n ...(typeof name === 'string' ? { name } : {}),\n ...(typeof description === 'string' ? { description } : {}),\n ...(validatedThresholds !== undefined ? { thresholds: validatedThresholds } : {}),\n ...(typeof preserveK === 'number' ? { preserveK } : {}),\n ...(typeof eliseThreshold === 'number' ? { eliseThreshold } : {}),\n },\n };\n}\n\nexport interface ShellOpenPayload {\n path: string;\n target?: 'file' | 'terminal';\n}\n\nexport function validateShellOpenPayload(\n payload: unknown,\n): PayloadValidationResult<ShellOpenPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'shell.open payload must be an object with string path' };\n }\n const path = payload['path'];\n if (typeof path !== 'string' || path.trim().length === 0) {\n return { ok: false, message: 'shell.open payload.path must be a non-empty string' };\n }\n const target = payload['target'];\n if (target !== undefined && target !== 'file' && target !== 'terminal') {\n return {\n ok: false,\n message: 'shell.open payload.target must be \"file\" or \"terminal\" when provided',\n };\n }\n return {\n ok: true,\n value: {\n path,\n ...(target !== undefined ? { target: target as 'file' | 'terminal' } : {}),\n },\n };\n}\n\nexport interface GitDiffPayload {\n path: string;\n}\n\nexport function validateGitDiffPayload(payload: unknown): PayloadValidationResult<GitDiffPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'git.diff payload must be an object' };\n }\n const path = payload['path'];\n if (path === undefined || path === null) {\n return { ok: true, value: { path: '' } };\n }\n if (typeof path !== 'string') {\n return { ok: false, message: 'git.diff payload.path must be a string when provided' };\n }\n return { ok: true, value: { path } };\n}\n\nexport interface ProjectsAddPayload {\n root: string;\n name?: string;\n}\n\nexport function validateProjectsAddPayload(\n payload: unknown,\n): PayloadValidationResult<ProjectsAddPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'projects.add payload must be an object with string root' };\n }\n const root = payload['root'];\n if (typeof root !== 'string' || root.trim().length === 0) {\n return { ok: false, message: 'projects.add payload.root must be a non-empty string' };\n }\n const name = payload['name'];\n if (name !== undefined && typeof name !== 'string') {\n return { ok: false, message: 'projects.add payload.name must be a string when provided' };\n }\n return {\n ok: true,\n value: { root, ...(typeof name === 'string' ? { name } : {}) },\n };\n}\n\nexport interface ProjectsSelectPayload {\n root: string;\n name?: string;\n}\n\nexport function validateProjectsSelectPayload(\n payload: unknown,\n): PayloadValidationResult<ProjectsSelectPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'projects.select payload must be an object with string root' };\n }\n const root = payload['root'];\n if (typeof root !== 'string' || root.trim().length === 0) {\n return { ok: false, message: 'projects.select payload.root must be a non-empty string' };\n }\n const name = payload['name'];\n if (name !== undefined && typeof name !== 'string') {\n return { ok: false, message: 'projects.select payload.name must be a string when provided' };\n }\n return {\n ok: true,\n value: { root, ...(typeof name === 'string' ? { name } : {}) },\n };\n}\n", "// \u2500\u2500 Shared Worklist Handlers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Extracted from standalone server (packages/webui/src/server/index.ts) and CLI\n// embedded server (packages/cli/src/webui-server/). Both servers use these\n// handlers for todos, tasks, and plan operations. Keep them in sync.\n//\n// Message types handled here:\n// todos.get | todos.clear | todos.remove | todo.update\n// tasks.get | task.update\n// plan.get | plan.template_use | plan.item.update\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nimport type { WebSocket } from 'ws';\nimport type { TodoItem } from '@wrongstack/core';\nimport { validatePlanTemplateUsePayload } from '../ws-payload-validation.js';\n\n// \u2500\u2500 Shared result helper \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction sendResult(\n ws: WebSocket,\n ctx: WorklistContext,\n ok: boolean,\n message: string,\n): void {\n ctx.send(ws, { type: ok ? 'ok' : 'error', message });\n}\n\nfunction sessionPayload<T extends Record<string, unknown>>(\n ctx: WorklistContext,\n payload: T,\n): T & { sessionId: string } {\n const provided = payload['sessionId'];\n const fallback = ctx.context.session?.id ?? '';\n const sessionId = typeof provided === 'string' && provided.length > 0 ? provided : fallback;\n return { ...payload, sessionId };\n}\n\n// \u2500\u2500 Context interface \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Both servers satisfy this with their own local state.\n\nexport interface WorklistContext {\n context: {\n todos: TodoItem[];\n meta: Record<string, unknown>;\n session: { id: string } | null;\n state?: unknown;\n };\n send: (ws: WebSocket, msg: object) => void;\n broadcast: (msg: object) => void;\n /**\n * Optional mutator for in-memory todo state. Servers that manage live\n * agent state (e.g. the CLI embedded server) provide this so handlers\n * can update the agent's todo list directly. Standalone server may omit.\n */\n replaceTodos?: (todos: TodoItem[]) => void;\n}\n\n// \u2500\u2500 Todos \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function handleTodosGet(ctx: WorklistContext, ws: WebSocket): void {\n ctx.send(ws, { type: 'todos.updated', payload: sessionPayload(ctx, { todos: ctx.context.todos }) });\n}\n\nexport function handleTodosClear(ctx: WorklistContext, ws: WebSocket): void {\n ctx.replaceTodos?.([]);\n ctx.broadcast({ type: 'todos.cleared', payload: sessionPayload(ctx, {}) });\n sendResult(ws, ctx, true, 'Todo board cleared.');\n}\n\nexport function handleTodosRemove(\n ctx: WorklistContext,\n ws: WebSocket,\n payload: { id?: string; index?: number } | undefined,\n): void {\n if (!payload || (payload.id === undefined && payload.index === undefined)) {\n sendResult(ws, ctx, false, 'todos.remove requires id or index.');\n return;\n }\n const next =\n payload.id !== undefined\n ? ctx.context.todos.filter((t) => t.id !== payload.id)\n : ctx.context.todos.filter((_, i) => i !== (payload.index as number));\n ctx.replaceTodos?.(next);\n ctx.broadcast({ type: 'todos.updated', payload: sessionPayload(ctx, { todos: next }) });\n sendResult(ws, ctx, true, 'Todo item removed.');\n}\n\nexport function handleTodoUpdate(\n ctx: WorklistContext,\n ws: WebSocket,\n payload: { id: string; status?: TodoItem['status']; activeForm?: string },\n): void {\n const todo = ctx.context.todos.find((t) => t.id === payload.id);\n if (!todo) {\n sendResult(ws, ctx, false, `No todo with id \"${payload.id}\".`);\n return;\n }\n const next = ctx.context.todos.map((t) =>\n t.id === payload.id\n ? { ...t, ...(payload.status !== undefined && { status: payload.status }), ...(payload.activeForm !== undefined && { activeForm: payload.activeForm }) }\n : t,\n );\n ctx.replaceTodos?.(next);\n ctx.broadcast({ type: 'todos.updated', payload: sessionPayload(ctx, { todos: next }) });\n sendResult(ws, ctx, true, `Todo \"${todo.content}\" updated.`);\n}\n\n// \u2500\u2500 Tasks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport async function handleTasksGet(ctx: WorklistContext, ws: WebSocket): Promise<void> {\n const taskPath = ctx.context.meta['task.path'];\n if (typeof taskPath === 'string' && taskPath) {\n try {\n const { loadTasks } = await import('@wrongstack/core');\n const file = await loadTasks(taskPath);\n ctx.send(ws, { type: 'tasks.updated', payload: sessionPayload(ctx, { tasks: file?.tasks ?? [] }) });\n } catch {\n ctx.send(ws, { type: 'tasks.updated', payload: sessionPayload(ctx, { tasks: [] }) });\n }\n } else {\n ctx.send(ws, {\n type: 'tasks.updated',\n payload: sessionPayload(ctx, { tasks: [], error: 'Task storage not configured.' }),\n });\n }\n}\n\nexport async function handleTaskUpdate(\n ctx: WorklistContext,\n ws: WebSocket,\n payload: {\n id: string;\n status: 'pending' | 'in_progress' | 'blocked' | 'failed' | 'review' | 'completed';\n },\n): Promise<void> {\n const taskPath = ctx.context.meta['task.path'];\n if (typeof taskPath !== 'string' || !taskPath) {\n sendResult(ws, ctx, false, 'Task storage is not configured for this session.');\n return;\n }\n try {\n const { loadTasks, saveTasks } = await import('@wrongstack/core');\n const file = await loadTasks(taskPath);\n if (!file) {\n sendResult(ws, ctx, false, 'No task file found.');\n return;\n }\n const idx = file.tasks.findIndex((t) => t.id === payload.id);\n if (idx === -1) {\n sendResult(ws, ctx, false, `Task \"${payload.id}\" not found.`);\n return;\n }\n const existing = file.tasks[idx];\n if (!existing) {\n // noUncheckedIndexedAccess: array access can be undefined. We just\n // confirmed idx is in-range via findIndex, but the type system needs\n // the explicit guard.\n sendResult(ws, ctx, false, `Task \"${payload.id}\" not found.`);\n return;\n }\n file.tasks[idx] = { ...existing, status: payload.status };\n await saveTasks(taskPath, file);\n ctx.broadcast({ type: 'tasks.updated', payload: sessionPayload(ctx, { tasks: file.tasks }) });\n sendResult(ws, ctx, true, `Task \"${payload.id}\" marked ${payload.status}.`);\n } catch (err) {\n sendResult(ws, ctx, false, String(err));\n }\n}\n\n// \u2500\u2500 Plan \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport async function handlePlanGet(ctx: WorklistContext, ws: WebSocket): Promise<void> {\n const planPath = ctx.context.meta['plan.path'];\n const sessionId = ctx.context.session?.id ?? '';\n if (typeof planPath === 'string' && planPath) {\n try {\n const { loadPlan } = await import('@wrongstack/core');\n const plan = await loadPlan(planPath);\n ctx.send(ws, {\n type: 'plan.updated',\n payload: sessionPayload(ctx, {\n plan: plan ?? {\n version: 1,\n sessionId,\n updatedAt: new Date().toISOString(),\n items: [],\n },\n }),\n });\n } catch {\n ctx.send(ws, {\n type: 'plan.updated',\n payload: sessionPayload(ctx, {\n plan: {\n version: 1,\n sessionId,\n updatedAt: new Date().toISOString(),\n items: [],\n },\n }),\n });\n }\n } else {\n ctx.send(ws, {\n type: 'plan.updated',\n payload: sessionPayload(ctx, { plan: null, error: 'Plan storage is not configured for this session.' }),\n });\n }\n}\n\nexport async function handlePlanTemplateUse(ctx: WorklistContext, ws: WebSocket, template: string): Promise<void> {\n const planPath = ctx.context.meta['plan.path'];\n const sessionId = ctx.context.session?.id ?? '';\n if (typeof planPath !== 'string' || !planPath) {\n sendResult(ws, ctx, false, 'Plan storage is not configured for this session.');\n return;\n }\n try {\n const { getPlanTemplate, loadPlan, savePlan, emptyPlan, addPlanItem } = await import('@wrongstack/core');\n const tpl = getPlanTemplate(template);\n if (!tpl) {\n sendResult(ws, ctx, false, `Unknown template \"${template}\".`);\n return;\n }\n let plan = (await loadPlan(planPath)) ?? emptyPlan(sessionId);\n for (const item of tpl.items) {\n ({ plan } = addPlanItem(plan, item.title, item.details));\n }\n await savePlan(planPath, plan);\n sendResult(ws, ctx, true, `Applied template \"${tpl.name}\" \u2014 ${tpl.items.length} items added.`);\n ctx.broadcast({ type: 'plan.updated', payload: sessionPayload(ctx, { plan }) });\n } catch (err) {\n sendResult(ws, ctx, false, String(err));\n }\n}\n\nexport async function handlePlanItemUpdate(\n ctx: WorklistContext,\n ws: WebSocket,\n payload: { target: string; status: 'open' | 'in_progress' | 'done' },\n): Promise<void> {\n const planPath = ctx.context.meta['plan.path'];\n const sessionId = ctx.context.session?.id ?? '';\n if (typeof planPath !== 'string' || !planPath) {\n sendResult(ws, ctx, false, 'Plan storage is not configured for this session.');\n return;\n }\n try {\n const { mutatePlan, setPlanItemStatus } = await import('@wrongstack/core');\n let changed = false;\n const plan = await mutatePlan(planPath, sessionId, async (p) => {\n const before = p.updatedAt;\n const updated = setPlanItemStatus(p, payload.target, payload.status);\n changed = updated.updatedAt !== before;\n return updated;\n });\n if (!changed) {\n sendResult(ws, ctx, false, `No plan item matched \"${payload.target}\".`);\n return;\n }\n sendResult(ws, ctx, true, `Plan item status updated to \"${payload.status}\".`);\n ctx.broadcast({ type: 'plan.updated', payload: sessionPayload(ctx, { plan }) });\n } catch (err) {\n sendResult(ws, ctx, false, String(err));\n }\n}\n\n// \u2500\u2500 Dispatcher \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Single entry point for the nine worklist message types, so the host server's\n// switch delegates one grouped case here instead of repeating the per-type\n// `makeWorklistContext()` boilerplate. Unknown types are a no-op (the caller\n// only routes worklist types to this function).\n\n/** Loosely-typed worklist WS message \u2014 payload shapes are narrowed per case. */\nexport interface WorklistMessage {\n type: string;\n payload?: unknown;\n}\n\nexport async function handleWorklistMessage(\n ctx: WorklistContext,\n ws: WebSocket,\n msg: WorklistMessage,\n): Promise<void> {\n switch (msg.type) {\n case 'todos.get':\n handleTodosGet(ctx, ws);\n return;\n case 'todos.clear':\n handleTodosClear(ctx, ws);\n return;\n case 'todos.remove':\n handleTodosRemove(ctx, ws, msg.payload as { id?: string; index?: number } | undefined);\n return;\n case 'todo.update':\n handleTodoUpdate(\n ctx,\n ws,\n msg.payload as { id: string; status?: TodoItem['status']; activeForm?: string },\n );\n return;\n case 'tasks.get':\n await handleTasksGet(ctx, ws);\n return;\n case 'task.update':\n await handleTaskUpdate(\n ctx,\n ws,\n msg.payload as {\n id: string;\n status: 'pending' | 'in_progress' | 'blocked' | 'failed' | 'review' | 'completed';\n },\n );\n return;\n case 'plan.get':\n await handlePlanGet(ctx, ws);\n return;\n case 'plan.template_use': {\n const parsed = validatePlanTemplateUsePayload(msg.payload);\n if (!parsed.ok) {\n sendResult(ws, ctx, false, parsed.message);\n return;\n }\n await handlePlanTemplateUse(ctx, ws, parsed.value.template);\n return;\n }\n case 'plan.item.update':\n await handlePlanItemUpdate(\n ctx,\n ws,\n msg.payload as { target: string; status: 'open' | 'in_progress' | 'done' },\n );\n return;\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { FORBIDDEN_PROTO_KEYS } from '@wrongstack/core/utils';\n\nexport type PayloadValidationResult<T> = { ok: true; value: T } | { ok: false; message: string };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nexport interface ModelSwitchPayload {\n provider: string;\n model: string;\n}\n\nexport function validateModelSwitchPayload(\n payload: unknown,\n): PayloadValidationResult<ModelSwitchPayload> {\n if (!isRecord(payload)) {\n return {\n ok: false,\n message: 'model.switch payload must be an object with string provider and model',\n };\n }\n const provider = payload['provider'];\n const model = payload['model'];\n if (typeof provider !== 'string' || provider.trim().length === 0) {\n return { ok: false, message: 'model.switch payload.provider must be a non-empty string' };\n }\n if (typeof model !== 'string' || model.trim().length === 0) {\n return { ok: false, message: 'model.switch payload.model must be a non-empty string' };\n }\n return { ok: true, value: { provider, model } };\n}\n\nexport interface PrefsUpdatePayload {\n prefs: Record<string, unknown>;\n}\n\nconst AUTONOMY_VALUES = new Set(['off', 'suggest', 'auto', 'eternal', 'eternal-parallel']);\n\nexport interface MailboxMessagesPayload {\n limit?: number;\n agentId?: string;\n unreadOnly?: boolean;\n incompleteOnly?: boolean;\n}\n\nexport function validateMailboxMessagesPayload(\n payload: unknown,\n): PayloadValidationResult<MailboxMessagesPayload | undefined> {\n if (payload === undefined) return { ok: true, value: undefined };\n if (!isRecord(payload)) {\n return { ok: false, message: 'mailbox.messages payload must be an object when provided' };\n }\n const limit = payload['limit'];\n const agentId = payload['agentId'];\n const unreadOnly = payload['unreadOnly'];\n const incompleteOnly = payload['incompleteOnly'];\n if (limit !== undefined && (typeof limit !== 'number' || !Number.isFinite(limit) || limit < 1)) {\n return {\n ok: false,\n message: 'mailbox.messages payload.limit must be a positive number when provided',\n };\n }\n if (agentId !== undefined && typeof agentId !== 'string') {\n return {\n ok: false,\n message: 'mailbox.messages payload.agentId must be a string when provided',\n };\n }\n if (unreadOnly !== undefined && typeof unreadOnly !== 'boolean') {\n return {\n ok: false,\n message: 'mailbox.messages payload.unreadOnly must be a boolean when provided',\n };\n }\n if (incompleteOnly !== undefined && typeof incompleteOnly !== 'boolean') {\n return {\n ok: false,\n message: 'mailbox.messages payload.incompleteOnly must be a boolean when provided',\n };\n }\n return {\n ok: true,\n value: {\n ...(limit !== undefined ? { limit: limit as number } : {}),\n ...(agentId !== undefined ? { agentId: agentId as string } : {}),\n ...(unreadOnly !== undefined ? { unreadOnly: unreadOnly as boolean } : {}),\n ...(incompleteOnly !== undefined ? { incompleteOnly: incompleteOnly as boolean } : {}),\n },\n };\n}\n\nexport interface MailboxAgentsPayload {\n onlineOnly?: boolean;\n}\n\nexport function validateMailboxAgentsPayload(\n payload: unknown,\n): PayloadValidationResult<MailboxAgentsPayload | undefined> {\n if (payload === undefined) return { ok: true, value: undefined };\n if (!isRecord(payload)) {\n return { ok: false, message: 'mailbox.agents payload must be an object when provided' };\n }\n const onlineOnly = payload['onlineOnly'];\n if (onlineOnly !== undefined && typeof onlineOnly !== 'boolean') {\n return {\n ok: false,\n message: 'mailbox.agents payload.onlineOnly must be a boolean when provided',\n };\n }\n return {\n ok: true,\n value: { ...(onlineOnly !== undefined ? { onlineOnly: onlineOnly as boolean } : {}) },\n };\n}\n\nexport interface MailboxPurgePayload {\n completedMaxAgeMs?: number;\n incompleteMaxAgeMs?: number;\n}\n\nexport function validateMailboxPurgePayload(\n payload: unknown,\n): PayloadValidationResult<MailboxPurgePayload | undefined> {\n if (payload === undefined) return { ok: true, value: undefined };\n if (!isRecord(payload)) {\n return { ok: false, message: 'mailbox.purge payload must be an object when provided' };\n }\n const completedMaxAgeMs = payload['completedMaxAgeMs'];\n const incompleteMaxAgeMs = payload['incompleteMaxAgeMs'];\n if (\n completedMaxAgeMs !== undefined &&\n (typeof completedMaxAgeMs !== 'number' ||\n !Number.isFinite(completedMaxAgeMs) ||\n completedMaxAgeMs < 0)\n ) {\n return {\n ok: false,\n message:\n 'mailbox.purge payload.completedMaxAgeMs must be a non-negative number when provided',\n };\n }\n if (\n incompleteMaxAgeMs !== undefined &&\n (typeof incompleteMaxAgeMs !== 'number' ||\n !Number.isFinite(incompleteMaxAgeMs) ||\n incompleteMaxAgeMs < 0)\n ) {\n return {\n ok: false,\n message:\n 'mailbox.purge payload.incompleteMaxAgeMs must be a non-negative number when provided',\n };\n }\n return {\n ok: true,\n value: {\n ...(completedMaxAgeMs !== undefined ? { completedMaxAgeMs: completedMaxAgeMs as number } : {}),\n ...(incompleteMaxAgeMs !== undefined\n ? { incompleteMaxAgeMs: incompleteMaxAgeMs as number }\n : {}),\n },\n };\n}\n\nexport interface BrainRiskPayload {\n level: string;\n}\n\nconst BRAIN_RISK_VALUES = new Set(['off', 'low', 'medium', 'high', 'all']);\n\nexport function validateBrainRiskPayload(\n payload: unknown,\n): PayloadValidationResult<BrainRiskPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'brain.risk payload must be an object with string level' };\n }\n const level = payload['level'];\n if (typeof level !== 'string' || !BRAIN_RISK_VALUES.has(level)) {\n return {\n ok: false,\n message: 'brain.risk payload.level must be one of off, low, medium, high, all',\n };\n }\n return { ok: true, value: { level } };\n}\n\nexport interface BrainAskPayload {\n question: string;\n}\n\nexport function validateBrainAskPayload(\n payload: unknown,\n): PayloadValidationResult<BrainAskPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'brain.ask payload must be an object with string question' };\n }\n const question = payload['question'];\n if (typeof question !== 'string' || question.trim().length === 0) {\n return { ok: false, message: 'brain.ask payload.question must be a non-empty string' };\n }\n return { ok: true, value: { question: question.trim() } };\n}\n\nexport interface BrainConfigSetPayload {\n patch: Record<string, unknown>;\n}\n\nexport function validateBrainConfigSetPayload(\n payload: unknown,\n): PayloadValidationResult<BrainConfigSetPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'brain.config.set payload must be an object with a patch object' };\n }\n const patch = payload['patch'];\n if (!isRecord(patch)) {\n // Field-level validation happens in BrainRuntime.apply(), which throws\n // BEFORE any live state changes \u2014 this only guards the envelope shape.\n return { ok: false, message: 'brain.config.set payload.patch must be an object' };\n }\n return { ok: true, value: { patch } };\n}\n\nexport interface AutonomySwitchPayload {\n mode: string;\n}\n\nexport function validateAutonomySwitchPayload(\n payload: unknown,\n): PayloadValidationResult<AutonomySwitchPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'autonomy.switch payload must be an object with string mode' };\n }\n const mode = payload['mode'];\n if (typeof mode !== 'string' || !AUTONOMY_VALUES.has(mode)) {\n return { ok: false, message: 'autonomy.switch payload.mode must be a valid autonomy mode' };\n }\n return { ok: true, value: { mode } };\n}\n\nexport interface PlanTemplateUsePayload {\n template: string;\n}\n\nexport function validatePlanTemplateUsePayload(\n payload: unknown,\n): PayloadValidationResult<PlanTemplateUsePayload> {\n if (!isRecord(payload)) {\n return {\n ok: false,\n message: 'plan.template_use payload must be an object with string template',\n };\n }\n const template = payload['template'];\n if (typeof template !== 'string' || template.trim().length === 0) {\n return { ok: false, message: 'plan.template_use payload.template must be a non-empty string' };\n }\n return { ok: true, value: { template } };\n}\nconst CONTEXT_STRATEGY_VALUES = new Set(['hybrid', 'intelligent', 'selective']);\nconst CONTEXT_MODE_VALUES = new Set(['balanced', 'frugal', 'deep', 'archival']);\nconst TOKEN_SAVING_TIER_VALUES = new Set(['off', 'minimal', 'light', 'medium', 'aggressive']);\nconst ENHANCE_LANGUAGE_VALUES = new Set(['original', 'english']);\nconst LOG_LEVEL_VALUES = new Set(['debug', 'info', 'warn', 'error']);\nconst AUDIT_LEVEL_VALUES = new Set(['minimal', 'standard', 'full']);\nconst REASONING_MODE_VALUES = new Set(['auto', 'on', 'off']);\nconst REASONING_EFFORT_VALUES = new Set([\n 'none',\n 'minimal',\n 'low',\n 'medium',\n 'high',\n 'xhigh',\n 'max',\n]);\nconst CACHE_TTL_VALUES = new Set(['default', '5m', '1h']);\n\nconst BOOLEAN_PREF_KEYS = new Set([\n 'yolo',\n 'chime',\n 'confirmExit',\n 'streamFleet',\n 'nextPrediction',\n 'titleAnimation',\n 'enhanceEnabled',\n 'featureMcp',\n 'featurePlugins',\n 'featureMemory',\n 'featureSkills',\n 'featureModelsRegistry',\n 'indexOnStart',\n 'contextAutoCompact',\n 'tgSessionEnd',\n 'tgDelegate',\n 'reasoningPreserve',\n 'hqEnabled',\n 'hqRawContent',\n 'fallbackAuto',\n 'favoriteModelsOnly',\n 'breakerEnabled',\n 'debugStream',\n // Chimera + auto-review master toggles\n 'chimeraEnabled',\n 'autoReviewEnabled',\n 'showModelReasoning',\n]);\n\n/** Keys whose value must be an array of strings (e.g. an ordered model list). */\nconst STRING_ARRAY_PREF_KEYS = new Set([\n 'fallbackModels',\n 'favoriteModels',\n // Auto-review explicit fallback chain (derived when fallbackProfile is unset;\n // surfaced for visibility/override).\n 'autoReviewFallbackModels',\n]);\nconst STRING_ARRAY_RECORD_PREF_KEYS = new Set(['fallbackProfiles']);\nconst MODEL_MATRIX_PREF_KEYS = new Set(['modelMatrix']);\n// Object of booleans, e.g. { 'plugin-name': true }. Parity with the embedded\n// server, which accepts `pluginsEnabled` and persists it to\n// extensions.<name>.enabled \u2014 the standalone server rejected it as unknown.\nconst BOOLEAN_RECORD_PREF_KEYS = new Set(['pluginsEnabled']);\n\nconst NUMBER_PREF_KEYS = new Set([\n 'autonomyDelayMs',\n 'autoProceedMaxIterations',\n 'maxIterations',\n 'maxConcurrent',\n 'enhanceDelayMs',\n 'tgLongToolMs',\n 'breakerAutoKillResetMs',\n // Chimera + auto-review numeric knobs\n 'chimeraMaxFiles',\n 'autoReviewDebounceMs',\n 'autoReviewMaxFilesPerBatch',\n 'autoReviewMaxConcurrentReviews',\n]);\n\nconst STRING_PREF_KEYS = new Set([\n 'hqUrl',\n 'hqToken',\n 'uiLocale',\n 'thinkingWord',\n 'refinerProvider',\n 'refinerModel',\n 'refinerFallbackProfile',\n // Chimera + auto-review override strings\n 'chimeraProvider',\n 'chimeraModel',\n 'autoReviewProvider',\n 'autoReviewModel',\n 'autoReviewFallbackProfile',\n]);\n\nconst ENUM_PREF_KEYS: Record<string, Set<string>> = {\n autonomy: AUTONOMY_VALUES,\n contextStrategy: CONTEXT_STRATEGY_VALUES,\n contextMode: CONTEXT_MODE_VALUES,\n tokenSavingTier: TOKEN_SAVING_TIER_VALUES,\n enhanceLanguage: ENHANCE_LANGUAGE_VALUES,\n logLevel: LOG_LEVEL_VALUES,\n auditLevel: AUDIT_LEVEL_VALUES,\n reasoningMode: REASONING_MODE_VALUES,\n reasoningEffort: REASONING_EFFORT_VALUES,\n cacheTtl: CACHE_TTL_VALUES,\n statuslineMode: new Set(['minimum', 'detailed', 'no-color']),\n animationStyle: new Set(['rainbow', 'wave', 'pulse', 'dots', 'breathe', 'cycle']),\n fsAccess: new Set(['unrestricted', 'project']),\n // Chimera autoFix + auto-review cascade threshold\n chimeraAutoFix: new Set(['off', 'ask', 'auto']),\n autoReviewCascadeOn: new Set(['off', 'critical', 'high']),\n};\n\nfunction validateModelRuntimeValue(modelRuntime: Record<string, unknown>, path: string): string | null {\n const reasoning = modelRuntime['reasoning'];\n if (reasoning !== undefined) {\n if (!isRecord(reasoning)) return `${path}.reasoning must be an object when provided`;\n const mode = reasoning['mode'];\n const effort = reasoning['effort'];\n const preserve = reasoning['preserve'];\n if (mode !== undefined && (typeof mode !== 'string' || !REASONING_MODE_VALUES.has(mode))) {\n return `${path}.reasoning.mode must be one of: ${Array.from(REASONING_MODE_VALUES).join(', ')}`;\n }\n if (effort !== undefined && (typeof effort !== 'string' || !REASONING_EFFORT_VALUES.has(effort))) {\n return `${path}.reasoning.effort must be one of: ${Array.from(REASONING_EFFORT_VALUES).join(', ')}`;\n }\n if (preserve !== undefined && typeof preserve !== 'boolean') {\n return `${path}.reasoning.preserve must be a boolean when provided`;\n }\n }\n\n const cache = modelRuntime['cache'];\n if (cache !== undefined) {\n if (!isRecord(cache)) return `${path}.cache must be an object when provided`;\n const ttl = cache['ttl'];\n if (ttl !== undefined && (typeof ttl !== 'string' || !CACHE_TTL_VALUES.has(ttl) || ttl === 'default')) {\n return `${path}.cache.ttl must be one of: 5m, 1h`;\n }\n }\n\n const parameters = modelRuntime['parameters'];\n if (parameters !== undefined && !isRecord(parameters)) {\n return `${path}.parameters must be an object when provided`;\n }\n\n return null;\n}\n\nfunction validatePreferenceValue(key: string, value: unknown): string | null {\n if (BOOLEAN_PREF_KEYS.has(key)) {\n return typeof value === 'boolean' ? null : `prefs.update payload.${key} must be a boolean`;\n }\n if (NUMBER_PREF_KEYS.has(key)) {\n return typeof value === 'number' && Number.isFinite(value)\n ? null\n : `prefs.update payload.${key} must be a finite number`;\n }\n if (STRING_PREF_KEYS.has(key)) {\n return typeof value === 'string' ? null : `prefs.update payload.${key} must be a string`;\n }\n if (STRING_ARRAY_PREF_KEYS.has(key)) {\n return Array.isArray(value) && value.every((v) => typeof v === 'string')\n ? null\n : `prefs.update payload.${key} must be an array of strings`;\n }\n if (STRING_ARRAY_RECORD_PREF_KEYS.has(key)) {\n return isRecord(value) &&\n Object.values(value).every(\n (v) => Array.isArray(v) && v.every((item) => typeof item === 'string'),\n )\n ? null\n : `prefs.update payload.${key} must be an object of string arrays`;\n }\n if (BOOLEAN_RECORD_PREF_KEYS.has(key)) {\n if (!isRecord(value) || !Object.values(value).every((v) => typeof v === 'boolean')) {\n return `prefs.update payload.${key} must be an object of booleans`;\n }\n // The KEYS here are plugin names that get used as property keys downstream\n // (extensions.<name>.enabled), so they must be checked too \u2014 validating\n // only the values let `{\"__proto__\": true}` through to a write that\n // polluted Object.prototype. The persist layer guards as well; this\n // rejects loudly instead of silently dropping the entry.\n const badKey = Object.keys(value).find((k) => FORBIDDEN_PROTO_KEYS.has(k));\n if (badKey) {\n return `prefs.update payload.${key} contains a forbidden key: ${badKey}`;\n }\n return null;\n }\n if (MODEL_MATRIX_PREF_KEYS.has(key)) {\n if (!isRecord(value)) return `prefs.update payload.${key} must be an object`;\n for (const entry of Object.values(value)) {\n if (!isRecord(entry)) return `prefs.update payload.${key} entries must be objects`;\n const provider = entry['provider'];\n const model = entry['model'];\n const fallbackProfile = entry['fallbackProfile'];\n const modelRuntime = entry['modelRuntime'];\n if (provider !== undefined && typeof provider !== 'string') {\n return `prefs.update payload.${key}.provider must be a string when provided`;\n }\n if (model !== undefined && typeof model !== 'string') {\n return `prefs.update payload.${key}.model must be a string when provided`;\n }\n if (fallbackProfile !== undefined && typeof fallbackProfile !== 'string') {\n return `prefs.update payload.${key}.fallbackProfile must be a string when provided`;\n }\n if (modelRuntime !== undefined && !isRecord(modelRuntime)) {\n return `prefs.update payload.${key}.modelRuntime must be an object when provided`;\n }\n if (isRecord(modelRuntime)) {\n const runtimeError = validateModelRuntimeValue(modelRuntime, `prefs.update payload.${key}.modelRuntime`);\n if (runtimeError) return runtimeError;\n }\n if (model === undefined && fallbackProfile === undefined && modelRuntime === undefined) {\n return `prefs.update payload.${key} entries require model, fallbackProfile, or modelRuntime`;\n }\n }\n return null;\n }\n const allowed = ENUM_PREF_KEYS[key];\n if (allowed) {\n return typeof value === 'string' && allowed.has(value)\n ? null\n : `prefs.update payload.${key} must be one of: ${Array.from(allowed).join(', ')}`;\n }\n return `prefs.update payload contains unknown preference key: ${key}`;\n}\n\nexport function validatePrefsUpdatePayload(\n payload: unknown,\n): PayloadValidationResult<PrefsUpdatePayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'prefs.update payload must be an object' };\n }\n for (const [key, value] of Object.entries(payload)) {\n const error = validatePreferenceValue(key, value);\n if (error) return { ok: false, message: error };\n }\n return { ok: true, value: { prefs: payload } };\n}\n\nexport interface SkillsCreatePayload {\n name: string;\n description: string;\n scope: 'project' | 'global';\n}\n\nexport function validateSkillsCreatePayload(\n payload: unknown,\n): PayloadValidationResult<SkillsCreatePayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'skills.create payload must be an object' };\n }\n const name = payload['name'];\n const description = payload['description'];\n const scope = payload['scope'];\n if (typeof name !== 'string' || name.trim().length === 0) {\n return { ok: false, message: 'Skill name is required' };\n }\n if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(name.trim())) {\n return { ok: false, message: 'Skill name must be kebab-case (e.g. my-new-skill)' };\n }\n if (typeof description !== 'string' || description.trim().length === 0) {\n return { ok: false, message: 'Description/trigger is required' };\n }\n if (scope !== 'project' && scope !== 'global') {\n return { ok: false, message: 'skills.create payload.scope must be project or global' };\n }\n return { ok: true, value: { name, description, scope } };\n}\n\nexport interface SkillsEditPayload {\n name: string;\n body: string;\n}\n\nexport function validateSkillsEditPayload(\n payload: unknown,\n): PayloadValidationResult<SkillsEditPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'skills.edit payload must be an object' };\n }\n const name = payload['name'];\n const body = payload['body'];\n if (typeof name !== 'string' || name.trim().length === 0) {\n return { ok: false, message: 'Skill name is required' };\n }\n if (typeof body !== 'string' || body.length === 0) {\n return { ok: false, message: 'Skill body is required' };\n }\n return { ok: true, value: { name, body } };\n}\n\nexport interface ProcessKillPayload {\n pid: number;\n}\n\nexport function validateProcessKillPayload(\n payload: unknown,\n): PayloadValidationResult<ProcessKillPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'process.kill payload must be an object with numeric pid' };\n }\n const pid = payload['pid'];\n if (typeof pid !== 'number' || !Number.isInteger(pid) || pid <= 0) {\n return { ok: false, message: 'process.kill payload.pid must be a positive integer' };\n }\n return { ok: true, value: { pid } };\n}\n\nexport interface WorkingDirSetPayload {\n path: string;\n}\n\nexport function validateWorkingDirSetPayload(\n payload: unknown,\n): PayloadValidationResult<WorkingDirSetPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'working_dir.set payload must be an object with string path' };\n }\n const newPath = payload['path'];\n if (typeof newPath !== 'string' || newPath.trim().length === 0) {\n return { ok: false, message: 'working_dir.set payload.path must be a non-empty string' };\n }\n return { ok: true, value: { path: newPath } };\n}\n\nexport interface ModeSwitchPayload {\n id: string;\n}\n\nexport function validateModeSwitchPayload(\n payload: unknown,\n): PayloadValidationResult<ModeSwitchPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'mode.switch payload must be an object with string id' };\n }\n const id = payload['id'];\n if (typeof id !== 'string' || id.trim().length === 0) {\n return { ok: false, message: 'mode.switch payload.id must be a non-empty string' };\n }\n return { ok: true, value: { id } };\n}\n\nexport interface ContextModeIdPayload {\n id: string;\n}\n\nfunction validateContextModeIdPayload(\n payload: unknown,\n type: 'context.mode.switch' | 'context.mode.delete',\n): PayloadValidationResult<ContextModeIdPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: `${type} payload must be an object with string id` };\n }\n const id = payload['id'];\n if (typeof id !== 'string' || id.trim().length === 0) {\n return { ok: false, message: `${type} payload.id must be a non-empty string` };\n }\n return { ok: true, value: { id } };\n}\n\nexport function validateContextModeSwitchPayload(\n payload: unknown,\n): PayloadValidationResult<ContextModeIdPayload> {\n return validateContextModeIdPayload(payload, 'context.mode.switch');\n}\n\nexport function validateContextModeDeletePayload(\n payload: unknown,\n): PayloadValidationResult<ContextModeIdPayload> {\n return validateContextModeIdPayload(payload, 'context.mode.delete');\n}\n\nexport interface ContextModeCreatePayload {\n id: string;\n name: string;\n description: string;\n thresholds: { warn: number; soft: number; hard: number };\n preserveK: number;\n eliseThreshold: number;\n}\n\nfunction isFiniteNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value);\n}\n\nexport function validateContextModeCreatePayload(\n payload: unknown,\n): PayloadValidationResult<ContextModeCreatePayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'context.mode.create payload must be an object' };\n }\n const id = payload['id'];\n const name = payload['name'];\n const description = payload['description'];\n const thresholds = payload['thresholds'];\n const preserveK = payload['preserveK'];\n const eliseThreshold = payload['eliseThreshold'];\n\n if (typeof id !== 'string' || id.trim().length === 0) {\n return { ok: false, message: 'context.mode.create payload.id must be a non-empty string' };\n }\n if (typeof name !== 'string' || name.trim().length === 0) {\n return { ok: false, message: 'context.mode.create payload.name must be a non-empty string' };\n }\n if (typeof description !== 'string') {\n return { ok: false, message: 'context.mode.create payload.description must be a string' };\n }\n if (!isRecord(thresholds)) {\n return {\n ok: false,\n message:\n 'context.mode.create payload.thresholds must be an object with warn/soft/hard numbers',\n };\n }\n if (\n !isFiniteNumber(thresholds['warn']) ||\n !isFiniteNumber(thresholds['soft']) ||\n !isFiniteNumber(thresholds['hard'])\n ) {\n return {\n ok: false,\n message: 'context.mode.create payload.thresholds.warn/soft/hard must be finite numbers',\n };\n }\n if (!isFiniteNumber(preserveK)) {\n return { ok: false, message: 'context.mode.create payload.preserveK must be a finite number' };\n }\n if (!isFiniteNumber(eliseThreshold)) {\n return {\n ok: false,\n message: 'context.mode.create payload.eliseThreshold must be a finite number',\n };\n }\n return {\n ok: true,\n value: {\n id,\n name,\n description,\n thresholds: { warn: thresholds['warn'], soft: thresholds['soft'], hard: thresholds['hard'] },\n preserveK,\n eliseThreshold,\n },\n };\n}\n\nexport interface ContextModeUpdatePayload {\n id: string;\n name?: string;\n description?: string;\n thresholds?: { warn?: number; soft?: number; hard?: number };\n preserveK?: number;\n eliseThreshold?: number;\n}\n\nexport function validateContextModeUpdatePayload(\n payload: unknown,\n): PayloadValidationResult<ContextModeUpdatePayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'context.mode.update payload must be an object' };\n }\n const id = payload['id'];\n if (typeof id !== 'string' || id.trim().length === 0) {\n return { ok: false, message: 'context.mode.update payload.id must be a non-empty string' };\n }\n\n const name = payload['name'];\n if (name !== undefined && typeof name !== 'string') {\n return {\n ok: false,\n message: 'context.mode.update payload.name must be a string when provided',\n };\n }\n\n const description = payload['description'];\n if (description !== undefined && typeof description !== 'string') {\n return {\n ok: false,\n message: 'context.mode.update payload.description must be a string when provided',\n };\n }\n\n const thresholds = payload['thresholds'];\n let validatedThresholds: ContextModeUpdatePayload['thresholds'];\n if (thresholds !== undefined) {\n if (!isRecord(thresholds)) {\n return {\n ok: false,\n message: 'context.mode.update payload.thresholds must be an object when provided',\n };\n }\n for (const key of ['warn', 'soft', 'hard'] as const) {\n const val = thresholds[key];\n if (val !== undefined && !isFiniteNumber(val)) {\n return {\n ok: false,\n message: `context.mode.update payload.thresholds.${key} must be a finite number when provided`,\n };\n }\n }\n validatedThresholds = {\n ...(typeof thresholds['warn'] === 'number' ? { warn: thresholds['warn'] } : {}),\n ...(typeof thresholds['soft'] === 'number' ? { soft: thresholds['soft'] } : {}),\n ...(typeof thresholds['hard'] === 'number' ? { hard: thresholds['hard'] } : {}),\n };\n }\n\n const preserveK = payload['preserveK'];\n if (preserveK !== undefined && !isFiniteNumber(preserveK)) {\n return {\n ok: false,\n message: 'context.mode.update payload.preserveK must be a finite number when provided',\n };\n }\n\n const eliseThreshold = payload['eliseThreshold'];\n if (eliseThreshold !== undefined && !isFiniteNumber(eliseThreshold)) {\n return {\n ok: false,\n message: 'context.mode.update payload.eliseThreshold must be a finite number when provided',\n };\n }\n\n return {\n ok: true,\n value: {\n id,\n ...(typeof name === 'string' ? { name } : {}),\n ...(typeof description === 'string' ? { description } : {}),\n ...(validatedThresholds !== undefined ? { thresholds: validatedThresholds } : {}),\n ...(typeof preserveK === 'number' ? { preserveK } : {}),\n ...(typeof eliseThreshold === 'number' ? { eliseThreshold } : {}),\n },\n };\n}\n\nexport interface ShellOpenPayload {\n path: string;\n target?: 'file' | 'terminal';\n}\n\nexport function validateShellOpenPayload(\n payload: unknown,\n): PayloadValidationResult<ShellOpenPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'shell.open payload must be an object with string path' };\n }\n const path = payload['path'];\n if (typeof path !== 'string' || path.trim().length === 0) {\n return { ok: false, message: 'shell.open payload.path must be a non-empty string' };\n }\n const target = payload['target'];\n if (target !== undefined && target !== 'file' && target !== 'terminal') {\n return {\n ok: false,\n message: 'shell.open payload.target must be \"file\" or \"terminal\" when provided',\n };\n }\n return {\n ok: true,\n value: {\n path,\n ...(target !== undefined ? { target: target as 'file' | 'terminal' } : {}),\n },\n };\n}\n\nexport interface GitDiffPayload {\n path: string;\n}\n\nexport function validateGitDiffPayload(payload: unknown): PayloadValidationResult<GitDiffPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'git.diff payload must be an object' };\n }\n const path = payload['path'];\n if (path === undefined || path === null) {\n return { ok: true, value: { path: '' } };\n }\n if (typeof path !== 'string') {\n return { ok: false, message: 'git.diff payload.path must be a string when provided' };\n }\n return { ok: true, value: { path } };\n}\n\nexport interface ProjectsAddPayload {\n root: string;\n name?: string;\n}\n\nexport function validateProjectsAddPayload(\n payload: unknown,\n): PayloadValidationResult<ProjectsAddPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'projects.add payload must be an object with string root' };\n }\n const root = payload['root'];\n if (typeof root !== 'string' || root.trim().length === 0) {\n return { ok: false, message: 'projects.add payload.root must be a non-empty string' };\n }\n const name = payload['name'];\n if (name !== undefined && typeof name !== 'string') {\n return { ok: false, message: 'projects.add payload.name must be a string when provided' };\n }\n return {\n ok: true,\n value: { root, ...(typeof name === 'string' ? { name } : {}) },\n };\n}\n\nexport interface ProjectsSelectPayload {\n root: string;\n name?: string;\n}\n\nexport function validateProjectsSelectPayload(\n payload: unknown,\n): PayloadValidationResult<ProjectsSelectPayload> {\n if (!isRecord(payload)) {\n return { ok: false, message: 'projects.select payload must be an object with string root' };\n }\n const root = payload['root'];\n if (typeof root !== 'string' || root.trim().length === 0) {\n return { ok: false, message: 'projects.select payload.root must be a non-empty string' };\n }\n const name = payload['name'];\n if (name !== undefined && typeof name !== 'string') {\n return { ok: false, message: 'projects.select payload.name must be a string when provided' };\n }\n return {\n ok: true,\n value: { root, ...(typeof name === 'string' ? { name } : {}) },\n };\n}\n", "// \u2500\u2500 Shared Worklist Handlers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Extracted from standalone server (packages/webui/src/server/index.ts) and CLI\n// embedded server (packages/cli/src/webui-server/). Both servers use these\n// handlers for todos, tasks, and plan operations. Keep them in sync.\n//\n// Message types handled here:\n// todos.get | todos.clear | todos.remove | todo.update\n// tasks.get | task.update\n// plan.get | plan.template_use | plan.item.update\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nimport type { WebSocket } from 'ws';\nimport type { TodoItem } from '@wrongstack/core';\nimport { validatePlanTemplateUsePayload } from '../ws-payload-validation.js';\n\n// \u2500\u2500 Shared result helper \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction sendResult(\n ws: WebSocket,\n ctx: WorklistContext,\n ok: boolean,\n message: string,\n): void {\n ctx.send(ws, { type: ok ? 'ok' : 'error', message });\n}\n\nfunction sessionPayload<T extends Record<string, unknown>>(\n ctx: WorklistContext,\n payload: T,\n): T & { sessionId: string } {\n const provided = payload['sessionId'];\n const fallback = ctx.context.session?.id ?? '';\n const sessionId = typeof provided === 'string' && provided.length > 0 ? provided : fallback;\n return { ...payload, sessionId };\n}\n\n// \u2500\u2500 Context interface \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Both servers satisfy this with their own local state.\n\nexport interface WorklistContext {\n context: {\n todos: TodoItem[];\n meta: Record<string, unknown>;\n session: { id: string } | null;\n state?: unknown;\n };\n send: (ws: WebSocket, msg: object) => void;\n broadcast: (msg: object) => void;\n /**\n * Optional mutator for in-memory todo state. Servers that manage live\n * agent state (e.g. the CLI embedded server) provide this so handlers\n * can update the agent's todo list directly. Standalone server may omit.\n */\n replaceTodos?: (todos: TodoItem[]) => void;\n}\n\n// \u2500\u2500 Todos \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function handleTodosGet(ctx: WorklistContext, ws: WebSocket): void {\n ctx.send(ws, { type: 'todos.updated', payload: sessionPayload(ctx, { todos: ctx.context.todos }) });\n}\n\nexport function handleTodosClear(ctx: WorklistContext, ws: WebSocket): void {\n ctx.replaceTodos?.([]);\n ctx.broadcast({ type: 'todos.cleared', payload: sessionPayload(ctx, {}) });\n sendResult(ws, ctx, true, 'Todo board cleared.');\n}\n\nexport function handleTodosRemove(\n ctx: WorklistContext,\n ws: WebSocket,\n payload: { id?: string; index?: number } | undefined,\n): void {\n if (!payload || (payload.id === undefined && payload.index === undefined)) {\n sendResult(ws, ctx, false, 'todos.remove requires id or index.');\n return;\n }\n const next =\n payload.id !== undefined\n ? ctx.context.todos.filter((t) => t.id !== payload.id)\n : ctx.context.todos.filter((_, i) => i !== (payload.index as number));\n ctx.replaceTodos?.(next);\n ctx.broadcast({ type: 'todos.updated', payload: sessionPayload(ctx, { todos: next }) });\n sendResult(ws, ctx, true, 'Todo item removed.');\n}\n\nexport function handleTodoUpdate(\n ctx: WorklistContext,\n ws: WebSocket,\n payload: { id: string; status?: TodoItem['status']; activeForm?: string },\n): void {\n const todo = ctx.context.todos.find((t) => t.id === payload.id);\n if (!todo) {\n sendResult(ws, ctx, false, `No todo with id \"${payload.id}\".`);\n return;\n }\n const next = ctx.context.todos.map((t) =>\n t.id === payload.id\n ? { ...t, ...(payload.status !== undefined && { status: payload.status }), ...(payload.activeForm !== undefined && { activeForm: payload.activeForm }) }\n : t,\n );\n ctx.replaceTodos?.(next);\n ctx.broadcast({ type: 'todos.updated', payload: sessionPayload(ctx, { todos: next }) });\n sendResult(ws, ctx, true, `Todo \"${todo.content}\" updated.`);\n}\n\n// \u2500\u2500 Tasks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport async function handleTasksGet(ctx: WorklistContext, ws: WebSocket): Promise<void> {\n const taskPath = ctx.context.meta['task.path'];\n if (typeof taskPath === 'string' && taskPath) {\n try {\n const { loadTasks } = await import('@wrongstack/core');\n const file = await loadTasks(taskPath);\n ctx.send(ws, { type: 'tasks.updated', payload: sessionPayload(ctx, { tasks: file?.tasks ?? [] }) });\n } catch {\n ctx.send(ws, { type: 'tasks.updated', payload: sessionPayload(ctx, { tasks: [] }) });\n }\n } else {\n ctx.send(ws, {\n type: 'tasks.updated',\n payload: sessionPayload(ctx, { tasks: [], error: 'Task storage not configured.' }),\n });\n }\n}\n\nexport async function handleTaskUpdate(\n ctx: WorklistContext,\n ws: WebSocket,\n payload: {\n id: string;\n status: 'pending' | 'in_progress' | 'blocked' | 'failed' | 'review' | 'completed';\n },\n): Promise<void> {\n const taskPath = ctx.context.meta['task.path'];\n if (typeof taskPath !== 'string' || !taskPath) {\n sendResult(ws, ctx, false, 'Task storage is not configured for this session.');\n return;\n }\n try {\n const { loadTasks, saveTasks } = await import('@wrongstack/core');\n const file = await loadTasks(taskPath);\n if (!file) {\n sendResult(ws, ctx, false, 'No task file found.');\n return;\n }\n const idx = file.tasks.findIndex((t) => t.id === payload.id);\n if (idx === -1) {\n sendResult(ws, ctx, false, `Task \"${payload.id}\" not found.`);\n return;\n }\n const existing = file.tasks[idx];\n if (!existing) {\n // noUncheckedIndexedAccess: array access can be undefined. We just\n // confirmed idx is in-range via findIndex, but the type system needs\n // the explicit guard.\n sendResult(ws, ctx, false, `Task \"${payload.id}\" not found.`);\n return;\n }\n file.tasks[idx] = { ...existing, status: payload.status };\n await saveTasks(taskPath, file);\n ctx.broadcast({ type: 'tasks.updated', payload: sessionPayload(ctx, { tasks: file.tasks }) });\n sendResult(ws, ctx, true, `Task \"${payload.id}\" marked ${payload.status}.`);\n } catch (err) {\n sendResult(ws, ctx, false, String(err));\n }\n}\n\n// \u2500\u2500 Plan \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport async function handlePlanGet(ctx: WorklistContext, ws: WebSocket): Promise<void> {\n const planPath = ctx.context.meta['plan.path'];\n const sessionId = ctx.context.session?.id ?? '';\n if (typeof planPath === 'string' && planPath) {\n try {\n const { loadPlan } = await import('@wrongstack/core');\n const plan = await loadPlan(planPath);\n ctx.send(ws, {\n type: 'plan.updated',\n payload: sessionPayload(ctx, {\n plan: plan ?? {\n version: 1,\n sessionId,\n updatedAt: new Date().toISOString(),\n items: [],\n },\n }),\n });\n } catch {\n ctx.send(ws, {\n type: 'plan.updated',\n payload: sessionPayload(ctx, {\n plan: {\n version: 1,\n sessionId,\n updatedAt: new Date().toISOString(),\n items: [],\n },\n }),\n });\n }\n } else {\n ctx.send(ws, {\n type: 'plan.updated',\n payload: sessionPayload(ctx, { plan: null, error: 'Plan storage is not configured for this session.' }),\n });\n }\n}\n\nexport async function handlePlanTemplateUse(ctx: WorklistContext, ws: WebSocket, template: string): Promise<void> {\n const planPath = ctx.context.meta['plan.path'];\n const sessionId = ctx.context.session?.id ?? '';\n if (typeof planPath !== 'string' || !planPath) {\n sendResult(ws, ctx, false, 'Plan storage is not configured for this session.');\n return;\n }\n try {\n const { getPlanTemplate, loadPlan, savePlan, emptyPlan, addPlanItem } = await import('@wrongstack/core');\n const tpl = getPlanTemplate(template);\n if (!tpl) {\n sendResult(ws, ctx, false, `Unknown template \"${template}\".`);\n return;\n }\n let plan = (await loadPlan(planPath)) ?? emptyPlan(sessionId);\n for (const item of tpl.items) {\n ({ plan } = addPlanItem(plan, item.title, item.details));\n }\n await savePlan(planPath, plan);\n sendResult(ws, ctx, true, `Applied template \"${tpl.name}\" \u2014 ${tpl.items.length} items added.`);\n ctx.broadcast({ type: 'plan.updated', payload: sessionPayload(ctx, { plan }) });\n } catch (err) {\n sendResult(ws, ctx, false, String(err));\n }\n}\n\nexport async function handlePlanItemUpdate(\n ctx: WorklistContext,\n ws: WebSocket,\n payload: { target: string; status: 'open' | 'in_progress' | 'done' },\n): Promise<void> {\n const planPath = ctx.context.meta['plan.path'];\n const sessionId = ctx.context.session?.id ?? '';\n if (typeof planPath !== 'string' || !planPath) {\n sendResult(ws, ctx, false, 'Plan storage is not configured for this session.');\n return;\n }\n try {\n const { mutatePlan, setPlanItemStatus } = await import('@wrongstack/core');\n let changed = false;\n const plan = await mutatePlan(planPath, sessionId, async (p) => {\n const before = p.updatedAt;\n const updated = setPlanItemStatus(p, payload.target, payload.status);\n changed = updated.updatedAt !== before;\n return updated;\n });\n if (!changed) {\n sendResult(ws, ctx, false, `No plan item matched \"${payload.target}\".`);\n return;\n }\n sendResult(ws, ctx, true, `Plan item status updated to \"${payload.status}\".`);\n ctx.broadcast({ type: 'plan.updated', payload: sessionPayload(ctx, { plan }) });\n } catch (err) {\n sendResult(ws, ctx, false, String(err));\n }\n}\n\n// \u2500\u2500 Dispatcher \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Single entry point for the nine worklist message types, so the host server's\n// switch delegates one grouped case here instead of repeating the per-type\n// `makeWorklistContext()` boilerplate. Unknown types are a no-op (the caller\n// only routes worklist types to this function).\n\n/** Loosely-typed worklist WS message \u2014 payload shapes are narrowed per case. */\nexport interface WorklistMessage {\n type: string;\n payload?: unknown;\n}\n\nexport async function handleWorklistMessage(\n ctx: WorklistContext,\n ws: WebSocket,\n msg: WorklistMessage,\n): Promise<void> {\n switch (msg.type) {\n case 'todos.get':\n handleTodosGet(ctx, ws);\n return;\n case 'todos.clear':\n handleTodosClear(ctx, ws);\n return;\n case 'todos.remove':\n handleTodosRemove(ctx, ws, msg.payload as { id?: string; index?: number } | undefined);\n return;\n case 'todo.update':\n handleTodoUpdate(\n ctx,\n ws,\n msg.payload as { id: string; status?: TodoItem['status']; activeForm?: string },\n );\n return;\n case 'tasks.get':\n await handleTasksGet(ctx, ws);\n return;\n case 'task.update':\n await handleTaskUpdate(\n ctx,\n ws,\n msg.payload as {\n id: string;\n status: 'pending' | 'in_progress' | 'blocked' | 'failed' | 'review' | 'completed';\n },\n );\n return;\n case 'plan.get':\n await handlePlanGet(ctx, ws);\n return;\n case 'plan.template_use': {\n const parsed = validatePlanTemplateUsePayload(msg.payload);\n if (!parsed.ok) {\n sendResult(ws, ctx, false, parsed.message);\n return;\n }\n await handlePlanTemplateUse(ctx, ws, parsed.value.template);\n return;\n }\n case 'plan.item.update':\n await handlePlanItemUpdate(\n ctx,\n ws,\n msg.payload as { target: string; status: 'open' | 'in_progress' | 'done' },\n );\n return;\n }\n}\n"],
5
+ "mappings": ";AAAA,SAAS,4BAA4B;AAIrC,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AA8OO,SAAS,+BACd,SACiD;AACjD,MAAI,CAAC,SAAS,OAAO,GAAG;AACtB,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,SAAS;AAAA,IACX;AAAA,EACF;AACA,QAAM,WAAW,QAAQ,UAAU;AACnC,MAAI,OAAO,aAAa,YAAY,SAAS,KAAK,EAAE,WAAW,GAAG;AAChE,WAAO,EAAE,IAAI,OAAO,SAAS,gEAAgE;AAAA,EAC/F;AACA,SAAO,EAAE,IAAI,MAAM,OAAO,EAAE,SAAS,EAAE;AACzC;;;ACjPA,SAAS,WACP,IACA,KACA,IACA,SACM;AACN,MAAI,KAAK,IAAI,EAAE,MAAM,KAAK,OAAO,SAAS,QAAQ,CAAC;AACrD;AAEA,SAAS,eACP,KACA,SAC2B;AAC3B,QAAM,WAAW,QAAQ,WAAW;AACpC,QAAM,WAAW,IAAI,QAAQ,SAAS,MAAM;AAC5C,QAAM,YAAY,OAAO,aAAa,YAAY,SAAS,SAAS,IAAI,WAAW;AACnF,SAAO,EAAE,GAAG,SAAS,UAAU;AACjC;AAwBO,SAAS,eAAe,KAAsB,IAAqB;AACxE,MAAI,KAAK,IAAI,EAAE,MAAM,iBAAiB,SAAS,eAAe,KAAK,EAAE,OAAO,IAAI,QAAQ,MAAM,CAAC,EAAE,CAAC;AACpG;AAEO,SAAS,iBAAiB,KAAsB,IAAqB;AAC1E,MAAI,eAAe,CAAC,CAAC;AACrB,MAAI,UAAU,EAAE,MAAM,iBAAiB,SAAS,eAAe,KAAK,CAAC,CAAC,EAAE,CAAC;AACzE,aAAW,IAAI,KAAK,MAAM,qBAAqB;AACjD;AAEO,SAAS,kBACd,KACA,IACA,SACM;AACN,MAAI,CAAC,WAAY,QAAQ,OAAO,UAAa,QAAQ,UAAU,QAAY;AACzE,eAAW,IAAI,KAAK,OAAO,oCAAoC;AAC/D;AAAA,EACF;AACA,QAAM,OACJ,QAAQ,OAAO,SACX,IAAI,QAAQ,MAAM,OAAO,CAAC,MAAM,EAAE,OAAO,QAAQ,EAAE,IACnD,IAAI,QAAQ,MAAM,OAAO,CAAC,GAAG,MAAM,MAAO,QAAQ,KAAgB;AACxE,MAAI,eAAe,IAAI;AACvB,MAAI,UAAU,EAAE,MAAM,iBAAiB,SAAS,eAAe,KAAK,EAAE,OAAO,KAAK,CAAC,EAAE,CAAC;AACtF,aAAW,IAAI,KAAK,MAAM,oBAAoB;AAChD;AAEO,SAAS,iBACd,KACA,IACA,SACM;AACN,QAAM,OAAO,IAAI,QAAQ,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ,EAAE;AAC9D,MAAI,CAAC,MAAM;AACT,eAAW,IAAI,KAAK,OAAO,oBAAoB,QAAQ,EAAE,IAAI;AAC7D;AAAA,EACF;AACA,QAAM,OAAO,IAAI,QAAQ,MAAM;AAAA,IAAI,CAAC,MAClC,EAAE,OAAO,QAAQ,KACb,EAAE,GAAG,GAAG,GAAI,QAAQ,WAAW,UAAa,EAAE,QAAQ,QAAQ,OAAO,GAAI,GAAI,QAAQ,eAAe,UAAa,EAAE,YAAY,QAAQ,WAAW,EAAG,IACrJ;AAAA,EACN;AACA,MAAI,eAAe,IAAI;AACvB,MAAI,UAAU,EAAE,MAAM,iBAAiB,SAAS,eAAe,KAAK,EAAE,OAAO,KAAK,CAAC,EAAE,CAAC;AACtF,aAAW,IAAI,KAAK,MAAM,SAAS,KAAK,OAAO,YAAY;AAC7D;AAIA,eAAsB,eAAe,KAAsB,IAA8B;AACvF,QAAM,WAAW,IAAI,QAAQ,KAAK,WAAW;AAC7C,MAAI,OAAO,aAAa,YAAY,UAAU;AAC5C,QAAI;AACF,YAAM,EAAE,UAAU,IAAI,MAAM,OAAO,kBAAkB;AACrD,YAAM,OAAO,MAAM,UAAU,QAAQ;AACrC,UAAI,KAAK,IAAI,EAAE,MAAM,iBAAiB,SAAS,eAAe,KAAK,EAAE,OAAO,MAAM,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;AAAA,IACpG,QAAQ;AACN,UAAI,KAAK,IAAI,EAAE,MAAM,iBAAiB,SAAS,eAAe,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;AAAA,IACrF;AAAA,EACF,OAAO;AACL,QAAI,KAAK,IAAI;AAAA,MACX,MAAM;AAAA,MACN,SAAS,eAAe,KAAK,EAAE,OAAO,CAAC,GAAG,OAAO,+BAA+B,CAAC;AAAA,IACnF,CAAC;AAAA,EACH;AACF;AAEA,eAAsB,iBACpB,KACA,IACA,SAIe;AACf,QAAM,WAAW,IAAI,QAAQ,KAAK,WAAW;AAC7C,MAAI,OAAO,aAAa,YAAY,CAAC,UAAU;AAC7C,eAAW,IAAI,KAAK,OAAO,kDAAkD;AAC7E;AAAA,EACF;AACA,MAAI;AACF,UAAM,EAAE,WAAW,UAAU,IAAI,MAAM,OAAO,kBAAkB;AAChE,UAAM,OAAO,MAAM,UAAU,QAAQ;AACrC,QAAI,CAAC,MAAM;AACT,iBAAW,IAAI,KAAK,OAAO,qBAAqB;AAChD;AAAA,IACF;AACA,UAAM,MAAM,KAAK,MAAM,UAAU,CAAC,MAAM,EAAE,OAAO,QAAQ,EAAE;AAC3D,QAAI,QAAQ,IAAI;AACd,iBAAW,IAAI,KAAK,OAAO,SAAS,QAAQ,EAAE,cAAc;AAC5D;AAAA,IACF;AACA,UAAM,WAAW,KAAK,MAAM,GAAG;AAC/B,QAAI,CAAC,UAAU;AAIb,iBAAW,IAAI,KAAK,OAAO,SAAS,QAAQ,EAAE,cAAc;AAC5D;AAAA,IACF;AACA,SAAK,MAAM,GAAG,IAAI,EAAE,GAAG,UAAU,QAAQ,QAAQ,OAAO;AACxD,UAAM,UAAU,UAAU,IAAI;AAC9B,QAAI,UAAU,EAAE,MAAM,iBAAiB,SAAS,eAAe,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC,EAAE,CAAC;AAC5F,eAAW,IAAI,KAAK,MAAM,SAAS,QAAQ,EAAE,YAAY,QAAQ,MAAM,GAAG;AAAA,EAC5E,SAAS,KAAK;AACZ,eAAW,IAAI,KAAK,OAAO,OAAO,GAAG,CAAC;AAAA,EACxC;AACF;AAIA,eAAsB,cAAc,KAAsB,IAA8B;AACtF,QAAM,WAAW,IAAI,QAAQ,KAAK,WAAW;AAC7C,QAAM,YAAY,IAAI,QAAQ,SAAS,MAAM;AAC7C,MAAI,OAAO,aAAa,YAAY,UAAU;AAC5C,QAAI;AACF,YAAM,EAAE,SAAS,IAAI,MAAM,OAAO,kBAAkB;AACpD,YAAM,OAAO,MAAM,SAAS,QAAQ;AACpC,UAAI,KAAK,IAAI;AAAA,QACX,MAAM;AAAA,QACN,SAAS,eAAe,KAAK;AAAA,UAC3B,MAAM,QAAQ;AAAA,YACZ,SAAS;AAAA,YACT;AAAA,YACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,YAClC,OAAO,CAAC;AAAA,UACV;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAAA,IACH,QAAQ;AACN,UAAI,KAAK,IAAI;AAAA,QACX,MAAM;AAAA,QACN,SAAS,eAAe,KAAK;AAAA,UAC3B,MAAM;AAAA,YACJ,SAAS;AAAA,YACT;AAAA,YACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,YAClC,OAAO,CAAC;AAAA,UACV;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAAA,EACF,OAAO;AACL,QAAI,KAAK,IAAI;AAAA,MACX,MAAM;AAAA,MACN,SAAS,eAAe,KAAK,EAAE,MAAM,MAAM,OAAO,mDAAmD,CAAC;AAAA,IACxG,CAAC;AAAA,EACH;AACF;AAEA,eAAsB,sBAAsB,KAAsB,IAAe,UAAiC;AAChH,QAAM,WAAW,IAAI,QAAQ,KAAK,WAAW;AAC7C,QAAM,YAAY,IAAI,QAAQ,SAAS,MAAM;AAC7C,MAAI,OAAO,aAAa,YAAY,CAAC,UAAU;AAC7C,eAAW,IAAI,KAAK,OAAO,kDAAkD;AAC7E;AAAA,EACF;AACA,MAAI;AACF,UAAM,EAAE,iBAAiB,UAAU,UAAU,WAAW,YAAY,IAAI,MAAM,OAAO,kBAAkB;AACvG,UAAM,MAAM,gBAAgB,QAAQ;AACpC,QAAI,CAAC,KAAK;AACR,iBAAW,IAAI,KAAK,OAAO,qBAAqB,QAAQ,IAAI;AAC5D;AAAA,IACF;AACA,QAAI,OAAQ,MAAM,SAAS,QAAQ,KAAM,UAAU,SAAS;AAC5D,eAAW,QAAQ,IAAI,OAAO;AAC5B,OAAC,EAAE,KAAK,IAAI,YAAY,MAAM,KAAK,OAAO,KAAK,OAAO;AAAA,IACxD;AACA,UAAM,SAAS,UAAU,IAAI;AAC7B,eAAW,IAAI,KAAK,MAAM,qBAAqB,IAAI,IAAI,YAAO,IAAI,MAAM,MAAM,eAAe;AAC7F,QAAI,UAAU,EAAE,MAAM,gBAAgB,SAAS,eAAe,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;AAAA,EAChF,SAAS,KAAK;AACZ,eAAW,IAAI,KAAK,OAAO,OAAO,GAAG,CAAC;AAAA,EACxC;AACF;AAEA,eAAsB,qBACpB,KACA,IACA,SACe;AACf,QAAM,WAAW,IAAI,QAAQ,KAAK,WAAW;AAC7C,QAAM,YAAY,IAAI,QAAQ,SAAS,MAAM;AAC7C,MAAI,OAAO,aAAa,YAAY,CAAC,UAAU;AAC7C,eAAW,IAAI,KAAK,OAAO,kDAAkD;AAC7E;AAAA,EACF;AACA,MAAI;AACF,UAAM,EAAE,YAAY,kBAAkB,IAAI,MAAM,OAAO,kBAAkB;AACzE,QAAI,UAAU;AACd,UAAM,OAAO,MAAM,WAAW,UAAU,WAAW,OAAO,MAAM;AAC9D,YAAM,SAAS,EAAE;AACjB,YAAM,UAAU,kBAAkB,GAAG,QAAQ,QAAQ,QAAQ,MAAM;AACnE,gBAAU,QAAQ,cAAc;AAChC,aAAO;AAAA,IACT,CAAC;AACD,QAAI,CAAC,SAAS;AACZ,iBAAW,IAAI,KAAK,OAAO,yBAAyB,QAAQ,MAAM,IAAI;AACtE;AAAA,IACF;AACA,eAAW,IAAI,KAAK,MAAM,gCAAgC,QAAQ,MAAM,IAAI;AAC5E,QAAI,UAAU,EAAE,MAAM,gBAAgB,SAAS,eAAe,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;AAAA,EAChF,SAAS,KAAK;AACZ,eAAW,IAAI,KAAK,OAAO,OAAO,GAAG,CAAC;AAAA,EACxC;AACF;AAcA,eAAsB,sBACpB,KACA,IACA,KACe;AACf,UAAQ,IAAI,MAAM;AAAA,IAChB,KAAK;AACH,qBAAe,KAAK,EAAE;AACtB;AAAA,IACF,KAAK;AACH,uBAAiB,KAAK,EAAE;AACxB;AAAA,IACF,KAAK;AACH,wBAAkB,KAAK,IAAI,IAAI,OAAsD;AACrF;AAAA,IACF,KAAK;AACH;AAAA,QACE;AAAA,QACA;AAAA,QACA,IAAI;AAAA,MACN;AACA;AAAA,IACF,KAAK;AACH,YAAM,eAAe,KAAK,EAAE;AAC5B;AAAA,IACF,KAAK;AACH,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,IAAI;AAAA,MAIN;AACA;AAAA,IACF,KAAK;AACH,YAAM,cAAc,KAAK,EAAE;AAC3B;AAAA,IACF,KAAK,qBAAqB;AACxB,YAAM,SAAS,+BAA+B,IAAI,OAAO;AACzD,UAAI,CAAC,OAAO,IAAI;AACd,mBAAW,IAAI,KAAK,OAAO,OAAO,OAAO;AACzC;AAAA,MACF;AACA,YAAM,sBAAsB,KAAK,IAAI,OAAO,MAAM,QAAQ;AAC1D;AAAA,IACF;AAAA,IACA,KAAK;AACH,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,IAAI;AAAA,MACN;AACA;AAAA,EACJ;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,5 @@
1
1
  import * as http from 'node:http';
2
+ import { type TechStackEvent } from './techstack-handlers.js';
2
3
  import type { FileWatcherMetrics } from './setup-events.js';
3
4
  export interface CreateHttpServerOptions {
4
5
  /** Port to listen on. Defaults to 3456 (or the `PORT` env var). */
@@ -50,6 +51,25 @@ export interface CreateHttpServerOptions {
50
51
  * reaches the map without waiting on the file-watch/poll. Best-effort.
51
52
  */
52
53
  onFleetPing?: (() => void) | undefined;
54
+ /**
55
+ * Project root path for the codebase index. When provided, the
56
+ * /api/codemap/* endpoints serve the dependency graph.
57
+ */
58
+ projectRoot?: string | undefined;
59
+ /** Optional codebase-index directory override (tests). */
60
+ indexDir?: string | undefined;
61
+ /** Project-wide TechStack events projected onto the WebSocket transport. */
62
+ onTechStackEvent?: ((event: TechStackEvent) => void) | undefined;
63
+ /**
64
+ * Live provider access for TechStack's LLM research stage. Omit it and
65
+ * `analyze` stays deterministic (registry + advisories only).
66
+ *
67
+ * A getter, not a value — see `TechStackHandlerDeps.getLlm`.
68
+ */
69
+ getLlm?: (() => {
70
+ provider: import('@wrongstack/core').Provider;
71
+ model: string;
72
+ } | undefined) | undefined;
53
73
  }
54
74
  /**
55
75
  * Inject the live WS port into the served HTML so the frontend connects to
@@ -1 +1 @@
1
- {"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../../src/server/http-server.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAiBlC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,MAAM,WAAW,uBAAuB;IACtC,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,kFAAkF;IAClF,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACrC;;;OAGG;IACH,cAAc,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAChD;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;CACxC;AAYD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CASjE;AAUD,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACzD,MAAM,CAQR;AAiED,qEAAqE;AACrE,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,EAChC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,GAC/B,MAAM,CAoBR;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAIxE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMvD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,IAAI,CAAC,MAAM,CA0T3E"}
1
+ {"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../../src/server/http-server.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAqBlC,OAAO,EAQL,KAAK,cAAc,EACpB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,MAAM,WAAW,uBAAuB;IACtC,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,kFAAkF;IAClF,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACrC;;;OAGG;IACH,cAAc,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAChD;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IACvC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACjE;;;;;OAKG;IACH,MAAM,CAAC,EACH,CAAC,MAAM;QAAE,QAAQ,EAAE,OAAO,kBAAkB,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,GACpF,SAAS,CAAC;CACf;AAYD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CASjE;AAUD,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACzD,MAAM,CAQR;AAiED,qEAAqE;AACrE,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,EAChC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,GAC/B,MAAM,CAoBR;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAIxE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMvD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,IAAI,CAAC,MAAM,CAod3E"}
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * The package's `exports["./server"]` field points here.
10
10
  */
11
- export { AutoPhaseWebSocketHandler } from './autophase-ws-handler.js';
11
+ export { GoalWebSocketHandler } from './goal-ws-handler.js';
12
12
  export { type CompletionHandlerOptions, type CompletionItemKind, type CompletionSuggestion, createToolLspCompletionSource, handleCompletionRequest, type LspCompletionSource, type LspCompletionSourceRequest, } from './completion-handlers.js';
13
13
  export { type CustomContextMode, type CustomModeStore, createCustomModeStore, } from './custom-context-modes.js';
14
14
  export { type DesignContext, handleDesignList, handleDesignMaterialize, handleDesignSet, handleDesignState, handleDesignUse, handleDesignVerify, } from './design-handlers.js';
@@ -19,7 +19,7 @@ export type { CreateHttpServerOptions } from './http-server.js';
19
19
  export { buildCspHeader, createHttpServer, injectWsPort } from './http-server.js';
20
20
  export { defaultBaseDir, formatInstances, listInstances, registerInstance, registryPath, unregisterInstance, type WebUIInstanceRecord, } from './instance-registry.js';
21
21
  export { handleMcpAdd, handleMcpDisable, handleMcpDiscover, handleMcpEnable, handleMcpList, handleMcpPromptGet, handleMcpPrompts, handleMcpRemove, handleMcpResourceRead, handleMcpResources, handleMcpRestart, handleMcpSleep, handleMcpUpdate, handleMcpWake, } from './mcp-handlers.js';
22
- export { handleMemoryList, handleSuperMemoryDelete, handleSuperMemoryGet, handleSuperMemoryList, handleSuperMemoryRemember, handleSuperMemoryUpdate, } from './memory-handlers.js';
22
+ export { handleMemoryList, handleSuperMemoryDelete, handleSuperMemoryGet, handleSuperMemoryList, handleSuperMemoryRemember, handleSuperMemoryUpdate, handleSuperMemoryRecover, handleSuperMemoryCandidateResolve, handleSuperMemoryBackfillRecoverable, handleSuperMemoryForFile, } from './memory-handlers.js';
23
23
  export { browserOpenCommand, openBrowser } from './open-browser.js';
24
24
  export { findFreePort, getSurfaceDefaultPorts, isPortFree, surfaceLabel, type SurfaceKind, SURFACE_DEFAULT_PORTS, } from './port-utils.js';
25
25
  export { handlePromptsContent, handlePromptsCreate, handlePromptsFavorite, handlePromptsList, handlePromptsRecent, handlePromptsSearch, handlePromptsUsed, type PromptsContext, } from './prompts-handlers.js';
@@ -39,10 +39,11 @@ export { WorktreeWebSocketHandler } from './worktree-ws-handler.js';
39
39
  export { extractToken, hostHeaderOk, isLoopbackBind, isLoopbackHostname, tokenMatches, type VerifyClientInput, verifyClient, } from './ws-auth.js';
40
40
  export { broadcast, buildWebUIAccessUrl, envFlag, errMessage, generateAuthToken, hostForBrowserUrl, resolveAuthToken, send, sendResult, } from './ws-utils.js';
41
41
  export { startWebUI } from './start-webui.js';
42
- export { handleAutoPhaseRoute } from './autophase-routes.js';
43
- export type { AutoPhaseRouteHandlers } from './autophase-routes.js';
42
+ export { handleGoalRoute } from './goal-routes.js';
43
+ export type { GoalRouteHandlers } from './goal-routes.js';
44
44
  export { bootConfig, patchConfig } from './boot.js';
45
45
  export { setupWebUICodebaseIndexing } from './codebase-indexing.js';
46
+ export { extractCodeMapFileTargets, normalizeCodeMapFileTarget, type CodeMapFileTarget, type CodeMapOperation, } from './codemap-telemetry.js';
46
47
  export { handleGoalGet } from './goal-handlers.js';
47
48
  export { handleProcessKill, handleProcessKillAll, handleProcessList, } from './process-handlers.js';
48
49
  export { handleBrainRoute } from './brain-routes.js';
@@ -52,7 +53,7 @@ export { SKIP_DIRS, isHiddenEntry, rankFiles } from './file-picker.js';
52
53
  export { handleWorklistMessage } from './handlers/worklist-handlers.js';
53
54
  export type { WorklistContext } from './handlers/worklist-handlers.js';
54
55
  export { decodeSessionId, injectWsConfig, isInsideDist } from './http-server.js';
55
- export { clearAnalyticsBuffer, getAnalyticsBuffer, handleApiAnalyticsGet, handleApiAnalyticsPost, handleApiAnalyticsSummary } from './http-server/analytics-handler.js';
56
+ export { clearAnalyticsBuffer, getAnalyticsBuffer, handleApiAnalyticsGet, handleApiAnalyticsPost, handleApiAnalyticsSummary, } from './http-server/analytics-handler.js';
56
57
  export { isPidAlive } from './instance-registry.js';
57
58
  export { createShutdown, registerShutdownHandlers } from './lifecycle.js';
58
59
  export { handleMailboxMessages } from './mailbox-handlers.js';
@@ -63,13 +64,13 @@ export { handleModeRoute } from './mode-routes.js';
63
64
  export type { ModeRouteHandlers } from './mode-routes.js';
64
65
  export { resolveProviderCatalogForModels, resolveProviderModelMetadata } from './model-catalog.js';
65
66
  export { isPathInside, resolveWorkingDirInsideProject } from './path-containment.js';
66
- export { resolveAllPendingConfirms, resolveYoloEligiblePendingConfirms } from './pending-confirms.js';
67
+ export { resolveAllPendingConfirms, resolveYoloEligiblePendingConfirms, } from './pending-confirms.js';
67
68
  export type { PendingConfirm } from './pending-confirms.js';
68
69
  export { persistPrefsToConfig } from './pref-helpers.js';
69
70
  export { handlePrefsRoute } from './prefs-routes.js';
70
71
  export { handleProjectRoute } from './project-routes.js';
71
72
  export type { ProjectRouteHandlers } from './project-routes.js';
72
- export { ensureProjectDataDir, loadManifest, projectsJsonPath, saveManifest } from './projects-manifest.js';
73
+ export { ensureProjectDataDir, loadManifest, projectsJsonPath, saveManifest, } from './projects-manifest.js';
73
74
  export { createProviderHandlers, projectSavedProviders } from './provider-handlers.js';
74
75
  export type { SavedProviderView } from './provider-handlers.js';
75
76
  export { handleProviderRoute } from './provider-routes.js';
@@ -89,6 +90,6 @@ export { TerminalWebSocketHandler } from './terminal-ws-handler.js';
89
90
  export { estimateContextBreakdown } from './token-estimator.js';
90
91
  export { computeUsageCost, getCostRates } from './usage-cost.js';
91
92
  export { extractTokenFromCookie, isWildcardBind } from './ws-auth.js';
92
- export { validateAutonomySwitchPayload, validateBrainAskPayload, validateBrainRiskPayload, validateContextModeCreatePayload, validateContextModeDeletePayload, validateContextModeSwitchPayload, validateContextModeUpdatePayload, validateGitDiffPayload, validateMailboxAgentsPayload, validateMailboxMessagesPayload, validateMailboxPurgePayload, validateModeSwitchPayload, validateModelSwitchPayload, validatePlanTemplateUsePayload, validatePrefsUpdatePayload, validateProcessKillPayload, validateProjectsAddPayload, validateProjectsSelectPayload, validateShellOpenPayload, validateSkillsCreatePayload, validateSkillsEditPayload, validateWorkingDirSetPayload } from './ws-payload-validation.js';
93
+ export { validateAutonomySwitchPayload, validateBrainAskPayload, validateBrainRiskPayload, validateContextModeCreatePayload, validateContextModeDeletePayload, validateContextModeSwitchPayload, validateContextModeUpdatePayload, validateGitDiffPayload, validateMailboxAgentsPayload, validateMailboxMessagesPayload, validateMailboxPurgePayload, validateModeSwitchPayload, validateModelSwitchPayload, validatePlanTemplateUsePayload, validatePrefsUpdatePayload, validateProcessKillPayload, validateProjectsAddPayload, validateProjectsSelectPayload, validateShellOpenPayload, validateSkillsCreatePayload, validateSkillsEditPayload, validateWorkingDirSetPayload, } from './ws-payload-validation.js';
93
94
  export { createZipBuffer, readZipEntries, type ZipEntryInput } from './zip.js';
94
95
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Running-instance registry for the standalone WebUI server.
3
3
  *
4
- * Every live `wstackui` process records itself in a single JSON file under the
4
+ * Every live `wstack --webui` process records itself in a single JSON file under the
5
5
  * wstack home dir (`~/.wrongstack/webui-instances.json`) so a user running
6
6
  * several instances (one per project, or several per project on different
7
7
  * ports) can see at a glance which ports are open for which path.
@@ -60,6 +60,6 @@ export declare function registerInstance(record: WebUIInstanceRecord, baseDir?:
60
60
  export declare function unregisterInstance(pid: number, baseDir?: string): Promise<void>;
61
61
  /** List live instances, pruning any dead entries encountered. */
62
62
  export declare function listInstances(baseDir?: string): Promise<WebUIInstanceRecord[]>;
63
- /** Human-readable table of running instances for `wstackui --list`. */
63
+ /** Human-readable table of running instances for `wstack --webui --list`. */
64
64
  export declare function formatInstances(instances: WebUIInstanceRecord[]): string;
65
65
  //# sourceMappingURL=instance-registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"instance-registry.d.ts","sourceRoot":"","sources":["../../src/server/instance-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAOH,4CAA4C;AAC5C,MAAM,WAAW,mBAAmB;IAClC,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,4CAA4C;IAC5C,OAAO,EAAE,OAAO,GAAG,UAAU,CAAC;IAC9B,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;CACb;AAOD,gFAAgF;AAChF,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,2DAA2D;AAC3D,wBAAgB,YAAY,CAAC,OAAO,GAAE,MAAyB,GAAG,MAAM,CAEvE;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ/C;AA0BD;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,mBAAmB,EAC3B,OAAO,GAAE,MAAyB,GACjC,OAAO,CAAC,IAAI,CAAC,CAMf;AAED,iFAAiF;AACjF,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAAyB,GACjC,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,iEAAiE;AACjE,wBAAsB,aAAa,CACjC,OAAO,GAAE,MAAyB,GACjC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAUhC;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,SAAS,EAAE,mBAAmB,EAAE,GAAG,MAAM,CAaxE"}
1
+ {"version":3,"file":"instance-registry.d.ts","sourceRoot":"","sources":["../../src/server/instance-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAOH,4CAA4C;AAC5C,MAAM,WAAW,mBAAmB;IAClC,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,4CAA4C;IAC5C,OAAO,EAAE,OAAO,GAAG,UAAU,CAAC;IAC9B,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;CACb;AAOD,gFAAgF;AAChF,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,2DAA2D;AAC3D,wBAAgB,YAAY,CAAC,OAAO,GAAE,MAAyB,GAAG,MAAM,CAEvE;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ/C;AA0BD;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,mBAAmB,EAC3B,OAAO,GAAE,MAAyB,GACjC,OAAO,CAAC,IAAI,CAAC,CAMf;AAED,iFAAiF;AACjF,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAAyB,GACjC,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,iEAAiE;AACjE,wBAAsB,aAAa,CACjC,OAAO,GAAE,MAAyB,GACjC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAUhC;AAED,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,SAAS,EAAE,mBAAmB,EAAE,GAAG,MAAM,CAaxE"}
@@ -1 +1 @@
1
- {"version":3,"file":"kanban-routes.d.ts","sourceRoot":"","sources":["../../src/server/kanban-routes.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAIb,MAAM,kBAAkB,CAAC;AA6C1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlD,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACjD;AA8BD,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,SAAS,EACb,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,kBAAkB,GACtB,OAAO,CAAC,OAAO,CAAC,CAuwBlB"}
1
+ {"version":3,"file":"kanban-routes.d.ts","sourceRoot":"","sources":["../../src/server/kanban-routes.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAIb,MAAM,kBAAkB,CAAC;AAqD1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlD,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACjD;AA+DD,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,SAAS,EACb,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,kBAAkB,GACtB,OAAO,CAAC,OAAO,CAAC,CAi7BlB"}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MCP management handlers for the WebUI server (both the standalone
3
- * `wstackui` server and the CLI's embedded `--webui` server).
3
+ * standalone server and the CLI's embedded `--webui` server).
4
4
  *
5
5
  * These are thin WebSocket translators over the shared, surface-agnostic
6
6
  * management core in `@wrongstack/mcp` (`manage.ts`) — the SAME core the REPL