@wrongstack/core 0.260.0 → 0.265.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/dist/{agent-bridge-BbskZ7HH.d.ts → agent-bridge-DrkBxszZ.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-BNIGZx18.d.ts → agent-subagent-runner-DM2pP-B6.d.ts} +116 -12
  3. package/dist/{brain-C2yDd7Lw.d.ts → brain-BXd_61kQ.d.ts} +32 -3
  4. package/dist/{compactor-t0R_AIt_.d.ts → compactor-B8pOf45Y.d.ts} +1 -1
  5. package/dist/{config-FG6As4H5.d.ts → config-BMCj_XDs.d.ts} +86 -12
  6. package/dist/{context-JFOVvu6z.d.ts → context-MRk5PhNv.d.ts} +26 -1
  7. package/dist/coordination/index.d.ts +1737 -15
  8. package/dist/coordination/index.js +3152 -494
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/{default-config-CXsDvOmP.d.ts → default-config-B0cj-Hry.d.ts} +11 -1
  11. package/dist/defaults/index.d.ts +28 -28
  12. package/dist/defaults/index.js +1804 -1363
  13. package/dist/defaults/index.js.map +1 -1
  14. package/dist/dispatcher-types.d-BBeXBQgS.d.ts +66 -0
  15. package/dist/execution/index.d.ts +16 -16
  16. package/dist/execution/index.js +933 -672
  17. package/dist/execution/index.js.map +1 -1
  18. package/dist/execution/prompt-enhancer.d.ts +1 -1
  19. package/dist/execution/prompt-enhancer.js +7 -1
  20. package/dist/execution/prompt-enhancer.js.map +1 -1
  21. package/dist/extension/index.d.ts +6 -6
  22. package/dist/extension/index.js.map +1 -1
  23. package/dist/{goal-preamble-B1IXJtLX.d.ts → goal-preamble-DvHDSKSe.d.ts} +26 -10
  24. package/dist/{goal-store-CPXz6Mml.d.ts → goal-store-DtLMySNb.d.ts} +1 -1
  25. package/dist/{index-CebbJB94.d.ts → index-B-ch8K9C.d.ts} +8 -8
  26. package/dist/{index-BPcg4N3M.d.ts → index-CEDeNodM.d.ts} +5 -5
  27. package/dist/index.d.ts +189 -104
  28. package/dist/index.js +24693 -21162
  29. package/dist/index.js.map +1 -1
  30. package/dist/infrastructure/index.d.ts +6 -6
  31. package/dist/infrastructure/index.js +12 -8
  32. package/dist/infrastructure/index.js.map +1 -1
  33. package/dist/kernel/index.d.ts +9 -9
  34. package/dist/kernel/index.js +7 -2
  35. package/dist/kernel/index.js.map +1 -1
  36. package/dist/{llm-selector-DXxI2tlu.d.ts → llm-selector-C0tfTCUe.d.ts} +14 -2
  37. package/dist/{mcp-servers-OwNHo43-.d.ts → mcp-servers-2x4w6Jn9.d.ts} +3 -3
  38. package/dist/models/index.d.ts +5 -5
  39. package/dist/models/index.js +80 -31
  40. package/dist/models/index.js.map +1 -1
  41. package/dist/{models-registry-Djlmq4uB.d.ts → models-registry-DmJlKuNp.d.ts} +1 -1
  42. package/dist/{multi-agent-coordinator-CEmrSCMJ.d.ts → multi-agent-coordinator-DyCkCZnU.d.ts} +2 -2
  43. package/dist/{null-fleet-bus-DT92xqgJ.d.ts → null-fleet-bus-CG9QY2aP.d.ts} +6 -6
  44. package/dist/observability/index.d.ts +2 -2
  45. package/dist/observability/index.js +8 -3
  46. package/dist/observability/index.js.map +1 -1
  47. package/dist/{parallel-eternal-engine-0SItuq5r.d.ts → parallel-eternal-engine-Jw9uhEoT.d.ts} +9 -9
  48. package/dist/{path-resolver-DKBh6Jlo.d.ts → path-resolver-Dy2ej-gE.d.ts} +3 -3
  49. package/dist/{permission-BJ7eO9Vl.d.ts → permission-B9SB45lp.d.ts} +1 -1
  50. package/dist/{permission-policy-DEXOfnpm.d.ts → permission-policy-CkjSXabK.d.ts} +2 -2
  51. package/dist/{pipeline-zflkI2dp.d.ts → pipeline-DPDxH_7m.d.ts} +59 -4
  52. package/dist/{plan-templates-BFXyRkEK.d.ts → plan-templates-CzD9GnAU.d.ts} +32 -8
  53. package/dist/{provider-runner-BC-uywtT.d.ts → provider-runner-DMa70ODu.d.ts} +3 -3
  54. package/dist/{retry-policy-Cavrzmtk.d.ts → retry-policy-CN0khdlj.d.ts} +1 -1
  55. package/dist/sdd/index.d.ts +8 -8
  56. package/dist/sdd/index.js +313 -122
  57. package/dist/sdd/index.js.map +1 -1
  58. package/dist/{secret-vault-CDvDYXWX.d.ts → secret-vault-B2yw84VT.d.ts} +43 -4
  59. package/dist/secret-vault-BAKpgFw_.d.ts +57 -0
  60. package/dist/security/index.d.ts +5 -5
  61. package/dist/security/index.js +411 -225
  62. package/dist/security/index.js.map +1 -1
  63. package/dist/{selector-B7AivHsu.d.ts → selector-CzHh_igB.d.ts} +1 -1
  64. package/dist/{session-event-bridge-BmIDxdJd.d.ts → session-event-bridge-BUI6Jf-4.d.ts} +8 -2
  65. package/dist/{session-reader-DtofsB-2.d.ts → session-reader-CMgdMSRP.d.ts} +1 -1
  66. package/dist/skills/index.js +67 -64
  67. package/dist/skills/index.js.map +1 -1
  68. package/dist/storage/index.d.ts +132 -16
  69. package/dist/storage/index.js +851 -432
  70. package/dist/storage/index.js.map +1 -1
  71. package/dist/tools/index.d.ts +57 -0
  72. package/dist/tools/index.js +411 -0
  73. package/dist/tools/index.js.map +1 -0
  74. package/dist/types/index.d.ts +21 -21
  75. package/dist/types/index.js +928 -711
  76. package/dist/types/index.js.map +1 -1
  77. package/dist/utils/error.d.ts +7 -0
  78. package/dist/utils/error.js +8 -0
  79. package/dist/utils/error.js.map +1 -0
  80. package/dist/utils/index.d.ts +8 -68
  81. package/dist/utils/index.js +20 -10
  82. package/dist/utils/index.js.map +1 -1
  83. package/dist/{wstack-paths-CJjEwPXn.d.ts → wstack-paths-hOpNLmvf.d.ts} +2 -0
  84. package/package.json +5 -1
  85. package/skills/api-design/SKILL.md +1 -1
  86. package/skills/audit-log/SKILL.md +6 -6
  87. package/skills/bug-hunter/SKILL.md +5 -5
  88. package/skills/chimera/SKILL.md +4 -4
  89. package/skills/docker-deploy/SKILL.md +1 -1
  90. package/skills/git-flow/SKILL.md +3 -3
  91. package/skills/multi-agent/SKILL.md +3 -3
  92. package/skills/node-modern/SKILL.md +1 -0
  93. package/skills/observability/SKILL.md +2 -2
  94. package/skills/output-standards/SKILL.md +51 -28
  95. package/skills/refactor-planner/SKILL.md +3 -3
  96. package/skills/security-scanner/SKILL.md +4 -3
  97. package/skills/tech-stack/SKILL.md +1 -2
  98. package/dist/package-outdated-watcher-C70ag2G9.d.ts +0 -581
  99. package/dist/secret-vault-BJDY28ev.d.ts +0 -25
@@ -44,6 +44,27 @@ var PATTERNS = [
44
44
  { type: "postgres_uri", regex: /postgres(?:ql)?:\/\/[^\s"'`]+/g },
45
45
  { type: "mysql_uri", regex: /mysql:\/\/[^\s"'`]+/g },
46
46
  { type: "redis_uri", regex: /redis:\/\/[^\s"'`]+/g },
47
+ // AI/ML provider keys — modern LLM services with well-known prefixes
48
+ {
49
+ type: "huggingface_token",
50
+ // HuggingFace tokens: hf_ followed by 34 alphanumeric chars
51
+ regex: /(?<![A-Za-z0-9])hf_[A-Za-z0-9]{34}(?![A-Za-z0-9])/g
52
+ },
53
+ {
54
+ type: "replicate_token",
55
+ // Replicate tokens: r8_ followed by 40+ alphanumeric chars
56
+ regex: /(?<![A-Za-z0-9])r8_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g
57
+ },
58
+ {
59
+ type: "perplexity_key",
60
+ // Perplexity API keys: pplx- followed by 40+ alphanumeric chars
61
+ regex: /(?<![A-Za-z0-9])pplx-[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g
62
+ },
63
+ {
64
+ type: "groq_key",
65
+ // Groq API keys: gsk_ followed by 40+ alphanumeric chars
66
+ regex: /(?<![A-Za-z0-9])gsk_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g
67
+ },
47
68
  {
48
69
  type: "bearer_token",
49
70
  // Anchored with alternation instead of negative lookahead — avoids V8
@@ -77,6 +98,10 @@ function hasCredentialAnchors(text) {
77
98
  text.includes("xox") || // Slack token (xoxa/xoxb/xoxp/xoxo/xoxs)
78
99
  text.includes("Bearer ") || // Bearer token (space suffix reduces false positives)
79
100
  text.includes("/bot") || // Telegram bot token (URL path pattern)
101
+ text.includes("hf_") || // HuggingFace token
102
+ text.includes("r8_") || // Replicate token
103
+ text.includes("pplx-") || // Perplexity API key
104
+ text.includes("gsk_") || // Groq API key
80
105
  text.includes("_KEY=") || // High-entropy env vars: API_KEY=, SECRET_KEY=, ...
81
106
  text.includes("_TOKEN=") || // ACCESS_TOKEN=, AUTH_TOKEN=, ...
82
107
  text.includes("_SECRET=") || // API_SECRET=, CLIENT_SECRET=, ...
@@ -143,108 +168,6 @@ var DefaultSecretScrubber = class {
143
168
  return visit(obj);
144
169
  }
145
170
  };
146
-
147
- // src/types/errors.ts
148
- var ERROR_CODES = {
149
- // Provider
150
- PROVIDER_RATE_LIMITED: "PROVIDER_RATE_LIMITED",
151
- PROVIDER_AUTH_FAILED: "PROVIDER_AUTH_FAILED",
152
- PROVIDER_OVERLOADED: "PROVIDER_OVERLOADED",
153
- PROVIDER_INVALID_REQUEST: "PROVIDER_INVALID_REQUEST",
154
- PROVIDER_SERVER_ERROR: "PROVIDER_SERVER_ERROR",
155
- PROVIDER_NETWORK_ERROR: "PROVIDER_NETWORK_ERROR",
156
- PROVIDER_CONTEXT_OVERFLOW: "PROVIDER_CONTEXT_OVERFLOW",
157
- // Tool
158
- TOOL_NOT_FOUND: "TOOL_NOT_FOUND",
159
- TOOL_PERMISSION_DENIED: "TOOL_PERMISSION_DENIED",
160
- TOOL_EXECUTION_FAILED: "TOOL_EXECUTION_FAILED",
161
- TOOL_TIMEOUT: "TOOL_TIMEOUT",
162
- TOOL_INPUT_INVALID: "TOOL_INPUT_INVALID",
163
- // Config
164
- CONFIG_INVALID: "CONFIG_INVALID",
165
- CONFIG_NOT_FOUND: "CONFIG_NOT_FOUND",
166
- CONFIG_PARSE_FAILED: "CONFIG_PARSE_FAILED",
167
- CONFIG_MIGRATION_NEEDED: "CONFIG_MIGRATION_NEEDED",
168
- // Plugin
169
- PLUGIN_LOAD_FAILED: "PLUGIN_LOAD_FAILED",
170
- PLUGIN_API_MISMATCH: "PLUGIN_API_MISMATCH",
171
- PLUGIN_MISSING_DEPENDENCY: "PLUGIN_MISSING_DEPENDENCY",
172
- // Agent
173
- AGENT_ITERATION_LIMIT: "AGENT_ITERATION_LIMIT",
174
- AGENT_CONTEXT_OVERFLOW: "AGENT_CONTEXT_OVERFLOW",
175
- AGENT_ABORTED: "AGENT_ABORTED",
176
- AGENT_RUN_FAILED: "AGENT_RUN_FAILED",
177
- // Session
178
- SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
179
- SESSION_CORRUPTED: "SESSION_CORRUPTED",
180
- SESSION_WRITE_FAILED: "SESSION_WRITE_FAILED",
181
- // Container / Registry
182
- CONTAINER_TOKEN_ALREADY_BOUND: "CONTAINER_TOKEN_ALREADY_BOUND",
183
- CONTAINER_TOKEN_NOT_BOUND: "CONTAINER_TOKEN_NOT_BOUND",
184
- CONTAINER_CIRCULAR_DEPENDENCY: "CONTAINER_CIRCULAR_DEPENDENCY",
185
- REGISTRY_DUPLICATE: "REGISTRY_DUPLICATE",
186
- REGISTRY_NOT_FOUND: "REGISTRY_NOT_FOUND",
187
- REGISTRY_INVALID: "REGISTRY_INVALID",
188
- // File system
189
- FS_READ_FAILED: "FS_READ_FAILED",
190
- FS_WRITE_FAILED: "FS_WRITE_FAILED",
191
- FS_MKDIR_FAILED: "FS_MKDIR_FAILED",
192
- FS_DELETE_FAILED: "FS_DELETE_FAILED",
193
- FS_ATOMIC_WRITE_FAILED: "FS_ATOMIC_WRITE_FAILED",
194
- // SDD (Spec-Driven Development)
195
- SDD_VALIDATION_FAILED: "SDD_VALIDATION_FAILED",
196
- SDD_PARSE_FAILED: "SDD_PARSE_FAILED",
197
- SDD_INVALID_STATE: "SDD_INVALID_STATE",
198
- SDD_NOT_READY: "SDD_NOT_READY",
199
- // General
200
- VALIDATION_ERROR: "VALIDATION_ERROR",
201
- UNKNOWN: "UNKNOWN"
202
- };
203
- var WrongStackError = class extends Error {
204
- code;
205
- subsystem;
206
- severity;
207
- recoverable;
208
- context;
209
- constructor(opts) {
210
- super(opts.message, { cause: opts.cause });
211
- this.name = "WrongStackError";
212
- this.code = opts.code;
213
- this.subsystem = opts.subsystem;
214
- this.severity = opts.severity ?? "error";
215
- this.recoverable = opts.recoverable ?? false;
216
- this.context = opts.context;
217
- }
218
- /**
219
- * Render a one-line user-facing description.
220
- * Subclasses should override for domain-specific formatting.
221
- */
222
- describe() {
223
- const ctx = this.context ? ` ${formatContext(this.context)}` : "";
224
- return `${this.code}: ${this.message}${ctx}`;
225
- }
226
- };
227
- function formatContext(ctx) {
228
- const parts = Object.entries(ctx).filter(([, v]) => v !== void 0).slice(0, 3).map(([k, v]) => `${k}=${String(v)}`);
229
- return parts.length > 0 ? `[${parts.join(" ")}]` : "";
230
- }
231
- var ConfigError = class extends WrongStackError {
232
- constructor(opts) {
233
- super({
234
- message: opts.message,
235
- code: opts.code,
236
- subsystem: "config",
237
- severity: "fatal",
238
- recoverable: false,
239
- context: opts.context,
240
- cause: opts.cause
241
- });
242
- this.name = "ConfigError";
243
- }
244
- };
245
-
246
- // src/types/secret-vault.ts
247
- var ENCRYPTED_PREFIX = "enc:v1:";
248
171
  async function atomicWrite(targetPath, content, opts = {}) {
249
172
  const dir = path3.dirname(targetPath);
250
173
  await fs.mkdir(dir, { recursive: true });
@@ -307,6 +230,112 @@ async function renameWithRetry(from, to) {
307
230
  throw lastErr;
308
231
  }
309
232
 
233
+ // src/utils/error.ts
234
+ function toErrorMessage(err) {
235
+ return err instanceof Error ? err.message : String(err);
236
+ }
237
+
238
+ // src/utils/safe-json.ts
239
+ function safeParse(input, maxBytes = 5e6) {
240
+ if (input.length > maxBytes) {
241
+ return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
242
+ }
243
+ try {
244
+ return { ok: true, value: JSON.parse(input) };
245
+ } catch (err) {
246
+ return {
247
+ ok: false,
248
+ error: toErrorMessage(err)
249
+ };
250
+ }
251
+ }
252
+
253
+ // src/utils/expect-defined.ts
254
+ function expectDefined(value, label) {
255
+ if (value === null || value === void 0) {
256
+ const err = new Error("Expected value to be defined");
257
+ err.name = "ExpectDefinedError";
258
+ throw err;
259
+ }
260
+ return value;
261
+ }
262
+
263
+ // src/utils/glob-match.ts
264
+ function escapeRegex(s) {
265
+ return s.replace(/[.+^${}()|\\]/g, "\\$&");
266
+ }
267
+ var COMPILED_GLOB_CACHE = /* @__PURE__ */ new Map();
268
+ var CACHE_MAX_SIZE = 2e3;
269
+ function getCachedGlob(pattern) {
270
+ const cached = COMPILED_GLOB_CACHE.get(pattern);
271
+ if (cached) return cached;
272
+ if (COMPILED_GLOB_CACHE.size >= CACHE_MAX_SIZE) {
273
+ const keys = [...COMPILED_GLOB_CACHE.keys()];
274
+ for (let i = 0; i < Math.floor(CACHE_MAX_SIZE / 4); i++) {
275
+ COMPILED_GLOB_CACHE.delete(expectDefined(keys[i]));
276
+ }
277
+ }
278
+ const re = compileGlob(pattern);
279
+ COMPILED_GLOB_CACHE.set(pattern, re);
280
+ return re;
281
+ }
282
+ var MAX_GLOB_PATTERN_LEN = 1024;
283
+ function compileGlob(pattern) {
284
+ if (pattern.length > MAX_GLOB_PATTERN_LEN) {
285
+ throw new Error(`Glob pattern exceeds ${MAX_GLOB_PATTERN_LEN} characters`);
286
+ }
287
+ let i = 0;
288
+ let re = "^";
289
+ while (i < pattern.length) {
290
+ const c = pattern[i];
291
+ if (c === "*") {
292
+ if (pattern[i + 1] === "*") {
293
+ re += ".*";
294
+ i += 2;
295
+ if (pattern[i] === "/") i++;
296
+ } else {
297
+ re += "[^/]*";
298
+ i++;
299
+ }
300
+ } else if (c === "?") {
301
+ re += "[^/]";
302
+ i++;
303
+ } else if (c === "[") {
304
+ let cls = "[";
305
+ i++;
306
+ if (pattern[i] === "!" || pattern[i] === "^") {
307
+ cls += "^";
308
+ i++;
309
+ }
310
+ while (i < pattern.length && pattern[i] !== "]") {
311
+ const ch = pattern[i] ?? "";
312
+ if (ch === "\\") {
313
+ cls += "\\\\";
314
+ } else if (ch === "]" || ch === "^") {
315
+ cls += `\\${ch}`;
316
+ } else {
317
+ cls += ch;
318
+ }
319
+ i++;
320
+ }
321
+ cls += "]";
322
+ re += cls;
323
+ i++;
324
+ } else {
325
+ re += escapeRegex(c ?? "");
326
+ i++;
327
+ }
328
+ }
329
+ re += "$";
330
+ return new RegExp(re);
331
+ }
332
+ function matchGlob(pattern, input) {
333
+ return getCachedGlob(pattern).test(input);
334
+ }
335
+ function matchAny(patterns, input) {
336
+ return patterns.some((p) => matchGlob(p, input));
337
+ }
338
+
310
339
  // src/utils/deep-merge.ts
311
340
  var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
312
341
  "__proto__",
@@ -366,12 +395,119 @@ function deepMerge(base, patch, options = {}) {
366
395
  return out;
367
396
  }
368
397
 
398
+ // src/types/errors.ts
399
+ var ERROR_CODES = {
400
+ // Provider
401
+ PROVIDER_RATE_LIMITED: "PROVIDER_RATE_LIMITED",
402
+ PROVIDER_AUTH_FAILED: "PROVIDER_AUTH_FAILED",
403
+ PROVIDER_OVERLOADED: "PROVIDER_OVERLOADED",
404
+ PROVIDER_INVALID_REQUEST: "PROVIDER_INVALID_REQUEST",
405
+ PROVIDER_SERVER_ERROR: "PROVIDER_SERVER_ERROR",
406
+ PROVIDER_NETWORK_ERROR: "PROVIDER_NETWORK_ERROR",
407
+ PROVIDER_CONTEXT_OVERFLOW: "PROVIDER_CONTEXT_OVERFLOW",
408
+ // Tool
409
+ TOOL_NOT_FOUND: "TOOL_NOT_FOUND",
410
+ TOOL_PERMISSION_DENIED: "TOOL_PERMISSION_DENIED",
411
+ TOOL_EXECUTION_FAILED: "TOOL_EXECUTION_FAILED",
412
+ TOOL_TIMEOUT: "TOOL_TIMEOUT",
413
+ TOOL_INPUT_INVALID: "TOOL_INPUT_INVALID",
414
+ // Config
415
+ CONFIG_INVALID: "CONFIG_INVALID",
416
+ CONFIG_NOT_FOUND: "CONFIG_NOT_FOUND",
417
+ CONFIG_PARSE_FAILED: "CONFIG_PARSE_FAILED",
418
+ CONFIG_MIGRATION_NEEDED: "CONFIG_MIGRATION_NEEDED",
419
+ // Plugin
420
+ PLUGIN_LOAD_FAILED: "PLUGIN_LOAD_FAILED",
421
+ PLUGIN_API_MISMATCH: "PLUGIN_API_MISMATCH",
422
+ PLUGIN_MISSING_DEPENDENCY: "PLUGIN_MISSING_DEPENDENCY",
423
+ // Agent
424
+ AGENT_ITERATION_LIMIT: "AGENT_ITERATION_LIMIT",
425
+ AGENT_CONTEXT_OVERFLOW: "AGENT_CONTEXT_OVERFLOW",
426
+ AGENT_ABORTED: "AGENT_ABORTED",
427
+ AGENT_RUN_FAILED: "AGENT_RUN_FAILED",
428
+ // Session
429
+ SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
430
+ SESSION_CORRUPTED: "SESSION_CORRUPTED",
431
+ SESSION_WRITE_FAILED: "SESSION_WRITE_FAILED",
432
+ // Container / Registry
433
+ CONTAINER_TOKEN_ALREADY_BOUND: "CONTAINER_TOKEN_ALREADY_BOUND",
434
+ CONTAINER_TOKEN_NOT_BOUND: "CONTAINER_TOKEN_NOT_BOUND",
435
+ CONTAINER_CIRCULAR_DEPENDENCY: "CONTAINER_CIRCULAR_DEPENDENCY",
436
+ REGISTRY_DUPLICATE: "REGISTRY_DUPLICATE",
437
+ REGISTRY_NOT_FOUND: "REGISTRY_NOT_FOUND",
438
+ REGISTRY_INVALID: "REGISTRY_INVALID",
439
+ // File system
440
+ FS_READ_FAILED: "FS_READ_FAILED",
441
+ FS_WRITE_FAILED: "FS_WRITE_FAILED",
442
+ FS_MKDIR_FAILED: "FS_MKDIR_FAILED",
443
+ FS_DELETE_FAILED: "FS_DELETE_FAILED",
444
+ FS_ATOMIC_WRITE_FAILED: "FS_ATOMIC_WRITE_FAILED",
445
+ // SDD (Spec-Driven Development)
446
+ SDD_VALIDATION_FAILED: "SDD_VALIDATION_FAILED",
447
+ SDD_PARSE_FAILED: "SDD_PARSE_FAILED",
448
+ SDD_INVALID_STATE: "SDD_INVALID_STATE",
449
+ SDD_NOT_READY: "SDD_NOT_READY",
450
+ // General
451
+ VALIDATION_ERROR: "VALIDATION_ERROR",
452
+ UNKNOWN: "UNKNOWN"
453
+ };
454
+ var WrongStackError = class extends Error {
455
+ code;
456
+ subsystem;
457
+ severity;
458
+ recoverable;
459
+ context;
460
+ constructor(opts) {
461
+ super(opts.message, { cause: opts.cause });
462
+ this.name = "WrongStackError";
463
+ this.code = opts.code;
464
+ this.subsystem = opts.subsystem;
465
+ this.severity = opts.severity ?? "error";
466
+ this.recoverable = opts.recoverable ?? false;
467
+ this.context = opts.context;
468
+ }
469
+ /**
470
+ * Render a one-line user-facing description.
471
+ * Subclasses should override for domain-specific formatting.
472
+ */
473
+ describe() {
474
+ const ctx = this.context ? ` ${formatContext(this.context)}` : "";
475
+ return `${this.code}: ${this.message}${ctx}`;
476
+ }
477
+ };
478
+ function formatContext(ctx) {
479
+ const parts = Object.entries(ctx).filter(([, v]) => v !== void 0).slice(0, 3).map(([k, v]) => `${k}=${String(v)}`);
480
+ return parts.length > 0 ? `[${parts.join(" ")}]` : "";
481
+ }
482
+ var ConfigError = class extends WrongStackError {
483
+ constructor(opts) {
484
+ super({
485
+ message: opts.message,
486
+ code: opts.code,
487
+ subsystem: "config",
488
+ severity: "fatal",
489
+ recoverable: false,
490
+ context: opts.context,
491
+ cause: opts.cause
492
+ });
493
+ this.name = "ConfigError";
494
+ }
495
+ };
496
+
497
+ // src/types/secret-vault.ts
498
+ var ENCRYPTED_PREFIX_PATTERN = /^enc:v(\d+):/;
499
+ function encryptedPrefixForVersion(version) {
500
+ return `enc:v${version}:`;
501
+ }
502
+
369
503
  // src/security/secret-vault.ts
370
504
  var KEY_BYTES = 32;
371
505
  var IV_BYTES = 12;
372
506
  var TAG_BYTES = 16;
373
507
  var ALGO = "aes-256-gcm";
374
508
  var KEY_FILE_MODE = 384;
509
+ var KEY_FILE_MAGIC = Buffer.from("WSKV", "ascii");
510
+ var VERSIONED_KEY_FILE_SIZE = KEY_FILE_MAGIC.length + 1 + KEY_BYTES;
375
511
  function checkKeyFilePermissions(keyFile) {
376
512
  if (process.platform === "win32") return;
377
513
  try {
@@ -394,11 +530,17 @@ function checkKeyFilePermissions(keyFile) {
394
530
  var DefaultSecretVault = class {
395
531
  keyFile;
396
532
  key;
533
+ _keyVersion = 1;
397
534
  constructor(opts) {
398
535
  this.keyFile = opts.keyFile;
399
536
  }
537
+ /** Current key version. Starts at 1; incremented by rotateKey(). */
538
+ get keyVersion() {
539
+ if (!this.key) this.loadOrCreateKey();
540
+ return this._keyVersion;
541
+ }
400
542
  isEncrypted(value) {
401
- return typeof value === "string" && value.startsWith(ENCRYPTED_PREFIX);
543
+ return typeof value === "string" && ENCRYPTED_PREFIX_PATTERN.test(value);
402
544
  }
403
545
  encrypt(plaintext) {
404
546
  if (this.isEncrypted(plaintext)) return plaintext;
@@ -407,11 +549,20 @@ var DefaultSecretVault = class {
407
549
  const cipher = createCipheriv(ALGO, key, iv);
408
550
  const ct = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
409
551
  const tag = cipher.getAuthTag();
410
- return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${tag.toString("base64")}:${ct.toString("base64")}`;
552
+ const prefix = encryptedPrefixForVersion(this._keyVersion);
553
+ return `${prefix}${iv.toString("base64")}:${tag.toString("base64")}:${ct.toString("base64")}`;
411
554
  }
412
555
  decrypt(value) {
413
556
  if (!this.isEncrypted(value)) return value;
414
- const rest = value.slice(ENCRYPTED_PREFIX.length);
557
+ const prefixMatch = value.match(ENCRYPTED_PREFIX_PATTERN);
558
+ if (!prefixMatch) {
559
+ throw new ConfigError({
560
+ message: "SecretVault: malformed encrypted value",
561
+ code: ERROR_CODES.CONFIG_PARSE_FAILED,
562
+ context: { field: "encrypted_value" }
563
+ });
564
+ }
565
+ const rest = value.slice(prefixMatch[0].length);
415
566
  const parts = rest.split(":");
416
567
  if (parts.length !== 3) {
417
568
  throw new ConfigError({
@@ -440,20 +591,64 @@ var DefaultSecretVault = class {
440
591
  const pt = Buffer.concat([decipher.update(ct), decipher.final()]);
441
592
  return pt.toString("utf8");
442
593
  }
594
+ /**
595
+ * Generate a new encryption key, write it to disk, and increment the key version.
596
+ * After rotation, encrypt() emits the new version prefix (e.g. enc:v2:).
597
+ * The caller must re-encrypt existing config values (see rotateConfigKeys()).
598
+ */
599
+ rotateKey() {
600
+ const oldVersion = this._keyVersion;
601
+ const newKey = randomBytes(KEY_BYTES);
602
+ const newVersion = oldVersion + 1;
603
+ const keyFileBuf = Buffer.alloc(VERSIONED_KEY_FILE_SIZE);
604
+ KEY_FILE_MAGIC.copy(keyFileBuf, 0);
605
+ keyFileBuf[KEY_FILE_MAGIC.length] = newVersion;
606
+ newKey.copy(keyFileBuf, KEY_FILE_MAGIC.length + 1);
607
+ fs2.mkdirSync(path3.dirname(this.keyFile), { recursive: true });
608
+ fs2.writeFileSync(this.keyFile, keyFileBuf, { mode: 384 });
609
+ checkKeyFilePermissions(this.keyFile);
610
+ this.key = newKey;
611
+ this._keyVersion = newVersion;
612
+ return { oldVersion, newVersion };
613
+ }
443
614
  loadOrCreateKey() {
444
615
  if (this.key) return this.key;
445
616
  try {
446
617
  const buf = fs2.readFileSync(this.keyFile);
447
- if (buf.length !== KEY_BYTES) {
448
- throw new ConfigError({
449
- message: `SecretVault: key file ${this.keyFile} is ${buf.length} bytes (expected ${KEY_BYTES}). Remove it manually to generate a new key.`,
450
- code: ERROR_CODES.CONFIG_INVALID,
451
- context: { keyFile: this.keyFile, expectedBytes: KEY_BYTES, actualBytes: buf.length }
452
- });
618
+ if (buf.length === KEY_BYTES) {
619
+ this.key = buf;
620
+ this._keyVersion = 1;
621
+ checkKeyFilePermissions(this.keyFile);
622
+ return this.key;
453
623
  }
454
- this.key = buf;
455
- checkKeyFilePermissions(this.keyFile);
456
- return this.key;
624
+ if (buf.length === VERSIONED_KEY_FILE_SIZE) {
625
+ const magic = buf.subarray(0, KEY_FILE_MAGIC.length);
626
+ if (!magic.equals(KEY_FILE_MAGIC)) {
627
+ throw new ConfigError({
628
+ message: `SecretVault: key file ${this.keyFile} has invalid magic header`,
629
+ code: ERROR_CODES.CONFIG_INVALID,
630
+ context: { keyFile: this.keyFile }
631
+ });
632
+ }
633
+ const version = buf[KEY_FILE_MAGIC.length];
634
+ const key2 = buf.subarray(KEY_FILE_MAGIC.length + 1);
635
+ if (key2.length !== KEY_BYTES) {
636
+ throw new ConfigError({
637
+ message: `SecretVault: key file ${this.keyFile} has wrong key size (${key2.length} bytes, expected ${KEY_BYTES})`,
638
+ code: ERROR_CODES.CONFIG_INVALID,
639
+ context: { keyFile: this.keyFile, expectedBytes: KEY_BYTES, actualBytes: key2.length }
640
+ });
641
+ }
642
+ this.key = Buffer.from(key2);
643
+ this._keyVersion = version;
644
+ checkKeyFilePermissions(this.keyFile);
645
+ return this.key;
646
+ }
647
+ throw new ConfigError({
648
+ message: `SecretVault: key file ${this.keyFile} is ${buf.length} bytes (expected ${KEY_BYTES} for v1 or ${VERSIONED_KEY_FILE_SIZE} for v2+). Remove it manually to generate a new key.`,
649
+ code: ERROR_CODES.CONFIG_INVALID,
650
+ context: { keyFile: this.keyFile, expectedBytes: KEY_BYTES, actualBytes: buf.length }
651
+ });
457
652
  } catch (err) {
458
653
  if (err.code !== "ENOENT") throw err;
459
654
  }
@@ -464,18 +659,36 @@ var DefaultSecretVault = class {
464
659
  } catch (err) {
465
660
  if (err.code !== "EEXIST") throw err;
466
661
  const buf = fs2.readFileSync(this.keyFile);
467
- if (buf.length !== KEY_BYTES) {
468
- throw new ConfigError({
469
- message: `SecretVault: key file ${this.keyFile} is ${buf.length} bytes (expected ${KEY_BYTES}). Remove it manually to generate a new key.`,
470
- code: ERROR_CODES.CONFIG_INVALID,
471
- context: { keyFile: this.keyFile, expectedBytes: KEY_BYTES, actualBytes: buf.length }
472
- });
662
+ if (buf.length === KEY_BYTES) {
663
+ this.key = buf;
664
+ this._keyVersion = 1;
665
+ checkKeyFilePermissions(this.keyFile);
666
+ return this.key;
667
+ }
668
+ if (buf.length === VERSIONED_KEY_FILE_SIZE) {
669
+ const magic = buf.subarray(0, KEY_FILE_MAGIC.length);
670
+ if (!magic.equals(KEY_FILE_MAGIC)) {
671
+ throw new ConfigError({
672
+ message: `SecretVault: key file ${this.keyFile} has invalid magic header`,
673
+ code: ERROR_CODES.CONFIG_INVALID,
674
+ context: { keyFile: this.keyFile }
675
+ });
676
+ }
677
+ const version = buf[KEY_FILE_MAGIC.length];
678
+ const winnerKey = buf.subarray(KEY_FILE_MAGIC.length + 1);
679
+ this.key = Buffer.from(winnerKey);
680
+ this._keyVersion = version;
681
+ checkKeyFilePermissions(this.keyFile);
682
+ return this.key;
473
683
  }
474
- this.key = buf;
475
- checkKeyFilePermissions(this.keyFile);
476
- return this.key;
684
+ throw new ConfigError({
685
+ message: `SecretVault: key file ${this.keyFile} is ${buf.length} bytes (expected ${KEY_BYTES} for v1 or ${VERSIONED_KEY_FILE_SIZE} for v2+). Remove it manually to generate a new key.`,
686
+ code: ERROR_CODES.CONFIG_INVALID,
687
+ context: { keyFile: this.keyFile, expectedBytes: KEY_BYTES, actualBytes: buf.length }
688
+ });
477
689
  }
478
690
  this.key = key;
691
+ this._keyVersion = 1;
479
692
  return key;
480
693
  }
481
694
  };
@@ -556,6 +769,80 @@ async function migratePlaintextSecrets(configPath, vault, logger) {
556
769
  );
557
770
  return { migrated: counter.n, file: configPath };
558
771
  }
772
+ async function rotateConfigKeys(configPath, vault, logger) {
773
+ const log = logger?.info ?? (() => {
774
+ });
775
+ const warn = logger?.warn ?? ((msg) => console.warn(msg));
776
+ let raw;
777
+ try {
778
+ raw = await fs.readFile(configPath, "utf8");
779
+ } catch {
780
+ const { oldVersion: oldVersion2, newVersion: newVersion2 } = vault.rotateKey();
781
+ log(`[secret-vault] Key rotated (v${oldVersion2} \u2192 v${newVersion2}) \u2014 no config file to re-encrypt`);
782
+ return { rotated: 0, oldVersion: oldVersion2, newVersion: newVersion2, file: configPath };
783
+ }
784
+ let parsed;
785
+ try {
786
+ parsed = JSON.parse(raw);
787
+ } catch {
788
+ warn(`[secret-vault] Config file ${configPath} is not valid JSON \u2014 skipping rotation`);
789
+ return { rotated: 0, oldVersion: vault.keyVersion, newVersion: vault.keyVersion, file: configPath };
790
+ }
791
+ const counter = { n: 0 };
792
+ const decrypted = walkDecryptCount(parsed, vault, counter);
793
+ if (counter.n === 0) {
794
+ const { oldVersion: oldVersion2, newVersion: newVersion2 } = vault.rotateKey();
795
+ log(`[secret-vault] Key rotated (v${oldVersion2} \u2192 v${newVersion2}) \u2014 no encrypted fields to re-encrypt`);
796
+ return { rotated: 0, oldVersion: oldVersion2, newVersion: newVersion2, file: configPath };
797
+ }
798
+ const { oldVersion, newVersion } = vault.rotateKey();
799
+ const reencrypted = walkReencrypt(decrypted, vault);
800
+ await atomicWrite(configPath, JSON.stringify(reencrypted, null, 2), { mode: 384 });
801
+ await restrictFilePermissions(configPath, { warn });
802
+ log(`[secret-vault] Key rotated (v${oldVersion} \u2192 v${newVersion}) \u2014 re-encrypted ${counter.n} field(s)`);
803
+ return { rotated: counter.n, oldVersion, newVersion, file: configPath };
804
+ }
805
+ function walkDecryptCount(node, vault, counter) {
806
+ if (node === null || node === void 0) return node;
807
+ if (typeof node !== "object") return node;
808
+ if (Array.isArray(node)) {
809
+ return node.map((item) => walkDecryptCount(item, vault, counter));
810
+ }
811
+ const out = /* @__PURE__ */ Object.create(null);
812
+ for (const [k, v] of Object.entries(node)) {
813
+ if (typeof v === "string" && vault.isEncrypted(v)) {
814
+ try {
815
+ out[k] = vault.decrypt(v);
816
+ counter.n++;
817
+ } catch {
818
+ out[k] = v;
819
+ }
820
+ } else if (typeof v === "object" && v !== null) {
821
+ out[k] = walkDecryptCount(v, vault, counter);
822
+ } else {
823
+ out[k] = v;
824
+ }
825
+ }
826
+ return out;
827
+ }
828
+ function walkReencrypt(node, vault) {
829
+ if (node === null || node === void 0) return node;
830
+ if (typeof node !== "object") return node;
831
+ if (Array.isArray(node)) {
832
+ return node.map((item) => walkReencrypt(item, vault));
833
+ }
834
+ const out = /* @__PURE__ */ Object.create(null);
835
+ for (const [k, v] of Object.entries(node)) {
836
+ if (typeof v === "string" && isSecretField(k) && v.length > 0 && !vault.isEncrypted(v)) {
837
+ out[k] = vault.encrypt(v);
838
+ } else if (typeof v === "object" && v !== null) {
839
+ out[k] = walkReencrypt(v, vault);
840
+ } else {
841
+ out[k] = v;
842
+ }
843
+ }
844
+ return out;
845
+ }
559
846
  async function restrictFilePermissions(filePath, opts) {
560
847
  const warn = opts?.warn ?? ((msg) => console.warn(msg));
561
848
  if (process.platform === "win32") {
@@ -664,107 +951,6 @@ function getDangerousCapabilities(toolOrCaps) {
664
951
  (c) => DANGEROUS_FOR_SUBAGENTS.includes(c)
665
952
  );
666
953
  }
667
-
668
- // src/utils/expect-defined.ts
669
- function expectDefined(value, label) {
670
- if (value === null || value === void 0) {
671
- const err = new Error("Expected value to be defined");
672
- err.name = "ExpectDefinedError";
673
- throw err;
674
- }
675
- return value;
676
- }
677
-
678
- // src/utils/glob-match.ts
679
- function escapeRegex(s) {
680
- return s.replace(/[.+^${}()|\\]/g, "\\$&");
681
- }
682
- var COMPILED_GLOB_CACHE = /* @__PURE__ */ new Map();
683
- var CACHE_MAX_SIZE = 2e3;
684
- function getCachedGlob(pattern) {
685
- const cached = COMPILED_GLOB_CACHE.get(pattern);
686
- if (cached) return cached;
687
- if (COMPILED_GLOB_CACHE.size >= CACHE_MAX_SIZE) {
688
- const keys = [...COMPILED_GLOB_CACHE.keys()];
689
- for (let i = 0; i < Math.floor(CACHE_MAX_SIZE / 4); i++) {
690
- COMPILED_GLOB_CACHE.delete(expectDefined(keys[i]));
691
- }
692
- }
693
- const re = compileGlob(pattern);
694
- COMPILED_GLOB_CACHE.set(pattern, re);
695
- return re;
696
- }
697
- var MAX_GLOB_PATTERN_LEN = 1024;
698
- function compileGlob(pattern) {
699
- if (pattern.length > MAX_GLOB_PATTERN_LEN) {
700
- throw new Error(`Glob pattern exceeds ${MAX_GLOB_PATTERN_LEN} characters`);
701
- }
702
- let i = 0;
703
- let re = "^";
704
- while (i < pattern.length) {
705
- const c = pattern[i];
706
- if (c === "*") {
707
- if (pattern[i + 1] === "*") {
708
- re += ".*";
709
- i += 2;
710
- if (pattern[i] === "/") i++;
711
- } else {
712
- re += "[^/]*";
713
- i++;
714
- }
715
- } else if (c === "?") {
716
- re += "[^/]";
717
- i++;
718
- } else if (c === "[") {
719
- let cls = "[";
720
- i++;
721
- if (pattern[i] === "!" || pattern[i] === "^") {
722
- cls += "^";
723
- i++;
724
- }
725
- while (i < pattern.length && pattern[i] !== "]") {
726
- const ch = pattern[i] ?? "";
727
- if (ch === "\\") {
728
- cls += "\\\\";
729
- } else if (ch === "]" || ch === "^") {
730
- cls += `\\${ch}`;
731
- } else {
732
- cls += ch;
733
- }
734
- i++;
735
- }
736
- cls += "]";
737
- re += cls;
738
- i++;
739
- } else {
740
- re += escapeRegex(c ?? "");
741
- i++;
742
- }
743
- }
744
- re += "$";
745
- return new RegExp(re);
746
- }
747
- function matchGlob(pattern, input) {
748
- return getCachedGlob(pattern).test(input);
749
- }
750
- function matchAny(patterns, input) {
751
- return patterns.some((p) => matchGlob(p, input));
752
- }
753
-
754
- // src/utils/safe-json.ts
755
- function safeParse(input, maxBytes = 5e6) {
756
- if (input.length > maxBytes) {
757
- return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
758
- }
759
- try {
760
- return { ok: true, value: JSON.parse(input) };
761
- } catch (err) {
762
- return {
763
- ok: false,
764
- error: err instanceof Error ? err.message : String(err)
765
- };
766
- }
767
- }
768
954
  var DESTRUCTIVE_BASH_PATTERNS = [
769
955
  /\bgit\s+(?:clean\s+-[^\s]*[xdf]|reset\s+--hard)\b/i,
770
956
  /\b(?:drop|truncate)\s+(?:table|database|schema)\b/i,
@@ -1217,6 +1403,6 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
1217
1403
  }
1218
1404
  };
1219
1405
 
1220
- export { AutoApprovePermissionPolicy, DANGEROUS_FOR_SUBAGENTS, DefaultPermissionPolicy, DefaultSecretScrubber, DefaultSecretVault, ToolCapabilities, decryptConfigSecrets, encryptConfigSecrets, getDangerousCapabilities, hasCapability, hasDangerousCapabilityForSubagents, isSecretField, migratePlaintextSecrets, rewriteConfigEncrypted };
1406
+ export { AutoApprovePermissionPolicy, DANGEROUS_FOR_SUBAGENTS, DefaultPermissionPolicy, DefaultSecretScrubber, DefaultSecretVault, ToolCapabilities, decryptConfigSecrets, encryptConfigSecrets, getDangerousCapabilities, hasCapability, hasDangerousCapabilityForSubagents, isSecretField, migratePlaintextSecrets, rewriteConfigEncrypted, rotateConfigKeys };
1221
1407
  //# sourceMappingURL=index.js.map
1222
1408
  //# sourceMappingURL=index.js.map