@wrongstack/cli 0.306.3 → 0.307.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 (76) hide show
  1. package/dist/{auth-L3WGSGDX.js → auth-TVOOHL4Q.js} +45 -106
  2. package/dist/boot/short-circuit-flags.d.ts +2 -2
  3. package/dist/chimera-cascade-evidence.d.ts +1 -1
  4. package/dist/chunk-2VMJ3E5Z.js +71 -0
  5. package/dist/{chunk-5N34EWJR.js → chunk-2ZNBRZ4W.js} +2 -2
  6. package/dist/{chunk-RHLQT3EV.js → chunk-CB35UVXO.js} +1636 -1528
  7. package/dist/{chunk-YKBRFSGL.js → chunk-DOJLAULS.js} +28 -28
  8. package/dist/{chunk-JUHRI6OV.js → chunk-G7337TTK.js} +24 -5
  9. package/dist/{chunk-KXN5HZDL.js → chunk-PKHGLWUJ.js} +24 -6
  10. package/dist/{chunk-HITHEXE3.js → chunk-TOG7OMKJ.js} +2 -2
  11. package/dist/{chunk-ARWGQUB3.js → chunk-XSG33WHN.js} +15 -5
  12. package/dist/{cli-main-3PUWFKSL.js → cli-main-YDOZZOZA.js} +29026 -28205
  13. package/dist/cli-main.d.ts +0 -20
  14. package/dist/{execution-4IG5XKOL.js → execution-CGT6J5ZN.js} +306 -274
  15. package/dist/fleet/host-learning-scheduler.d.ts +27 -0
  16. package/dist/fleet/host-shadow-manager.d.ts +64 -0
  17. package/dist/fleet/host.d.ts +5 -140
  18. package/dist/{hq-ZK6PGN3W.js → hq-PA6RH2UW.js} +2 -2
  19. package/dist/hq-server/mailbox-gateway-manager.d.ts +32 -0
  20. package/dist/hq-server/routes/auth/bootstrap-routes.d.ts +5 -0
  21. package/dist/hq-server/routes/auth/common.d.ts +11 -0
  22. package/dist/hq-server/routes/auth/password-routes.d.ts +9 -0
  23. package/dist/hq-server/routes/auth/session-audit-routes.d.ts +6 -0
  24. package/dist/hq-server/routes/auth/totp-routes.d.ts +9 -0
  25. package/dist/hq-server/routes/auth-handlers.d.ts +5 -132
  26. package/dist/hq-server/routes/command-handlers.d.ts +7 -0
  27. package/dist/hq-server/routes/data-handlers.d.ts +8 -0
  28. package/dist/hq-server/routes/mailbox-handlers.d.ts +22 -0
  29. package/dist/hq-server/routes.d.ts +3 -3
  30. package/dist/hq-server/server-lifecycle.d.ts +37 -0
  31. package/dist/hq-server/snapshot.d.ts +6 -5
  32. package/dist/hq-server/types.d.ts +4 -0
  33. package/dist/hq-server/upgrade-handler.d.ts +34 -0
  34. package/dist/hq-server/ws-client-events.d.ts +25 -0
  35. package/dist/hq-server/ws-leader.d.ts +47 -0
  36. package/dist/hq-server/ws-resume.d.ts +27 -0
  37. package/dist/hq-server/ws.d.ts +4 -71
  38. package/dist/{hq-server-IVZLVCFV.js → hq-server-PBRU275L.js} +2 -2
  39. package/dist/hq-server.d.ts +0 -38
  40. package/dist/index.js +1267 -122
  41. package/dist/{mcp-JNWEQ6T2.js → mcp-CMLFHRG2.js} +19 -9
  42. package/dist/mcp-serve.d.ts +31 -1
  43. package/dist/{modeldiag-6VS5KDP5.js → modeldiag-ICMPHT5L.js} +868 -831
  44. package/dist/{project-manifest-GE25PW4S.js → project-manifest-YFTJ3D5N.js} +2 -2
  45. package/dist/{project-picker-BOCWHBXP.js → project-picker-R5TYAE3Q.js} +3 -3
  46. package/dist/{providers-models-HQN3AT3K.js → providers-models-4FAKM2WO.js} +4 -4
  47. package/dist/repl-eternal-loop.d.ts +8 -0
  48. package/dist/repl-goal-banner.d.ts +21 -0
  49. package/dist/slash-commands/brain-council.d.ts +36 -0
  50. package/dist/slash-commands/brain-status.d.ts +12 -0
  51. package/dist/slash-commands/fallback.d.ts +6 -0
  52. package/dist/slash-commands/kanban-board-handlers.d.ts +13 -0
  53. package/dist/slash-commands/kanban-lifecycle-diagnostics.d.ts +16 -0
  54. package/dist/slash-commands/kanban-task-subcommands.d.ts +8 -0
  55. package/dist/slash-commands/memory-audience.d.ts +14 -0
  56. package/dist/slash-commands/memory-gather.d.ts +9 -0
  57. package/dist/slash-commands/memory-stats.d.ts +5 -0
  58. package/dist/slash-commands/settings-mutations.d.ts +6 -0
  59. package/dist/slash-commands/settings-view.d.ts +5 -0
  60. package/dist/subcommands/handlers/modeldiag-bench.d.ts +10 -0
  61. package/dist/subcommands/handlers/modeldiag-eval.d.ts +27 -0
  62. package/dist/subcommands/handlers/modeldiag-test.d.ts +8 -0
  63. package/dist/{version-help-ASKQYXDJ.js → version-help-TFD6VPF3.js} +2 -2
  64. package/dist/vibe-protocol-wiring.d.ts +10 -0
  65. package/dist/webui-server/credential-watcher.d.ts +15 -0
  66. package/dist/webui-server/domain-handlers.d.ts +21 -0
  67. package/dist/webui-server/route-contexts.d.ts +41 -0
  68. package/dist/{webui-server-FTG7JAZU.js → webui-server-IPHGJGXE.js} +515 -437
  69. package/dist/wiring/cli-execute-builder.d.ts +88 -0
  70. package/dist/wiring/cli-heap-watchdog.d.ts +33 -0
  71. package/dist/wiring/cli-prompt-and-tools-setup.d.ts +25 -0
  72. package/dist/wiring/cli-slash-commands-setup.d.ts +79 -0
  73. package/dist/wiring/heap-watchdog-setup.d.ts +53 -0
  74. package/dist/wiring/metrics.d.ts +1 -0
  75. package/dist/wiring/replay-governance-setup.d.ts +31 -0
  76. package/package.json +27 -27
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Learning optimization scheduler and dispatch classifier for MultiAgentHost.
3
+ *
4
+ * @module fleet/host-learning-scheduler
5
+ */
6
+ import { LearningOptimizationScheduler, type LearningOptimizerLlm } from '@wrongstack/core/agent-catalog';
7
+ import type { MultiAgentDeps } from './host-types.js';
8
+ export declare class HostLearningScheduler {
9
+ private readonly deps;
10
+ private learningOptimizer;
11
+ private learningSwept;
12
+ constructor(deps: MultiAgentDeps);
13
+ getOptimizer(): LearningOptimizationScheduler | null;
14
+ notifyCaptured(role: string): void;
15
+ sweep(): void;
16
+ classifyDispatch(task: string, candidates: {
17
+ role: string;
18
+ name: string;
19
+ summary: string;
20
+ }[]): Promise<{
21
+ role: string;
22
+ reason?: string | undefined;
23
+ } | null>;
24
+ resolveOptimizerLlm(): Promise<LearningOptimizerLlm | undefined>;
25
+ dispose(): void;
26
+ }
27
+ //# sourceMappingURL=host-learning-scheduler.d.ts.map
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Host-level Shadow Agent lifecycle and observation management.
3
+ *
4
+ * @module fleet/host-shadow-manager
5
+ */
6
+ import type { SubagentConfig } from '@wrongstack/core/types';
7
+ import type { Director } from '@wrongstack/core/coordination';
8
+ import type { MultiAgentDeps, MultiAgentHostOptions } from './host-types.js';
9
+ export interface HostShadowManagerContext {
10
+ deps: MultiAgentDeps;
11
+ opts: MultiAgentHostOptions;
12
+ getDirector: () => Director | undefined;
13
+ spawnAndAssign: (subagentConfig: SubagentConfig, description: string, opts?: {
14
+ internalTask?: boolean;
15
+ stopShadowAfterTask?: boolean;
16
+ shadowIntervalMs?: number | undefined;
17
+ taskContext?: {
18
+ kanban?: {
19
+ boardId?: string;
20
+ taskId?: string;
21
+ projectRoot?: string;
22
+ };
23
+ } | undefined;
24
+ }) => Promise<{
25
+ subagentId: string;
26
+ taskId: string;
27
+ }>;
28
+ }
29
+ export declare class HostShadowManager {
30
+ private readonly ctx;
31
+ private shadowAgentId;
32
+ private shadowTaskId;
33
+ private readonly shadowTaskIds;
34
+ private readonly shadowOutstandingTaskIds;
35
+ private readonly shadowStopAfterTaskIds;
36
+ private shadowHeartbeatIntervalMs;
37
+ private shadowAutoStartSuppressions;
38
+ private shadowObservedWorkDepth;
39
+ private shadowPassInFlight;
40
+ private shadowQueuedProblem;
41
+ private readonly shadowActivityOffHandles;
42
+ constructor(ctx: HostShadowManagerContext);
43
+ getTaskIds(): ReadonlySet<string>;
44
+ isShadowTask(taskId: string): boolean;
45
+ getAgentId(): string | null;
46
+ getTaskId(): string | null;
47
+ enterShadowSpawn(): void;
48
+ exitShadowSpawn(): void;
49
+ onShadowTaskCompleted(taskId: string, subagentId: string): void;
50
+ armIfNeeded(): void;
51
+ recordShadowAgent(subagentId: string, taskId: string, intervalMs?: number): void;
52
+ clearShadowAgent(subagentId?: string): void;
53
+ markShadowTask(taskId: string): void;
54
+ addStopAfterTaskId(taskId: string): void;
55
+ removeShadowTask(taskId: string): void;
56
+ isActiveSubagent(subagentId: string): boolean;
57
+ private noteShadowWorkStarted;
58
+ private noteShadowWorkCompleted;
59
+ requestShadowPass(reason: string): void;
60
+ runShadowPass(reason: string): Promise<void>;
61
+ private stopShadowAfterTask;
62
+ dispose(): void;
63
+ }
64
+ //# sourceMappingURL=host-shadow-manager.d.ts.map
@@ -39,30 +39,12 @@ export declare class MultiAgentHost {
39
39
  * Bounded to 20 entries with LRU eviction to prevent unbounded memory growth. */
40
40
  private readonly acpRunnerCache;
41
41
  private readonly learningRoles;
42
- /** Background distillation of captured learning into per-skill addenda. */
43
- private learningOptimizer;
44
- private learningSwept;
42
+ private readonly learningScheduler;
45
43
  /** Adaptive concurrency controller — created in buildDirector() when config has
46
44
  * adaptiveConcurrency.enabled = true. Monitors FleetBus for 429 errors and
47
45
  * automatically adjusts maxConcurrent to prevent rate limiting. */
48
46
  private adaptiveConcurrencyController?;
49
- /** Active Shadow Agent spawned by the host or /shadow start. */
50
- private shadowAgentId;
51
- /** Assigned monitoring task for the active Shadow Agent. */
52
- private shadowTaskId;
53
- /** All internal Shadow Agent startup/heartbeat task ids, excluded from fleet summaries. */
54
- private readonly shadowTaskIds;
55
- /** Shadow task ids assigned but not yet completed. Prevents heartbeat backlog. */
56
- private readonly shadowOutstandingTaskIds;
57
- /** Shadow task ids whose subagent should stop immediately after the one-shot pass. */
58
- private readonly shadowStopAfterTaskIds;
59
- private shadowHeartbeatIntervalMs;
60
- /** Suppresses buildDirector() auto-start while /shadow start is explicitly spawning one. */
61
- private shadowAutoStartSuppressions;
62
- private shadowObservedWorkDepth;
63
- private shadowPassInFlight;
64
- private shadowQueuedProblem;
65
- private readonly shadowActivityOffHandles;
47
+ private readonly shadowManager;
66
48
  /** Peer-awareness status broadcaster (mailbox `status` mails on subagent
67
49
  * transitions + rich registry heartbeats). Started in buildDirector(),
68
50
  * stopped in dispose(). */
@@ -92,23 +74,10 @@ export declare class MultiAgentHost {
92
74
  private getCoordinator;
93
75
  /** Public accessor for the Director — used by buildRoutingRunner. */
94
76
  getDirector(): Director | undefined;
77
+ /** Run one shadow review pass immediately. */
78
+ runShadowPass(reason: string): Promise<void>;
95
79
  private ensureCoordinator;
96
80
  private buildDirector;
97
- /**
98
- * Arm host-owned Shadow observation. Healthy work windows stay fully
99
- * deterministic; a one-shot LLM Shadow pass runs only after problematic work
100
- * finishes, or when the user explicitly invokes /shadow start.
101
- */
102
- private armShadowAgentIfNeeded;
103
- private recordShadowAgent;
104
- private clearShadowAgent;
105
- private markShadowTask;
106
- private isActiveSubagent;
107
- private noteShadowWorkStarted;
108
- private noteShadowWorkCompleted;
109
- private requestShadowPass;
110
- private runShadowPass;
111
- private stopShadowAfterTask;
112
81
  /**
113
82
  * Returns the FleetEmitTool for director-mode subagents, if the director
114
83
  * has been built. Used by makeSubagentFactory to inject the tool into
@@ -118,127 +87,30 @@ export declare class MultiAgentHost {
118
87
  /**
119
88
  * Build a per-role subagent factory: given a SubagentConfig, construct a
120
89
  * fresh, isolated Agent with the role's filtered tools and (when the config
121
- * carries one) the role's persona as an appended system-prompt block. Public
122
- * so the autonomy-parallel engine can reuse the exact same agent-construction
123
- * path the director/spawn flow uses — each parallel slot then runs as a real,
124
- * specialized, concurrency-safe agent instead of sharing the leader's Context.
90
+ * carries one) the role's persona as an appended system-prompt block.
125
91
  */
126
92
  makeSubagentFactory(config: Config): AgentFactory;
127
93
  /**
128
94
  * Build the per-subagent runner.
129
- *
130
- * ACP agents (provider: 'acp') get their own runner via
131
- * makeACPSubagentRunner — they run external processes and don't go
132
- * through the Agent factory. Regular agents use the standard
133
- * makeAgentSubagentRunner path.
134
95
  */
135
96
  buildSubagentRunner(config: Config): Promise<SubagentRunner>;
136
97
  buildACPRunner(subagentId: string): Promise<SubagentRunner>;
137
98
  spawnACP(subagentId: string, task: string, config: Config): Promise<string>;
138
- /** Returns a tool slice for the subagent — full set unless restricted. */
139
99
  private filterTools;
140
- /** Resolved shared-mailbox project dir — same as the leader's checker. */
141
100
  private mailboxProjectDir;
142
- /**
143
- * Construct + start the brain-gated FleetSupervisor over this director's
144
- * fleet. Requires a BrainArbiter (production always wires one); without
145
- * it there is no safe decision path, so supervision stays off. All
146
- * actions flow through Director APIs — the supervisor never touches
147
- * coordinator internals or budget negotiation.
148
- */
149
101
  private buildFleetSupervisor;
150
- /**
151
- * Fire-and-forget report-back (wired as the Director's
152
- * `taskResultNotifier`): posts a non-awaited task's outcome to THIS
153
- * session's leader via the project mailbox. The leader's mailbox checker
154
- * injects it inline before the next step, so `assign_task`-without-await
155
- * results reach the conversation without polling. The body carries a
156
- * short excerpt only — the full result stays in the director's completed
157
- * cache, retrievable via `await_tasks`/`roll_up`.
158
- */
159
102
  private reportTaskResultToLeader;
160
103
  private subagentToolRegistry;
161
- /**
162
- * Spawn a fresh subagent and assign a single task. Returns task id.
163
- *
164
- * Optional `opts` lets the caller (a `/spawn` slash command or the
165
- * future director surface) override the subagent's provider, model,
166
- * and tool slice on a per-spawn basis. Without options, the legacy
167
- * behavior holds: the subagent uses the leader's provider/model and
168
- * the full tool registry.
169
- */
170
104
  spawn(description: string, opts?: HostSpawnOptions): Promise<{
171
105
  subagentId: string;
172
106
  taskId: string;
173
107
  }>;
174
- /**
175
- * Spawn a fresh subagent, assign a task, and **await** its completion.
176
- *
177
- * Unlike `spawn()`, which returns immediately with spawn metadata, this
178
- * method blocks until the subagent finishes (success, failure, or timeout)
179
- * and returns the full `TaskResult`. Use this when the caller needs the
180
- * subagent's actual output — e.g. `/techstack` displaying the generated report
181
- * in chat, or `/spawn` showing the result inline.
182
- *
183
- * Optional `opts` lets the caller override the subagent's provider, model,
184
- * and tool slice per spawn.
185
- */
186
108
  spawnAndWait(description: string, opts?: HostSpawnAndWaitOptions): Promise<TaskResult>;
187
- /**
188
- * Common spawn + assign logic shared by both director mode and raw
189
- * coordinator mode. Extracts the identical body from the two branches
190
- * in `spawn()` so future changes (e.g. adding a new field to both
191
- * paths) are made in one place.
192
- *
193
- * Returns `{ subagentId, taskId }`. Caller holds `pending` tracking
194
- * and event emission — the helper only talks to the coordinator.
195
- */
196
109
  private _spawnAndAssign;
197
- /**
198
- * Relay a `task.completed` notification (from either the Director or
199
- * the raw coordinator) to the EventBus so non-director TUIs and any
200
- * other observer can react. We forward the full result shape rather
201
- * than mutating the existing `task.completed` schema — coordination
202
- * code already binds to that event, and adding subscribers there
203
- * would change ordering semantics for those listeners.
204
- */
205
110
  private emitLifecycleCompleted;
206
111
  private recordLearningRole;
207
112
  private captureCompletedTaskLearning;
208
- /**
209
- * Lazily built so a session that never spawns a subagent pays nothing, and
210
- * so the policy is read from the live config rather than frozen at boot.
211
- * Returns null when auto-optimization is switched off.
212
- */
213
- private getLearningOptimizer;
214
- /**
215
- * Break a dispatch tie with a model when the keyword heuristic is ambiguous.
216
- *
217
- * Routes through the `dispatcher` model-matrix slot when one is configured —
218
- * picking a role is a short classification, so it belongs on a cheap fast
219
- * model rather than the leader's — and the session default otherwise.
220
- * Returning `null` on any failure leaves the dispatcher on its heuristic
221
- * result, so routing degrades rather than breaking a spawn.
222
- */
223
- private classifyDispatch;
224
- /**
225
- * Resolve a model for the distillation pass. Uses the `memory-curator` slot
226
- * of the model matrix when one is configured — curating learned knowledge is
227
- * exactly that role's job — and the session default otherwise. A failure
228
- * here is not fatal: the pass still writes the deterministic skill addenda.
229
- */
230
- private resolveOptimizerLlm;
231
- /**
232
- * Evaluate every role with learning data once per session. Without it, a
233
- * role that became eligible before this session would wait for its next
234
- * capture — which for a rarely-used role can be never.
235
- */
236
- private sweepLearningOptimization;
237
113
  status(): FleetHostStatus;
238
- /**
239
- * Read-only concurrency + lifetime spawn budget for `/fleet status` and
240
- * resume diagnostics. Safe when no director/fleet is active yet.
241
- */
242
114
  budgetView(): import('./host-status.js').FleetBudgetView;
243
115
  usage(): FleetHostUsage;
244
116
  manifest(): Promise<string | null>;
@@ -248,13 +120,6 @@ export declare class MultiAgentHost {
248
120
  stopAll(): Promise<void>;
249
121
  getMaxConcurrent(): number;
250
122
  setMaxConcurrent(n: number): void;
251
- /**
252
- * Clean up all listeners and resources held by the host.
253
- * Unregisters all EventBus/FleetBus listeners registered in `buildDirector`
254
- * and stops the Director and its coordinator.
255
- *
256
- * Safe to call multiple times — subsequent calls are no-ops.
257
- */
258
123
  dispose(): Promise<void>;
259
124
  }
260
125
  //# sourceMappingURL=host.d.ts.map
@@ -51,7 +51,7 @@ var hqCmd = async (args, deps) => {
51
51
  return 1;
52
52
  };
53
53
  async function startServer(deps) {
54
- const { startHqServer } = await import("./hq-server-IVZLVCFV.js");
54
+ const { startHqServer } = await import("./hq-server-PBRU275L.js");
55
55
  const dataDir = resolveDataDir(deps);
56
56
  const flags = deps.flags ?? {};
57
57
  const host = typeof flags["host"] === "string" ? flags["host"] : HQ_CLI_DEFAULT_HOST;
@@ -701,4 +701,4 @@ export {
701
701
  hqCmd,
702
702
  resolveAuditActor
703
703
  };
704
- //# sourceMappingURL=hq-ZK6PGN3W.js.map
704
+ //# sourceMappingURL=hq-PA6RH2UW.js.map
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Mailbox gateway management and idle eviction for HQ server.
3
+ *
4
+ * @module hq-server/mailbox-gateway-manager
5
+ */
6
+ import type { IncomingMessage } from 'node:http';
7
+ import { type MailboxHttpAccessDecision, MailboxHttpRateLimiter } from '@wrongstack/core/coordination';
8
+ import type { HqAuthState } from './auth-state.js';
9
+ import { type HqRouterMailboxGateway } from './routes.js';
10
+ import type { HqSessionEntry } from './types.js';
11
+ export interface MailboxGatewayManagerDeps {
12
+ host: string;
13
+ port: number;
14
+ mutableAuth: HqAuthState['mutableAuth'];
15
+ sessions: Map<string, HqSessionEntry>;
16
+ }
17
+ export declare class MailboxGatewayManager {
18
+ private readonly deps;
19
+ readonly mailboxGateways: Map<string, HqRouterMailboxGateway>;
20
+ readonly mailboxGatewayLastUsed: Map<string, number>;
21
+ readonly mailboxGatewayRateLimiter: MailboxHttpRateLimiter;
22
+ private readonly rateLimitCleanupTimer;
23
+ private readonly sweepTimer;
24
+ private readonly MAILBOX_GATEWAY_IDLE_TTL_MS;
25
+ private readonly MAILBOX_GATEWAY_SWEEP_INTERVAL_MS;
26
+ constructor(deps: MailboxGatewayManagerDeps);
27
+ authorizeMailboxGateway(req: IncomingMessage, projectDir: string): MailboxHttpAccessDecision;
28
+ getMailboxGateway(projectDir: string): HqRouterMailboxGateway;
29
+ private evictIdleGateways;
30
+ close(): void;
31
+ }
32
+ //# sourceMappingURL=mailbox-gateway-manager.d.ts.map
@@ -0,0 +1,5 @@
1
+ import type * as http from 'node:http';
2
+ import type { HqRouterMutableAuth, HqSessionEntry } from '../../types.js';
3
+ export declare function handleApiBootstrap(req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, secureCookies: boolean | undefined, bootstrapStore: import('@wrongstack/core/hq').HqBootstrapCodeStore): Promise<void>;
4
+ export declare function handleApiTokenUpgrade(req: http.IncomingMessage, res: http.ServerResponse, url: URL, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, secureCookies: boolean | undefined): Promise<void>;
5
+ //# sourceMappingURL=bootstrap-routes.d.ts.map
@@ -0,0 +1,11 @@
1
+ import type * as http from 'node:http';
2
+ import { type HqAuthFile } from '@wrongstack/core/hq';
3
+ import { type HqBrowserAuthResult } from '../../auth.js';
4
+ import type { HqRouterMutableAuth, HqSessionEntry } from '../../types.js';
5
+ export declare function isLoopbackRequest(req: http.IncomingMessage): boolean;
6
+ export declare const HQ_AUTH_ADMIN_CAPABILITY = "auth.admin";
7
+ export declare function callerCanAdministerAuth(auth: HqBrowserAuthResult): boolean;
8
+ export declare function writeAuthAdminRequired(res: http.ServerResponse): void;
9
+ export declare function authorizeAuthAdmin(req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>): HqBrowserAuthResult;
10
+ export type ApplyHqAuthFile = (next: HqAuthFile) => void;
11
+ //# sourceMappingURL=common.d.ts.map
@@ -0,0 +1,9 @@
1
+ import type * as http from 'node:http';
2
+ import type { LoginAttemptStore } from '../../login-attempt-store.js';
3
+ import type { HqRouterMutableAuth, HqSessionEntry } from '../../types.js';
4
+ import { type ApplyHqAuthFile } from './common.js';
5
+ export declare function handleApiAuthStatus(req: http.IncomingMessage, res: http.ServerResponse, url: URL, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, requireBrowserAuth: boolean | undefined, trustedPublicOrigins: Set<string>, secureCookies: boolean | undefined): Promise<void>;
6
+ export declare function handleApiLogin(req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, loginAttempts: LoginAttemptStore, secureCookies: boolean | undefined, trustedProxyHops: number): Promise<void>;
7
+ export declare function handleApiLogout(req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, secureCookies: boolean | undefined): Promise<void>;
8
+ export declare function handleApiPassword(req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, dataDir: string, secureCookies: boolean | undefined, requireBrowserAuth: boolean | undefined, applyAuthFile: ApplyHqAuthFile): Promise<void>;
9
+ //# sourceMappingURL=password-routes.d.ts.map
@@ -0,0 +1,6 @@
1
+ import type * as http from 'node:http';
2
+ import type { HqRouterMutableAuth, HqSessionEntry } from '../../types.js';
3
+ export declare function handleApiAuthSessions(_req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>): void;
4
+ export declare function handleApiAuthSessionsRevoke(_req: http.IncomingMessage, res: http.ServerResponse, url: URL, sessions: Map<string, HqSessionEntry>): Promise<void>;
5
+ export declare function handleApiAuthAudit(_req: http.IncomingMessage, res: http.ServerResponse, dataDir: string): void;
6
+ //# sourceMappingURL=session-audit-routes.d.ts.map
@@ -0,0 +1,9 @@
1
+ import type * as http from 'node:http';
2
+ import type { LoginAttemptStore } from '../../login-attempt-store.js';
3
+ import type { HqRouterMutableAuth, HqSessionEntry } from '../../types.js';
4
+ import { type ApplyHqAuthFile } from './common.js';
5
+ export declare function handleApiLoginVerify(req: http.IncomingMessage, res: http.ServerResponse, _url: URL, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, loginAttempts: LoginAttemptStore, dataDir: string, secureCookies: boolean | undefined, applyAuthFile: ApplyHqAuthFile, trustedProxyHops: number): Promise<void>;
6
+ export declare function handleApiTotpSetup(req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, dataDir: string, applyAuthFile: ApplyHqAuthFile): Promise<void>;
7
+ export declare function handleApiTotpEnable(req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, dataDir: string, applyAuthFile: ApplyHqAuthFile): Promise<void>;
8
+ export declare function handleApiTotpDisable(req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, loginAttempts: LoginAttemptStore, dataDir: string, applyAuthFile: ApplyHqAuthFile, trustedProxyHops: number): Promise<void>;
9
+ //# sourceMappingURL=totp-routes.d.ts.map
@@ -1,133 +1,6 @@
1
- import type * as http from 'node:http';
2
- import { type HqAuthFile } from '@wrongstack/core/hq';
3
- import { type HqBrowserAuthResult } from '../auth.js';
4
- import type { LoginAttemptStore } from '../login-attempt-store.js';
5
- import type { HqRouterMutableAuth, HqSessionEntry } from '../types.js';
6
- /**
7
- * Capability required to operate the account-security surface: enrolling or
8
- * removing 2FA, changing the password without knowing it, listing and revoking
9
- * browser sessions, and reading the auth audit log.
10
- *
11
- * WS-102: these routes used to gate on `if (!auth)` alone, so ANY authenticated
12
- * principal could reach them — including the least-privileged token HQ mints on
13
- * first run (`capabilities: ['control.enqueue']`). That token could
14
- * `POST /api/auth/totp/setup` → `/enable`, receive the recovery codes, and take
15
- * the `sessions.clear()` that enrollment performs. The operator was then held
16
- * at `totpRequired: true` on every password login with no authenticator and no
17
- * recovery codes, and `/totp/disable` demands auth they could no longer obtain:
18
- * a one-request, unrecoverable lockout from a token scoped to enqueueing
19
- * commands. Editing `auth.json` by hand was the only way back.
20
- */
21
- export declare const HQ_AUTH_ADMIN_CAPABILITY = "auth.admin";
22
- /**
23
- * True when `auth` may operate the account-security surface.
24
- *
25
- * - Password-origin cookie sessions (`kind: 'cookie'` with no `tokenId`) are
26
- * the operator themselves — always allowed.
27
- * - A token with no `capabilities` field is unrestricted by the documented
28
- * contract in `HqToken.capabilities`, so it is allowed.
29
- * - A capability-scoped token must list {@link HQ_AUTH_ADMIN_CAPABILITY}.
30
- *
31
- * Token-origin cookie sessions resolve their capabilities from the LIVE token
32
- * record on every request (see `authenticateBrowserRequest`), so upgrading a
33
- * token to a cookie never widens what it can do here.
34
- */
35
- export declare function callerCanAdministerAuth(auth: HqBrowserAuthResult): boolean;
36
- export declare function handleApiAuthStatus(req: http.IncomingMessage, res: http.ServerResponse, url: URL, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, requireBrowserAuth: boolean | undefined, trustedPublicOrigins: Set<string>, secureCookies: boolean | undefined): Promise<void>;
37
- export declare function handleApiLogin(req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, loginAttempts: LoginAttemptStore, secureCookies: boolean | undefined, trustedProxyHops: number): Promise<void>;
38
- export declare function handleApiLogout(req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, secureCookies: boolean | undefined): Promise<void>;
39
- export declare function handleApiPassword(req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, dataDir: string, secureCookies: boolean | undefined, requireBrowserAuth: boolean | undefined, applyAuthFile: ApplyHqAuthFile): Promise<void>;
40
- /**
41
- * Applies a freshly-persisted `auth.json` to the live server state.
42
- *
43
- * WS-101: this used to be a second, hand-rolled copy of the projection in
44
- * `hq-server/auth-state.ts`. The two drifted in exactly the ways duplicated
45
- * security code drifts: this copy skipped the expired-token filter on the
46
- * CLIENT scope (re-admitting expired `/ws/client` tokens, where the upgrade
47
- * gate does no expiry check of its own), never refreshed the raw token lists
48
- * behind `tokenStats()`, and — worst — never re-ran the WS-010 exposure
49
- * assessment, so `DELETE /api/auth/password` on a non-loopback bind dropped
50
- * every gate into open mode until the `fs.watch` debounce happened to fire.
51
- *
52
- * It is now a function type supplied by the server, bound to
53
- * `HqAuthState.apply`, so the mutation routes and the reload watcher share one
54
- * projection and one floor evaluation.
55
- */
56
- export type ApplyHqAuthFile = (next: HqAuthFile) => void;
57
- export declare function handleApiBootstrap(req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, secureCookies: boolean | undefined, bootstrapStore: import('@wrongstack/core/hq').HqBootstrapCodeStore): Promise<void>;
58
- /**
59
- * WS-065 — exchange a live browser token for an HttpOnly session cookie.
60
- *
61
- * The bootstrap flow above already gives fresh startup URLs a cookie and never
62
- * persists a client-side credential. What it does not cover is the *legacy*
63
- * paths: an older `?token=` startup URL, and manual token entry in the token
64
- * gate. Both leave the raw token in `sessionStorage`, where any script on the
65
- * origin can read it. This endpoint lets the SPA complete that migration on
66
- * its own — authenticate once with the token it already holds, receive a
67
- * cookie, and delete the stored copy.
68
- *
69
- * The minted session is exactly the one `handleApiBootstrap` mints: `kind:
70
- * 'token'` carrying the source token's id. That matters because
71
- * `authenticateBrowserRequest` re-resolves a token-origin session's
72
- * capabilities from the LIVE token record on every request, so upgrading
73
- * grants nothing the token did not already grant and dies the moment the token
74
- * is revoked. No privilege boundary is crossed: the caller proved possession
75
- * of the token, and the cookie is strictly weaker (HttpOnly, same-origin,
76
- * server-expiring).
77
- */
78
- export declare function handleApiTokenUpgrade(req: http.IncomingMessage, res: http.ServerResponse, url: URL, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, secureCookies: boolean | undefined): Promise<void>;
79
- /**
80
- * POST `/api/login/verify` — complete 2FA for a pending-2FA session.
81
- * Accepts either a TOTP `code` or a `recoveryCode`. On success, the pending
82
- * session is upgraded to a full session (pending2fa cleared). Recovery codes
83
- * are single-use: the matched hash is removed from `auth.json` on success.
84
- */
85
- export declare function handleApiLoginVerify(req: http.IncomingMessage, res: http.ServerResponse, _url: URL, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, loginAttempts: LoginAttemptStore, dataDir: string, secureCookies: boolean | undefined, applyAuthFile: ApplyHqAuthFile, trustedProxyHops: number): Promise<void>;
86
- /**
87
- * POST `/api/auth/totp/setup` — generate a new TOTP secret (not yet active).
88
- * Returns the base32 secret + otpauth URI for QR display. The secret is
89
- * persisted as `totpPendingSecret` — login does NOT check this field, so
90
- * an unconfirmed setup cannot lock the operator out. 2FA becomes active
91
- * only after `/api/auth/totp/enable` verifies a code.
92
- *
93
- * Requires existing auth (cookie or token) — only the operator can set up 2FA.
94
- */
95
- export declare function handleApiTotpSetup(req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, dataDir: string, applyAuthFile: ApplyHqAuthFile): Promise<void>;
96
- /**
97
- * POST `/api/auth/totp/enable` — confirm 2FA enrollment by providing a valid
98
- * TOTP code. Promotes `totpPendingSecret` → `totpSecret` (active), generates
99
- * and returns recovery codes (shown once). After this, 2FA is required for
100
- * password login.
101
- */
102
- export declare function handleApiTotpEnable(req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, dataDir: string, applyAuthFile: ApplyHqAuthFile): Promise<void>;
103
- /**
104
- * POST `/api/auth/totp/disable` — remove TOTP 2FA entirely. Requires the
105
- * current password (or a valid TOTP code) as confirmation.
106
- */
107
- export declare function handleApiTotpDisable(req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, loginAttempts: LoginAttemptStore, dataDir: string, applyAuthFile: ApplyHqAuthFile, trustedProxyHops: number): Promise<void>;
108
- /**
109
- * GET `/api/auth/sessions` — list all active browser sessions for the
110
- * session-management UI.
111
- *
112
- * The full session ID is returned because the revoke endpoint below is keyed
113
- * on it. That is safe on its own — the cookie value is `<id>.<HMAC>` and the
114
- * HMAC needs `cookieSecret` — but it does hand every reader a working revoke
115
- * handle for every other session, so the route is gated on `auth.admin`
116
- * (WS-102) rather than on "is authenticated" as it was.
117
- *
118
- * The previous docstring claimed the full ID was never returned while the code
119
- * returned it; the code was right about what the UI needs, the docstring was
120
- * right about it deserving a gate.
121
- */
122
- export declare function handleApiAuthSessions(_req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>): void;
123
- /**
124
- * DELETE `/api/auth/sessions/:id` — revoke a single session by ID.
125
- * DELETE `/api/auth/sessions` — revoke ALL sessions (force logout everyone).
126
- */
127
- export declare function handleApiAuthSessionsRevoke(_req: http.IncomingMessage, res: http.ServerResponse, url: URL, sessions: Map<string, HqSessionEntry>): Promise<void>;
128
- /**
129
- * GET `/api/auth/audit` — recent auth events for the audit panel.
130
- * Returns the last 50 entries from auth-audit.jsonl, newest first.
131
- */
132
- export declare function handleApiAuthAudit(_req: http.IncomingMessage, res: http.ServerResponse, dataDir: string): void;
1
+ export { authorizeAuthAdmin, callerCanAdministerAuth, HQ_AUTH_ADMIN_CAPABILITY, isLoopbackRequest, writeAuthAdminRequired, type ApplyHqAuthFile, } from './auth/common.js';
2
+ export { handleApiAuthStatus, handleApiLogin, handleApiLogout, handleApiPassword, } from './auth/password-routes.js';
3
+ export { handleApiLoginVerify, handleApiTotpDisable, handleApiTotpEnable, handleApiTotpSetup, } from './auth/totp-routes.js';
4
+ export { handleApiBootstrap, handleApiTokenUpgrade, } from './auth/bootstrap-routes.js';
5
+ export { handleApiAuthAudit, handleApiAuthSessions, handleApiAuthSessionsRevoke, } from './auth/session-audit-routes.js';
133
6
  //# sourceMappingURL=auth-handlers.d.ts.map
@@ -0,0 +1,7 @@
1
+ import type * as http from 'node:http';
2
+ import type { HqCommandAuditLog } from '@wrongstack/core/hq';
3
+ import type { TrustBoundary } from '@wrongstack/core/security';
4
+ import type { WebSocket } from 'ws';
5
+ import type { ConnectedClient, HqRouterMutableAuth, HqSessionEntry } from '../types.js';
6
+ export declare function handleApiCommand(req: http.IncomingMessage, res: http.ServerResponse, url: URL, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, clients: Map<WebSocket, ConnectedClient>, browsers: Set<WebSocket>, auditLog: HqCommandAuditLog, trustBoundary: TrustBoundary): Promise<void>;
7
+ //# sourceMappingURL=command-handlers.d.ts.map
@@ -0,0 +1,8 @@
1
+ import type * as http from 'node:http';
2
+ import type { createHqPersistence } from '@wrongstack/core/hq';
3
+ import type { WebSocket } from 'ws';
4
+ import type { ConnectedClient } from '../types.js';
5
+ export declare function handleApiProjectDetail(_req: http.IncomingMessage, res: http.ServerResponse, url: URL, clients: Map<WebSocket, ConnectedClient>): Promise<void>;
6
+ export declare function handleApiEvents(req: http.IncomingMessage, res: http.ServerResponse, persistence: ReturnType<typeof createHqPersistence>): Promise<void>;
7
+ export declare function handleApiTrendsCost(req: http.IncomingMessage, res: http.ServerResponse, persistence: ReturnType<typeof createHqPersistence>): Promise<void>;
8
+ //# sourceMappingURL=data-handlers.d.ts.map
@@ -0,0 +1,22 @@
1
+ import type * as http from 'node:http';
2
+ import { type MailboxHttpAccessDecision } from '@wrongstack/core/coordination';
3
+ import { authenticateBrowserRequest } from '../auth.js';
4
+ import type { HqRouterMailboxGateway, HqRouterMutableAuth, HqSessionEntry } from '../types.js';
5
+ /**
6
+ * Does this authenticated caller hold `control.enqueue`?
7
+ *
8
+ * WS-012: this predicate was inlined at two call sites with a subtly different
9
+ * unauthenticated fallback (`!inBrowserTokenMode` vs
10
+ * `mutableAuth.browserTokens.size === 0` — equivalent, but drifting), and a
11
+ * third route, `POST /api/mailbox/messages/:id/action`, skipped it entirely:
12
+ * its auth parameters were `_`-prefixed to mark them deliberately unused. That
13
+ * left mailbox mutation (mark-read, acknowledge, reopen, soft-delete, restore)
14
+ * reachable by any authenticated caller regardless of capability.
15
+ *
16
+ * One helper so the three cannot diverge again.
17
+ */
18
+ export declare function callerCanEnqueue(auth: ReturnType<typeof authenticateBrowserRequest>, mutableAuth: HqRouterMutableAuth): boolean;
19
+ export declare function handleMailboxGateway(_req: http.IncomingMessage, res: http.ServerResponse, match: RegExpMatchArray, authorizeMailboxGateway: (req: http.IncomingMessage, projectDir: string) => MailboxHttpAccessDecision, getMailboxGateway: (projectDir: string) => HqRouterMailboxGateway, dataDir: string): Promise<void>;
20
+ export declare function handleApiMailboxSend(_req: http.IncomingMessage, res: http.ServerResponse, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, dataDir: string, getMailboxGateway: (projectDir: string) => HqRouterMailboxGateway, hqSessionTag: string): Promise<void>;
21
+ export declare function handleMailboxAction(_req: http.IncomingMessage, res: http.ServerResponse, match: RegExpMatchArray, mutableAuth: HqRouterMutableAuth, sessions: Map<string, HqSessionEntry>, dataDir: string, getMailboxGateway: (projectDir: string) => HqRouterMailboxGateway): Promise<void>;
22
+ //# sourceMappingURL=mailbox-handlers.d.ts.map
@@ -9,9 +9,9 @@
9
9
  */
10
10
  import type * as http from 'node:http';
11
11
  import type { createMailboxHttpRouter } from '@wrongstack/core/coordination';
12
- import { type Mailbox, type MailboxHttpAccessDecision, type MailboxHttpRateLimiter } from '@wrongstack/core/coordination';
12
+ import type { Mailbox, MailboxHttpAccessDecision, MailboxHttpRateLimiter } from '@wrongstack/core/coordination';
13
13
  import type { HqAlertEngine, HqCommandAuditLog, HqEventEnvelope, HqSnapshot, HqTranscriptEntry } from '@wrongstack/core/hq';
14
- import { type createHqPersistence } from '@wrongstack/core/hq';
14
+ import type { createHqPersistence } from '@wrongstack/core/hq';
15
15
  import type { TrustBoundary } from '@wrongstack/core/security';
16
16
  import type { WebSocket } from 'ws';
17
17
  import * as HqServerAuth from './auth.js';
@@ -89,7 +89,7 @@ export interface HqRouterDeps {
89
89
  bootstrapStore?: import('@wrongstack/core/hq').HqBootstrapCodeStore | undefined;
90
90
  /**
91
91
  * Applies a freshly-persisted `auth.json` to live server state. Bound to
92
- * `HqAuthState.apply` so the in-process mutation routes share one projection
92
+ * `HqAuthState.apply` so the in-process mutation routes share one projection
93
93
  * — and one WS-010 exposure re-assessment — with the reload watcher.
94
94
  */
95
95
  applyAuthFile: ApplyHqAuthFile;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * HQ server lifecycle and shutdown management.
3
+ *
4
+ * @module hq-server/server-lifecycle
5
+ */
6
+ import type { Server as HttpServer } from 'node:http';
7
+ import type { WebSocket, WebSocketServer } from 'ws';
8
+ import type { HqPersistence } from '@wrongstack/core/hq';
9
+ import type { LoginAttemptStore } from './login-attempt-store.js';
10
+ import type { MailboxGatewayManager } from './mailbox-gateway-manager.js';
11
+ export interface HqServerShutdownParams {
12
+ cleanupTimer: NodeJS.Timeout;
13
+ browserHeartbeatTimer: NodeJS.Timeout;
14
+ timeseriesFlushTimer: NodeJS.Timeout;
15
+ sessionCleanupTimer: NodeJS.Timeout;
16
+ loginAttempts: LoginAttemptStore;
17
+ stopAlertEngine: () => void;
18
+ authWatcher: {
19
+ close(): void;
20
+ };
21
+ snapshotBroadcaster: {
22
+ close(): void;
23
+ };
24
+ bootstrapStore: {
25
+ clear(): void;
26
+ };
27
+ persistence: HqPersistence;
28
+ browsers: Set<WebSocket>;
29
+ clients: Map<WebSocket, unknown>;
30
+ wss: WebSocketServer;
31
+ mailboxManager: MailboxGatewayManager;
32
+ httpServer: HttpServer;
33
+ dataDir: string;
34
+ hqUrl: string;
35
+ }
36
+ export declare function createHqServerShutdown(params: HqServerShutdownParams): () => Promise<void>;
37
+ //# sourceMappingURL=server-lifecycle.d.ts.map