@snapback/cli 1.6.0 → 3.0.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 (57) hide show
  1. package/README.md +120 -21
  2. package/dist/SkippedTestDetector-AXTMWWHC.js +5 -0
  3. package/dist/SkippedTestDetector-QLSQV7K7.js +5 -0
  4. package/dist/analysis-6WTBZJH3.js +6 -0
  5. package/dist/analysis-C472LUGW.js +2475 -0
  6. package/dist/auth-TDIHGKKL.js +1446 -0
  7. package/dist/auto-provision-organization-CXHL46P3.js +161 -0
  8. package/dist/{chunk-FVIYXFCL.js → chunk-4YTE4JEW.js} +2 -3
  9. package/dist/chunk-5EOPYJ4Y.js +12 -0
  10. package/dist/{chunk-ARVV3F4K.js → chunk-5SQA44V7.js} +1085 -18
  11. package/dist/{chunk-RB7H4UQJ.js → chunk-7ADPL4Q3.js} +10 -3
  12. package/dist/chunk-CBGOC6RV.js +293 -0
  13. package/dist/chunk-CPZWXRP2.js +4432 -0
  14. package/dist/{chunk-7JX6Y4TL.js → chunk-DPWFZNMY.js} +21 -34
  15. package/dist/{chunk-R7CUQ7CU.js → chunk-E6V6QKS7.js} +317 -33
  16. package/dist/chunk-FMWCFAY7.js +111 -0
  17. package/dist/chunk-GQ73B37K.js +314 -0
  18. package/dist/chunk-LIBBDBW5.js +6136 -0
  19. package/dist/chunk-O7HMAZ7L.js +3497 -0
  20. package/dist/chunk-PL4HF4M2.js +593 -0
  21. package/dist/chunk-Q4VC7GND.js +2300 -0
  22. package/dist/chunk-WS36HDEU.js +3735 -0
  23. package/dist/chunk-ZBQDE6WJ.js +108 -0
  24. package/dist/client-62E3L6DW.js +8 -0
  25. package/dist/dist-5LR7APG5.js +5 -0
  26. package/dist/dist-NFU5UJEW.js +9 -0
  27. package/dist/dist-OO5LJHL6.js +12 -0
  28. package/dist/index.js +61644 -37198
  29. package/dist/local-service-adapter-AB3UYRUK.js +6 -0
  30. package/dist/pioneer-oauth-hook-V2JKEXM7.js +12 -0
  31. package/dist/{secure-credentials-IWQB6KU4.js → secure-credentials-UEPG7GWW.js} +2 -3
  32. package/dist/snapback-dir-MG7DTRMF.js +6 -0
  33. package/package.json +12 -11
  34. package/scripts/postinstall.mjs +2 -3
  35. package/dist/SkippedTestDetector-5WJZKZQ3.js +0 -5
  36. package/dist/SkippedTestDetector-5WJZKZQ3.js.map +0 -1
  37. package/dist/analysis-YI4UNUCM.js +0 -6
  38. package/dist/analysis-YI4UNUCM.js.map +0 -1
  39. package/dist/chunk-7JX6Y4TL.js.map +0 -1
  40. package/dist/chunk-ARVV3F4K.js.map +0 -1
  41. package/dist/chunk-EU2IZPOK.js +0 -13002
  42. package/dist/chunk-EU2IZPOK.js.map +0 -1
  43. package/dist/chunk-FVIYXFCL.js.map +0 -1
  44. package/dist/chunk-R7CUQ7CU.js.map +0 -1
  45. package/dist/chunk-RB7H4UQJ.js.map +0 -1
  46. package/dist/chunk-SOABQWAU.js +0 -385
  47. package/dist/chunk-SOABQWAU.js.map +0 -1
  48. package/dist/dist-O6EBXLN6.js +0 -5
  49. package/dist/dist-O6EBXLN6.js.map +0 -1
  50. package/dist/dist-PJVBBZTF.js +0 -5
  51. package/dist/dist-PJVBBZTF.js.map +0 -1
  52. package/dist/index.js.map +0 -1
  53. package/dist/learning-pruner-QC4CTJDX.js +0 -5
  54. package/dist/learning-pruner-QC4CTJDX.js.map +0 -1
  55. package/dist/secure-credentials-IWQB6KU4.js.map +0 -1
  56. package/dist/snapback-dir-V6MWXIW4.js +0 -5
  57. package/dist/snapback-dir-V6MWXIW4.js.map +0 -1
@@ -0,0 +1,3497 @@
1
+ #!/usr/bin/env node --no-warnings=ExperimentalWarning
2
+ import { LearningApiSchema } from './chunk-CBGOC6RV.js';
3
+ import { __name } from './chunk-7ADPL4Q3.js';
4
+ import { existsSync, readFileSync } from 'fs';
5
+ import * as os from 'os';
6
+ import { homedir } from 'os';
7
+ import * as path from 'path';
8
+ import { join } from 'path';
9
+ import * as net from 'net';
10
+ import { z } from 'zod';
11
+ import pRetry from 'p-retry';
12
+
13
+ process.env.SNAPBACK_CLI='true';
14
+
15
+ // ../../packages/contracts/dist/local-service/protocol.js
16
+ var ProtocolErrorCode;
17
+ (function(ProtocolErrorCode2) {
18
+ ProtocolErrorCode2[ProtocolErrorCode2["ParseError"] = -32700] = "ParseError";
19
+ ProtocolErrorCode2[ProtocolErrorCode2["InvalidRequest"] = -32600] = "InvalidRequest";
20
+ ProtocolErrorCode2[ProtocolErrorCode2["MethodNotFound"] = -32601] = "MethodNotFound";
21
+ ProtocolErrorCode2[ProtocolErrorCode2["InvalidParams"] = -32602] = "InvalidParams";
22
+ ProtocolErrorCode2[ProtocolErrorCode2["InternalError"] = -32603] = "InternalError";
23
+ ProtocolErrorCode2[ProtocolErrorCode2["ServerError"] = -32e3] = "ServerError";
24
+ ProtocolErrorCode2[ProtocolErrorCode2["NotInitialized"] = -32001] = "NotInitialized";
25
+ ProtocolErrorCode2[ProtocolErrorCode2["AlreadyInitialized"] = -32002] = "AlreadyInitialized";
26
+ ProtocolErrorCode2[ProtocolErrorCode2["ShuttingDown"] = -32003] = "ShuttingDown";
27
+ ProtocolErrorCode2[ProtocolErrorCode2["NotAuthenticated"] = 1e3] = "NotAuthenticated";
28
+ ProtocolErrorCode2[ProtocolErrorCode2["TierRequired"] = 1001] = "TierRequired";
29
+ ProtocolErrorCode2[ProtocolErrorCode2["ApiUnavailable"] = 1002] = "ApiUnavailable";
30
+ ProtocolErrorCode2[ProtocolErrorCode2["QuotaExceeded"] = 1003] = "QuotaExceeded";
31
+ ProtocolErrorCode2[ProtocolErrorCode2["WorkspaceNotFound"] = 1500] = "WorkspaceNotFound";
32
+ ProtocolErrorCode2[ProtocolErrorCode2["WorkspaceAlreadyExists"] = 1501] = "WorkspaceAlreadyExists";
33
+ ProtocolErrorCode2[ProtocolErrorCode2["SessionNotFound"] = 2e3] = "SessionNotFound";
34
+ ProtocolErrorCode2[ProtocolErrorCode2["SessionEnded"] = 2001] = "SessionEnded";
35
+ ProtocolErrorCode2[ProtocolErrorCode2["SnapshotNotFound"] = 3e3] = "SnapshotNotFound";
36
+ ProtocolErrorCode2[ProtocolErrorCode2["ContentHashMismatch"] = 3001] = "ContentHashMismatch";
37
+ ProtocolErrorCode2[ProtocolErrorCode2["RestoreFailed"] = 3002] = "RestoreFailed";
38
+ ProtocolErrorCode2[ProtocolErrorCode2["FileNotFound"] = 4e3] = "FileNotFound";
39
+ ProtocolErrorCode2[ProtocolErrorCode2["FileAccessDenied"] = 4001] = "FileAccessDenied";
40
+ ProtocolErrorCode2[ProtocolErrorCode2["InvalidPath"] = 4002] = "InvalidPath";
41
+ ProtocolErrorCode2[ProtocolErrorCode2["LearningNotFound"] = 5e3] = "LearningNotFound";
42
+ ProtocolErrorCode2[ProtocolErrorCode2["LearningEvaluationFailed"] = 5001] = "LearningEvaluationFailed";
43
+ ProtocolErrorCode2[ProtocolErrorCode2["SyncUnavailable"] = 6e3] = "SyncUnavailable";
44
+ ProtocolErrorCode2[ProtocolErrorCode2["SyncAuthRequired"] = 6001] = "SyncAuthRequired";
45
+ ProtocolErrorCode2[ProtocolErrorCode2["ValidationTimeout"] = 7e3] = "ValidationTimeout";
46
+ ProtocolErrorCode2[ProtocolErrorCode2["IntelligenceUnavailable"] = 8e3] = "IntelligenceUnavailable";
47
+ ProtocolErrorCode2[ProtocolErrorCode2["PatternNotFound"] = 8001] = "PatternNotFound";
48
+ })(ProtocolErrorCode || (ProtocolErrorCode = {}));
49
+ ({
50
+ [ProtocolErrorCode.ParseError]: "Invalid JSON",
51
+ [ProtocolErrorCode.InvalidRequest]: "Invalid JSON-RPC request",
52
+ [ProtocolErrorCode.MethodNotFound]: "Method does not exist",
53
+ [ProtocolErrorCode.InvalidParams]: "Invalid method parameters",
54
+ [ProtocolErrorCode.InternalError]: "Internal service error",
55
+ [ProtocolErrorCode.ServerError]: "Server error",
56
+ [ProtocolErrorCode.NotInitialized]: "Client must call initialize before other methods",
57
+ [ProtocolErrorCode.AlreadyInitialized]: "Client has already called initialize",
58
+ [ProtocolErrorCode.ShuttingDown]: "Service is shutting down",
59
+ [ProtocolErrorCode.NotAuthenticated]: "Authentication required for this method",
60
+ [ProtocolErrorCode.TierRequired]: "PRO tier required for this method",
61
+ [ProtocolErrorCode.ApiUnavailable]: "Cannot reach api.snapback.dev",
62
+ [ProtocolErrorCode.QuotaExceeded]: "Storage or API quota exceeded",
63
+ [ProtocolErrorCode.SessionNotFound]: "Session does not exist",
64
+ [ProtocolErrorCode.SessionEnded]: "Session has already ended",
65
+ [ProtocolErrorCode.SnapshotNotFound]: "Snapshot does not exist",
66
+ [ProtocolErrorCode.ContentHashMismatch]: "Content integrity check failed",
67
+ [ProtocolErrorCode.RestoreFailed]: "Could not write restored file",
68
+ [ProtocolErrorCode.FileNotFound]: "File does not exist",
69
+ [ProtocolErrorCode.FileAccessDenied]: "Cannot read/write file",
70
+ [ProtocolErrorCode.InvalidPath]: "Path is outside allowed directories",
71
+ [ProtocolErrorCode.WorkspaceNotFound]: "Workspace not found or not initialized",
72
+ [ProtocolErrorCode.WorkspaceAlreadyExists]: "Workspace already exists",
73
+ [ProtocolErrorCode.LearningNotFound]: "Learning not found",
74
+ [ProtocolErrorCode.LearningEvaluationFailed]: "Learning evaluation failed",
75
+ [ProtocolErrorCode.SyncUnavailable]: "Sync worker not available",
76
+ [ProtocolErrorCode.SyncAuthRequired]: "Sync authentication required",
77
+ [ProtocolErrorCode.ValidationTimeout]: "Validation timed out",
78
+ [ProtocolErrorCode.IntelligenceUnavailable]: "Intelligence service not available",
79
+ [ProtocolErrorCode.PatternNotFound]: "Pattern not found"
80
+ });
81
+ z.string().regex(/^\d+\.\d+\.\d+$/);
82
+ var SnapshotTrigger = z.enum([
83
+ "save",
84
+ "ai_burst",
85
+ "manual",
86
+ "timer",
87
+ "pre_restore",
88
+ "external"
89
+ ]);
90
+ var ProtectionLevel = z.enum([
91
+ "watch",
92
+ "warn",
93
+ "block"
94
+ ]);
95
+ var RiskLevel = z.enum([
96
+ "low",
97
+ "medium",
98
+ "high",
99
+ "critical"
100
+ ]);
101
+ var SessionState = z.enum([
102
+ "active",
103
+ "idle",
104
+ "ended"
105
+ ]);
106
+ z.enum([
107
+ "free",
108
+ "pro",
109
+ "enterprise"
110
+ ]);
111
+ var LearningType = z.enum([
112
+ "pattern",
113
+ "pitfall",
114
+ "efficiency",
115
+ "discovery",
116
+ "workflow"
117
+ ]);
118
+ var TaskIntent = z.enum([
119
+ "implement",
120
+ "debug",
121
+ "refactor",
122
+ "review",
123
+ "explore"
124
+ ]);
125
+ var MemoryEntryType = z.enum([
126
+ "learning",
127
+ "pattern",
128
+ "violation",
129
+ "context",
130
+ "task",
131
+ "session"
132
+ ]);
133
+ var WarningSeverity = z.enum([
134
+ "info",
135
+ "warning",
136
+ "error"
137
+ ]);
138
+ var CoChangeRelationship = z.enum([
139
+ "import_dependency",
140
+ "test_coverage",
141
+ "config_pair",
142
+ "co_change_historical",
143
+ "type_definition"
144
+ ]).or(z.string());
145
+ var ClientType = z.enum([
146
+ "vscode",
147
+ "mcp-stdio",
148
+ "mcp-remote",
149
+ "cli",
150
+ "api",
151
+ "unknown"
152
+ ]);
153
+ var TimingSpan = z.object({
154
+ /** Name of the span (e.g., "ipc_receive", "service_dispatch", "decision") */
155
+ name: z.string(),
156
+ /** Start time in milliseconds relative to request start */
157
+ startMs: z.number(),
158
+ /** End time in milliseconds relative to request start */
159
+ endMs: z.number()
160
+ });
161
+ var SavePathMetrics = z.object({
162
+ /** Unique identifier for this request */
163
+ requestId: z.string(),
164
+ /** Total time from request receive to response send */
165
+ totalMs: z.number(),
166
+ /** Breakdown of time spent in each phase */
167
+ spans: z.array(TimingSpan),
168
+ /** Whether a snapshot was actually created */
169
+ snapshotCreated: z.boolean()
170
+ });
171
+ var ShouldCreateReason = z.enum([
172
+ "no_previous",
173
+ "hash_changed",
174
+ "time_threshold",
175
+ "size_threshold",
176
+ "no_change",
177
+ "cooldown"
178
+ ]);
179
+ var FileConflict = z.object({
180
+ /** The OTHER session ID involved in the conflict */
181
+ sessionId: z.string(),
182
+ /** Client type of the other session */
183
+ clientType: z.enum([
184
+ "vscode",
185
+ "mcp-stdio",
186
+ "mcp-remote",
187
+ "cli",
188
+ "api",
189
+ "unknown"
190
+ ]),
191
+ /** Overlapping file paths */
192
+ files: z.array(z.string()),
193
+ /** When the conflict was detected */
194
+ detectedAt: z.number(),
195
+ /** Severity level */
196
+ severity: z.enum([
197
+ "info",
198
+ "warn"
199
+ ])
200
+ });
201
+ z.object({
202
+ /** Unique session identifier (UUID v4) */
203
+ id: z.string().uuid(),
204
+ /** Absolute path to the workspace root */
205
+ workspacePath: z.string(),
206
+ /** ISO 8601 timestamp when session started */
207
+ startedAt: z.string().datetime(),
208
+ /** ISO 8601 timestamp of last activity */
209
+ lastActivityAt: z.string().datetime(),
210
+ /** Current session state */
211
+ state: SessionState,
212
+ /** Session metadata */
213
+ metadata: z.object({
214
+ /** AI tools detected during this session */
215
+ aiToolsDetected: z.array(z.string()),
216
+ /** Highest risk level seen in this session */
217
+ highestRiskLevel: RiskLevel,
218
+ /** Count of snapshots by trigger type */
219
+ triggerCounts: z.record(z.string(), z.number())
220
+ }),
221
+ // === Phase 1 Additions ===
222
+ /** Client ID that owns this session (populated from ClientRegistry) */
223
+ clientId: z.string().optional(),
224
+ /** Client type (vscode, mcp-stdio, mcp-remote, cli, api, unknown) */
225
+ clientType: z.enum([
226
+ "vscode",
227
+ "mcp-stdio",
228
+ "mcp-remote",
229
+ "cli",
230
+ "api",
231
+ "unknown"
232
+ ]).optional(),
233
+ /** Unix ms timestamp when session ended (null if active) */
234
+ endedAt: z.number().nullable().optional(),
235
+ /** Snapshot IDs created during this session */
236
+ snapshotIds: z.array(z.string()).optional(),
237
+ /** Learning IDs captured during this session */
238
+ learningIds: z.array(z.string()).optional(),
239
+ /** Coherence score computed at session end */
240
+ coherenceScore: z.enum([
241
+ "high",
242
+ "medium",
243
+ "low",
244
+ "scattered"
245
+ ]).nullable().optional(),
246
+ /** Maximum risk score observed during session */
247
+ riskPeakScore: z.number().nullable().optional(),
248
+ /** Other active sessions on same workspace */
249
+ concurrentSessionIds: z.array(z.string()).optional(),
250
+ /** Cross-session file overlap detections */
251
+ fileConflicts: z.array(FileConflict).optional(),
252
+ /** Files touched during this session */
253
+ touchedFiles: z.array(z.string()).optional()
254
+ });
255
+ var Snapshot = z.object({
256
+ /** Unique snapshot identifier (UUID v4) */
257
+ id: z.string().uuid(),
258
+ /** Session that created this snapshot */
259
+ sessionId: z.string().uuid(),
260
+ /** Absolute path to the file */
261
+ filePath: z.string(),
262
+ /** Path relative to workspace root */
263
+ relativePath: z.string(),
264
+ /** Content hash (SHA-256, prefixed with "sha256:") */
265
+ contentHash: z.string().startsWith("sha256:"),
266
+ /** What triggered this snapshot */
267
+ trigger: SnapshotTrigger,
268
+ /** ISO 8601 timestamp when snapshot was created */
269
+ createdAt: z.string().datetime(),
270
+ /** Snapshot metadata */
271
+ metadata: z.object({
272
+ /** File size in bytes */
273
+ fileSize: z.number().int().nonnegative(),
274
+ /** Number of lines in the file */
275
+ lineCount: z.number().int().nonnegative(),
276
+ /** Programming language (optional) */
277
+ language: z.string().optional(),
278
+ /** AI tool that was active (if detected) */
279
+ aiToolDetected: z.string().optional(),
280
+ /** Confidence score for AI detection (0-1) */
281
+ aiConfidence: z.number().min(0).max(1).optional(),
282
+ /** Risk level assessment (optional) */
283
+ riskLevel: RiskLevel.optional()
284
+ })
285
+ });
286
+ z.object({
287
+ /** Determined protection level */
288
+ level: ProtectionLevel,
289
+ /** Whether this was automatically determined */
290
+ automatic: z.boolean(),
291
+ /** Source of the decision */
292
+ source: z.enum([
293
+ "auto",
294
+ "user",
295
+ "config",
296
+ "default"
297
+ ]),
298
+ /** Confidence in the decision (0-1) */
299
+ confidence: z.number().min(0).max(1)
300
+ });
301
+ z.object({
302
+ /** Factor name/description */
303
+ name: z.string(),
304
+ /** Factor score (0-1, higher = more protection needed) */
305
+ score: z.number().min(0).max(1),
306
+ /** Factor weight in overall decision (0-1) */
307
+ weight: z.number().min(0).max(1),
308
+ /** Additional details (optional) */
309
+ details: z.string().optional()
310
+ });
311
+ z.object({
312
+ /** Type of detection signal */
313
+ type: z.enum([
314
+ "pattern",
315
+ "timing",
316
+ "volume",
317
+ "style"
318
+ ]),
319
+ /** Human-readable description */
320
+ description: z.string(),
321
+ /** Signal strength (0-1, higher = stronger evidence) */
322
+ strength: z.number().min(0).max(1)
323
+ });
324
+ z.object({
325
+ /** Risk category */
326
+ category: z.enum([
327
+ "security",
328
+ "stability",
329
+ "data",
330
+ "infrastructure"
331
+ ]),
332
+ /** Human-readable description */
333
+ description: z.string(),
334
+ /** Severity level */
335
+ severity: z.enum([
336
+ "low",
337
+ "medium",
338
+ "high"
339
+ ]),
340
+ /** Location in code (optional) */
341
+ location: z.object({
342
+ /** Starting line number (1-indexed) */
343
+ startLine: z.number().int().positive(),
344
+ /** Ending line number (1-indexed) */
345
+ endLine: z.number().int().positive()
346
+ }).optional()
347
+ });
348
+ z.object({
349
+ /** Overall risk score: 0-100 */
350
+ score: z.number().int().min(0).max(100),
351
+ /** Risk level classification (reuses existing RiskLevel from local-service) */
352
+ level: RiskLevel,
353
+ /** All identified risk factors */
354
+ factors: z.array(z.object({
355
+ /** Factor name */
356
+ name: z.string(),
357
+ /** Score contribution: -100 to 100 */
358
+ score: z.number().min(-100).max(100),
359
+ /** Human-readable description */
360
+ description: z.string(),
361
+ /** Source system */
362
+ source: z.enum([
363
+ "github",
364
+ "context7",
365
+ "sentry",
366
+ "local",
367
+ "ai_detection"
368
+ ]),
369
+ /** Optional mitigation suggestion */
370
+ suggestion: z.string().optional()
371
+ })),
372
+ /** Recommended action */
373
+ recommendation: z.enum([
374
+ "proceed",
375
+ "warn",
376
+ "block"
377
+ ]),
378
+ /** Confidence in assessment: 0-100 */
379
+ confidence: z.number().int().min(0).max(100),
380
+ /** Human-readable explanation */
381
+ explanation: z.string()
382
+ });
383
+ z.discriminatedUnion("detected", [
384
+ z.object({
385
+ detected: z.literal(false),
386
+ /** Detection confidence: 0-1 (confidence that no AI was involved) */
387
+ confidence: z.number().min(0).max(1),
388
+ /** Detection signals evaluated */
389
+ signals: z.array(z.object({
390
+ type: z.enum([
391
+ "pattern",
392
+ "timing",
393
+ "volume",
394
+ "style"
395
+ ]),
396
+ description: z.string(),
397
+ strength: z.number().min(0).max(1)
398
+ }))
399
+ }),
400
+ z.object({
401
+ detected: z.literal(true),
402
+ /** Detected AI tool name */
403
+ toolName: z.string(),
404
+ /** Detection confidence: 0-1 */
405
+ confidence: z.number().min(0).max(1),
406
+ /** Detection signals that contributed to the result */
407
+ signals: z.array(z.object({
408
+ type: z.enum([
409
+ "pattern",
410
+ "timing",
411
+ "volume",
412
+ "style"
413
+ ]),
414
+ description: z.string(),
415
+ strength: z.number().min(0).max(1)
416
+ }))
417
+ })
418
+ ]);
419
+ z.object({
420
+ /** Relative file path */
421
+ filePath: z.string(),
422
+ /** Fragility score: 0-1 (1 = very fragile) */
423
+ fragilityScore: z.number().min(0).max(1),
424
+ /** Severity classification */
425
+ severity: z.enum([
426
+ "moderate",
427
+ "high",
428
+ "critical"
429
+ ]),
430
+ /** Why this file is fragile */
431
+ reason: z.string(),
432
+ /** Suggested action */
433
+ suggestion: z.string(),
434
+ /** Number of rollbacks in recent history */
435
+ rollbackCount: z.number().int().nonnegative(),
436
+ /** Number of AI-triggered modifications */
437
+ aiChurnCount: z.number().int().nonnegative(),
438
+ /** Blast radius (number of dependent files) */
439
+ blastRadius: z.number().int().nonnegative().optional(),
440
+ /** Last modified timestamp (ISO 8601) */
441
+ lastModified: z.string().datetime().optional()
442
+ });
443
+ z.object({
444
+ /** Primary file path */
445
+ fileA: z.string(),
446
+ /** Co-changed file path */
447
+ fileB: z.string(),
448
+ /** Co-occurrence frequency: 0-1 (1 = always changed together) */
449
+ frequency: z.number().min(0).max(1),
450
+ /** Number of times these files were changed together */
451
+ occurrences: z.number().int().positive(),
452
+ /** Relationship type — extensible union */
453
+ relationship: CoChangeRelationship,
454
+ /** Human-readable description of why they co-change */
455
+ reason: z.string(),
456
+ /** When this pattern was last observed (ISO 8601) */
457
+ lastObserved: z.string().datetime()
458
+ });
459
+ z.object({
460
+ /** Relative file path */
461
+ path: z.string().min(1),
462
+ /** Composite fragility score: 0-100 (100 = most fragile) */
463
+ compositeScore: z.number().min(0).max(100),
464
+ /** Weighted churn rate contribution */
465
+ churnScore: z.number().min(0),
466
+ /** Weighted dependency count contribution */
467
+ blastRadiusScore: z.number().min(0),
468
+ /** How often restores involve this file */
469
+ rollbackScore: z.number().min(0),
470
+ /** Files that import this file */
471
+ dependentCount: z.number().int().nonnegative(),
472
+ /** Ranking: 1 = most fragile */
473
+ rank: z.number().int().positive()
474
+ });
475
+ z.object({
476
+ /** Primary file path */
477
+ fileA: z.string().min(1),
478
+ /** Co-changed file path */
479
+ fileB: z.string().min(1),
480
+ /** Co-occurrence frequency: 0-1 (1 = always changed together) */
481
+ frequency: z.number().min(0).max(1),
482
+ /** Number of times these files were changed together */
483
+ occurrences: z.number().int().positive(),
484
+ /** Relationship type — extensible string */
485
+ relationship: z.string().min(1),
486
+ /** Human-readable description of why they co-change */
487
+ reason: z.string().min(1),
488
+ /** When this pattern was last observed (epoch timestamp) */
489
+ lastObserved: z.number().int().positive()
490
+ });
491
+ var RiskFactorStorageSchema = z.object({
492
+ /** Pillar source */
493
+ source: z.enum([
494
+ "git-risk",
495
+ "rollback-patterns",
496
+ "fatigue",
497
+ "annotations",
498
+ "poisoning",
499
+ "history",
500
+ "structural",
501
+ "co-change"
502
+ ]),
503
+ /** Risk score contribution */
504
+ score: z.number(),
505
+ /** Human-readable description */
506
+ description: z.string().min(1),
507
+ /** Severity level */
508
+ severity: z.enum([
509
+ "info",
510
+ "warning",
511
+ "critical"
512
+ ]),
513
+ /** Additional metadata */
514
+ meta: z.record(z.unknown()).optional()
515
+ });
516
+ z.object({
517
+ /** Overall risk score (1.0 = neutral, higher = riskier) */
518
+ riskScore: z.number().min(0),
519
+ /** Action recommendation */
520
+ action: z.enum([
521
+ "PROCEED",
522
+ "PROCEED_WITH_SNAPSHOT",
523
+ "WARN",
524
+ "BLOCK"
525
+ ]),
526
+ /** All contributing factors */
527
+ factors: z.array(RiskFactorStorageSchema),
528
+ /** Whether write is allowed */
529
+ allowed: z.boolean(),
530
+ /** Human-readable recommendation */
531
+ recommendation: z.string().min(1),
532
+ /** Alternative actions if blocked */
533
+ alternativeActions: z.array(z.object({
534
+ tool: z.string(),
535
+ description: z.string(),
536
+ params: z.record(z.unknown()).optional()
537
+ })).optional(),
538
+ /** Assessment timestamp (epoch) */
539
+ timestamp: z.number().int().positive(),
540
+ /** Correlation ID for tracing */
541
+ correlationId: z.string().optional()
542
+ });
543
+ z.object({
544
+ /** Total learnings captured for this workspace */
545
+ learningsCount: z.number().int().nonnegative(),
546
+ /** Total patterns detected */
547
+ patternsCount: z.number().int().nonnegative(),
548
+ /** Number of fragile files identified */
549
+ fragileFilesCount: z.number().int().nonnegative(),
550
+ /** Number of co-change relationships detected */
551
+ coChangePatternsCount: z.number().int().nonnegative(),
552
+ /** Total sessions recorded (cumulative) */
553
+ totalSessions: z.number().int().nonnegative(),
554
+ /** Total lines analyzed (cumulative — retained for Pioneer Program) */
555
+ linesAnalyzed: z.number().int().nonnegative(),
556
+ /** Total snapshots created (cumulative) */
557
+ totalSnapshots: z.number().int().nonnegative(),
558
+ /** Total restores performed (cumulative) */
559
+ totalRestores: z.number().int().nonnegative(),
560
+ // ── Time-windowed counters (v2 — meaningful recency) ──
561
+ /** Snapshots created in the last 24 hours */
562
+ snapshotsToday: z.number().int().nonnegative(),
563
+ /** Snapshots created in the last 7 days */
564
+ snapshotsThisWeek: z.number().int().nonnegative(),
565
+ /** Lines analyzed in the last 24 hours */
566
+ linesAnalyzedToday: z.number().int().nonnegative(),
567
+ /** Restores performed in the last 7 days */
568
+ restoresThisWeek: z.number().int().nonnegative(),
569
+ /** Patterns detected in the last 7 days */
570
+ patternsThisWeek: z.number().int().nonnegative(),
571
+ // ── Health summary ──
572
+ /** Workspace health score: 0-100 */
573
+ healthScore: z.number().int().min(0).max(100),
574
+ /** Health trajectory */
575
+ healthTrajectory: z.enum([
576
+ "improving",
577
+ "stable",
578
+ "declining"
579
+ ])
580
+ });
581
+ z.object({
582
+ /** Warning code (e.g., 'FRAGILE_FILE', 'LOOP_DETECTED') */
583
+ code: z.string(),
584
+ /** Severity level (ordered: info < warning < error) */
585
+ level: WarningSeverity,
586
+ /** Human-readable message */
587
+ message: z.string(),
588
+ /** Affected file (optional) */
589
+ file: z.string().optional(),
590
+ /** Suggested action */
591
+ suggestion: z.string().optional()
592
+ });
593
+ z.object({
594
+ /** Coherence score: 0-100 (100 = highly focused session) */
595
+ score: z.number().int().min(0).max(100),
596
+ /** Assessment */
597
+ assessment: z.enum([
598
+ "focused",
599
+ "moderate",
600
+ "scattered",
601
+ "chaotic"
602
+ ]),
603
+ /** Number of distinct file clusters in the session */
604
+ clusterCount: z.number().int().nonnegative(),
605
+ /** Primary focus area (most-edited directory or module) */
606
+ primaryFocus: z.string().nullable(),
607
+ /** Files that seem unrelated to the main session focus */
608
+ outlierFiles: z.array(z.string())
609
+ });
610
+ var SnapshotAIAttribution = z.discriminatedUnion("detected", [
611
+ z.object({
612
+ detected: z.literal(false)
613
+ }),
614
+ z.object({
615
+ detected: z.literal(true),
616
+ toolName: z.string(),
617
+ confidence: z.number().min(0).max(1)
618
+ })
619
+ ]);
620
+ Snapshot.extend({
621
+ /** Risk assessment at time of creation */
622
+ risk: z.object({
623
+ score: z.number().int().min(0).max(100),
624
+ level: RiskLevel
625
+ }).optional(),
626
+ /** AI tool attribution (absent = not checked, present = checked) */
627
+ aiAttribution: SnapshotAIAttribution.optional(),
628
+ /** Session cluster ID (for grouping in timeline) */
629
+ sessionClusterId: z.string().uuid().nullable(),
630
+ /** Whether this snapshot has been restored */
631
+ wasRestored: z.boolean()
632
+ });
633
+ var WorkspaceBase = z.object({
634
+ /** Absolute path to workspace root */
635
+ workspace: z.string().min(1)
636
+ });
637
+ WorkspaceBase.extend({
638
+ /** Skip baseline computation (faster, less complete) */
639
+ skipBaseline: z.boolean().optional(),
640
+ /** Skip learning seeding */
641
+ skipLearnings: z.boolean().optional(),
642
+ /** Force re-seed learnings even if already seeded */
643
+ forceSeedLearnings: z.boolean().optional()
644
+ });
645
+ WorkspaceBase.extend({
646
+ /** Optional path to global knowledge DB (defaults to ~/.snapback/knowledge.db) */
647
+ globalDbPath: z.string().optional()
648
+ });
649
+ var GuardStatus = z.enum([
650
+ "pass",
651
+ "warn",
652
+ "fail"
653
+ ]);
654
+ var GuardFile = z.object({
655
+ /** Absolute or workspace-relative path to the flagged file */
656
+ path: z.string().min(1),
657
+ /** Line number where the issue was found (optional) */
658
+ line: z.number().int().positive().optional(),
659
+ /** Human-readable description of the issue */
660
+ message: z.string().min(1)
661
+ });
662
+ var GuardResult = z.object({
663
+ /** Guard name (e.g., "console-logs", "test-pollution") */
664
+ guard: z.string().min(1),
665
+ /** Execution status */
666
+ status: GuardStatus,
667
+ /** Files flagged by this guard (empty array if status is "pass") */
668
+ files: z.array(GuardFile).default([]),
669
+ /** Execution duration in milliseconds */
670
+ durationMs: z.number().nonnegative()
671
+ });
672
+ z.object({
673
+ /** Results from all guards in the run */
674
+ guards: z.array(GuardResult).default([]),
675
+ /** Unix timestamp (ms) when this run completed */
676
+ timestamp: z.number().nonnegative(),
677
+ /** Age of cached results in milliseconds (Date.now() - timestamp) */
678
+ staleMs: z.number().nonnegative(),
679
+ /** Profile used for this run ("fast" or "full") */
680
+ profile: z.enum([
681
+ "fast",
682
+ "full"
683
+ ]),
684
+ /** Whether a background refresh is in progress */
685
+ refreshing: z.boolean()
686
+ });
687
+ WorkspaceBase.extend({
688
+ /** Guard profile to run (optional, defaults to "fast") */
689
+ profile: z.enum([
690
+ "fast",
691
+ "full"
692
+ ]).optional().default("fast")
693
+ });
694
+ z.object({
695
+ /** Guards that changed status since last run */
696
+ changed: z.array(GuardResult),
697
+ /** Current state of all guards */
698
+ current: z.array(GuardResult),
699
+ /** Unix timestamp (ms) when the change was detected */
700
+ timestamp: z.number().nonnegative()
701
+ });
702
+ z.object({
703
+ /** Protocol version the client supports */
704
+ protocolVersion: z.string().regex(/^\d+\.\d+\.\d+$/),
705
+ /** Client type identifying the connecting surface */
706
+ clientType: ClientType.optional().default("unknown"),
707
+ /** Client version (semantic versioning) */
708
+ clientVersion: z.string().optional().default("0.0.0"),
709
+ /** Client identification (legacy field, kept for backward compatibility) */
710
+ clientInfo: z.object({
711
+ /** Client name (e.g., "vscode-extension", "cli") */
712
+ name: z.string().min(1).max(100),
713
+ /** Client version (semantic versioning) */
714
+ version: z.string()
715
+ }).optional(),
716
+ /** Client capabilities (optional) */
717
+ capabilities: z.object({
718
+ /** Client accepts server-sent notifications */
719
+ notifications: z.boolean().default(true),
720
+ /** Client can handle base64-encoded binary content */
721
+ binaryContent: z.boolean().default(false)
722
+ }).optional()
723
+ });
724
+ z.object({
725
+ /** Include detailed health information */
726
+ verbose: z.boolean().optional()
727
+ });
728
+ z.object({
729
+ /** Filter by workspace path (optional) */
730
+ workspacePath: z.string().optional()
731
+ });
732
+ z.object({
733
+ /** Absolute path to workspace root */
734
+ workspacePath: z.string().min(1),
735
+ /** Additional session metadata (optional) */
736
+ metadata: z.object({
737
+ /** Editor name (e.g., "Visual Studio Code") */
738
+ editorName: z.string().optional(),
739
+ /** Editor version */
740
+ editorVersion: z.string().optional()
741
+ }).optional()
742
+ });
743
+ z.object({
744
+ /** Session to end (defaults to current session if omitted) */
745
+ sessionId: z.string().uuid().optional(),
746
+ /** Reason for ending session */
747
+ reason: z.enum([
748
+ "user",
749
+ "idle",
750
+ "workspace_closed"
751
+ ]).optional()
752
+ });
753
+ z.object({
754
+ /** Filter by workspace path (optional) */
755
+ workspacePath: z.string().optional(),
756
+ /** Maximum number of sessions to return */
757
+ limit: z.number().int().min(1).max(100).default(20),
758
+ /** Pagination cursor (session ID to start after) */
759
+ before: z.string().uuid().optional(),
760
+ /** Include ended sessions in results */
761
+ includeEnded: z.boolean().default(true)
762
+ });
763
+ WorkspaceBase.extend({
764
+ /** Task description */
765
+ task: z.string().min(1),
766
+ /** Files involved in this task */
767
+ files: z.array(z.string()).optional(),
768
+ /** Keywords for context matching */
769
+ keywords: z.array(z.string()).optional()
770
+ });
771
+ WorkspaceBase.extend({
772
+ /**
773
+ * Free-text outcome description — what was accomplished, what remains, decisions made.
774
+ * Becomes the opening context for the next session briefing.
775
+ * Do NOT constrain to an enum: the MCP tool description promises free-text paragraphs.
776
+ */
777
+ outcome: z.string().min(1).max(2e3).optional(),
778
+ /** Additional notes */
779
+ notes: z.string().optional(),
780
+ /** Create a snapshot on end */
781
+ createSnapshot: z.boolean().optional(),
782
+ /** Learning IDs to accept */
783
+ acceptLearnings: z.array(z.number()).optional()
784
+ });
785
+ WorkspaceBase.extend({
786
+ /** Files to review */
787
+ files: z.array(z.string()).optional(),
788
+ /** Include commit message suggestion */
789
+ includeCommitMessage: z.boolean().optional(),
790
+ /** Skip pattern checking */
791
+ skipPatterns: z.boolean().optional()
792
+ });
793
+ WorkspaceBase.extend({
794
+ /** Include diff in results */
795
+ includeDiff: z.boolean().optional(),
796
+ /** Filter to specific files */
797
+ filterFiles: z.array(z.string()).optional(),
798
+ /** Include AI attribution info */
799
+ includeAIAttribution: z.boolean().default(true)
800
+ });
801
+ z.object({
802
+ /** Filter by workspace path */
803
+ workspace: z.string().optional(),
804
+ /** Pagination cursor (session ID) */
805
+ cursor: z.string().optional(),
806
+ /** Max number of sessions to return (default 20) */
807
+ limit: z.number().optional()
808
+ });
809
+ z.object({
810
+ /** Pulse level based on file count thresholds */
811
+ pulse: z.enum([
812
+ "resting",
813
+ "elevated",
814
+ "racing",
815
+ "critical"
816
+ ]),
817
+ /** Pressure: ratio of unprotected changes (0-100) */
818
+ pressure: z.number().min(0).max(100),
819
+ /** Trajectory based on pressure level */
820
+ trajectory: z.enum([
821
+ "stable",
822
+ "increasing",
823
+ "decreasing"
824
+ ]),
825
+ /** Whether a snapshot is recommended */
826
+ snapshotRecommended: z.boolean(),
827
+ /** Files modified during session */
828
+ filesModified: z.number(),
829
+ /** Snapshots created during session */
830
+ snapshotsCreated: z.number(),
831
+ /** Learnings captured during session */
832
+ learningsCaptured: z.number()
833
+ });
834
+ z.object({
835
+ /** Absolute path to the file */
836
+ filePath: z.string().min(1),
837
+ /** File content to snapshot */
838
+ content: z.string(),
839
+ /** What triggered this snapshot */
840
+ trigger: SnapshotTrigger,
841
+ /** Additional snapshot metadata (optional) */
842
+ metadata: z.object({
843
+ /** AI tool that was active */
844
+ aiToolDetected: z.string().optional(),
845
+ /** Confidence score for AI detection (0-1) */
846
+ aiConfidence: z.number().min(0).max(1).optional(),
847
+ /** Risk level assessment */
848
+ riskLevel: RiskLevel.optional(),
849
+ /** Custom tags for organization */
850
+ customTags: z.array(z.string()).optional()
851
+ }).optional()
852
+ });
853
+ z.object({
854
+ /** Absolute path to the file being saved */
855
+ filePath: z.string().min(1),
856
+ /** SHA-256 hash of the file content (optional, for deduplication) */
857
+ contentHash: z.string().optional(),
858
+ /** Last modification timestamp in milliseconds */
859
+ lastModified: z.number().optional(),
860
+ /** File size in bytes */
861
+ byteSize: z.number().optional()
862
+ });
863
+ z.object({
864
+ /** Whether a snapshot should be created */
865
+ shouldCreate: z.boolean(),
866
+ /** Reason for the decision */
867
+ reason: ShouldCreateReason,
868
+ /** Optional timing metrics for performance analysis */
869
+ metrics: SavePathMetrics.optional()
870
+ });
871
+ z.object({
872
+ /** Snapshot ID to retrieve */
873
+ snapshotId: z.string().uuid(),
874
+ /** Include full content in response (expensive) */
875
+ includeContent: z.boolean().default(false)
876
+ });
877
+ var SnapshotListParams = z.object({
878
+ /** Filter by session ID (optional) */
879
+ sessionId: z.string().uuid().optional(),
880
+ /** Filter by file path (optional) */
881
+ filePath: z.string().optional(),
882
+ /** Filter by trigger types (optional) */
883
+ trigger: z.array(SnapshotTrigger).optional(),
884
+ /** Filter by created after timestamp (ISO 8601) */
885
+ since: z.string().datetime().optional(),
886
+ /** Filter by created before timestamp (ISO 8601) */
887
+ until: z.string().datetime().optional(),
888
+ /** Maximum number of snapshots to return */
889
+ limit: z.number().int().min(1).max(200).default(50),
890
+ /** Pagination cursor (opaque string) */
891
+ cursor: z.string().optional(),
892
+ /** Sort field */
893
+ orderBy: z.enum([
894
+ "createdAt",
895
+ "filePath"
896
+ ]).default("createdAt"),
897
+ /** Sort direction */
898
+ orderDir: z.enum([
899
+ "asc",
900
+ "desc"
901
+ ]).default("desc")
902
+ });
903
+ z.object({
904
+ /** Snapshot ID to restore */
905
+ snapshotId: z.string().uuid(),
906
+ /** Target path (defaults to original path if omitted) */
907
+ targetPath: z.string().optional(),
908
+ /** Create backup snapshot before restoring */
909
+ createBackup: z.boolean().default(true),
910
+ /** Preview restore without writing (dry run) */
911
+ dryRun: z.boolean().default(false)
912
+ });
913
+ z.object({
914
+ /** Base snapshot ID for comparison */
915
+ baseSnapshotId: z.string().uuid(),
916
+ /** Snapshot to compare against (defaults to current file if omitted) */
917
+ compareSnapshotId: z.string().uuid().optional(),
918
+ /** Number of context lines around changes */
919
+ contextLines: z.number().int().min(0).max(10).default(3),
920
+ /** Diff output format */
921
+ format: z.enum([
922
+ "unified",
923
+ "split"
924
+ ]).default("unified")
925
+ });
926
+ z.object({
927
+ /** Specific snapshot IDs to delete (optional) */
928
+ snapshotIds: z.array(z.string().uuid()).optional(),
929
+ /** Delete all snapshots older than timestamp (ISO 8601) (optional) */
930
+ olderThan: z.string().datetime().optional(),
931
+ /** Delete all snapshots in session (optional) */
932
+ sessionId: z.string().uuid().optional(),
933
+ /** Preview deletion without removing (dry run) */
934
+ dryRun: z.boolean().default(false)
935
+ });
936
+ WorkspaceBase.extend({
937
+ /** Files to snapshot (relative paths) */
938
+ files: z.array(z.string().min(1)).min(1),
939
+ /** Reason for snapshot */
940
+ reason: z.string().optional(),
941
+ /** Snapshot trigger */
942
+ trigger: z.enum([
943
+ "manual",
944
+ "mcp",
945
+ "ai_assist",
946
+ "session_end"
947
+ ]).optional()
948
+ });
949
+ WorkspaceBase.extend({
950
+ /** Delete snapshots older than N days */
951
+ olderThanDays: z.number().int().positive().optional(),
952
+ /** Keep protected snapshots */
953
+ keepProtected: z.boolean().default(true)
954
+ });
955
+ WorkspaceBase.extend({
956
+ /** Snapshot ID */
957
+ snapshotId: z.string().min(1)
958
+ });
959
+ WorkspaceBase.extend({
960
+ /** Snapshot ID */
961
+ snapshotId: z.string().min(1),
962
+ /** New name */
963
+ newName: z.string().min(1)
964
+ });
965
+ SnapshotListParams.extend({
966
+ /** Include risk assessment per snapshot */
967
+ includeRisk: z.boolean().default(true),
968
+ /** Include AI attribution per snapshot */
969
+ includeAttribution: z.boolean().default(true),
970
+ /** Group by session cluster */
971
+ groupBySessions: z.boolean().default(false)
972
+ });
973
+ z.object({
974
+ /** File path to evaluate */
975
+ filePath: z.string().min(1),
976
+ /** Current file content (optional, reads from disk if omitted) */
977
+ content: z.string().optional(),
978
+ /** Additional context for evaluation (optional) */
979
+ context: z.object({
980
+ /** Number of recent changes to the file */
981
+ recentChanges: z.number().int().nonnegative().optional(),
982
+ /** Milliseconds since last save */
983
+ timeSinceLastSave: z.number().int().nonnegative().optional(),
984
+ /** Whether an AI tool is currently active */
985
+ aiToolActive: z.boolean().optional()
986
+ }).optional()
987
+ });
988
+ z.object({
989
+ /** File paths to get protection levels for */
990
+ filePaths: z.array(z.string().min(1)).min(1).max(100)
991
+ });
992
+ z.object({
993
+ /** File path to set protection for */
994
+ filePath: z.string().min(1),
995
+ /** Protection level to set ("auto" to reset to automatic) */
996
+ level: z.enum([
997
+ "watch",
998
+ "warn",
999
+ "block",
1000
+ "auto"
1001
+ ]),
1002
+ /** Scope of the protection rule */
1003
+ scope: z.enum([
1004
+ "file",
1005
+ "directory",
1006
+ "pattern"
1007
+ ]).default("file"),
1008
+ /** Pattern for pattern-based rules (glob syntax) (optional) */
1009
+ pattern: z.string().optional()
1010
+ });
1011
+ WorkspaceBase.extend({
1012
+ /** File path */
1013
+ filePath: z.string().min(1)
1014
+ });
1015
+ WorkspaceBase.extend({
1016
+ /** File path */
1017
+ filePath: z.string().min(1),
1018
+ /** Protection level */
1019
+ level: ProtectionLevel,
1020
+ /** Reason for setting level */
1021
+ reason: z.string().optional()
1022
+ });
1023
+ WorkspaceBase.extend({
1024
+ /** Filter by level */
1025
+ level: ProtectionLevel.optional(),
1026
+ /** Max results */
1027
+ limit: z.number().int().min(1).max(500).optional()
1028
+ });
1029
+ z.object({
1030
+ /** File path being checked */
1031
+ filePath: z.string().min(1),
1032
+ /** Current file content */
1033
+ content: z.string(),
1034
+ /** Previous content for comparison (optional) */
1035
+ previousContent: z.string().optional(),
1036
+ /** Additional metadata (optional) */
1037
+ metadata: z.object({
1038
+ /** Editor information string */
1039
+ editorInfo: z.string().optional(),
1040
+ /** Timestamp in milliseconds since epoch */
1041
+ timestamp: z.number().int().positive().optional()
1042
+ }).optional()
1043
+ });
1044
+ z.object({
1045
+ /** Unified diff string to analyze */
1046
+ diff: z.string(),
1047
+ /** Context about the change */
1048
+ context: z.object({
1049
+ /** File path being changed */
1050
+ filePath: z.string(),
1051
+ /** Programming language (optional) */
1052
+ language: z.string().optional(),
1053
+ /** Project type (e.g., "web", "mobile", "backend") (optional) */
1054
+ projectType: z.string().optional()
1055
+ })
1056
+ });
1057
+ z.object({
1058
+ /** Snapshot IDs to analyze for grouping */
1059
+ snapshotIds: z.array(z.string().uuid()).min(1)
1060
+ });
1061
+ z.object({
1062
+ /** Sync direction */
1063
+ direction: z.enum([
1064
+ "push",
1065
+ "pull",
1066
+ "both"
1067
+ ]).default("both"),
1068
+ /** Specific snapshot IDs to sync (optional, syncs all if omitted) */
1069
+ snapshotIds: z.array(z.string().uuid()).optional()
1070
+ });
1071
+ WorkspaceBase.extend({
1072
+ /** Learning type */
1073
+ type: LearningType,
1074
+ /** Trigger condition */
1075
+ trigger: z.string().min(1),
1076
+ /** Action to take */
1077
+ action: z.string().min(1),
1078
+ /** Source of the learning */
1079
+ source: z.string().optional()
1080
+ });
1081
+ WorkspaceBase.extend({
1082
+ /** Keywords to search */
1083
+ keywords: z.array(z.string()).min(1),
1084
+ /** Max results */
1085
+ limit: z.number().int().min(1).max(100).optional()
1086
+ });
1087
+ WorkspaceBase.extend({
1088
+ /** Max results */
1089
+ limit: z.number().int().min(1).max(200).optional()
1090
+ });
1091
+ WorkspaceBase.extend({
1092
+ /** Command being evaluated */
1093
+ commandName: z.string().min(1),
1094
+ /** Command arguments */
1095
+ args: z.record(z.unknown()).optional(),
1096
+ /** Files or paths involved */
1097
+ filesOrPaths: z.array(z.string()).optional(),
1098
+ /** Intent of the command */
1099
+ intent: z.enum([
1100
+ "implement",
1101
+ "debug",
1102
+ "refactor",
1103
+ "review"
1104
+ ]).optional(),
1105
+ /** Learning mode */
1106
+ mode: z.enum([
1107
+ "observe",
1108
+ "warn",
1109
+ "apply-safe",
1110
+ "apply-all",
1111
+ "off"
1112
+ ]).optional()
1113
+ });
1114
+ WorkspaceBase.extend({
1115
+ /** Session ID */
1116
+ sessionId: z.string().min(1),
1117
+ /** Learning IDs to associate */
1118
+ learningIds: z.array(z.string())
1119
+ });
1120
+ WorkspaceBase.extend({
1121
+ /** GC operation */
1122
+ operation: z.enum([
1123
+ "archive",
1124
+ "delete",
1125
+ "all"
1126
+ ]).optional(),
1127
+ /** Dry run (audit only) */
1128
+ dryRun: z.boolean().default(true)
1129
+ });
1130
+ WorkspaceBase.extend({
1131
+ /** Optional auth token for platform sync */
1132
+ authToken: z.string().optional()
1133
+ });
1134
+ WorkspaceBase.extend({
1135
+ /** Task description */
1136
+ task: z.string().min(1),
1137
+ /** Files involved */
1138
+ files: z.array(z.string()).optional(),
1139
+ /** Keywords for context matching */
1140
+ keywords: z.array(z.string()).optional()
1141
+ });
1142
+ WorkspaceBase.extend({
1143
+ /** Code to check */
1144
+ code: z.string(),
1145
+ /** File path */
1146
+ filePath: z.string().min(1)
1147
+ });
1148
+ WorkspaceBase.extend({
1149
+ /** Single file */
1150
+ file: z.string().optional(),
1151
+ /** Multiple files */
1152
+ files: z.array(z.string()).optional(),
1153
+ /** Run tests */
1154
+ runTests: z.boolean().optional(),
1155
+ /** Skip TypeScript check */
1156
+ skipTypeScript: z.boolean().optional(),
1157
+ /** Skip tests */
1158
+ skipTests: z.boolean().optional(),
1159
+ /** Skip lint */
1160
+ skipLint: z.boolean().optional()
1161
+ });
1162
+ WorkspaceBase.extend({
1163
+ /** Code to validate */
1164
+ code: z.string(),
1165
+ /** File path */
1166
+ filePath: z.string().min(1)
1167
+ });
1168
+ WorkspaceBase.extend({
1169
+ /** Violation type */
1170
+ type: z.string().min(1),
1171
+ /** File where violation occurred */
1172
+ file: z.string().min(1),
1173
+ /** What happened */
1174
+ whatHappened: z.string().min(1),
1175
+ /** Why it happened */
1176
+ whyItHappened: z.string().min(1),
1177
+ /** How to prevent it */
1178
+ prevention: z.string().min(1)
1179
+ });
1180
+ z.object({
1181
+ /** Absolute path to check */
1182
+ path: z.string().min(1),
1183
+ /** Absolute workspace root to check against */
1184
+ workspace: z.string().min(1)
1185
+ });
1186
+ WorkspaceBase.extend({
1187
+ /** Glob patterns to watch */
1188
+ patterns: z.array(z.string()).optional()
1189
+ });
1190
+ WorkspaceBase.extend({
1191
+ /** File that changed */
1192
+ file: z.string().min(1),
1193
+ /** Timestamp of change */
1194
+ timestamp: z.number().int().positive()
1195
+ });
1196
+ WorkspaceBase.extend({
1197
+ /** Snapshot ID */
1198
+ id: z.string().min(1),
1199
+ /** File path */
1200
+ filePath: z.string().optional(),
1201
+ /** Trigger type */
1202
+ trigger: z.enum([
1203
+ "manual",
1204
+ "auto",
1205
+ "ai-detection"
1206
+ ]).optional(),
1207
+ /** Source surface */
1208
+ source: z.enum([
1209
+ "mcp",
1210
+ "cli",
1211
+ "extension"
1212
+ ]).optional()
1213
+ });
1214
+ WorkspaceBase.extend({
1215
+ /** File path */
1216
+ path: z.string().min(1),
1217
+ /** Lines changed */
1218
+ linesChanged: z.number().int().nonnegative().optional(),
1219
+ /** AI-attributed change */
1220
+ aiAttributed: z.boolean().optional()
1221
+ });
1222
+ WorkspaceBase.extend({
1223
+ /** Pattern content */
1224
+ content: z.string().min(1),
1225
+ /** Knowledge domain */
1226
+ domain: z.enum([
1227
+ "architecture",
1228
+ "testing",
1229
+ "documentation",
1230
+ "performance",
1231
+ "security",
1232
+ "workflow",
1233
+ "tooling"
1234
+ ]).optional(),
1235
+ /** Pattern type */
1236
+ type: z.enum([
1237
+ "pattern",
1238
+ "anti-pattern",
1239
+ "best-practice",
1240
+ "constraint",
1241
+ "heuristic"
1242
+ ]).optional(),
1243
+ /** Severity */
1244
+ severity: z.enum([
1245
+ "info",
1246
+ "warning",
1247
+ "error"
1248
+ ]).optional(),
1249
+ /** Related pattern IDs */
1250
+ relatedTo: z.array(z.string()).optional(),
1251
+ /** Gap Fields: Outcome type for dashboard tracking (prevented/recovered/monitored) */
1252
+ outcome: z.enum([
1253
+ "prevented",
1254
+ "recovered",
1255
+ "monitored"
1256
+ ]).optional(),
1257
+ /** Gap Fields: File categories involved (for risksPreventedContext) */
1258
+ fileCategories: z.array(z.string()).optional(),
1259
+ /** Gap Fields: Blast radius count for the decision */
1260
+ blastRadius: z.number().int().nonnegative().optional(),
1261
+ /** Gap Fields: File path for the event */
1262
+ filePath: z.string().optional(),
1263
+ /** Gap Fields: AI tool detected (cursor/copilot/claude) */
1264
+ aiTool: z.enum([
1265
+ "cursor",
1266
+ "copilot",
1267
+ "claude"
1268
+ ]).optional(),
1269
+ /** Gap Fields: Risk level (high/medium/low) */
1270
+ risk: z.enum([
1271
+ "high",
1272
+ "medium",
1273
+ "low"
1274
+ ]).optional()
1275
+ });
1276
+ WorkspaceBase.extend({
1277
+ /** Pattern ID or Decision ID */
1278
+ patternId: z.string().min(1),
1279
+ /**
1280
+ * Outcome type
1281
+ * - resolved/regressed/ignored: Original pattern outcomes
1282
+ * - prevented/recovered/monitored: Gap Fields dashboard outcomes
1283
+ */
1284
+ outcome: z.enum([
1285
+ "resolved",
1286
+ "regressed",
1287
+ "ignored",
1288
+ "prevented",
1289
+ "recovered",
1290
+ "monitored"
1291
+ ]),
1292
+ /** Notes */
1293
+ notes: z.string().optional(),
1294
+ /** Gap Fields: Whether decision was reverted within 5min (for accuracy calc) */
1295
+ revertedWithin5Min: z.boolean().optional(),
1296
+ /** Gap Fields: File categories involved */
1297
+ fileCategories: z.array(z.string()).optional()
1298
+ });
1299
+ WorkspaceBase.extend({
1300
+ /** Unique decision ID */
1301
+ decisionId: z.string().min(1),
1302
+ /** Decision outcome */
1303
+ outcome: z.enum([
1304
+ "prevented",
1305
+ "recovered",
1306
+ "monitored",
1307
+ "reverted"
1308
+ ]),
1309
+ /** Whether the decision was reverted within 5 minutes */
1310
+ revertedWithin5Min: z.boolean().optional(),
1311
+ /** File categories involved in the decision */
1312
+ fileCategories: z.array(z.string()).optional(),
1313
+ /** Human-readable action taken */
1314
+ action: z.string().optional(),
1315
+ /** Notes */
1316
+ notes: z.string().optional()
1317
+ });
1318
+ WorkspaceBase.extend({
1319
+ /** Minimum fragility score to include (0-1) */
1320
+ minScore: z.number().min(0).max(1).default(0.5),
1321
+ /** Maximum results */
1322
+ limit: z.number().int().min(1).max(50).default(20)
1323
+ });
1324
+ WorkspaceBase.extend({
1325
+ /** Minimum co-occurrence frequency to include (0-1) */
1326
+ minFrequency: z.number().min(0).max(1).default(0.5),
1327
+ /** Filter to patterns involving this file */
1328
+ filePath: z.string().optional(),
1329
+ /** Maximum results */
1330
+ limit: z.number().int().min(1).max(50).default(20)
1331
+ });
1332
+ WorkspaceBase.extend({
1333
+ /** Filter by minimum severity (uses WARNING_SEVERITY_ORDER for comparison) */
1334
+ minLevel: WarningSeverity.default("info"),
1335
+ /** Maximum results */
1336
+ limit: z.number().int().min(1).max(20).default(10)
1337
+ });
1338
+ WorkspaceBase.extend({
1339
+ /** Days to look back (default: 7) */
1340
+ days: z.number().int().min(1).max(30).default(7)
1341
+ });
1342
+ WorkspaceBase.extend({
1343
+ /** Weeks to look back (default: 8) */
1344
+ weeks: z.number().int().min(1).max(12).default(8)
1345
+ });
1346
+ var GitRiskFactor = z.object({
1347
+ /** Unique identifier for this risk factor */
1348
+ id: z.string(),
1349
+ /** Human-readable label */
1350
+ label: z.string(),
1351
+ /** Contribution to risk multiplier (additive) */
1352
+ contribution: z.number(),
1353
+ /** Severity level */
1354
+ severity: z.enum([
1355
+ "info",
1356
+ "warning",
1357
+ "critical"
1358
+ ])
1359
+ });
1360
+ z.object({
1361
+ /** Multiplicative factor applied to base risk score (1.0 = neutral) */
1362
+ multiplier: z.number(),
1363
+ /** Contributing factors that built up the multiplier */
1364
+ factors: z.array(GitRiskFactor),
1365
+ /** Whether git context was available */
1366
+ hasGitContext: z.boolean(),
1367
+ /** Timestamp of evaluation for caching */
1368
+ evaluatedAt: z.number()
1369
+ });
1370
+ z.object({
1371
+ /** Workspace path to evaluate */
1372
+ workspace: z.string()
1373
+ });
1374
+ z.object({
1375
+ /** AI tool that was active when rollback happened */
1376
+ tool: z.string(),
1377
+ /** File extension involved */
1378
+ fileExt: z.string(),
1379
+ /** Number of lines in the rolled-back change */
1380
+ linesChanged: z.number(),
1381
+ /** Programming language */
1382
+ language: z.string(),
1383
+ /** File category classification */
1384
+ fileCategory: z.enum([
1385
+ "config",
1386
+ "code",
1387
+ "test",
1388
+ "docs",
1389
+ "build",
1390
+ "other"
1391
+ ]),
1392
+ /** Was it a multi-file change? */
1393
+ fileCount: z.number(),
1394
+ /** Time of day bucket (0-23) for fatigue correlation */
1395
+ hourBucket: z.number().int().min(0).max(23),
1396
+ /** Session duration at rollback time (minutes) */
1397
+ sessionMinutes: z.number(),
1398
+ /** Workspace where rollback occurred */
1399
+ workspace: z.string(),
1400
+ /** Timestamp of rollback */
1401
+ timestamp: z.number()
1402
+ });
1403
+ z.object({
1404
+ /** Confidence that this matches a known failure pattern (0-1) */
1405
+ confidence: z.number().min(0).max(1),
1406
+ /** Human-readable warning message */
1407
+ message: z.string(),
1408
+ /** Number of similar rollbacks in the cluster */
1409
+ clusterSize: z.number(),
1410
+ /** Dominant factors in the cluster */
1411
+ dominantFactors: z.array(z.string()),
1412
+ /** Risk boost to apply (additive) */
1413
+ riskBoost: z.number()
1414
+ });
1415
+ z.object({
1416
+ /** Workspace where rollback occurred */
1417
+ workspace: z.string(),
1418
+ /** AI tool that was active */
1419
+ tool: z.string(),
1420
+ /** File extension */
1421
+ fileExt: z.string(),
1422
+ /** Lines changed */
1423
+ linesChanged: z.number(),
1424
+ /** Programming language */
1425
+ language: z.string(),
1426
+ /** File category */
1427
+ fileCategory: z.enum([
1428
+ "config",
1429
+ "code",
1430
+ "test",
1431
+ "docs",
1432
+ "build",
1433
+ "other"
1434
+ ]),
1435
+ /** File count */
1436
+ fileCount: z.number(),
1437
+ /** Session duration in minutes */
1438
+ sessionMinutes: z.number()
1439
+ });
1440
+ z.object({
1441
+ /** Workspace to check */
1442
+ workspace: z.string(),
1443
+ /** AI tool making the change */
1444
+ tool: z.string(),
1445
+ /** File extension */
1446
+ fileExt: z.string(),
1447
+ /** Lines being changed */
1448
+ linesChanged: z.number(),
1449
+ /** Programming language */
1450
+ language: z.string(),
1451
+ /** File category */
1452
+ fileCategory: z.enum([
1453
+ "config",
1454
+ "code",
1455
+ "test",
1456
+ "docs",
1457
+ "build",
1458
+ "other"
1459
+ ]),
1460
+ /** File count in change */
1461
+ fileCount: z.number(),
1462
+ /** Current session duration */
1463
+ sessionMinutes: z.number()
1464
+ });
1465
+ z.object({
1466
+ /** Workspace to get stats for */
1467
+ workspace: z.string()
1468
+ });
1469
+ z.object({
1470
+ /** Total rollbacks recorded */
1471
+ totalRollbacks: z.number(),
1472
+ /** Number of distinct clusters */
1473
+ clusters: z.number(),
1474
+ /** Size of largest cluster */
1475
+ largestCluster: z.number()
1476
+ });
1477
+ z.object({
1478
+ /** Additive risk boost (0.0 - 0.5) */
1479
+ riskBoost: z.number().min(0).max(0.5),
1480
+ /** Human-readable fatigue level */
1481
+ level: z.enum([
1482
+ "rested",
1483
+ "normal",
1484
+ "elevated",
1485
+ "fatigued"
1486
+ ]),
1487
+ /** Ratio of rapid accepts in the window (0-1) */
1488
+ rapidAcceptRatio: z.number().min(0).max(1),
1489
+ /** Current accept velocity (accepts per minute) */
1490
+ velocityPerMinute: z.number(),
1491
+ /** Session duration contributing to assessment */
1492
+ sessionMinutes: z.number(),
1493
+ /** Contributing factors to the assessment */
1494
+ factors: z.array(z.string())
1495
+ });
1496
+ z.object({
1497
+ /** Workspace to assess */
1498
+ workspace: z.string()
1499
+ });
1500
+ var AnnotationTag = z.enum([
1501
+ "good_state",
1502
+ "before_refactor",
1503
+ "it_broke_here",
1504
+ "uncertain",
1505
+ "ai_diverged",
1506
+ "manual_recovery",
1507
+ "shipped"
1508
+ ]);
1509
+ z.object({
1510
+ /** Snapshot hash (content-addressable ID) */
1511
+ snapshotHash: z.string(),
1512
+ /** Annotation tag */
1513
+ tag: AnnotationTag,
1514
+ /** Optional freeform note (max 280 chars) */
1515
+ note: z.string().max(280).optional(),
1516
+ /** Active AI tool at time of annotation */
1517
+ aiTool: z.string().optional(),
1518
+ /** Files in the snapshot */
1519
+ fileCount: z.number(),
1520
+ /** Risk score at time of annotation */
1521
+ riskScore: z.number().optional(),
1522
+ /** Timestamp */
1523
+ timestamp: z.number(),
1524
+ /** Workspace */
1525
+ workspace: z.string()
1526
+ });
1527
+ z.object({
1528
+ /** Snapshot hash to annotate */
1529
+ snapshotHash: z.string(),
1530
+ /** Annotation tag */
1531
+ tag: AnnotationTag,
1532
+ /** Optional note */
1533
+ note: z.string().max(280).optional(),
1534
+ /** AI tool active */
1535
+ aiTool: z.string().optional(),
1536
+ /** File count */
1537
+ fileCount: z.number(),
1538
+ /** Risk score at time */
1539
+ riskScore: z.number().optional(),
1540
+ /** Workspace */
1541
+ workspace: z.string()
1542
+ });
1543
+ z.object({
1544
+ /** Whether annotation was recorded */
1545
+ recorded: z.boolean(),
1546
+ /** Risk signal from the annotation */
1547
+ riskSignal: z.object({
1548
+ direction: z.enum([
1549
+ "increase",
1550
+ "decrease",
1551
+ "neutral"
1552
+ ]),
1553
+ magnitude: z.number()
1554
+ })
1555
+ });
1556
+ z.object({
1557
+ /** Workspace to query */
1558
+ workspace: z.string(),
1559
+ /** Filter by timestamp (ms since epoch) */
1560
+ since: z.number().optional(),
1561
+ /** Filter by tags */
1562
+ tags: z.array(AnnotationTag).optional(),
1563
+ /** Limit number of results */
1564
+ limit: z.number().optional()
1565
+ });
1566
+ z.object({
1567
+ /** Workspace to get calibration for */
1568
+ workspace: z.string()
1569
+ });
1570
+ z.object({
1571
+ /** Net risk adjustment from historical annotations */
1572
+ netAdjustment: z.number().min(-0.5).max(0.5),
1573
+ /** Number of annotations considered */
1574
+ annotationCount: z.number(),
1575
+ /** Breakdown by tag */
1576
+ breakdown: z.record(AnnotationTag, z.number())
1577
+ });
1578
+ z.object({
1579
+ /** Whether poisoning was detected */
1580
+ isPoisoned: z.boolean(),
1581
+ /** How many times this pattern has been rejected */
1582
+ occurrences: z.number(),
1583
+ /** How many distinct sessions it appeared in */
1584
+ distinctSessions: z.number(),
1585
+ /** Risk boost to apply if poisoned */
1586
+ riskBoost: z.number(),
1587
+ /** Human-readable warning */
1588
+ warning: z.string().optional(),
1589
+ /** Structural fingerprint (for debugging) */
1590
+ fingerprint: z.string().optional()
1591
+ });
1592
+ z.object({
1593
+ /** Workspace to check */
1594
+ workspace: z.string(),
1595
+ /** Unified diff of proposed change */
1596
+ diff: z.string()
1597
+ });
1598
+ z.object({
1599
+ /** Workspace where rejection occurred */
1600
+ workspace: z.string(),
1601
+ /** Diff that was rejected */
1602
+ diff: z.string(),
1603
+ /** Session ID */
1604
+ sessionId: z.string(),
1605
+ /** AI tool involved */
1606
+ tool: z.string().optional(),
1607
+ /** File extension */
1608
+ fileExt: z.string().optional()
1609
+ });
1610
+ z.object({
1611
+ /** Workspace to get stats for */
1612
+ workspace: z.string()
1613
+ });
1614
+ z.object({
1615
+ /** Number of patterns being tracked */
1616
+ trackedPatterns: z.number(),
1617
+ /** Number of poisoned patterns (>= threshold) */
1618
+ poisonedPatterns: z.number(),
1619
+ /** Top tools involved in poisoning */
1620
+ topTools: z.array(z.string())
1621
+ });
1622
+ var SafeToWriteAction = z.enum([
1623
+ "PROCEED",
1624
+ "PROCEED_WITH_SNAPSHOT",
1625
+ "WARN",
1626
+ "BLOCK"
1627
+ ]);
1628
+ var SafeToWriteFactor = z.object({
1629
+ /** Source of the factor (which pillar) */
1630
+ source: z.string(),
1631
+ /** Score contribution */
1632
+ score: z.number(),
1633
+ /** Description of the factor */
1634
+ description: z.string()
1635
+ });
1636
+ var AlternativeAction = z.object({
1637
+ /** Tool to use instead */
1638
+ tool: z.string(),
1639
+ /** Description of what to do */
1640
+ description: z.string(),
1641
+ /** Optional parameters for the tool */
1642
+ params: z.record(z.string(), z.unknown()).optional()
1643
+ });
1644
+ z.object({
1645
+ /** Whether the write is allowed */
1646
+ allowed: z.boolean(),
1647
+ /** Action to take */
1648
+ action: SafeToWriteAction,
1649
+ /** Composite risk score (capped at 2.5) */
1650
+ riskScore: z.number(),
1651
+ /** Contributing factors from all pillars */
1652
+ factors: z.array(SafeToWriteFactor),
1653
+ /** Human-readable recommendation */
1654
+ recommendation: z.string(),
1655
+ /** Snapshot hash if auto-created */
1656
+ snapshotHash: z.string().optional(),
1657
+ /** Whether the operation can be retried */
1658
+ retryable: z.boolean(),
1659
+ /** Alternative actions to take instead */
1660
+ alternativeActions: z.array(AlternativeAction)
1661
+ });
1662
+ z.object({
1663
+ /** Workspace path */
1664
+ workspace: z.string(),
1665
+ /** File path being written */
1666
+ filePath: z.string(),
1667
+ /** Proposed diff */
1668
+ proposedDiff: z.string(),
1669
+ /** AI tool making the change */
1670
+ tool: z.string().optional(),
1671
+ /** Intent of the change */
1672
+ intent: z.string().optional()
1673
+ });
1674
+ WorkspaceBase.extend({
1675
+ /** Compare against this commit (default HEAD~10) */
1676
+ since: z.string().optional(),
1677
+ /** Force full re-fit */
1678
+ full: z.boolean().optional()
1679
+ });
1680
+ WorkspaceBase.extend({
1681
+ /** File path to score */
1682
+ filePath: z.string().min(1)
1683
+ });
1684
+ WorkspaceBase.extend({
1685
+ /** Quick mode (git + AST only) */
1686
+ quick: z.boolean().optional()
1687
+ });
1688
+ z.object({});
1689
+ z.object({});
1690
+ z.object({});
1691
+ z.object({});
1692
+ z.object({
1693
+ /** Absolute path to workspace root */
1694
+ workspace: z.string().min(1),
1695
+ /** Component type */
1696
+ type: z.enum([
1697
+ "mcp-stdio",
1698
+ "mcp-http"
1699
+ ]),
1700
+ /** Process ID of the component */
1701
+ pid: z.number().int().positive(),
1702
+ /** Restart strategy */
1703
+ restartStrategy: z.enum([
1704
+ "one-for-one",
1705
+ "one-for-all"
1706
+ ]).default("one-for-one")
1707
+ });
1708
+ z.object({
1709
+ /** Process ID of the component */
1710
+ pid: z.number().int().positive()
1711
+ });
1712
+ z.object({
1713
+ /** Include verbose details */
1714
+ verbose: z.boolean().optional()
1715
+ });
1716
+ WorkspaceBase.extend({
1717
+ /** Entry type */
1718
+ type: MemoryEntryType,
1719
+ /** Entry data */
1720
+ data: z.record(z.unknown()),
1721
+ /** TTL in days (0 = permanent) */
1722
+ ttlDays: z.number().int().min(0).default(0)
1723
+ });
1724
+ WorkspaceBase.extend({
1725
+ /** Entry IDs to retrieve */
1726
+ ids: z.array(z.string().min(1)).min(1)
1727
+ });
1728
+ WorkspaceBase.extend({
1729
+ /** Filter by type */
1730
+ type: MemoryEntryType.optional(),
1731
+ /** Keywords to match */
1732
+ keywords: z.array(z.string()).optional(),
1733
+ /** Minimum relevance score (0-1) */
1734
+ minRelevance: z.number().min(0).max(1).default(0.3),
1735
+ /** Maximum results */
1736
+ limit: z.number().int().min(1).max(100).default(50),
1737
+ /** Created after timestamp */
1738
+ since: z.number().optional()
1739
+ });
1740
+ WorkspaceBase.extend({
1741
+ /** Preview only (don't modify) */
1742
+ dryRun: z.boolean().default(false)
1743
+ });
1744
+ WorkspaceBase.extend({
1745
+ /** Task summary/description */
1746
+ task: z.string().min(1),
1747
+ /** Files involved in the task */
1748
+ files: z.array(z.string()).optional(),
1749
+ /** Keywords for context matching */
1750
+ keywords: z.array(z.string()).optional(),
1751
+ /** Task intent classification */
1752
+ intent: TaskIntent.optional()
1753
+ });
1754
+ WorkspaceBase.extend({
1755
+ /** Progress message */
1756
+ message: z.string().min(1),
1757
+ /** Files modified in this progress */
1758
+ filesModified: z.array(z.string()).optional(),
1759
+ /** Partial completion percentage */
1760
+ completionPercent: z.number().min(0).max(100).optional()
1761
+ });
1762
+ WorkspaceBase.extend({
1763
+ /**
1764
+ * Free-text outcome. Do NOT constrain to enum — snap_end promises free-text paragraphs.
1765
+ */
1766
+ outcome: z.string().min(1).max(2e3),
1767
+ /** Outcome notes */
1768
+ notes: z.string().optional(),
1769
+ /** Learnings captured during task */
1770
+ learnings: z.array(z.string()).optional()
1771
+ });
1772
+ WorkspaceBase.extend({
1773
+ /** Optional: specific component ID to assess */
1774
+ targetComponentId: z.string().optional(),
1775
+ /** Optional: minimum test coverage threshold (0.0-1.0) */
1776
+ minTestCoverage: z.number().min(0).max(1).optional(),
1777
+ /** Optional: maximum dependency count threshold */
1778
+ maxDependencyCount: z.number().int().positive().optional(),
1779
+ /** Optional: minimum confidence threshold (0.0-1.0) */
1780
+ minConfidence: z.number().min(0).max(1).optional()
1781
+ });
1782
+ WorkspaceBase.extend({
1783
+ /** Include all candidates in report (not just recommendations) */
1784
+ includeAllCandidates: z.boolean().optional()
1785
+ });
1786
+ WorkspaceBase.extend({
1787
+ /** Maximum number of recommendations to return */
1788
+ limit: z.number().int().positive().optional(),
1789
+ /** Minimum priority level to include */
1790
+ minPriority: z.enum([
1791
+ "critical",
1792
+ "high",
1793
+ "medium",
1794
+ "low"
1795
+ ]).optional()
1796
+ });
1797
+ WorkspaceBase.extend({
1798
+ /** Component ID to analyze */
1799
+ componentId: z.string()
1800
+ });
1801
+ WorkspaceBase.extend({
1802
+ /** Component ID to set gate for */
1803
+ componentId: z.string(),
1804
+ /** Gate to set */
1805
+ gate: z.enum([
1806
+ "migrationPlan",
1807
+ "rollbackStrategy",
1808
+ "ownerAssigned",
1809
+ "stakeholderSignoff"
1810
+ ]),
1811
+ /** Gate status */
1812
+ status: z.enum([
1813
+ "pass",
1814
+ "fail",
1815
+ "warn",
1816
+ "pending"
1817
+ ])
1818
+ });
1819
+ WorkspaceBase.extend({
1820
+ /** Component ID to get gates for */
1821
+ componentId: z.string()
1822
+ });
1823
+ WorkspaceBase.extend({
1824
+ /** Filter by actions */
1825
+ actions: z.array(z.enum([
1826
+ "refactor-now",
1827
+ "schedule",
1828
+ "defer",
1829
+ "monitor",
1830
+ "reject"
1831
+ ])).optional(),
1832
+ /** Filter by priorities */
1833
+ priorities: z.array(z.enum([
1834
+ "critical",
1835
+ "high",
1836
+ "medium",
1837
+ "low"
1838
+ ])).optional(),
1839
+ /** Minimum confidence threshold */
1840
+ minConfidence: z.number().min(0).max(1).optional()
1841
+ });
1842
+ z.object({
1843
+ /** Workspace root path */
1844
+ workspace: z.string().min(1).describe("Workspace root path"),
1845
+ /** Force refresh cache */
1846
+ force: z.boolean().optional().default(false).describe("Force refresh cache")
1847
+ });
1848
+ z.object({
1849
+ /** Workspace root path */
1850
+ workspace: z.string().min(1).describe("Workspace root path")
1851
+ });
1852
+ z.object({});
1853
+ var AITool = z.enum([
1854
+ "cursor",
1855
+ "claude-code",
1856
+ "copilot",
1857
+ "windsurf",
1858
+ "cline",
1859
+ "universal"
1860
+ ]);
1861
+ var FileAccessRule = z.object({
1862
+ pattern: z.string(),
1863
+ permission: z.enum([
1864
+ "allow",
1865
+ "deny",
1866
+ "caution"
1867
+ ]),
1868
+ source: z.string()
1869
+ });
1870
+ z.object({
1871
+ // Identity
1872
+ tool: AITool,
1873
+ filePath: z.string(),
1874
+ fileName: z.string(),
1875
+ lastModifiedAt: z.number(),
1876
+ rawContent: z.string(),
1877
+ // Extracted intelligence
1878
+ protectedFiles: z.array(z.string()),
1879
+ focusedDomains: z.array(z.string()),
1880
+ explicitInstructions: z.array(z.string()),
1881
+ toolPermissions: z.array(z.string()),
1882
+ workflowConstraints: z.array(z.string()),
1883
+ fileAccessRules: z.array(FileAccessRule),
1884
+ behaviorRules: z.array(z.string()),
1885
+ // Computed
1886
+ snapbackInstructionsPresent: z.boolean(),
1887
+ snapbackSectionHash: z.string().nullable()
1888
+ });
1889
+ z.object({
1890
+ id: z.string(),
1891
+ conflictType: z.enum([
1892
+ "file-access",
1893
+ "workflow",
1894
+ "behavior",
1895
+ "scope-overlap",
1896
+ "coverage-gap"
1897
+ ]),
1898
+ tools: z.array(AITool),
1899
+ files: z.array(z.string()),
1900
+ description: z.string(),
1901
+ severity: z.enum([
1902
+ "info",
1903
+ "warn",
1904
+ "error"
1905
+ ]),
1906
+ recommendation: z.string(),
1907
+ detectedAt: z.number()
1908
+ });
1909
+ z.object({
1910
+ fileName: z.string().optional(),
1911
+ pattern: z.string().optional(),
1912
+ tool: AITool,
1913
+ priority: z.number().default(0)
1914
+ });
1915
+ z.object({
1916
+ filePath: z.string(),
1917
+ tool: AITool,
1918
+ exists: z.boolean(),
1919
+ lastModified: z.number().nullable()
1920
+ });
1921
+ var FileRecord = z.object({
1922
+ path: z.string(),
1923
+ category: z.enum([
1924
+ "config",
1925
+ "source",
1926
+ "test",
1927
+ "build",
1928
+ "docs",
1929
+ "other"
1930
+ ]),
1931
+ domain: z.string(),
1932
+ sizeBytes: z.number(),
1933
+ lineCount: z.number(),
1934
+ extension: z.string()
1935
+ });
1936
+ var ChurnRecord = z.object({
1937
+ filePath: z.string(),
1938
+ changes30d: z.number(),
1939
+ changes90d: z.number(),
1940
+ lastChangedAt: z.number(),
1941
+ uniqueAuthors: z.number()
1942
+ });
1943
+ var CoChangeCluster = z.object({
1944
+ files: z.array(z.string()),
1945
+ coOccurrenceRate: z.number().min(0).max(1),
1946
+ sampleSize: z.number()
1947
+ });
1948
+ var FragileFileRecord = z.object({
1949
+ path: z.string(),
1950
+ compositeScore: z.number().min(0).max(100),
1951
+ churnScore: z.number(),
1952
+ blastRadiusScore: z.number(),
1953
+ rollbackScore: z.number(),
1954
+ dependentCount: z.number(),
1955
+ rank: z.number()
1956
+ });
1957
+ var DomainHealthScore = z.object({
1958
+ domain: z.string(),
1959
+ score: z.number().min(0).max(100),
1960
+ fileCount: z.number(),
1961
+ avgChurn: z.number(),
1962
+ fragileFileCount: z.number(),
1963
+ trend: z.enum([
1964
+ "improving",
1965
+ "stable",
1966
+ "degrading",
1967
+ "unknown"
1968
+ ]),
1969
+ trendComputedAt: z.number().nullable()
1970
+ });
1971
+ var BaselineNarrative = z.object({
1972
+ domainSummaries: z.record(z.string()),
1973
+ topRisks: z.array(z.object({
1974
+ file: z.string(),
1975
+ reason: z.string(),
1976
+ severity: z.enum([
1977
+ "high",
1978
+ "medium"
1979
+ ])
1980
+ })),
1981
+ agentContextTemplate: z.string()
1982
+ });
1983
+ var DependencyGraph = z.object({
1984
+ nodes: z.array(z.string()),
1985
+ edges: z.array(z.object({
1986
+ from: z.string(),
1987
+ to: z.string()
1988
+ })),
1989
+ blastRadii: z.record(z.number())
1990
+ });
1991
+ var BaselineRecord = z.object({
1992
+ workspacePath: z.string(),
1993
+ computedAt: z.number(),
1994
+ version: z.string(),
1995
+ // Tier 1: Structural
1996
+ fileInventory: z.array(FileRecord),
1997
+ domainMap: z.record(z.array(z.string())),
1998
+ totalFiles: z.number(),
1999
+ totalLines: z.number(),
2000
+ // Tier 2: Historical
2001
+ churnRates: z.record(ChurnRecord),
2002
+ coChangeClusters: z.array(CoChangeCluster),
2003
+ rollbackHotspots: z.array(z.string()),
2004
+ // Tier 3: Computed scores
2005
+ fragileFiles: z.array(FragileFileRecord),
2006
+ domainHealthScores: z.array(DomainHealthScore),
2007
+ overallHealthScore: z.number().min(0).max(100),
2008
+ // Dependency graph
2009
+ dependencyGraph: DependencyGraph,
2010
+ // AI config files (imported from ai-config.ts for enhanced types)
2011
+ aiConfigFiles: z.array(z.any()),
2012
+ aiConfigConflicts: z.array(z.any()),
2013
+ // AI synthesis
2014
+ narrative: BaselineNarrative.nullable(),
2015
+ narrativeComputedAt: z.number().nullable()
2016
+ });
2017
+ var BaselineStatus = z.enum([
2018
+ "ready",
2019
+ "stale",
2020
+ "computing",
2021
+ "not_computed"
2022
+ ]);
2023
+ z.object({
2024
+ status: BaselineStatus,
2025
+ progress: z.number().min(0).max(100),
2026
+ stage: z.string().optional(),
2027
+ error: z.string().optional()
2028
+ });
2029
+ z.object({
2030
+ workspace: z.string()
2031
+ });
2032
+ z.object({
2033
+ workspace: z.string()
2034
+ });
2035
+ z.object({
2036
+ workspace: z.string()
2037
+ });
2038
+ z.object({
2039
+ workspace: z.string()
2040
+ });
2041
+ z.object({
2042
+ workspace: z.string(),
2043
+ record: BaselineRecord
2044
+ });
2045
+ var SessionContextResult = z.object({
2046
+ /** Fragile files within scope of current task */
2047
+ fragileFilesInScope: z.array(z.object({
2048
+ path: z.string(),
2049
+ fragility: z.number(),
2050
+ reason: z.string().optional()
2051
+ })),
2052
+ /** Co-change patterns within scope */
2053
+ coChangePatternsInScope: z.array(z.object({
2054
+ source: z.string(),
2055
+ target: z.string(),
2056
+ confidence: z.number()
2057
+ })),
2058
+ /** Warning about concurrent sessions */
2059
+ concurrentSessionWarning: z.object({
2060
+ sessionId: z.string(),
2061
+ workspacePath: z.string(),
2062
+ touchedFiles: z.array(z.string())
2063
+ }).nullable()
2064
+ });
2065
+ z.object({
2066
+ /** Session ID (primary identifier) */
2067
+ id: z.string(),
2068
+ /** Task ID (alias for extension compatibility) */
2069
+ taskId: z.string().optional(),
2070
+ /** Workspace path */
2071
+ workspacePath: z.string(),
2072
+ /** Session start timestamp (ISO string) */
2073
+ startedAt: z.string(),
2074
+ /** Current session state */
2075
+ state: z.enum([
2076
+ "active",
2077
+ "ended"
2078
+ ]),
2079
+ /** Session metadata */
2080
+ metadata: z.object({
2081
+ task: z.string().optional(),
2082
+ keywords: z.array(z.string()).optional(),
2083
+ aiToolsDetected: z.array(z.string()).optional(),
2084
+ highestRiskLevel: z.enum([
2085
+ "low",
2086
+ "medium",
2087
+ "high"
2088
+ ]).optional()
2089
+ }),
2090
+ /** Files touched in this session */
2091
+ touchedFiles: z.array(z.string()).optional(),
2092
+ /** Baseline context enrichment */
2093
+ context: SessionContextResult.nullable(),
2094
+ /** Learnings loaded for this session (from enrichment) */
2095
+ learnings: z.array(z.object({
2096
+ id: z.string().optional(),
2097
+ type: z.string().optional(),
2098
+ trigger: z.string(),
2099
+ action: z.string(),
2100
+ relevanceScore: z.number().optional()
2101
+ })).optional()
2102
+ });
2103
+ z.object({
2104
+ /** Whether the session was ended successfully */
2105
+ success: z.boolean(),
2106
+ /** Session ID that was ended */
2107
+ sessionId: z.string().optional(),
2108
+ /** Coherence score (0-1) */
2109
+ coherenceScore: z.number().min(0).max(1).optional(),
2110
+ /** Session duration in milliseconds */
2111
+ duration: z.number().optional(),
2112
+ /** Number of files modified */
2113
+ filesModified: z.number().optional(),
2114
+ /** Number of learnings captured */
2115
+ learningsCaptured: z.number().optional(),
2116
+ /** Number of snapshots created */
2117
+ snapshotsCreated: z.number().optional()
2118
+ });
2119
+ var SessionSummarySchema = z.object({
2120
+ sessionId: z.string(),
2121
+ workspace: z.string(),
2122
+ startedAt: z.number(),
2123
+ endedAt: z.number().nullable(),
2124
+ duration: z.number(),
2125
+ filesModified: z.number(),
2126
+ snapshotsCreated: z.number(),
2127
+ restoresTriggered: z.number(),
2128
+ aiEditsDetected: z.number(),
2129
+ protectionLevel: z.enum([
2130
+ "standard",
2131
+ "heightened",
2132
+ "maximum"
2133
+ ])
2134
+ });
2135
+ var SessionPatternSchema = z.object({
2136
+ type: z.enum([
2137
+ "co-change",
2138
+ "fragile-file",
2139
+ "temporal",
2140
+ "behavioral"
2141
+ ]),
2142
+ description: z.string(),
2143
+ confidence: z.number().min(0).max(1),
2144
+ filesInvolved: z.array(z.string()),
2145
+ promotedToHot: z.boolean()
2146
+ });
2147
+ var SessionLearningsSchema = z.object({
2148
+ patterns: z.array(SessionPatternSchema),
2149
+ totalNew: z.number(),
2150
+ totalPromoted: z.number(),
2151
+ totalPruned: z.number()
2152
+ });
2153
+ var PitfallWarningSchema = z.object({
2154
+ trigger: z.string(),
2155
+ risk: z.string(),
2156
+ outcome: z.enum([
2157
+ "heeded",
2158
+ "dismissed",
2159
+ "auto-resolved"
2160
+ ]),
2161
+ filesInvolved: z.array(z.string()),
2162
+ timestamp: z.number()
2163
+ });
2164
+ var PitfallsAvoidedSchema = z.object({
2165
+ warnings: z.array(PitfallWarningSchema),
2166
+ estimatedTimeSaved: z.number()
2167
+ });
2168
+ var IntelligenceMetricsSchema = z.object({
2169
+ tokenSavingsEstimate: z.number(),
2170
+ coherenceScore: z.number().min(0).max(100),
2171
+ contextReusageRate: z.number().min(0).max(1),
2172
+ intelligenceEventsTotal: z.number()
2173
+ });
2174
+ var TimelineEventSchema = z.object({
2175
+ timestamp: z.number(),
2176
+ type: z.enum([
2177
+ "session-start",
2178
+ "session-end",
2179
+ "snapshot-created",
2180
+ "snapshot-restored",
2181
+ "learning-added",
2182
+ "learning-promoted",
2183
+ "warning-fired",
2184
+ "ai-edit-detected",
2185
+ "protection-changed",
2186
+ "fragile-detected",
2187
+ "risk-spike"
2188
+ ]),
2189
+ summary: z.string(),
2190
+ detail: z.string().optional(),
2191
+ filesInvolved: z.array(z.string()).optional(),
2192
+ severity: z.enum([
2193
+ "info",
2194
+ "warning",
2195
+ "critical"
2196
+ ]).optional()
2197
+ });
2198
+ var CeremonyPayloadSchema = z.object({
2199
+ summary: SessionSummarySchema,
2200
+ learnings: SessionLearningsSchema,
2201
+ pitfalls: PitfallsAvoidedSchema,
2202
+ metrics: IntelligenceMetricsSchema,
2203
+ timeline: z.array(TimelineEventSchema)
2204
+ }).superRefine((payload, ctx) => {
2205
+ const sessionStart = payload.summary.startedAt;
2206
+ const sessionEnd = payload.summary.endedAt ?? Date.now();
2207
+ for (const event of payload.timeline) {
2208
+ if (event.timestamp < sessionStart) {
2209
+ ctx.addIssue({
2210
+ code: "custom",
2211
+ message: `Timeline event ${event.type} has timestamp before session start`,
2212
+ path: [
2213
+ "timeline"
2214
+ ]
2215
+ });
2216
+ }
2217
+ if (event.timestamp > sessionEnd) {
2218
+ ctx.addIssue({
2219
+ code: "custom",
2220
+ message: `Timeline event ${event.type} has timestamp after session end`,
2221
+ path: [
2222
+ "timeline"
2223
+ ]
2224
+ });
2225
+ }
2226
+ }
2227
+ const patternCount = payload.learnings.patterns.length;
2228
+ const totalLearnings = payload.learnings.totalNew + payload.learnings.totalPromoted;
2229
+ if (patternCount > 0 && totalLearnings === 0) {
2230
+ ctx.addIssue({
2231
+ code: "custom",
2232
+ message: "Learnings has patterns but totalNew + totalPromoted is 0",
2233
+ path: [
2234
+ "learnings"
2235
+ ]
2236
+ });
2237
+ }
2238
+ if (payload.metrics.coherenceScore > 100) {
2239
+ ctx.addIssue({
2240
+ code: "custom",
2241
+ message: "coherenceScore must be between 0-100",
2242
+ path: [
2243
+ "metrics",
2244
+ "coherenceScore"
2245
+ ]
2246
+ });
2247
+ }
2248
+ if (payload.summary.filesModified > 10 && payload.metrics.intelligenceEventsTotal < 5) {
2249
+ ctx.addIssue({
2250
+ code: "custom",
2251
+ message: "High file modification count with low intelligence events - possible under-instrumentation",
2252
+ path: [
2253
+ "metrics"
2254
+ ]
2255
+ });
2256
+ }
2257
+ });
2258
+ var CoherenceScoreSchema = z.discriminatedUnion("type", [
2259
+ z.object({
2260
+ type: z.literal("numeric"),
2261
+ value: z.number().min(0).max(1)
2262
+ }),
2263
+ z.object({
2264
+ type: z.literal("categorical"),
2265
+ level: z.enum([
2266
+ "high",
2267
+ "medium",
2268
+ "low",
2269
+ "scattered"
2270
+ ]),
2271
+ /** Optional numeric approximation for analytics */
2272
+ approximateValue: z.number().min(0).max(1).optional()
2273
+ })
2274
+ ]);
2275
+ z.object({
2276
+ /** Session ID */
2277
+ sessionId: z.string(),
2278
+ /** Files modified during session */
2279
+ filesModified: z.number(),
2280
+ /** Snapshots created during session */
2281
+ snapshotsCreated: z.number(),
2282
+ /** Learnings captured during session */
2283
+ learningsCaptured: z.number(),
2284
+ /** Pitfalls avoided (proactive warnings heeded) */
2285
+ pitfallsAvoided: z.number().optional(),
2286
+ /** Estimated token savings */
2287
+ tokensSaved: z.number().optional(),
2288
+ /** Coherence score - discriminated union for type-safe handling */
2289
+ coherenceScore: CoherenceScoreSchema.optional(),
2290
+ /** Key insights from the session */
2291
+ insights: z.array(z.string()).optional(),
2292
+ /** Items to carry forward to next session */
2293
+ carryForward: z.array(z.string()).optional(),
2294
+ /** Commit message suggestion */
2295
+ commitMessage: z.string().optional(),
2296
+ /** Full ceremony payload (March 18 spec) */
2297
+ ceremony: CeremonyPayloadSchema.optional(),
2298
+ /** Top learnings from ceremony */
2299
+ topLearnings: z.array(z.object({
2300
+ content: z.string(),
2301
+ captureMethod: z.string(),
2302
+ confidence: z.number()
2303
+ })).optional(),
2304
+ /** Duration in ms */
2305
+ duration: z.number().optional()
2306
+ });
2307
+ z.object({
2308
+ /** Whether there's an active session */
2309
+ active: z.boolean(),
2310
+ /** Current session ID if active */
2311
+ id: z.string().optional(),
2312
+ /** Task ID if active */
2313
+ taskId: z.string().optional(),
2314
+ /** Task description */
2315
+ task: z.string().optional(),
2316
+ /** Session start timestamp */
2317
+ startedAt: z.string().optional(),
2318
+ /** Files modified count */
2319
+ filesModified: z.number().optional(),
2320
+ /** Snapshot count */
2321
+ snapshotCount: z.number().optional()
2322
+ });
2323
+ z.object({
2324
+ /** Whether the learning was added successfully */
2325
+ success: z.boolean(),
2326
+ /** Learning ID */
2327
+ id: z.string(),
2328
+ /** Tier assigned (hot, warm, cold) */
2329
+ tier: z.enum([
2330
+ "hot",
2331
+ "warm",
2332
+ "cold"
2333
+ ]),
2334
+ /** Whether this is a new learning vs update */
2335
+ isNew: z.boolean().optional()
2336
+ });
2337
+ var LearningSearchEntry = z.object({
2338
+ /** Learning ID */
2339
+ id: z.string(),
2340
+ /** Learning type */
2341
+ type: z.string().optional(),
2342
+ /** Trigger condition */
2343
+ trigger: z.string(),
2344
+ /** Action to take */
2345
+ action: z.string(),
2346
+ /** Relevance score (0-1) */
2347
+ relevanceScore: z.number().min(0).max(1).optional(),
2348
+ /** Source of the learning */
2349
+ source: z.string().optional(),
2350
+ /** Tier of the learning */
2351
+ tier: z.enum([
2352
+ "hot",
2353
+ "warm",
2354
+ "cold"
2355
+ ]).optional()
2356
+ });
2357
+ z.object({
2358
+ /** Search results */
2359
+ results: z.array(LearningSearchEntry),
2360
+ /** Total matching learnings */
2361
+ total: z.number().optional(),
2362
+ /** Query that was searched */
2363
+ query: z.string().optional()
2364
+ });
2365
+ var FragileFileEntry = z.object({
2366
+ /** File path */
2367
+ path: z.string(),
2368
+ /** Fragility score (0-1) */
2369
+ fragility: z.number().min(0).max(1),
2370
+ /** Reason for fragility */
2371
+ reason: z.string().optional()
2372
+ });
2373
+ var CoChangeEntry = z.object({
2374
+ /** Source file */
2375
+ source: z.string(),
2376
+ /** Target file (changes when source changes) */
2377
+ target: z.string(),
2378
+ /** Confidence score (0-1) */
2379
+ confidence: z.number().min(0).max(1)
2380
+ });
2381
+ z.object({
2382
+ /** Elevated, deduplicated learnings (LearningApi format) */
2383
+ learnings: z.array(LearningApiSchema),
2384
+ /** Fragile files in scope */
2385
+ fragileFiles: z.array(FragileFileEntry).optional(),
2386
+ /** Co-change patterns */
2387
+ coChanges: z.array(CoChangeEntry).optional(),
2388
+ /** Files that were queried */
2389
+ files: z.array(z.string()),
2390
+ /** Compiled wire format context (for LLM consumption) */
2391
+ compiled: z.string(),
2392
+ /** Token estimate for compiled context */
2393
+ compiledTokens: z.number(),
2394
+ /** Estimated tokens saved vs natural language */
2395
+ tokensSaved: z.number(),
2396
+ /** Metadata about what's included */
2397
+ metadata: z.object({
2398
+ /** Number of learnings included */
2399
+ learningCount: z.number().optional(),
2400
+ /** Number of fragile files included */
2401
+ fragileFileCount: z.number().optional(),
2402
+ /** Number of co-change patterns included */
2403
+ coChangePatternCount: z.number().optional(),
2404
+ /** Estimated token count */
2405
+ tokenCount: z.number().optional(),
2406
+ /** Whether context was loaded from cache */
2407
+ fromCache: z.boolean().optional()
2408
+ }).optional()
2409
+ });
2410
+ z.object({
2411
+ /** Composite risk score */
2412
+ score: z.number(),
2413
+ /** Action recommendation */
2414
+ action: z.enum([
2415
+ "PROCEED",
2416
+ "PROCEED_WITH_SNAPSHOT",
2417
+ "WARN",
2418
+ "BLOCK"
2419
+ ]),
2420
+ /** Risk level classification */
2421
+ level: z.enum([
2422
+ "L",
2423
+ "M",
2424
+ "H"
2425
+ ]),
2426
+ /** Contributing factors from all pillars */
2427
+ factors: z.array(z.object({
2428
+ source: z.string(),
2429
+ score: z.number(),
2430
+ description: z.string()
2431
+ })),
2432
+ /** Git risk pillar data */
2433
+ gitRisk: z.object({
2434
+ multiplier: z.number(),
2435
+ uncommittedChanges: z.boolean().optional(),
2436
+ detachedHead: z.boolean().optional(),
2437
+ onMainBranch: z.boolean().optional()
2438
+ }).optional(),
2439
+ /** Fatigue pillar data */
2440
+ fatigue: z.object({
2441
+ level: z.enum([
2442
+ "rested",
2443
+ "normal",
2444
+ "elevated",
2445
+ "fatigued"
2446
+ ]),
2447
+ riskBoost: z.number()
2448
+ }).optional(),
2449
+ /** Rollback warning from pillar 2 */
2450
+ rollbackWarning: z.object({
2451
+ confidence: z.number(),
2452
+ message: z.string(),
2453
+ riskBoost: z.number()
2454
+ }).optional(),
2455
+ /** Poisoning detection from pillar 5 */
2456
+ poisoning: z.object({
2457
+ detected: z.boolean(),
2458
+ files: z.array(z.string()).optional(),
2459
+ riskBoost: z.number().optional()
2460
+ }).optional(),
2461
+ /** Per-file risk breakdown */
2462
+ fileRisks: z.record(z.string(), z.object({
2463
+ fragility: z.number(),
2464
+ rollbackCount: z.number().optional(),
2465
+ reason: z.string().optional()
2466
+ })).optional(),
2467
+ /** Co-change alerts */
2468
+ coChanges: z.array(z.object({
2469
+ source: z.string(),
2470
+ target: z.string(),
2471
+ confidence: z.number()
2472
+ })).optional()
2473
+ });
2474
+ ({
2475
+ "health/ping": z.object({}),
2476
+ // Sync namespace (original local-service style)
2477
+ "sync/status": z.object({}),
2478
+ // DORA metrics namespace (routed through daemon)
2479
+ "dora/record-snapshot": z.object({
2480
+ workspace: z.string(),
2481
+ event: z.object({
2482
+ snapshotId: z.string(),
2483
+ timestamp: z.number(),
2484
+ timeSinceLastChange: z.number(),
2485
+ isRecoveryTriggered: z.boolean(),
2486
+ trigger: z.enum([
2487
+ "manual",
2488
+ "auto",
2489
+ "ai-detected",
2490
+ "recovery"
2491
+ ])
2492
+ })
2493
+ }),
2494
+ "dora/record-recovery": z.object({
2495
+ workspace: z.string(),
2496
+ event: z.object({
2497
+ snapshotId: z.string(),
2498
+ requestTime: z.number(),
2499
+ completionTime: z.number(),
2500
+ success: z.boolean(),
2501
+ filesRestored: z.number(),
2502
+ failureReason: z.string().optional()
2503
+ })
2504
+ }),
2505
+ "dora/get-metrics": z.object({
2506
+ workspace: z.string()
2507
+ }),
2508
+ "dora/get-trends": z.object({
2509
+ workspace: z.string()
2510
+ }),
2511
+ // CTI: Codebase Topology Intelligence namespace (Phase A)
2512
+ "topology/scan": z.object({
2513
+ workspace: z.string(),
2514
+ force: z.boolean().optional().default(false)
2515
+ }),
2516
+ "topology/status": z.object({
2517
+ workspace: z.string()
2518
+ }),
2519
+ "topology/query": z.object({
2520
+ workspace: z.string(),
2521
+ task: z.string().min(1, "task description required for topology query"),
2522
+ max_files: z.number().int().positive().optional().default(15),
2523
+ include_deviations: z.boolean().optional().default(true),
2524
+ include_behavioral: z.boolean().optional().default(true)
2525
+ }),
2526
+ // Provenance namespace (AI attribution via daemon session data)
2527
+ "provenance/query": z.object({
2528
+ workspaceRoot: z.string(),
2529
+ files: z.array(z.string())
2530
+ })
2531
+ });
2532
+ var __defProp = Object.defineProperty;
2533
+ var __name2 = /* @__PURE__ */ __name((target, value) => __defProp(target, "name", {
2534
+ value,
2535
+ configurable: true
2536
+ }), "__name");
2537
+ var JsonRpcClientError = class extends Error {
2538
+ static {
2539
+ __name(this, "JsonRpcClientError");
2540
+ }
2541
+ static {
2542
+ __name2(this, "JsonRpcClientError");
2543
+ }
2544
+ code;
2545
+ data;
2546
+ constructor(message, code, data) {
2547
+ super(message);
2548
+ this.name = "JsonRpcClientError";
2549
+ this.code = code;
2550
+ this.data = data;
2551
+ }
2552
+ };
2553
+ var ConnectionTimeoutError = class extends Error {
2554
+ static {
2555
+ __name(this, "ConnectionTimeoutError");
2556
+ }
2557
+ static {
2558
+ __name2(this, "ConnectionTimeoutError");
2559
+ }
2560
+ constructor(message = "Connection timeout") {
2561
+ super(message);
2562
+ this.name = "ConnectionTimeoutError";
2563
+ }
2564
+ };
2565
+ var RequestTimeoutError = class extends Error {
2566
+ static {
2567
+ __name(this, "RequestTimeoutError");
2568
+ }
2569
+ static {
2570
+ __name2(this, "RequestTimeoutError");
2571
+ }
2572
+ requestId;
2573
+ constructor(message = "Request timeout", requestId) {
2574
+ super(message), this.requestId = requestId;
2575
+ this.name = "RequestTimeoutError";
2576
+ }
2577
+ };
2578
+ function getDefaultSocketPath() {
2579
+ const platform2 = os.platform();
2580
+ if (platform2 === "win32") {
2581
+ return "\\\\.\\pipe\\snapback-service";
2582
+ }
2583
+ const homeDir = os.homedir();
2584
+ return path.join(homeDir, ".snapback", "service.sock");
2585
+ }
2586
+ __name(getDefaultSocketPath, "getDefaultSocketPath");
2587
+ __name2(getDefaultSocketPath, "getDefaultSocketPath");
2588
+ var IpcConnection = class {
2589
+ static {
2590
+ __name(this, "IpcConnection");
2591
+ }
2592
+ static {
2593
+ __name2(this, "IpcConnection");
2594
+ }
2595
+ socket = null;
2596
+ buffer = "";
2597
+ listeners = {};
2598
+ socketPath;
2599
+ timeout;
2600
+ constructor(options = {}) {
2601
+ this.socketPath = options.socketPath ?? getDefaultSocketPath();
2602
+ this.timeout = options.timeout ?? 5e3;
2603
+ }
2604
+ /**
2605
+ * Connect to the IPC socket
2606
+ *
2607
+ * Fixed race conditions:
2608
+ * - Uses settled flag to prevent timeout firing after successful connect
2609
+ * - Destroys socket on error to prevent orphaned handles
2610
+ * - Cleanup function ensures timeout is always cleared
2611
+ */
2612
+ async connect() {
2613
+ return new Promise((resolve, reject) => {
2614
+ const socket = net.createConnection({
2615
+ path: this.socketPath
2616
+ });
2617
+ let timeoutId = null;
2618
+ let settled = false;
2619
+ const cleanup = /* @__PURE__ */ __name2(() => {
2620
+ if (timeoutId) {
2621
+ clearTimeout(timeoutId);
2622
+ timeoutId = null;
2623
+ }
2624
+ }, "cleanup");
2625
+ if (this.timeout > 0) {
2626
+ timeoutId = setTimeout(() => {
2627
+ if (settled) return;
2628
+ settled = true;
2629
+ cleanup();
2630
+ socket.destroy();
2631
+ reject(new ConnectionTimeoutError(`Connection timeout after ${this.timeout}ms`));
2632
+ }, this.timeout);
2633
+ timeoutId.unref();
2634
+ }
2635
+ socket.once("connect", () => {
2636
+ if (settled) return;
2637
+ settled = true;
2638
+ cleanup();
2639
+ this.socket = socket;
2640
+ this.setupSocketHandlers();
2641
+ resolve();
2642
+ });
2643
+ socket.once("error", (error) => {
2644
+ if (settled) return;
2645
+ settled = true;
2646
+ cleanup();
2647
+ socket.destroy();
2648
+ reject(error);
2649
+ });
2650
+ });
2651
+ }
2652
+ /**
2653
+ * Set up socket event handlers
2654
+ *
2655
+ * IMPORTANT: Removes old listeners before adding new ones to prevent
2656
+ * memory leak on reconnect. Without this, each reconnect would add
2657
+ * duplicate handlers that never get garbage collected.
2658
+ */
2659
+ setupSocketHandlers() {
2660
+ if (!this.socket) {
2661
+ return;
2662
+ }
2663
+ this.socket.removeAllListeners("data");
2664
+ this.socket.removeAllListeners("close");
2665
+ this.socket.removeAllListeners("error");
2666
+ this.socket.on("data", (chunk) => {
2667
+ this.buffer += chunk.toString("utf8");
2668
+ this.processBuffer();
2669
+ });
2670
+ this.socket.on("close", (hadError) => {
2671
+ const error = hadError ? new Error("Socket closed with error") : void 0;
2672
+ this.emit("close", error);
2673
+ this.socket = null;
2674
+ this.buffer = "";
2675
+ });
2676
+ this.socket.on("error", (error) => {
2677
+ this.emit("error", error);
2678
+ this.buffer = "";
2679
+ });
2680
+ }
2681
+ /**
2682
+ * Process incoming data buffer (newline-delimited JSON)
2683
+ */
2684
+ processBuffer() {
2685
+ let newlineIndex;
2686
+ while ((newlineIndex = this.buffer.indexOf("\n")) !== -1) {
2687
+ const line = this.buffer.slice(0, newlineIndex);
2688
+ this.buffer = this.buffer.slice(newlineIndex + 1);
2689
+ if (line.trim().length === 0) {
2690
+ continue;
2691
+ }
2692
+ try {
2693
+ const message = JSON.parse(line);
2694
+ this.emit("message", message);
2695
+ } catch (error) {
2696
+ this.emit("error", new Error(`Failed to parse JSON-RPC message: ${error.message}`));
2697
+ }
2698
+ }
2699
+ }
2700
+ /**
2701
+ * Send a JSON-RPC request
2702
+ *
2703
+ * TOCTOU Fix: Captures socket reference at start to prevent race condition
2704
+ * where socket becomes null between the check and the write.
2705
+ */
2706
+ async send(request) {
2707
+ const socket = this.socket;
2708
+ if (!socket || socket.destroyed) {
2709
+ throw new Error("Socket not connected");
2710
+ }
2711
+ return new Promise((resolve, reject) => {
2712
+ const data = `${JSON.stringify(request)}
2713
+ `;
2714
+ socket.write(data, "utf8", (error) => {
2715
+ if (error) {
2716
+ reject(error);
2717
+ } else {
2718
+ resolve();
2719
+ }
2720
+ });
2721
+ });
2722
+ }
2723
+ /**
2724
+ * Close the connection
2725
+ *
2726
+ * Proper cleanup:
2727
+ * - Removes all listeners to prevent memory leaks
2728
+ * - Calls end() for graceful close
2729
+ * - Calls destroy() to ensure immediate cleanup
2730
+ * - Resets buffer to prevent stale data on reconnect
2731
+ */
2732
+ close() {
2733
+ if (this.socket) {
2734
+ this.socket.removeAllListeners();
2735
+ this.socket.end();
2736
+ this.socket.destroy();
2737
+ this.socket = null;
2738
+ }
2739
+ this.buffer = "";
2740
+ }
2741
+ /**
2742
+ * Check if connected
2743
+ */
2744
+ isConnected() {
2745
+ return this.socket !== null && !this.socket.destroyed;
2746
+ }
2747
+ /**
2748
+ * Register event listener
2749
+ */
2750
+ on(event, handler) {
2751
+ this.listeners[event] = handler;
2752
+ }
2753
+ /**
2754
+ * Remove event listener
2755
+ */
2756
+ off(event) {
2757
+ delete this.listeners[event];
2758
+ }
2759
+ /**
2760
+ * Emit event
2761
+ */
2762
+ emit(event, ...args) {
2763
+ const handler = this.listeners[event];
2764
+ if (handler) {
2765
+ handler(...args);
2766
+ }
2767
+ }
2768
+ };
2769
+ var ReconnectManager = class {
2770
+ static {
2771
+ __name(this, "ReconnectManager");
2772
+ }
2773
+ static {
2774
+ __name2(this, "ReconnectManager");
2775
+ }
2776
+ isReconnecting = false;
2777
+ abortController = null;
2778
+ options;
2779
+ constructor(options = {}) {
2780
+ this.options = {
2781
+ maxAttempts: options.maxAttempts ?? 5,
2782
+ initialDelay: options.initialDelay ?? 1e3,
2783
+ maxDelay: options.maxDelay ?? 3e4,
2784
+ backoffMultiplier: options.backoffMultiplier ?? 2
2785
+ };
2786
+ }
2787
+ /**
2788
+ * Start reconnection process
2789
+ */
2790
+ async start(connectFn, onAttempt, onFailed) {
2791
+ if (this.isReconnecting) {
2792
+ return;
2793
+ }
2794
+ this.isReconnecting = true;
2795
+ this.abortController = new AbortController();
2796
+ const pRetryOptions = {
2797
+ retries: this.options.maxAttempts,
2798
+ factor: this.options.backoffMultiplier,
2799
+ minTimeout: this.options.initialDelay,
2800
+ maxTimeout: this.options.maxDelay,
2801
+ randomize: false,
2802
+ signal: this.abortController.signal,
2803
+ onFailedAttempt: /* @__PURE__ */ __name2((error) => {
2804
+ if (onAttempt) {
2805
+ onAttempt(error.attemptNumber, this.options.maxAttempts);
2806
+ }
2807
+ }, "onFailedAttempt")
2808
+ };
2809
+ try {
2810
+ await pRetry(connectFn, pRetryOptions);
2811
+ this.reset();
2812
+ } catch (error) {
2813
+ this.reset();
2814
+ if (onFailed) {
2815
+ onFailed();
2816
+ }
2817
+ throw error;
2818
+ }
2819
+ }
2820
+ /**
2821
+ * Stop reconnection process
2822
+ */
2823
+ stop() {
2824
+ if (this.abortController) {
2825
+ this.abortController.abort();
2826
+ this.abortController = null;
2827
+ }
2828
+ this.reset();
2829
+ }
2830
+ /**
2831
+ * Reset reconnection state
2832
+ */
2833
+ reset() {
2834
+ this.isReconnecting = false;
2835
+ }
2836
+ /**
2837
+ * Check if currently reconnecting
2838
+ */
2839
+ isActive() {
2840
+ return this.isReconnecting;
2841
+ }
2842
+ /**
2843
+ * Get current attempt number (not tracked in p-retry wrapper)
2844
+ * @deprecated p-retry handles attempt tracking internally
2845
+ */
2846
+ getCurrentAttempt() {
2847
+ return this.isReconnecting ? 1 : 0;
2848
+ }
2849
+ };
2850
+ function createContextMethods(call) {
2851
+ return {
2852
+ get: /* @__PURE__ */ __name2((params) => call("context/get", params), "get"),
2853
+ validate: /* @__PURE__ */ __name2((params) => call("context/validate", params), "validate"),
2854
+ checkPatterns: /* @__PURE__ */ __name2((params) => call("context/check-patterns", params), "checkPatterns")
2855
+ };
2856
+ }
2857
+ __name(createContextMethods, "createContextMethods");
2858
+ __name2(createContextMethods, "createContextMethods");
2859
+ function createDaemonMethods(call) {
2860
+ return {
2861
+ ping: /* @__PURE__ */ __name2(() => call("daemon/ping"), "ping"),
2862
+ status: /* @__PURE__ */ __name2(() => call("daemon/status"), "status"),
2863
+ shutdown: /* @__PURE__ */ __name2(() => call("daemon/shutdown"), "shutdown"),
2864
+ reload: /* @__PURE__ */ __name2(() => call("daemon/reload"), "reload")
2865
+ };
2866
+ }
2867
+ __name(createDaemonMethods, "createDaemonMethods");
2868
+ __name2(createDaemonMethods, "createDaemonMethods");
2869
+ function createDetectionMethods(call) {
2870
+ return {
2871
+ check: /* @__PURE__ */ __name2((params) => call("detection/check", params), "check")
2872
+ };
2873
+ }
2874
+ __name(createDetectionMethods, "createDetectionMethods");
2875
+ __name2(createDetectionMethods, "createDetectionMethods");
2876
+ function createHealthMethods(call) {
2877
+ return {
2878
+ check: /* @__PURE__ */ __name2((params) => call("health/check", params), "check"),
2879
+ ping: /* @__PURE__ */ __name2(() => call("health/ping"), "ping")
2880
+ };
2881
+ }
2882
+ __name(createHealthMethods, "createHealthMethods");
2883
+ __name2(createHealthMethods, "createHealthMethods");
2884
+ function createIntelligenceMethods(call) {
2885
+ return {
2886
+ capture: /* @__PURE__ */ __name2((params) => call("intelligence/capture", params), "capture"),
2887
+ outcome: /* @__PURE__ */ __name2((params) => call("intelligence/outcome", params), "outcome")
2888
+ };
2889
+ }
2890
+ __name(createIntelligenceMethods, "createIntelligenceMethods");
2891
+ __name2(createIntelligenceMethods, "createIntelligenceMethods");
2892
+ function createLearningMethods(call) {
2893
+ return {
2894
+ add: /* @__PURE__ */ __name2((params) => call("learning/add", params), "add"),
2895
+ search: /* @__PURE__ */ __name2((params) => call("learning/search", params), "search"),
2896
+ list: /* @__PURE__ */ __name2((params) => call("learning/list", params), "list"),
2897
+ prune: /* @__PURE__ */ __name2((params) => call("learning/prune", params), "prune"),
2898
+ evaluate: /* @__PURE__ */ __name2((params) => call("learning/evaluate", params), "evaluate"),
2899
+ updateSession: /* @__PURE__ */ __name2((params) => call("learning/update-session", params), "updateSession"),
2900
+ gc: /* @__PURE__ */ __name2((params) => call("learning/gc", params), "gc"),
2901
+ seed: /* @__PURE__ */ __name2((params) => call("learning/seed", params), "seed"),
2902
+ consolidate: /* @__PURE__ */ __name2((params) => call("learning/consolidate", params), "consolidate")
2903
+ };
2904
+ }
2905
+ __name(createLearningMethods, "createLearningMethods");
2906
+ __name2(createLearningMethods, "createLearningMethods");
2907
+ function createMCPMethods(call) {
2908
+ return {
2909
+ snapshotCreated: /* @__PURE__ */ __name2((params) => call("mcp/snapshot-created", params), "snapshotCreated"),
2910
+ fileModified: /* @__PURE__ */ __name2((params) => call("mcp/file-modified", params), "fileModified")
2911
+ };
2912
+ }
2913
+ __name(createMCPMethods, "createMCPMethods");
2914
+ __name2(createMCPMethods, "createMCPMethods");
2915
+ function createMomentumMethods(call) {
2916
+ return {
2917
+ refresh: /* @__PURE__ */ __name2((params) => call("momentum/refresh", params), "refresh"),
2918
+ score: /* @__PURE__ */ __name2((params) => call("momentum/score", params), "score"),
2919
+ sync: /* @__PURE__ */ __name2((params) => call("momentum/sync", params), "sync"),
2920
+ status: /* @__PURE__ */ __name2((params) => call("momentum/status", params), "status")
2921
+ };
2922
+ }
2923
+ __name(createMomentumMethods, "createMomentumMethods");
2924
+ __name2(createMomentumMethods, "createMomentumMethods");
2925
+ function createProtectionMethods(call) {
2926
+ return {
2927
+ evaluate: /* @__PURE__ */ __name2((params) => call("protection/evaluate", params), "evaluate"),
2928
+ levels: /* @__PURE__ */ __name2((params) => call("protection/levels", params), "levels"),
2929
+ set: /* @__PURE__ */ __name2((params) => call("protection/set", params), "set"),
2930
+ listDaemon: /* @__PURE__ */ __name2((params) => call("protection/list-daemon", params), "listDaemon")
2931
+ };
2932
+ }
2933
+ __name(createProtectionMethods, "createProtectionMethods");
2934
+ __name2(createProtectionMethods, "createProtectionMethods");
2935
+ function createSessionMethods(call) {
2936
+ return {
2937
+ current: /* @__PURE__ */ __name2((params) => call("session/current", params), "current"),
2938
+ start: /* @__PURE__ */ __name2((params) => call("session/start", params), "start"),
2939
+ begin: /* @__PURE__ */ __name2((params) => call("session/begin", params), "begin"),
2940
+ end: /* @__PURE__ */ __name2((params) => call("session/end", params), "end"),
2941
+ list: /* @__PURE__ */ __name2((params) => call("session/list", params), "list"),
2942
+ vitals: /* @__PURE__ */ __name2((params) => call("session/vitals", params), "vitals")
2943
+ };
2944
+ }
2945
+ __name(createSessionMethods, "createSessionMethods");
2946
+ __name2(createSessionMethods, "createSessionMethods");
2947
+ function createSnapshotMethods(call) {
2948
+ return {
2949
+ create: /* @__PURE__ */ __name2((params) => call("snapshot/create", params), "create"),
2950
+ get: /* @__PURE__ */ __name2((params) => call("snapshot/get", params), "get"),
2951
+ list: /* @__PURE__ */ __name2((params) => call("snapshot/list", params), "list"),
2952
+ restore: /* @__PURE__ */ __name2((params) => call("snapshot/restore", params), "restore"),
2953
+ diff: /* @__PURE__ */ __name2((params) => call("snapshot/diff", params), "diff"),
2954
+ delete: /* @__PURE__ */ __name2((params) => call("snapshot/delete", params), "delete")
2955
+ };
2956
+ }
2957
+ __name(createSnapshotMethods, "createSnapshotMethods");
2958
+ __name2(createSnapshotMethods, "createSnapshotMethods");
2959
+ function createSupervisorMethods(call) {
2960
+ return {
2961
+ register: /* @__PURE__ */ __name2((params) => call("supervisor/register", params), "register"),
2962
+ heartbeat: /* @__PURE__ */ __name2((params) => call("supervisor/heartbeat", params), "heartbeat"),
2963
+ systemHealth: /* @__PURE__ */ __name2((params) => call("system/health", params), "systemHealth")
2964
+ };
2965
+ }
2966
+ __name(createSupervisorMethods, "createSupervisorMethods");
2967
+ __name2(createSupervisorMethods, "createSupervisorMethods");
2968
+ function createSyncMethods(call) {
2969
+ return {
2970
+ status: /* @__PURE__ */ __name2((params) => call("sync/status-daemon", params), "status"),
2971
+ force: /* @__PURE__ */ __name2((params) => call("sync/force", params), "force"),
2972
+ stop: /* @__PURE__ */ __name2((params) => call("sync/stop", params), "stop"),
2973
+ start: /* @__PURE__ */ __name2((params) => call("sync/start", params), "start"),
2974
+ queue: /* @__PURE__ */ __name2((params) => call("sync/queue", params), "queue")
2975
+ };
2976
+ }
2977
+ __name(createSyncMethods, "createSyncMethods");
2978
+ __name2(createSyncMethods, "createSyncMethods");
2979
+ function createValidationMethods(call) {
2980
+ return {
2981
+ quick: /* @__PURE__ */ __name2((params) => call("validate/quick", params), "quick"),
2982
+ comprehensive: /* @__PURE__ */ __name2((params) => call("validate/comprehensive", params), "comprehensive")
2983
+ };
2984
+ }
2985
+ __name(createValidationMethods, "createValidationMethods");
2986
+ __name2(createValidationMethods, "createValidationMethods");
2987
+ function createViolationMethods(call) {
2988
+ return {
2989
+ report: /* @__PURE__ */ __name2((params) => call("violation/report", params), "report"),
2990
+ list: /* @__PURE__ */ __name2((params) => call("violation/list", params), "list")
2991
+ };
2992
+ }
2993
+ __name(createViolationMethods, "createViolationMethods");
2994
+ __name2(createViolationMethods, "createViolationMethods");
2995
+ function createWatchMethods(call) {
2996
+ return {
2997
+ subscribe: /* @__PURE__ */ __name2((params) => call("watch/subscribe", params), "subscribe"),
2998
+ unsubscribe: /* @__PURE__ */ __name2((params) => call("watch/unsubscribe", params), "unsubscribe"),
2999
+ fileChanged: /* @__PURE__ */ __name2((params) => call("watch/file-changed", params), "fileChanged")
3000
+ };
3001
+ }
3002
+ __name(createWatchMethods, "createWatchMethods");
3003
+ __name2(createWatchMethods, "createWatchMethods");
3004
+ function createWorkspaceMethods(call) {
3005
+ return {
3006
+ analyze: /* @__PURE__ */ __name2((params) => call("workspace/analyze", params), "analyze"),
3007
+ status: /* @__PURE__ */ __name2((params) => call("workspace/status", params), "status")
3008
+ };
3009
+ }
3010
+ __name(createWorkspaceMethods, "createWorkspaceMethods");
3011
+ __name2(createWorkspaceMethods, "createWorkspaceMethods");
3012
+ var SnapBackLocalClient = class {
3013
+ static {
3014
+ __name(this, "SnapBackLocalClient");
3015
+ }
3016
+ static {
3017
+ __name2(this, "SnapBackLocalClient");
3018
+ }
3019
+ connection;
3020
+ reconnectManager;
3021
+ state = "disconnected";
3022
+ nextId = 1;
3023
+ pending = /* @__PURE__ */ new Map();
3024
+ eventHandlers = {};
3025
+ options;
3026
+ /** Stored params from the last initialize() call — replayed after auto-reconnect */
3027
+ initializeParams = null;
3028
+ // Method namespaces - original
3029
+ health;
3030
+ session;
3031
+ snapshot;
3032
+ protection;
3033
+ detection;
3034
+ // Method namespaces - new domains
3035
+ learning;
3036
+ context;
3037
+ validation;
3038
+ violation;
3039
+ sync;
3040
+ intelligence;
3041
+ momentum;
3042
+ watch;
3043
+ mcp;
3044
+ supervisor;
3045
+ daemon;
3046
+ workspace;
3047
+ constructor(options = {}) {
3048
+ this.options = {
3049
+ socketPath: options.socketPath ?? getDefaultSocketPath(),
3050
+ timeout: options.timeout ?? 3e4,
3051
+ autoReconnect: options.autoReconnect ?? true,
3052
+ maxReconnectAttempts: options.maxReconnectAttempts ?? 5,
3053
+ reconnectDelay: options.reconnectDelay ?? 1e3,
3054
+ maxReconnectDelay: options.maxReconnectDelay ?? 3e4
3055
+ };
3056
+ this.connection = new IpcConnection({
3057
+ socketPath: this.options.socketPath,
3058
+ timeout: 5e3
3059
+ });
3060
+ this.reconnectManager = new ReconnectManager({
3061
+ maxAttempts: this.options.maxReconnectAttempts,
3062
+ initialDelay: this.options.reconnectDelay,
3063
+ maxDelay: this.options.maxReconnectDelay
3064
+ });
3065
+ const callFn = this.call.bind(this);
3066
+ this.health = createHealthMethods(callFn);
3067
+ this.session = createSessionMethods(callFn);
3068
+ this.snapshot = createSnapshotMethods(callFn);
3069
+ this.protection = createProtectionMethods(callFn);
3070
+ this.detection = createDetectionMethods(callFn);
3071
+ this.learning = createLearningMethods(callFn);
3072
+ this.context = createContextMethods(callFn);
3073
+ this.validation = createValidationMethods(callFn);
3074
+ this.violation = createViolationMethods(callFn);
3075
+ this.sync = createSyncMethods(callFn);
3076
+ this.intelligence = createIntelligenceMethods(callFn);
3077
+ this.momentum = createMomentumMethods(callFn);
3078
+ this.watch = createWatchMethods(callFn);
3079
+ this.mcp = createMCPMethods(callFn);
3080
+ this.supervisor = createSupervisorMethods(callFn);
3081
+ this.daemon = createDaemonMethods(callFn);
3082
+ this.workspace = createWorkspaceMethods(callFn);
3083
+ this.setupConnectionHandlers();
3084
+ }
3085
+ /**
3086
+ * Set up IPC connection event handlers
3087
+ */
3088
+ setupConnectionHandlers() {
3089
+ this.connection.on("message", (message) => {
3090
+ if ("method" in message && !("id" in message)) {
3091
+ const notification = message;
3092
+ this.emit("notification", notification.method, notification.params);
3093
+ return;
3094
+ }
3095
+ const response = message;
3096
+ if (response.id === null) {
3097
+ this.emit("error", new Error("Received error response with null id"));
3098
+ return;
3099
+ }
3100
+ const pending = this.pending.get(response.id);
3101
+ if (!pending) {
3102
+ this.emit("error", new Error(`Received response for unknown request ID: ${response.id}`));
3103
+ return;
3104
+ }
3105
+ clearTimeout(pending.timeout);
3106
+ this.pending.delete(response.id);
3107
+ if ("error" in response) {
3108
+ const error = new JsonRpcClientError(response.error.message, response.error.code, response.error.data);
3109
+ pending.reject(error);
3110
+ return;
3111
+ }
3112
+ pending.resolve(response.result);
3113
+ });
3114
+ this.connection.on("close", (error) => {
3115
+ this.setState("disconnected");
3116
+ this.emit("disconnected", error);
3117
+ for (const [, pending] of this.pending.entries()) {
3118
+ clearTimeout(pending.timeout);
3119
+ pending.reject(new Error("Connection closed"));
3120
+ }
3121
+ this.pending.clear();
3122
+ if (this.options.autoReconnect && !this.reconnectManager.isActive()) {
3123
+ this.attemptReconnect();
3124
+ }
3125
+ });
3126
+ this.connection.on("error", (error) => {
3127
+ this.emit("error", error);
3128
+ });
3129
+ }
3130
+ /**
3131
+ * Attempt to reconnect
3132
+ */
3133
+ async attemptReconnect() {
3134
+ this.setState("reconnecting");
3135
+ try {
3136
+ await this.reconnectManager.start(async () => {
3137
+ await this.connection.connect();
3138
+ if (this.initializeParams) {
3139
+ await this.initialize(this.initializeParams);
3140
+ }
3141
+ this.setState("connected");
3142
+ this.emit("connected");
3143
+ }, (attempt, maxAttempts) => {
3144
+ this.emit("reconnecting", attempt, maxAttempts);
3145
+ }, () => {
3146
+ this.emit("reconnectFailed");
3147
+ });
3148
+ } catch (error) {
3149
+ this.setState("disconnected");
3150
+ this.emit("error", error);
3151
+ }
3152
+ }
3153
+ /**
3154
+ * Connect to the local service
3155
+ */
3156
+ async connect() {
3157
+ if (this.state === "connected" || this.state === "connecting") {
3158
+ return;
3159
+ }
3160
+ this.setState("connecting");
3161
+ try {
3162
+ await this.connection.connect();
3163
+ this.setState("connected");
3164
+ this.emit("connected");
3165
+ } catch (error) {
3166
+ this.setState("disconnected");
3167
+ throw error;
3168
+ }
3169
+ }
3170
+ /**
3171
+ * Close the connection
3172
+ */
3173
+ close() {
3174
+ this.reconnectManager.stop();
3175
+ this.connection.close();
3176
+ this.setState("closed");
3177
+ for (const [, pending] of this.pending.entries()) {
3178
+ clearTimeout(pending.timeout);
3179
+ pending.reject(new Error("Client closed"));
3180
+ }
3181
+ this.pending.clear();
3182
+ }
3183
+ /**
3184
+ * Make a JSON-RPC method call
3185
+ */
3186
+ async call(method, params) {
3187
+ if (!this.connection.isConnected()) {
3188
+ throw new Error("Not connected to service");
3189
+ }
3190
+ const id = this.nextId++;
3191
+ const request = {
3192
+ jsonrpc: "2.0",
3193
+ id,
3194
+ method,
3195
+ params
3196
+ };
3197
+ return new Promise((resolve, reject) => {
3198
+ const timeout = setTimeout(() => {
3199
+ this.pending.delete(id);
3200
+ reject(new RequestTimeoutError(`Request timeout after ${this.options.timeout}ms`, id));
3201
+ }, this.options.timeout);
3202
+ this.pending.set(id, {
3203
+ resolve,
3204
+ reject,
3205
+ timeout
3206
+ });
3207
+ this.connection.send(request).catch((error) => {
3208
+ clearTimeout(timeout);
3209
+ this.pending.delete(id);
3210
+ reject(error);
3211
+ });
3212
+ });
3213
+ }
3214
+ /**
3215
+ * Initialize the client connection
3216
+ *
3217
+ * Must be called before any other method calls.
3218
+ * Params are stored and automatically replayed after auto-reconnect.
3219
+ */
3220
+ async initialize(params) {
3221
+ this.initializeParams = params;
3222
+ return this.call("initialize", params);
3223
+ }
3224
+ /**
3225
+ * Get current connection state
3226
+ */
3227
+ getState() {
3228
+ return this.state;
3229
+ }
3230
+ /**
3231
+ * Check if connected
3232
+ */
3233
+ isConnected() {
3234
+ return this.state === "connected";
3235
+ }
3236
+ /**
3237
+ * Register event listener
3238
+ */
3239
+ on(event, handler) {
3240
+ this.eventHandlers[event] = handler;
3241
+ }
3242
+ /**
3243
+ * Remove event listener
3244
+ */
3245
+ off(event) {
3246
+ delete this.eventHandlers[event];
3247
+ }
3248
+ /**
3249
+ * Set connection state and emit event
3250
+ */
3251
+ setState(state) {
3252
+ if (this.state !== state) {
3253
+ this.state = state;
3254
+ this.emit("stateChange", state);
3255
+ }
3256
+ }
3257
+ /**
3258
+ * Emit event to registered handlers
3259
+ */
3260
+ emit(event, ...args) {
3261
+ const handler = this.eventHandlers[event];
3262
+ if (handler) {
3263
+ handler(...args);
3264
+ }
3265
+ }
3266
+ };
3267
+
3268
+ // src/daemon/local-service-adapter.ts
3269
+ function getServicePidPath() {
3270
+ return join(homedir(), ".snapback", "service.pid");
3271
+ }
3272
+ __name(getServicePidPath, "getServicePidPath");
3273
+ function getServiceSocketPath() {
3274
+ return join(homedir(), ".snapback", "service.sock");
3275
+ }
3276
+ __name(getServiceSocketPath, "getServiceSocketPath");
3277
+ function isServiceRunning() {
3278
+ const pidPath = getServicePidPath();
3279
+ if (!existsSync(pidPath)) {
3280
+ return false;
3281
+ }
3282
+ try {
3283
+ const pid = Number.parseInt(readFileSync(pidPath, "utf-8").trim(), 10);
3284
+ if (Number.isNaN(pid)) {
3285
+ return false;
3286
+ }
3287
+ process.kill(pid, 0);
3288
+ return true;
3289
+ } catch {
3290
+ return false;
3291
+ }
3292
+ }
3293
+ __name(isServiceRunning, "isServiceRunning");
3294
+ function readServicePid() {
3295
+ const pidPath = getServicePidPath();
3296
+ try {
3297
+ const content = readFileSync(pidPath, "utf-8").trim();
3298
+ const pid = Number.parseInt(content, 10);
3299
+ return Number.isNaN(pid) ? null : pid;
3300
+ } catch {
3301
+ return null;
3302
+ }
3303
+ }
3304
+ __name(readServicePid, "readServicePid");
3305
+ function createServiceClient() {
3306
+ return new SnapBackLocalClient({
3307
+ timeout: 3e4,
3308
+ autoReconnect: false
3309
+ });
3310
+ }
3311
+ __name(createServiceClient, "createServiceClient");
3312
+ async function connectServiceClient(client) {
3313
+ await client.connect();
3314
+ await client.initialize({
3315
+ protocolVersion: "1.0.0",
3316
+ clientInfo: {
3317
+ name: "snapback-cli",
3318
+ version: "1.0.0"
3319
+ },
3320
+ capabilities: {
3321
+ notifications: false
3322
+ }
3323
+ });
3324
+ }
3325
+ __name(connectServiceClient, "connectServiceClient");
3326
+ async function pruneLearningsViaDaemon(workspaceRoot, client) {
3327
+ try {
3328
+ const result = await client.call("learning/prune", {
3329
+ workspace: workspaceRoot
3330
+ });
3331
+ return {
3332
+ success: true,
3333
+ result
3334
+ };
3335
+ } catch (error) {
3336
+ return {
3337
+ success: false,
3338
+ error: error instanceof Error ? error.message : String(error)
3339
+ };
3340
+ }
3341
+ }
3342
+ __name(pruneLearningsViaDaemon, "pruneLearningsViaDaemon");
3343
+ async function gcLearningsViaDaemon(workspaceRoot, client, options) {
3344
+ try {
3345
+ const result = await client.call("learning/gc", {
3346
+ workspace: workspaceRoot,
3347
+ operation: options?.operation ?? "all",
3348
+ dryRun: options?.dryRun ?? true
3349
+ });
3350
+ return {
3351
+ success: true,
3352
+ result
3353
+ };
3354
+ } catch (error) {
3355
+ return {
3356
+ success: false,
3357
+ error: error instanceof Error ? error.message : String(error)
3358
+ };
3359
+ }
3360
+ }
3361
+ __name(gcLearningsViaDaemon, "gcLearningsViaDaemon");
3362
+ async function listLearningsViaDaemon(workspaceRoot, client, limit) {
3363
+ try {
3364
+ const result = await client.call("learning/list", {
3365
+ workspace: workspaceRoot,
3366
+ limit: limit ?? 50
3367
+ });
3368
+ return {
3369
+ success: true,
3370
+ result
3371
+ };
3372
+ } catch (error) {
3373
+ return {
3374
+ success: false,
3375
+ error: error instanceof Error ? error.message : String(error)
3376
+ };
3377
+ }
3378
+ }
3379
+ __name(listLearningsViaDaemon, "listLearningsViaDaemon");
3380
+ async function searchLearningsViaDaemon(workspaceRoot, client, keywords, limit) {
3381
+ try {
3382
+ const result = await client.call("learning/search", {
3383
+ workspace: workspaceRoot,
3384
+ keywords,
3385
+ limit: limit ?? 10
3386
+ });
3387
+ return {
3388
+ success: true,
3389
+ result
3390
+ };
3391
+ } catch (error) {
3392
+ return {
3393
+ success: false,
3394
+ error: error instanceof Error ? error.message : String(error)
3395
+ };
3396
+ }
3397
+ }
3398
+ __name(searchLearningsViaDaemon, "searchLearningsViaDaemon");
3399
+ async function createSessionViaDaemon(client, params) {
3400
+ try {
3401
+ const result = await client.call("session/begin", {
3402
+ name: params?.name,
3403
+ workspacePath: params?.workspacePath,
3404
+ metadata: params?.metadata
3405
+ });
3406
+ return {
3407
+ success: true,
3408
+ result
3409
+ };
3410
+ } catch (error) {
3411
+ return {
3412
+ success: false,
3413
+ error: error instanceof Error ? error.message : String(error)
3414
+ };
3415
+ }
3416
+ }
3417
+ __name(createSessionViaDaemon, "createSessionViaDaemon");
3418
+ async function endSessionViaDaemon(client, sessionId) {
3419
+ try {
3420
+ await client.call("session/end-daemon", {
3421
+ sessionId
3422
+ });
3423
+ return {
3424
+ success: true
3425
+ };
3426
+ } catch (error) {
3427
+ return {
3428
+ success: false,
3429
+ error: error instanceof Error ? error.message : String(error)
3430
+ };
3431
+ }
3432
+ }
3433
+ __name(endSessionViaDaemon, "endSessionViaDaemon");
3434
+ async function getSessionStatusViaDaemon(client, sessionId) {
3435
+ try {
3436
+ const result = await client.call("session/status", {
3437
+ sessionId
3438
+ });
3439
+ return {
3440
+ success: true,
3441
+ result
3442
+ };
3443
+ } catch (error) {
3444
+ return {
3445
+ success: false,
3446
+ error: error instanceof Error ? error.message : String(error)
3447
+ };
3448
+ }
3449
+ }
3450
+ __name(getSessionStatusViaDaemon, "getSessionStatusViaDaemon");
3451
+ function formatDuration(ms) {
3452
+ const seconds = Math.floor(ms / 1e3);
3453
+ const minutes = Math.floor(seconds / 60);
3454
+ const hours = Math.floor(minutes / 60);
3455
+ const days = Math.floor(hours / 24);
3456
+ if (days > 0) {
3457
+ return `${days}d ${hours % 24}h`;
3458
+ }
3459
+ if (hours > 0) {
3460
+ return `${hours}h ${minutes % 60}m`;
3461
+ }
3462
+ if (minutes > 0) {
3463
+ return `${minutes}m ${seconds % 60}s`;
3464
+ }
3465
+ return `${seconds}s`;
3466
+ }
3467
+ __name(formatDuration, "formatDuration");
3468
+ function formatBytes(bytes) {
3469
+ const units = [
3470
+ "B",
3471
+ "KB",
3472
+ "MB",
3473
+ "GB"
3474
+ ];
3475
+ let unitIndex = 0;
3476
+ let value = bytes;
3477
+ while (value >= 1024 && unitIndex < units.length - 1) {
3478
+ value /= 1024;
3479
+ unitIndex++;
3480
+ }
3481
+ return `${value.toFixed(1)}${units[unitIndex]}`;
3482
+ }
3483
+ __name(formatBytes, "formatBytes");
3484
+ function getLogPath() {
3485
+ return join(homedir(), ".snapback", "daemon", "daemon.log");
3486
+ }
3487
+ __name(getLogPath, "getLogPath");
3488
+ var DAEMON_GENERATION = 2;
3489
+ function getDaemonVersion() {
3490
+ return {
3491
+ generation: DAEMON_GENERATION,
3492
+ version: "2.0.0"
3493
+ };
3494
+ }
3495
+ __name(getDaemonVersion, "getDaemonVersion");
3496
+
3497
+ export { DAEMON_GENERATION, SnapBackLocalClient, connectServiceClient, createServiceClient, createSessionViaDaemon, endSessionViaDaemon, formatBytes, formatDuration, gcLearningsViaDaemon, getDaemonVersion, getLogPath, getServicePidPath, getServiceSocketPath, getSessionStatusViaDaemon, isServiceRunning, listLearningsViaDaemon, pruneLearningsViaDaemon, readServicePid, searchLearningsViaDaemon };