salmon-loop 0.3.1 → 0.4.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 (123) hide show
  1. package/dist/cli/authorization/non-interactive.js +9 -13
  2. package/dist/cli/chat.js +12 -6
  3. package/dist/cli/commands/allowlist.js +1 -1
  4. package/dist/cli/commands/chat.js +13 -13
  5. package/dist/cli/commands/parallel.js +1 -1
  6. package/dist/cli/commands/run/handler.js +6 -3
  7. package/dist/cli/commands/run/loop-params.js +1 -0
  8. package/dist/cli/commands/run/parse-options.js +14 -26
  9. package/dist/cli/commands/run/runtime-llm.js +15 -12
  10. package/dist/cli/commands/serve.js +14 -1
  11. package/dist/cli/headless/openai-responses-canonical-applier.js +1 -7
  12. package/dist/cli/reporters/standard.js +2 -3
  13. package/dist/cli/reporters/stream-json.js +2 -1
  14. package/dist/cli/slash/runtime.js +2 -2
  15. package/dist/cli/ui/components/CommandSuggestionList.js +1 -1
  16. package/dist/cli/ui/hooks/useLoopEvents.js +1 -1
  17. package/dist/cli/ui/hooks/useLoopState.js +1 -1
  18. package/dist/core/ast/parser.js +18 -9
  19. package/dist/core/config/schema.js +738 -0
  20. package/dist/core/config/validate.js +11 -922
  21. package/dist/core/context/gatherers/ast-gatherer.js +4 -12
  22. package/dist/core/context/gatherers/ghost-dependency-gatherer.js +0 -1
  23. package/dist/core/context/gatherers/knowledge-gatherer.js +3 -0
  24. package/dist/core/context/service.js +39 -8
  25. package/dist/core/context/token/encoding-registry.js +7 -6
  26. package/dist/core/extensions/index.js +48 -3
  27. package/dist/core/extensions/load.js +3 -2
  28. package/dist/core/extensions/merge.js +5 -1
  29. package/dist/core/extensions/paths.js +6 -0
  30. package/dist/core/extensions/schemas.js +21 -0
  31. package/dist/core/facades/cli-command-chat.js +2 -0
  32. package/dist/core/facades/cli-run-handler.js +1 -0
  33. package/dist/core/facades/cli-utils-serialize.js +2 -0
  34. package/dist/core/grizzco/dsl/llm-strategy.js +3 -2
  35. package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +15 -10
  36. package/dist/core/grizzco/engine/pipeline/pipeline.js +149 -240
  37. package/dist/core/grizzco/engine/transaction/attempt-failure.js +5 -4
  38. package/dist/core/grizzco/engine/transaction/authorization-summary.js +2 -1
  39. package/dist/core/grizzco/runtime/apply-back-runtime.js +2 -1
  40. package/dist/core/grizzco/services/registry.js +18 -0
  41. package/dist/core/grizzco/steps/audit.js +20 -10
  42. package/dist/core/grizzco/steps/display-report.js +4 -11
  43. package/dist/core/grizzco/steps/explore.js +9 -2
  44. package/dist/core/grizzco/steps/patch/prompt-input.js +4 -1
  45. package/dist/core/grizzco/steps/patch.js +1 -0
  46. package/dist/core/grizzco/steps/plan.js +58 -49
  47. package/dist/core/grizzco/steps/tool-runtime.js +3 -0
  48. package/dist/core/grizzco/workers/strata-sync-worker.js +2 -1
  49. package/dist/core/llm/ai-sdk/message-mapper.js +24 -18
  50. package/dist/core/llm/ai-sdk/request-params.js +1 -3
  51. package/dist/core/llm/ai-sdk/result-mapper.js +14 -8
  52. package/dist/core/llm/ai-sdk/retry-classifier.js +6 -4
  53. package/dist/core/llm/contracts/repair.js +16 -8
  54. package/dist/core/llm/errors.js +13 -10
  55. package/dist/core/llm/output-policy.js +8 -0
  56. package/dist/core/llm/redact.js +1 -3
  57. package/dist/core/llm/sub-agent-factory.js +48 -0
  58. package/dist/core/llm/tool-calling-stub.js +48 -0
  59. package/dist/core/llm/utils.js +17 -6
  60. package/dist/core/mcp/bridge/prompt-command-provider.js +4 -3
  61. package/dist/core/mcp/bridge/tool-bridge.js +5 -14
  62. package/dist/core/mcp/client/connection-manager.js +3 -2
  63. package/dist/core/mcp/host/sampling-provider.js +1 -1
  64. package/dist/core/mcp/schema/json-schema-to-zod.js +2 -1
  65. package/dist/core/memory/relevant-retrieval.js +6 -4
  66. package/dist/core/observability/authorization-decisions.js +13 -12
  67. package/dist/core/observability/error-mapping.js +2 -1
  68. package/dist/core/observability/token-usage.js +5 -4
  69. package/dist/core/plugin/loader.js +5 -4
  70. package/dist/core/prompts/registry.js +11 -29
  71. package/dist/core/protocols/a2a/sdk/server.js +2 -3
  72. package/dist/core/protocols/acp/formal-agent.js +10 -4
  73. package/dist/core/protocols/acp/stdio-server.js +6 -6
  74. package/dist/core/runtime/agent-server-runtime.js +3 -2
  75. package/dist/core/runtime/initialize.js +70 -6
  76. package/dist/core/session/compaction/index.js +4 -3
  77. package/dist/core/session/manager.js +41 -47
  78. package/dist/core/session/token-tracker.js +18 -7
  79. package/dist/core/skills/parser.js +3 -2
  80. package/dist/core/skills/runtime/MicroTaskRunner.js +1 -1
  81. package/dist/core/skills/runtime/SkillRunner.js +5 -2
  82. package/dist/core/slash/steps/slash-execute.js +7 -5
  83. package/dist/core/slash/strategy.js +1 -1
  84. package/dist/core/strata/layers/worktree.js +7 -9
  85. package/dist/core/strata/runtime/synchronizer.js +10 -9
  86. package/dist/core/streaming/canonical/parts-from-llm-stream-chunk.js +1 -11
  87. package/dist/core/structured-output/json-schema-validator.js +1 -13
  88. package/dist/core/sub-agent/context-snapshot.js +12 -6
  89. package/dist/core/sub-agent/controller.js +70 -1
  90. package/dist/core/sub-agent/core/loop.js +25 -3
  91. package/dist/core/sub-agent/core/manager.js +319 -116
  92. package/dist/core/sub-agent/registry-defaults.js +12 -0
  93. package/dist/core/sub-agent/registry.js +8 -0
  94. package/dist/core/sub-agent/team.js +98 -0
  95. package/dist/core/sub-agent/tools/task-await.js +109 -0
  96. package/dist/core/sub-agent/tools/task-spawn.js +49 -7
  97. package/dist/core/sub-agent/tools/team.js +92 -0
  98. package/dist/core/sub-agent/types.js +11 -2
  99. package/dist/core/tools/budget.js +4 -11
  100. package/dist/core/tools/builtin/code-search/executor.js +46 -43
  101. package/dist/core/tools/builtin/fs.js +14 -6
  102. package/dist/core/tools/builtin/index.js +41 -107
  103. package/dist/core/tools/builtin/interaction.js +13 -15
  104. package/dist/core/tools/builtin/proposal.js +11 -2
  105. package/dist/core/tools/capability/executor.js +5 -5
  106. package/dist/core/tools/headless-payload.js +1 -3
  107. package/dist/core/tools/mapper.js +8 -42
  108. package/dist/core/tools/parallel/persistence.js +17 -5
  109. package/dist/core/tools/parallel/scheduler.js +23 -21
  110. package/dist/core/tools/permissions/permission-rules.js +66 -114
  111. package/dist/core/tools/plugins/loader.js +4 -3
  112. package/dist/core/tools/router.js +24 -53
  113. package/dist/core/tools/session.js +54 -97
  114. package/dist/core/tools/streaming/ToolCallAccumulator.js +1 -3
  115. package/dist/core/tools/tool-visibility.js +2 -1
  116. package/dist/core/tools/types.js +10 -0
  117. package/dist/core/utils/error.js +79 -0
  118. package/dist/core/utils/serialize.js +63 -0
  119. package/dist/core/utils/zod.js +29 -0
  120. package/dist/core/workspace/capabilities.js +3 -2
  121. package/dist/integrations/langfuse/litellm-langfuse-outcome-reporter.js +9 -8
  122. package/dist/locales/en.js +2 -1
  123. package/package.json +1 -1
@@ -0,0 +1,738 @@
1
+ import { z } from 'zod';
2
+ import { LLM_OUTPUT_KINDS } from '../types/index.js';
3
+ import { ConfigError } from './errors.js';
4
+ import { normalizePermissionMode, normalizeUiLogMode, normalizeUiLogView } from './normalize.js';
5
+ import { MARKDOWN_RENDER_MODES, MARKDOWN_THEMES, UI_LOG_MODES, UI_LOG_VIEWS } from './types.js';
6
+ // ---------------------------------------------------------------------------
7
+ // Primitive schemas
8
+ // ---------------------------------------------------------------------------
9
+ const finiteNumber = z.number().refine(Number.isFinite, { message: 'Expected finite number' });
10
+ const stringArray = z.array(z.string());
11
+ const nullableString = z.string().nullable();
12
+ // ---------------------------------------------------------------------------
13
+ // Observability
14
+ // ---------------------------------------------------------------------------
15
+ const langfuseSchema = z.object({
16
+ enabled: z.boolean().optional(),
17
+ outcome: z.boolean().optional(),
18
+ endpoint: z.string().optional(),
19
+ apiKey: nullableString.optional(),
20
+ sessionId: z.string().optional(),
21
+ userId: z.string().optional(),
22
+ });
23
+ const auditBufferSchema = z.object({
24
+ maxEvents: finiteNumber.optional(),
25
+ maxBytes: finiteNumber.optional(),
26
+ droppedWarn: finiteNumber.optional(),
27
+ });
28
+ const auditScopeSchema = z.enum(['repo', 'user']);
29
+ const auditSchema = z.object({
30
+ scope: auditScopeSchema.optional(),
31
+ buffer: auditBufferSchema.optional(),
32
+ });
33
+ const observabilitySchema = z.object({
34
+ langfuse: langfuseSchema.optional(),
35
+ audit: auditSchema.optional(),
36
+ });
37
+ // ---------------------------------------------------------------------------
38
+ // CLI
39
+ // ---------------------------------------------------------------------------
40
+ const cliDefaultsSchema = z.object({
41
+ verbosity: z.string().optional(),
42
+ strategy: z.string().optional(),
43
+ dryRun: z.boolean().optional(),
44
+ });
45
+ const cliSchema = z.object({
46
+ defaults: cliDefaultsSchema.optional(),
47
+ });
48
+ // ---------------------------------------------------------------------------
49
+ // Server
50
+ // ---------------------------------------------------------------------------
51
+ const serverA2aSchema = z.object({
52
+ host: z.string().optional(),
53
+ port: finiteNumber.optional(),
54
+ tokens: stringArray.optional(),
55
+ });
56
+ const serverAcpSessionStoreSchema = z.object({
57
+ maxEntries: finiteNumber.optional(),
58
+ maxAgeMs: finiteNumber.optional(),
59
+ historyMaxEntries: finiteNumber.optional(),
60
+ lockStaleMs: finiteNumber.optional(),
61
+ lockHeartbeatMs: finiteNumber.optional(),
62
+ });
63
+ const serverAcpCheckpointSchema = z.object({
64
+ lockStaleMs: finiteNumber.optional(),
65
+ lockHeartbeatMs: finiteNumber.optional(),
66
+ });
67
+ const serverAcpSchema = z.object({
68
+ sessionStore: serverAcpSessionStoreSchema.optional(),
69
+ checkpointManifest: serverAcpCheckpointSchema.optional(),
70
+ });
71
+ const serverSchema = z
72
+ .object({
73
+ a2a: serverA2aSchema.optional(),
74
+ acp: serverAcpSchema.optional(),
75
+ })
76
+ .strict();
77
+ // ---------------------------------------------------------------------------
78
+ // Context
79
+ // ---------------------------------------------------------------------------
80
+ const contextCacheSchema = z
81
+ .object({
82
+ mode: z.enum(['memory', 'persistent']).optional(),
83
+ path: z.string().optional(),
84
+ allowedRoots: stringArray.optional(),
85
+ strict: z.boolean().optional(),
86
+ fallbackToMemoryOnFailure: z.boolean().optional(),
87
+ maxEntries: finiteNumber.optional(),
88
+ ttlMs: finiteNumber.optional(),
89
+ maxPayloadBytes: finiteNumber.optional(),
90
+ })
91
+ .superRefine((val, ctx) => {
92
+ if (val.mode === 'persistent') {
93
+ if (typeof val.path !== 'string' || val.path.length === 0) {
94
+ ctx.addIssue({
95
+ code: 'custom',
96
+ path: ['path'],
97
+ params: {
98
+ configErrorCode: 'CONFIG_INVALID_CONTEXT_CACHE_PATH',
99
+ expected: 'non-empty string',
100
+ },
101
+ });
102
+ }
103
+ if (!Array.isArray(val.allowedRoots) ||
104
+ val.allowedRoots.length === 0 ||
105
+ val.allowedRoots.some((v) => typeof v !== 'string' || v.length === 0)) {
106
+ ctx.addIssue({
107
+ code: 'custom',
108
+ path: ['allowedRoots'],
109
+ params: {
110
+ configErrorCode: 'CONFIG_INVALID_CONTEXT_CACHE_ALLOWED_ROOTS',
111
+ expected: 'non-empty string[]',
112
+ },
113
+ });
114
+ }
115
+ }
116
+ });
117
+ const contextChurnWeightSchema = z.object({
118
+ primary: finiteNumber.optional(),
119
+ rerank: finiteNumber.optional(),
120
+ tiebreak: finiteNumber.optional(),
121
+ });
122
+ const contextChurnSchema = z.object({
123
+ weight: contextChurnWeightSchema.optional(),
124
+ });
125
+ const contextDynamicBudgetAlertsSchema = z.object({
126
+ truncationRateWarn: finiteNumber.optional(),
127
+ criticalDropRateWarn: finiteNumber.optional(),
128
+ });
129
+ const contextDynamicBudgetSchema = z.object({
130
+ enabled: z.boolean().optional(),
131
+ minBudget: finiteNumber.optional(),
132
+ maxBudget: finiteNumber.optional(),
133
+ adjustmentStep: finiteNumber.optional(),
134
+ alerts: contextDynamicBudgetAlertsSchema.optional(),
135
+ });
136
+ const contextSchema = z.object({
137
+ useTokenBudget: z.boolean().optional(),
138
+ cache: contextCacheSchema.optional(),
139
+ churn: contextChurnSchema.optional(),
140
+ dynamicBudget: contextDynamicBudgetSchema.optional(),
141
+ });
142
+ // ---------------------------------------------------------------------------
143
+ // Security
144
+ // ---------------------------------------------------------------------------
145
+ const securityRedactionSchema = z.object({
146
+ enabled: z.boolean().optional(),
147
+ mark: z.string().optional(),
148
+ maxDepth: finiteNumber.optional(),
149
+ keyAllowlist: stringArray.optional(),
150
+ keyDenylist: stringArray.optional(),
151
+ patterns: stringArray.optional(),
152
+ disableDefaults: z.boolean().optional(),
153
+ });
154
+ const securitySchema = z.object({
155
+ redaction: securityRedactionSchema.optional(),
156
+ });
157
+ // ---------------------------------------------------------------------------
158
+ // Output
159
+ // ---------------------------------------------------------------------------
160
+ const llmOutputKindSchema = z.enum([...LLM_OUTPUT_KINDS]);
161
+ const outputLlmSchema = z.object({
162
+ kinds: z.array(llmOutputKindSchema).optional(),
163
+ });
164
+ const outputMarkdownSchema = z.object({
165
+ theme: z.enum([...MARKDOWN_THEMES]).optional(),
166
+ mode: z.enum([...MARKDOWN_RENDER_MODES]).optional(),
167
+ });
168
+ const outputSchema = z.object({
169
+ llm: outputLlmSchema.optional(),
170
+ markdown: outputMarkdownSchema.optional(),
171
+ });
172
+ // ---------------------------------------------------------------------------
173
+ // UI
174
+ // ---------------------------------------------------------------------------
175
+ const uiLogSchema = z.object({
176
+ view: z.preprocess((val) => normalizeUiLogView(val), z.enum([...UI_LOG_VIEWS])).optional(),
177
+ mode: z.preprocess((val) => normalizeUiLogMode(val), z.enum([...UI_LOG_MODES])).optional(),
178
+ });
179
+ const uiSchema = z.object({
180
+ log: uiLogSchema.optional(),
181
+ });
182
+ // ---------------------------------------------------------------------------
183
+ // Verify & AST
184
+ // ---------------------------------------------------------------------------
185
+ const verifySchema = z.object({
186
+ command: z.string().optional(),
187
+ timeoutMs: finiteNumber.optional(),
188
+ });
189
+ const astValidationSchema = z.object({
190
+ strictness: z.enum(['lenient', 'strict']).optional(),
191
+ });
192
+ // ---------------------------------------------------------------------------
193
+ // LLM
194
+ // ---------------------------------------------------------------------------
195
+ const llmCapabilitiesSchema = z
196
+ .object({
197
+ toolCalling: z.boolean().optional(),
198
+ responseFormatJsonObject: z.boolean().optional(),
199
+ streaming: z.boolean().optional(),
200
+ })
201
+ .strict();
202
+ const CAPABILITY_KEYS = new Set([
203
+ 'capabilities',
204
+ 'toolCalling',
205
+ 'responseFormatJsonObject',
206
+ 'streaming',
207
+ ]);
208
+ const llmModelParamsSchema = z
209
+ .object({
210
+ temperature: finiteNumber.optional(),
211
+ maxTokens: finiteNumber.optional(),
212
+ topP: finiteNumber.optional(),
213
+ presencePenalty: finiteNumber.optional(),
214
+ frequencyPenalty: finiteNumber.optional(),
215
+ })
216
+ .passthrough()
217
+ .superRefine((val, ctx) => {
218
+ for (const key of CAPABILITY_KEYS) {
219
+ if (key in val && val[key] !== undefined) {
220
+ ctx.addIssue({
221
+ code: 'custom',
222
+ path: [key],
223
+ params: {
224
+ configErrorCode: 'CONFIG_INVALID_LLM_CAPABILITY_LOCATION',
225
+ capability: key,
226
+ expected: 'model.capabilities',
227
+ },
228
+ });
229
+ }
230
+ }
231
+ });
232
+ const modelProviderSchema = z.union([z.string(), z.array(z.string()).min(1)]);
233
+ const llmModelProfileSchema = z.object({
234
+ provider: modelProviderSchema,
235
+ id: z.string().min(1),
236
+ params: llmModelParamsSchema.optional(),
237
+ capabilities: llmCapabilitiesSchema.optional(),
238
+ });
239
+ const llmProviderApiSchema = z.object({
240
+ baseUrl: z.string().optional(),
241
+ apiKey: nullableString.optional(),
242
+ timeoutMs: finiteNumber.optional(),
243
+ headers: z.record(z.string(), z.string()).optional(),
244
+ });
245
+ const llmProviderClientSchema = z.object({
246
+ package: z.string().optional(),
247
+ });
248
+ const llmProviderSchema = z
249
+ .object({
250
+ type: z.string(),
251
+ client: llmProviderClientSchema.optional(),
252
+ api: llmProviderApiSchema.optional(),
253
+ capabilities: llmCapabilitiesSchema.optional(),
254
+ })
255
+ .superRefine((val, ctx) => {
256
+ if ('models' in val && val.models !== undefined) {
257
+ ctx.addIssue({
258
+ code: 'custom',
259
+ path: ['models'],
260
+ params: {
261
+ configErrorCode: 'CONFIG_LLM_PROVIDER_MODELS_NOT_SUPPORTED',
262
+ hint: 'use llm.models with provider references',
263
+ },
264
+ });
265
+ }
266
+ });
267
+ const llmRoutingSchema = z.object({
268
+ fallbackProviders: stringArray.optional(),
269
+ taskToModel: z.record(z.string(), z.string()).optional(),
270
+ phaseToModel: z.record(z.string(), z.string()).optional(),
271
+ });
272
+ const llmSchema = z.object({
273
+ activeModel: z.string().optional(),
274
+ simpleModel: z.string().optional(),
275
+ mediumModel: z.string().optional(),
276
+ complexModel: z.string().optional(),
277
+ reasoningModel: z.string().optional(),
278
+ providers: z.record(z.string(), llmProviderSchema).optional(),
279
+ models: z.record(z.string(), llmModelProfileSchema).optional(),
280
+ routing: llmRoutingSchema.optional(),
281
+ });
282
+ // ---------------------------------------------------------------------------
283
+ // Tool Authorization
284
+ // ---------------------------------------------------------------------------
285
+ const toolAuthNonInteractiveCmdSchema = z.object({
286
+ cmd: z.string(),
287
+ timeoutMs: finiteNumber.optional(),
288
+ });
289
+ const toolAuthNonInteractiveMcpSchema = z.object({
290
+ server: z.string(),
291
+ tool: z.string(),
292
+ timeoutMs: finiteNumber.optional(),
293
+ });
294
+ const toolAuthNonInteractiveSchema = z.object({
295
+ strategy: z.enum(['deny', 'command', 'mcp']).optional(),
296
+ command: toolAuthNonInteractiveCmdSchema.optional(),
297
+ mcp: toolAuthNonInteractiveMcpSchema.optional(),
298
+ });
299
+ const toolAuthAutoAllowRiskSchema = z.object({
300
+ low: z.boolean().optional(),
301
+ medium: z.boolean().optional(),
302
+ high: z.boolean().optional(),
303
+ });
304
+ const toolAuthAllowlistSummarySchema = z.object({
305
+ every: finiteNumber.optional(),
306
+ minIntervalMs: finiteNumber.optional(),
307
+ failureMinIntervalMs: finiteNumber.optional(),
308
+ maxToolStats: finiteNumber.optional(),
309
+ maxPathStats: finiteNumber.optional(),
310
+ });
311
+ const toolAuthAllowlistMatchingSchema = z.object({
312
+ denySideEffects: z.enum(['any', 'all']).optional(),
313
+ allowSideEffects: z.enum(['any', 'all']).optional(),
314
+ });
315
+ const toolAuthAllowlistSchema = z.object({
316
+ repoFile: z.string().optional(),
317
+ userFile: z.string().optional(),
318
+ summary: toolAuthAllowlistSummarySchema.optional(),
319
+ matching: toolAuthAllowlistMatchingSchema.optional(),
320
+ });
321
+ const toolAuthorizationSchema = z.object({
322
+ sessionTtlMs: finiteNumber.optional(),
323
+ autoAllowRisk: toolAuthAutoAllowRiskSchema.optional(),
324
+ nonInteractive: toolAuthNonInteractiveSchema.optional(),
325
+ allowlist: toolAuthAllowlistSchema.optional(),
326
+ });
327
+ // ---------------------------------------------------------------------------
328
+ // Root schema
329
+ // ---------------------------------------------------------------------------
330
+ export const configFileV1Schema = z
331
+ .object({
332
+ version: z.literal(1).optional(),
333
+ mode: z
334
+ .preprocess((val) => normalizePermissionMode(val), z.enum(['interactive', 'yolo']))
335
+ .optional(),
336
+ cli: cliSchema.optional(),
337
+ server: serverSchema.optional(),
338
+ context: contextSchema.optional(),
339
+ observability: observabilitySchema.optional(),
340
+ security: securitySchema.optional(),
341
+ output: outputSchema.optional(),
342
+ ui: uiSchema.optional(),
343
+ verify: verifySchema.optional(),
344
+ astValidation: astValidationSchema.optional(),
345
+ llm: llmSchema.optional(),
346
+ toolAuthorization: toolAuthorizationSchema.optional(),
347
+ })
348
+ .passthrough();
349
+ // ---------------------------------------------------------------------------
350
+ // Error code mapping
351
+ // ---------------------------------------------------------------------------
352
+ const ERROR_CODE_MAP = new Map([
353
+ // Root
354
+ ['version', 'CONFIG_UNSUPPORTED'],
355
+ // Mode
356
+ ['mode', 'CONFIG_INVALID_MODE'],
357
+ // CLI
358
+ ['cli', 'CONFIG_INVALID_CLI'],
359
+ ['cli.defaults', 'CONFIG_INVALID_CLI_DEFAULTS'],
360
+ ['cli.defaults.verbosity', 'CONFIG_INVALID_VERBOSITY'],
361
+ ['cli.defaults.strategy', 'CONFIG_INVALID_STRATEGY'],
362
+ ['cli.defaults.dryRun', 'CONFIG_INVALID_DRY_RUN'],
363
+ // Server
364
+ ['server', 'CONFIG_INVALID_SERVER'],
365
+ ['server.a2a', 'CONFIG_INVALID_SERVER_A2A'],
366
+ ['server.a2a.host', 'CONFIG_INVALID_SERVER_A2A_HOST'],
367
+ ['server.a2a.port', 'CONFIG_INVALID_SERVER_A2A_PORT'],
368
+ ['server.a2a.tokens', 'CONFIG_INVALID_SERVER_A2A_TOKENS'],
369
+ ['server.acp', 'CONFIG_INVALID_SERVER_ACP'],
370
+ ['server.acp.sessionStore', 'CONFIG_INVALID_SERVER_ACP_SESSION_STORE'],
371
+ ['server.acp.sessionStore.maxEntries', 'CONFIG_INVALID_SERVER_ACP_SESSION_STORE_MAX_ENTRIES'],
372
+ ['server.acp.sessionStore.maxAgeMs', 'CONFIG_INVALID_SERVER_ACP_SESSION_STORE_MAX_AGE_MS'],
373
+ [
374
+ 'server.acp.sessionStore.historyMaxEntries',
375
+ 'CONFIG_INVALID_SERVER_ACP_SESSION_STORE_HISTORY_MAX_ENTRIES',
376
+ ],
377
+ ['server.acp.sessionStore.lockStaleMs', 'CONFIG_INVALID_SERVER_ACP_SESSION_STORE_LOCK_STALE_MS'],
378
+ [
379
+ 'server.acp.sessionStore.lockHeartbeatMs',
380
+ 'CONFIG_INVALID_SERVER_ACP_SESSION_STORE_LOCK_HEARTBEAT_MS',
381
+ ],
382
+ ['server.acp.checkpointManifest', 'CONFIG_INVALID_SERVER_ACP_CHECKPOINT_MANIFEST'],
383
+ [
384
+ 'server.acp.checkpointManifest.lockStaleMs',
385
+ 'CONFIG_INVALID_SERVER_ACP_CHECKPOINT_MANIFEST_LOCK_STALE_MS',
386
+ ],
387
+ [
388
+ 'server.acp.checkpointManifest.lockHeartbeatMs',
389
+ 'CONFIG_INVALID_SERVER_ACP_CHECKPOINT_MANIFEST_LOCK_HEARTBEAT_MS',
390
+ ],
391
+ // Context
392
+ ['context', 'CONFIG_INVALID_CONTEXT'],
393
+ ['context.useTokenBudget', 'CONFIG_INVALID_USE_TOKEN_BUDGET'],
394
+ ['context.cache', 'CONFIG_INVALID_CONTEXT_CACHE'],
395
+ ['context.cache.mode', 'CONFIG_INVALID_CONTEXT_CACHE_MODE'],
396
+ ['context.cache.path', 'CONFIG_INVALID_CONTEXT_CACHE_PATH'],
397
+ ['context.cache.allowedRoots', 'CONFIG_INVALID_CONTEXT_CACHE_ALLOWED_ROOTS'],
398
+ ['context.cache.maxEntries', 'CONFIG_INVALID_CONTEXT_CACHE_MAX_ENTRIES'],
399
+ ['context.cache.ttlMs', 'CONFIG_INVALID_CONTEXT_CACHE_TTL'],
400
+ ['context.cache.maxPayloadBytes', 'CONFIG_INVALID_CONTEXT_CACHE_MAX_PAYLOAD'],
401
+ ['context.churn', 'CONFIG_INVALID_CHURN'],
402
+ ['context.churn.weight', 'CONFIG_INVALID_CHURN_WEIGHT'],
403
+ ['context.churn.weight.primary', 'CONFIG_INVALID_CHURN_WEIGHT_PRIMARY'],
404
+ ['context.churn.weight.rerank', 'CONFIG_INVALID_CHURN_WEIGHT_RERANK'],
405
+ ['context.churn.weight.tiebreak', 'CONFIG_INVALID_CHURN_WEIGHT_TIEBREAK'],
406
+ ['context.dynamicBudget', 'CONFIG_INVALID_DYNAMIC_BUDGET'],
407
+ ['context.dynamicBudget.enabled', 'CONFIG_INVALID_DYNAMIC_BUDGET_ENABLED'],
408
+ ['context.dynamicBudget.minBudget', 'CONFIG_INVALID_DYNAMIC_BUDGET_MIN'],
409
+ ['context.dynamicBudget.maxBudget', 'CONFIG_INVALID_DYNAMIC_BUDGET_MAX'],
410
+ ['context.dynamicBudget.adjustmentStep', 'CONFIG_INVALID_DYNAMIC_BUDGET_STEP'],
411
+ ['context.dynamicBudget.alerts', 'CONFIG_INVALID_DYNAMIC_BUDGET_ALERTS'],
412
+ [
413
+ 'context.dynamicBudget.alerts.truncationRateWarn',
414
+ 'CONFIG_INVALID_DYNAMIC_BUDGET_ALERT_TRUNCATION',
415
+ ],
416
+ [
417
+ 'context.dynamicBudget.alerts.criticalDropRateWarn',
418
+ 'CONFIG_INVALID_DYNAMIC_BUDGET_ALERT_CRITICAL_DROP',
419
+ ],
420
+ // Observability
421
+ ['observability', 'CONFIG_INVALID_OBSERVABILITY'],
422
+ ['observability.langfuse', 'CONFIG_INVALID_OBSERVABILITY_LANGFUSE'],
423
+ ['observability.langfuse.enabled', 'CONFIG_INVALID_LANGFUSE_ENABLED'],
424
+ ['observability.langfuse.outcome', 'CONFIG_INVALID_LANGFUSE_OUTCOME'],
425
+ ['observability.langfuse.endpoint', 'CONFIG_INVALID_LANGFUSE_ENDPOINT'],
426
+ ['observability.langfuse.apiKey', 'CONFIG_INVALID_LANGFUSE_API_KEY'],
427
+ ['observability.langfuse.sessionId', 'CONFIG_INVALID_LANGFUSE_SESSION_ID'],
428
+ ['observability.langfuse.userId', 'CONFIG_INVALID_LANGFUSE_USER_ID'],
429
+ ['observability.audit', 'CONFIG_INVALID_OBSERVABILITY_AUDIT'],
430
+ ['observability.audit.scope', 'CONFIG_INVALID_OBSERVABILITY_AUDIT_SCOPE'],
431
+ ['observability.audit.buffer', 'CONFIG_INVALID_OBSERVABILITY_AUDIT_BUFFER'],
432
+ ['observability.audit.buffer.maxEvents', 'CONFIG_INVALID_OBSERVABILITY_AUDIT_MAX_EVENTS'],
433
+ ['observability.audit.buffer.maxBytes', 'CONFIG_INVALID_OBSERVABILITY_AUDIT_MAX_BYTES'],
434
+ ['observability.audit.buffer.droppedWarn', 'CONFIG_INVALID_OBSERVABILITY_AUDIT_DROPPED_WARN'],
435
+ // Security
436
+ ['security', 'CONFIG_INVALID_SECURITY'],
437
+ ['security.redaction', 'CONFIG_INVALID_SECURITY_REDACTION'],
438
+ ['security.redaction.enabled', 'CONFIG_INVALID_SECURITY_REDACTION_ENABLED'],
439
+ ['security.redaction.mark', 'CONFIG_INVALID_SECURITY_REDACTION_MARK'],
440
+ ['security.redaction.maxDepth', 'CONFIG_INVALID_SECURITY_REDACTION_MAX_DEPTH'],
441
+ ['security.redaction.keyAllowlist', 'CONFIG_INVALID_SECURITY_REDACTION_KEY_ALLOWLIST'],
442
+ ['security.redaction.keyDenylist', 'CONFIG_INVALID_SECURITY_REDACTION_KEY_DENYLIST'],
443
+ ['security.redaction.patterns', 'CONFIG_INVALID_SECURITY_REDACTION_PATTERNS'],
444
+ ['security.redaction.disableDefaults', 'CONFIG_INVALID_SECURITY_REDACTION_DISABLE_DEFAULTS'],
445
+ // Output
446
+ ['output', 'CONFIG_INVALID_OUTPUT'],
447
+ ['output.llm', 'CONFIG_INVALID_LLM_OUTPUT'],
448
+ ['output.llm.kinds', 'CONFIG_INVALID_LLM_OUTPUT_KINDS'],
449
+ ['output.markdown', 'CONFIG_INVALID_OUTPUT_MARKDOWN'],
450
+ ['output.markdown.theme', 'CONFIG_INVALID_MARKDOWN_THEME'],
451
+ ['output.markdown.mode', 'CONFIG_INVALID_MARKDOWN_RENDER_MODE'],
452
+ // UI
453
+ ['ui', 'CONFIG_INVALID_UI'],
454
+ ['ui.log', 'CONFIG_INVALID_UI_LOG'],
455
+ ['ui.log.view', 'CONFIG_INVALID_UI_LOG_VIEW'],
456
+ ['ui.log.mode', 'CONFIG_INVALID_UI_LOG_MODE'],
457
+ // Verify
458
+ ['verify', 'CONFIG_INVALID_VERIFY'],
459
+ ['verify.command', 'CONFIG_INVALID_VERIFY_COMMAND'],
460
+ ['verify.timeoutMs', 'CONFIG_INVALID_VERIFY_TIMEOUT'],
461
+ // AST Validation
462
+ ['astValidation', 'CONFIG_INVALID_AST_VALIDATION'],
463
+ ['astValidation.strictness', 'CONFIG_INVALID_AST_VALIDATION_STRICTNESS'],
464
+ // LLM
465
+ ['llm', 'CONFIG_INVALID_LLM'],
466
+ ['llm.activeModel', 'CONFIG_INVALID_LLM_ACTIVE_MODEL'],
467
+ ['llm.simpleModel', 'CONFIG_INVALID_LLM_SIMPLE_MODEL'],
468
+ ['llm.mediumModel', 'CONFIG_INVALID_LLM_MEDIUM_MODEL'],
469
+ ['llm.complexModel', 'CONFIG_INVALID_LLM_COMPLEX_MODEL'],
470
+ ['llm.reasoningModel', 'CONFIG_INVALID_LLM_REASONING_MODEL'],
471
+ ['llm.providers', 'CONFIG_INVALID_LLM_PROVIDERS'],
472
+ ['llm.models', 'CONFIG_INVALID_LLM_MODELS'],
473
+ ['llm.routing', 'CONFIG_INVALID_ROUTING'],
474
+ ['llm.routing.fallbackProviders', 'CONFIG_INVALID_FALLBACK_PROVIDERS'],
475
+ ['llm.routing.taskToModel', 'CONFIG_INVALID_TASK_TO_MODEL'],
476
+ ['llm.routing.phaseToModel', 'CONFIG_INVALID_PHASE_TO_MODEL'],
477
+ // Tool Authorization
478
+ ['toolAuthorization', 'CONFIG_INVALID_TOOL_AUTH'],
479
+ ['toolAuthorization.sessionTtlMs', 'CONFIG_INVALID_TOOL_AUTH_TTL'],
480
+ ['toolAuthorization.autoAllowRisk', 'CONFIG_INVALID_TOOL_AUTH_RISK'],
481
+ ['toolAuthorization.autoAllowRisk.low', 'CONFIG_INVALID_TOOL_AUTH_RISK_LOW'],
482
+ ['toolAuthorization.autoAllowRisk.medium', 'CONFIG_INVALID_TOOL_AUTH_RISK_MEDIUM'],
483
+ ['toolAuthorization.autoAllowRisk.high', 'CONFIG_INVALID_TOOL_AUTH_RISK_HIGH'],
484
+ ['toolAuthorization.nonInteractive', 'CONFIG_INVALID_TOOL_AUTH_NON_INTERACTIVE'],
485
+ [
486
+ 'toolAuthorization.nonInteractive.strategy',
487
+ 'CONFIG_INVALID_TOOL_AUTH_NON_INTERACTIVE_STRATEGY',
488
+ ],
489
+ ['toolAuthorization.nonInteractive.command', 'CONFIG_INVALID_TOOL_AUTH_NON_INTERACTIVE_COMMAND'],
490
+ [
491
+ 'toolAuthorization.nonInteractive.command.cmd',
492
+ 'CONFIG_INVALID_TOOL_AUTH_NON_INTERACTIVE_COMMAND_CMD',
493
+ ],
494
+ [
495
+ 'toolAuthorization.nonInteractive.command.timeoutMs',
496
+ 'CONFIG_INVALID_TOOL_AUTH_NON_INTERACTIVE_COMMAND_TIMEOUT',
497
+ ],
498
+ ['toolAuthorization.nonInteractive.mcp', 'CONFIG_INVALID_TOOL_AUTH_NON_INTERACTIVE_MCP'],
499
+ [
500
+ 'toolAuthorization.nonInteractive.mcp.server',
501
+ 'CONFIG_INVALID_TOOL_AUTH_NON_INTERACTIVE_MCP_SERVER',
502
+ ],
503
+ [
504
+ 'toolAuthorization.nonInteractive.mcp.tool',
505
+ 'CONFIG_INVALID_TOOL_AUTH_NON_INTERACTIVE_MCP_TOOL',
506
+ ],
507
+ [
508
+ 'toolAuthorization.nonInteractive.mcp.timeoutMs',
509
+ 'CONFIG_INVALID_TOOL_AUTH_NON_INTERACTIVE_MCP_TIMEOUT',
510
+ ],
511
+ ['toolAuthorization.allowlist', 'CONFIG_INVALID_TOOL_AUTH_ALLOWLIST'],
512
+ ['toolAuthorization.allowlist.repoFile', 'CONFIG_INVALID_TOOL_AUTH_REPO_FILE'],
513
+ ['toolAuthorization.allowlist.userFile', 'CONFIG_INVALID_TOOL_AUTH_USER_FILE'],
514
+ ]);
515
+ // Patterns for dynamic path segments
516
+ const DYNAMIC_PATTERNS = [
517
+ // LLM output kind validation
518
+ {
519
+ test: (p) => p.length >= 3 && p[0] === 'output' && p[1] === 'llm' && p[2] === 'kinds',
520
+ code: 'CONFIG_INVALID_LLM_OUTPUT_KIND',
521
+ details: () => ({ expected: 'valid LlmOutputKind' }),
522
+ },
523
+ // LLM providers — per-provider errors
524
+ {
525
+ test: (p) => p.length >= 3 && p[0] === 'llm' && p[1] === 'providers' && p.length === 3,
526
+ code: 'CONFIG_INVALID_PROVIDER',
527
+ details: (p) => ({ provider: p[2], expected: 'object' }),
528
+ },
529
+ {
530
+ test: (p) => p.length >= 4 && p[0] === 'llm' && p[1] === 'providers' && p[3] === 'type',
531
+ code: 'CONFIG_INVALID_TYPE',
532
+ details: (p) => ({ provider: p[2], expected: 'string' }),
533
+ },
534
+ {
535
+ test: (p) => p.length >= 4 && p[0] === 'llm' && p[1] === 'providers' && p[3] === 'client',
536
+ code: 'CONFIG_INVALID_CLIENT',
537
+ details: (p) => ({ provider: p[2], expected: 'object' }),
538
+ },
539
+ {
540
+ test: (p) => p.length >= 5 &&
541
+ p[0] === 'llm' &&
542
+ p[1] === 'providers' &&
543
+ p[3] === 'client' &&
544
+ p[4] === 'package',
545
+ code: 'CONFIG_INVALID_CLIENT_PACKAGE',
546
+ details: (p) => ({ provider: p[2], expected: 'string' }),
547
+ },
548
+ {
549
+ test: (p) => p.length >= 4 && p[0] === 'llm' && p[1] === 'providers' && p[3] === 'api',
550
+ code: 'CONFIG_INVALID_API',
551
+ details: (p) => ({ provider: p[2], expected: 'object' }),
552
+ },
553
+ {
554
+ test: (p) => p.length >= 5 &&
555
+ p[0] === 'llm' &&
556
+ p[1] === 'providers' &&
557
+ p[3] === 'api' &&
558
+ p[4] === 'baseUrl',
559
+ code: 'CONFIG_INVALID_BASE_URL',
560
+ details: (p) => ({ provider: p[2], expected: 'string' }),
561
+ },
562
+ {
563
+ test: (p) => p.length >= 5 &&
564
+ p[0] === 'llm' &&
565
+ p[1] === 'providers' &&
566
+ p[3] === 'api' &&
567
+ p[4] === 'apiKey',
568
+ code: 'CONFIG_INVALID_API_KEY',
569
+ details: (p) => ({ provider: p[2], expected: 'string_or_null' }),
570
+ },
571
+ {
572
+ test: (p) => p.length >= 5 &&
573
+ p[0] === 'llm' &&
574
+ p[1] === 'providers' &&
575
+ p[3] === 'api' &&
576
+ p[4] === 'timeoutMs',
577
+ code: 'CONFIG_INVALID_TIMEOUT',
578
+ details: (p) => ({ provider: p[2], expected: 'number' }),
579
+ },
580
+ {
581
+ test: (p) => p.length >= 5 &&
582
+ p[0] === 'llm' &&
583
+ p[1] === 'providers' &&
584
+ p[3] === 'api' &&
585
+ p[4] === 'headers',
586
+ code: 'CONFIG_INVALID_HEADERS',
587
+ details: (p) => ({ provider: p[2], expected: 'object' }),
588
+ },
589
+ {
590
+ test: (p) => p.length >= 6 &&
591
+ p[0] === 'llm' &&
592
+ p[1] === 'providers' &&
593
+ p[3] === 'api' &&
594
+ p[4] === 'headers',
595
+ code: 'CONFIG_INVALID_HEADER_VALUE',
596
+ details: (p) => ({ provider: p[2], header: p[5], expected: 'string' }),
597
+ },
598
+ {
599
+ test: (p, issue) => p.length === 4 &&
600
+ p[0] === 'llm' &&
601
+ p[1] === 'providers' &&
602
+ p[3] === 'capabilities' &&
603
+ issue?.code !== 'unrecognized_keys',
604
+ code: 'CONFIG_INVALID_LLM_CAPABILITIES',
605
+ details: (p) => ({ provider: p[2], expected: 'object' }),
606
+ },
607
+ {
608
+ test: (p) => p.length >= 4 && p[0] === 'llm' && p[1] === 'providers' && p[3] === 'capabilities',
609
+ code: 'CONFIG_INVALID_LLM_CAPABILITY',
610
+ details: (p) => ({ provider: p[2], capability: p[4] ?? '', expected: 'boolean' }),
611
+ },
612
+ {
613
+ test: (p) => p.length >= 4 && p[0] === 'llm' && p[1] === 'providers' && p[3] === 'models',
614
+ code: 'CONFIG_LLM_PROVIDER_MODELS_NOT_SUPPORTED',
615
+ details: (p) => ({ provider: p[2], hint: 'use llm.models with provider references' }),
616
+ },
617
+ // LLM models — per-model errors
618
+ {
619
+ test: (p) => p.length >= 3 && p[0] === 'llm' && p[1] === 'models' && p.length === 3,
620
+ code: 'CONFIG_INVALID_LLM_MODEL_PROFILE',
621
+ details: (p) => ({ model: p[2], expected: 'object' }),
622
+ },
623
+ {
624
+ test: (p) => p.length >= 4 && p[0] === 'llm' && p[1] === 'models' && p[3] === 'provider',
625
+ code: 'CONFIG_INVALID_LLM_MODEL_PROVIDER',
626
+ details: (p) => ({ model: p[2], expected: 'string_or_non_empty_string_array' }),
627
+ },
628
+ {
629
+ test: (p) => p.length >= 4 && p[0] === 'llm' && p[1] === 'models' && p[3] === 'id',
630
+ code: 'CONFIG_INVALID_LLM_MODEL_ID',
631
+ details: (p) => ({ model: p[2], expected: 'non_empty_string' }),
632
+ },
633
+ {
634
+ test: (p, issue) => p.length === 4 &&
635
+ p[0] === 'llm' &&
636
+ p[1] === 'models' &&
637
+ p[3] === 'capabilities' &&
638
+ issue?.code !== 'unrecognized_keys',
639
+ code: 'CONFIG_INVALID_LLM_CAPABILITIES',
640
+ details: (p) => ({ model: p[2], expected: 'object' }),
641
+ },
642
+ {
643
+ test: (p) => p.length >= 4 && p[0] === 'llm' && p[1] === 'models' && p[3] === 'capabilities',
644
+ code: 'CONFIG_INVALID_LLM_CAPABILITY',
645
+ details: (p) => ({ model: p[2], capability: p[4] ?? '', expected: 'boolean' }),
646
+ },
647
+ // LLM routing per-key errors
648
+ {
649
+ test: (p) => p.length >= 4 && p[0] === 'llm' && p[1] === 'routing' && p[2] === 'taskToModel',
650
+ code: 'CONFIG_INVALID_TASK_TO_MODEL_VALUE',
651
+ details: (p) => ({ task: p[3], expected: 'string' }),
652
+ },
653
+ {
654
+ test: (p) => p.length >= 4 && p[0] === 'llm' && p[1] === 'routing' && p[2] === 'phaseToModel',
655
+ code: 'CONFIG_INVALID_PHASE_TO_MODEL_VALUE',
656
+ details: (p) => ({ phase: p[3], expected: 'string' }),
657
+ },
658
+ ];
659
+ function resolveDynamicErrorCode(path, issue) {
660
+ for (const pattern of DYNAMIC_PATTERNS) {
661
+ if (pattern.test(path, issue)) {
662
+ const details = pattern.details(path);
663
+ // For unrecognized_keys, inject the unknown key into details
664
+ if (issue?.code === 'unrecognized_keys') {
665
+ const keys = issue.keys;
666
+ if (keys && keys.length > 0 && !details.capability) {
667
+ details.capability = keys[0];
668
+ }
669
+ }
670
+ return { code: pattern.code, details };
671
+ }
672
+ }
673
+ return undefined;
674
+ }
675
+ /**
676
+ * Maps a Zod validation issue to a ConfigError with the correct error code.
677
+ */
678
+ export function zodIssueToConfigError(issue) {
679
+ // 1. Custom issues from .superRefine() carry configErrorCode in params
680
+ if (issue.code === 'custom') {
681
+ const params = issue.params;
682
+ if (params?.configErrorCode && typeof params.configErrorCode === 'string') {
683
+ const details = {};
684
+ for (const [k, v] of Object.entries(params)) {
685
+ if (k !== 'configErrorCode' && typeof v === 'string') {
686
+ details[k] = v;
687
+ }
688
+ }
689
+ return new ConfigError(params.configErrorCode, details);
690
+ }
691
+ }
692
+ const path = issue.path.map(String);
693
+ const pathKey = path.join('.');
694
+ // 2. Handle unrecognized_keys (server .strict())
695
+ if (issue.code === 'unrecognized_keys') {
696
+ const keys = issue.keys;
697
+ if (pathKey === 'server' && keys && keys.length > 0) {
698
+ return new ConfigError('CONFIG_INVALID_SERVER_UNKNOWN_KEY', { key: keys[0] });
699
+ }
700
+ const staticCode = ERROR_CODE_MAP.get(pathKey);
701
+ if (staticCode) {
702
+ const details = {};
703
+ if (keys && keys.length > 0)
704
+ details.key = keys[0];
705
+ return new ConfigError(staticCode, details);
706
+ }
707
+ }
708
+ // 3. Dynamic pattern matching (before static to allow more specific matches)
709
+ const dynamic = resolveDynamicErrorCode(path, issue);
710
+ if (dynamic) {
711
+ const details = { ...dynamic.details };
712
+ if (issue.code === 'invalid_type') {
713
+ details.expected = issue.expected;
714
+ }
715
+ else if (issue.code === 'invalid_element') {
716
+ details.expected = issue.options?.join('|') ?? '';
717
+ }
718
+ return new ConfigError(dynamic.code, details);
719
+ }
720
+ // 4. Static map lookup
721
+ const staticCode = ERROR_CODE_MAP.get(pathKey);
722
+ if (staticCode) {
723
+ const details = {};
724
+ if (issue.code === 'invalid_type') {
725
+ details.expected = issue.expected;
726
+ }
727
+ else if (issue.code === 'invalid_value') {
728
+ details.expected = String(issue.expected ?? '');
729
+ }
730
+ else if (issue.code === 'invalid_element') {
731
+ details.expected = issue.options?.join('|') ?? '';
732
+ }
733
+ return new ConfigError(staticCode, details);
734
+ }
735
+ // 4. Fallback
736
+ return new ConfigError('CONFIG_INVALID_ROOT', { expected: 'valid configuration' });
737
+ }
738
+ //# sourceMappingURL=schema.js.map