@wrongstack/core 0.265.1 → 0.268.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 (75) hide show
  1. package/dist/{agent-bridge-DrkBxszZ.d.ts → agent-bridge-UhojbpWx.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-DM2pP-B6.d.ts → agent-subagent-runner-Bvtf1o9K.d.ts} +25 -7
  3. package/dist/{brain-BXd_61kQ.d.ts → brain-69wzMKp1.d.ts} +73 -1
  4. package/dist/{compactor-B8pOf45Y.d.ts → compactor-CBQAJoDc.d.ts} +19 -1
  5. package/dist/{config-BMCj_XDs.d.ts → config-VKfOZ-6X.d.ts} +122 -3
  6. package/dist/{context-MRk5PhNv.d.ts → context-C0U8B9NF.d.ts} +88 -1
  7. package/dist/coordination/index.d.ts +57 -161
  8. package/dist/coordination/index.js +471 -177
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +26 -25
  11. package/dist/defaults/index.js +1818 -844
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/execution/index.d.ts +72 -16
  14. package/dist/execution/index.js +1270 -265
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/execution/prompt-enhancer.d.ts +1 -1
  17. package/dist/extension/index.d.ts +7 -6
  18. package/dist/global-mailbox-KByEFFBa.d.ts +663 -0
  19. package/dist/{goal-preamble-DvHDSKSe.d.ts → goal-preamble-CrYjmdw4.d.ts} +28 -11
  20. package/dist/{goal-store-DtLMySNb.d.ts → goal-store-Y_zdLZ3q.d.ts} +1 -1
  21. package/dist/hq/index.d.ts +195 -0
  22. package/dist/hq/index.js +1884 -0
  23. package/dist/hq/index.js.map +1 -0
  24. package/dist/index-BfaS-f_m.d.ts +82 -0
  25. package/dist/{index-B-ch8K9C.d.ts → index-CtQnmkaS.d.ts} +8 -8
  26. package/dist/{index-CEDeNodM.d.ts → index-gCv830d7.d.ts} +5 -5
  27. package/dist/index.d.ts +124 -47
  28. package/dist/index.js +5600 -2662
  29. package/dist/index.js.map +1 -1
  30. package/dist/infrastructure/index.d.ts +6 -6
  31. package/dist/infrastructure/index.js +117 -19
  32. package/dist/infrastructure/index.js.map +1 -1
  33. package/dist/kernel/index.d.ts +10 -9
  34. package/dist/kernel/index.js.map +1 -1
  35. package/dist/{pipeline-DPDxH_7m.d.ts → mailbox-types-Ct2hJq0P.d.ts} +1 -244
  36. package/dist/{mcp-servers-2x4w6Jn9.d.ts → mcp-servers-HT3Fi7Bl.d.ts} +10 -4
  37. package/dist/models/index.d.ts +5 -5
  38. package/dist/models/index.js +33 -3
  39. package/dist/models/index.js.map +1 -1
  40. package/dist/{models-registry-DmJlKuNp.d.ts → models-registry-Bvcl3Vaa.d.ts} +1 -1
  41. package/dist/{multi-agent-coordinator-DyCkCZnU.d.ts → multi-agent-coordinator-BACjsmkC.d.ts} +1 -1
  42. package/dist/{null-fleet-bus-CG9QY2aP.d.ts → null-fleet-bus-DA7fvhUg.d.ts} +14 -9
  43. package/dist/observability/index.d.ts +2 -2
  44. package/dist/{parallel-eternal-engine-Jw9uhEoT.d.ts → parallel-eternal-engine-Ci71gYu_.d.ts} +11 -15
  45. package/dist/{path-resolver-Dy2ej-gE.d.ts → path-resolver-O1IJnmKE.d.ts} +4 -3
  46. package/dist/{permission-B9SB45lp.d.ts → permission-Bd-57Lbl.d.ts} +1 -1
  47. package/dist/{permission-policy-CkjSXabK.d.ts → permission-policy-uNXC6Kge.d.ts} +2 -3
  48. package/dist/pipeline-BDNvENyV.d.ts +245 -0
  49. package/dist/{plan-templates-CzD9GnAU.d.ts → plan-templates-EMsalEtN.d.ts} +5 -5
  50. package/dist/{llm-selector-C0tfTCUe.d.ts → provider-model-resolve-CEb9x886.d.ts} +40 -3
  51. package/dist/{provider-runner-DMa70ODu.d.ts → provider-runner-DWJbpo70.d.ts} +3 -3
  52. package/dist/{retry-policy-CN0khdlj.d.ts → retry-policy-C3s_lvdK.d.ts} +1 -1
  53. package/dist/sdd/index.d.ts +9 -8
  54. package/dist/sdd/index.js +44 -14
  55. package/dist/sdd/index.js.map +1 -1
  56. package/dist/{secret-vault-B2yw84VT.d.ts → secret-vault-Cgduf5xL.d.ts} +2 -2
  57. package/dist/security/index.d.ts +5 -67
  58. package/dist/security/index.js +129 -99
  59. package/dist/security/index.js.map +1 -1
  60. package/dist/{selector-CzHh_igB.d.ts → selector-47LBnBVk.d.ts} +1 -1
  61. package/dist/{session-event-bridge-BUI6Jf-4.d.ts → session-event-bridge-Cw7oqmW2.d.ts} +1 -1
  62. package/dist/{session-reader-CMgdMSRP.d.ts → session-reader-DD4v2Obw.d.ts} +1 -1
  63. package/dist/storage/index.d.ts +14 -12
  64. package/dist/storage/index.js +144 -120
  65. package/dist/storage/index.js.map +1 -1
  66. package/dist/tools/index.d.ts +4 -2
  67. package/dist/tools/index.js +166 -31
  68. package/dist/tools/index.js.map +1 -1
  69. package/dist/types/index.d.ts +20 -19
  70. package/dist/types/index.js +1358 -476
  71. package/dist/types/index.js.map +1 -1
  72. package/dist/utils/index.d.ts +472 -405
  73. package/dist/utils/index.js +2321 -1193
  74. package/dist/utils/index.js.map +1 -1
  75. package/package.json +5 -1
@@ -1,25 +1,366 @@
1
- import { s as TodoItem, M as Message, J as JSONSchema } from '../context-MRk5PhNv.js';
1
+ import { C as Context, z as ContextEvidenceState, k as ToolOutputMetadata, J as JSONSchema, M as Message, w as TodoItem } from '../context-C0U8B9NF.js';
2
+ export { H as HttpDispatcher, a as HttpsAgentAsDispatcher } from '../dispatcher-types.d-BBeXBQgS.js';
3
+ export { toErrorMessage } from './error.js';
4
+ export { expectDefined } from './expect-defined.js';
5
+ import { v as CustomModelDefinition, a as ModelsDevPayload } from '../config-VKfOZ-6X.js';
2
6
  export { T as TaskItem, c as computeTaskItemProgress, f as formatTaskList, a as formatTaskProgress } from '../task-format-vGOIftmK.js';
3
7
  export { a as WstackPathOptions, W as WstackPaths, p as projectHash, b as projectSlug, r as resolveWstackPaths, w as wstackGlobalRoot } from '../wstack-paths-hOpNLmvf.js';
4
- export { expectDefined } from './expect-defined.js';
5
- export { toErrorMessage } from './error.js';
6
- import { a as ModelsDevPayload, u as CustomModelDefinition } from '../config-BMCj_XDs.js';
7
- export { H as HttpDispatcher, a as HttpsAgentAsDispatcher } from '../dispatcher-types.d-BBeXBQgS.js';
8
8
  export { a as TaskPriority, b as TaskStatus, T as TaskType } from '../task-graph-u1q9Jkyk.js';
9
9
  import 'node:https';
10
10
  import 'undici';
11
11
 
12
- interface AtomicWriteOptions {
13
- mode?: number | undefined;
14
- encoding?: BufferEncoding | undefined;
12
+ /**
13
+ * Exhaustiveness check for discriminated union switches.
14
+ * Place in the `default` branch of a switch over a union type
15
+ * to get a compile-time error when a new variant is added.
16
+ *
17
+ * @example
18
+ * switch (block.type) {
19
+ * case 'text': return renderText(block);
20
+ * case 'tool_use': return renderToolUse(block);
21
+ * default: return assertNever(block);
22
+ * }
23
+ */
24
+ declare function assertNever(x: never, message?: string): never;
25
+
26
+ interface AtomicWriteOptions {
27
+ mode?: number | undefined;
28
+ encoding?: BufferEncoding | undefined;
29
+ }
30
+ interface FileLockOptions {
31
+ timeoutMs?: number | undefined;
32
+ staleMs?: number | undefined;
33
+ }
34
+ declare function atomicWrite(targetPath: string, content: string | Uint8Array, opts?: AtomicWriteOptions): Promise<void>;
35
+ declare function ensureDir(dir: string): Promise<void>;
36
+ declare function withFileLock<T>(targetPath: string, fn: () => Promise<T>, opts?: FileLockOptions): Promise<T>;
37
+
38
+ /**
39
+ * Build a sanitized child-process environment.
40
+ *
41
+ * The bash/exec tools and MCP stdio transports execute LLM-generated or
42
+ * configured commands. The parent process carries provider API keys
43
+ * (ANTHROPIC_API_KEY, OPENAI_API_KEY, ...), VCS tokens (GITHUB_TOKEN),
44
+ * and cloud credentials. Forwarding those to a child is an exfiltration
45
+ * vector even with `permission: 'confirm'` — a compromised MCP server
46
+ * or a cleverly composed shell pipeline can leak secrets.
47
+ *
48
+ * Strategy: copy a small, explicit allowlist of variables that real builds
49
+ * need, then copy anything else that does NOT look secret-bearing. This
50
+ * preserves user-friendly behavior (locale, terminal, npm config) while
51
+ * blocking the obvious leak channels.
52
+ *
53
+ * Override with `WRONGSTACK_CHILD_ENV_PASSTHROUGH=1` to forward the full
54
+ * parent environment unchanged (opt-in for advanced users who understand
55
+ * the risk).
56
+ */
57
+ interface BuildChildEnvOptions {
58
+ /** Session ID to inject as WRONGSTACK_SESSION_ID. */
59
+ sessionId?: string | undefined;
60
+ /** Additional env vars to merge (takes priority over filtered parent env). */
61
+ extra?: NodeJS.ProcessEnv | undefined;
62
+ }
63
+ /**
64
+ * Build a filtered child-process environment suitable for bash, exec, and
65
+ * MCP server subprocesses. Strips API keys, tokens, and other credentials
66
+ * while preserving system/tooling variables.
67
+ */
68
+ declare function buildChildEnv(optsOrSessionId?: BuildChildEnvOptions | string): NodeJS.ProcessEnv;
69
+
70
+ declare const color: {
71
+ reset: (s: string) => string;
72
+ bold: (s: string) => string;
73
+ dim: (s: string) => string;
74
+ italic: (s: string) => string;
75
+ underline: (s: string) => string;
76
+ red: (s: string) => string;
77
+ green: (s: string) => string;
78
+ yellow: (s: string) => string;
79
+ blue: (s: string) => string;
80
+ magenta: (s: string) => string;
81
+ cyan: (s: string) => string;
82
+ gray: (s: string) => string;
83
+ amber: (s: string) => string;
84
+ pink: (s: string) => string;
85
+ bgRed: (s: string) => string;
86
+ bgGreen: (s: string) => string;
87
+ };
88
+ declare function stripAnsi(s: string): string;
89
+
90
+ type JsonObject = Record<string, unknown>;
91
+ type JsonPathSegment = string | number;
92
+ type JsonPath = readonly JsonPathSegment[];
93
+ declare function readJsonObjectFile(filePath: string): Promise<JsonObject>;
94
+ declare function jsonObjectFileExists(filePath: string): Promise<boolean>;
95
+ declare function writeJsonObjectFile(filePath: string, value: JsonObject): Promise<void>;
96
+ declare function updateJsonObjectFile(filePath: string, mutator: (config: JsonObject) => void | JsonObject | Promise<void | JsonObject>): Promise<JsonObject>;
97
+ declare function getJsonPath(root: unknown, path: JsonPath): unknown;
98
+ declare function setJsonPath(root: JsonObject, path: JsonPath, value: unknown): JsonObject;
99
+ declare function removeJsonPath(root: JsonObject, path: JsonPath): boolean;
100
+ declare function setJsonPathInFile(filePath: string, path: JsonPath, value: unknown): Promise<JsonObject>;
101
+ declare function removeJsonPathInFile(filePath: string, path: JsonPath): Promise<JsonObject>;
102
+ declare function isJsonObject(value: unknown): value is JsonObject;
103
+
104
+ declare function createContextEvidenceState(): ContextEvidenceState;
105
+ interface RecordToolOutputEvidenceInput {
106
+ toolUseId: string;
107
+ toolName: string;
108
+ input: unknown;
109
+ content: string;
110
+ ok: boolean;
111
+ outputBytes?: number | undefined;
112
+ outputTokens?: number | undefined;
113
+ outputLines?: number | undefined;
114
+ }
115
+ declare function recordUserIntentEvidence(ctx: Context, text: string): void;
116
+ declare function recordToolOutputEvidence(ctx: Context, input: RecordToolOutputEvidenceInput): ToolOutputMetadata;
117
+ declare function markAssistantReferencedEvidence(ctx: Context, text: string): void;
118
+ declare function buildContextEvidenceDigest(ctx: Context): string;
119
+ declare function repeatedReadPressure(ctx: Context): number;
120
+
121
+ /**
122
+ * Deep merge utility — safely merges nested objects with configurable
123
+ * conflict resolution, array merging, and prototype-pollution guarding.
124
+ *
125
+ * Used by:
126
+ * - config-loader (config layer merging with primitive-array concatenation)
127
+ * - secret-vault (config patching)
128
+ * - json-path (json_merge tool with prefer-base / prefer-patch semantics)
129
+ *
130
+ * @module utils/deep-merge
131
+ */
132
+ declare const FORBIDDEN_PROTO_KEYS: Set<string>;
133
+ /** True when every element is a primitive or null (no nested objects/arrays). */
134
+ declare function isPrimitiveArray(a: unknown[]): boolean;
135
+ interface DeepMergeOptions {
136
+ /**
137
+ * Which side wins on collision for scalars and arrays.
138
+ *
139
+ * - `'prefer-patch'` (default): patch value replaces base value.
140
+ * - `'prefer-base'`: base value is kept, patch value is ignored.
141
+ */
142
+ conflictResolution?: 'prefer-base' | 'prefer-patch';
143
+ /**
144
+ * How to handle array values.
145
+ *
146
+ * - `'replace'` (default): patch array replaces base array entirely.
147
+ * - `'concat-primitives'`: when both values are primitive arrays,
148
+ * they are concatenated and deduped (via Set). Non-primitive
149
+ * arrays still replace the base wholesale.
150
+ */
151
+ arrayMode?: 'replace' | 'concat-primitives';
152
+ /**
153
+ * Skip prototype-pollution keys (`__proto__`, `constructor`, etc.).
154
+ * Enabled by default. Only disable when you control both inputs
155
+ * and the keyset (e.g. when merging trusted JSON schemas).
156
+ */
157
+ protectProto?: boolean;
158
+ /**
159
+ * Optional callback fired when a non-primitive (object) array is
160
+ * replaced wholesale (only relevant with `arrayMode: 'concat-primitives'`).
161
+ * Receives the key name, existing array length, and patch array length.
162
+ * Used by config-loader for debug logging.
163
+ */
164
+ onNonPrimitiveArrayReplace?: (key: string, existingLen: number, patchLen: number) => void;
165
+ }
166
+ /**
167
+ * Recursively merge `patch` into `base`, returning a new object.
168
+ *
169
+ * - Nested plain objects are merged recursively.
170
+ * - Arrays are handled per `options.arrayMode`.
171
+ * - Scalar collisions are resolved per `options.conflictResolution`.
172
+ * - `null` and non-object values in `patch` replace the base value
173
+ * (unless `conflictResolution` is `'prefer-base'`).
174
+ * - Keys in `base` that are absent from `patch` are preserved.
175
+ * - `FORBIDDEN_PROTO_KEYS` are skipped in the patch (unless
176
+ * `options.protectProto` is set to `false`).
177
+ *
178
+ * The function is generic over `T extends Record<string, unknown>` for
179
+ * callers that pass typed config objects, but the runtime signature
180
+ * also accepts `unknown` inputs (used by the json-path plugin).
181
+ */
182
+ declare function deepMerge<T extends Record<string, unknown>>(base: T, patch: Record<string, unknown>, options?: DeepMergeOptions): T;
183
+ declare function deepMerge(base: unknown, patch: unknown, options?: DeepMergeOptions): unknown;
184
+
185
+ /**
186
+ * Myers diff with unified-format output. No external dependencies.
187
+ * Operates on arrays of lines (newline-terminated or stripped).
188
+ */
189
+ interface UnifiedDiffOptions {
190
+ context?: number | undefined;
191
+ fromFile?: string | undefined;
192
+ toFile?: string | undefined;
193
+ }
194
+ declare function unifiedDiff(oldText: string, newText: string, opts?: UnifiedDiffOptions): string;
195
+
196
+ /**
197
+ * Resolve `pattern` to the set of concrete file paths it matches.
198
+ * Literal paths (no glob chars) are returned as-is.
199
+ *
200
+ * @example
201
+ * await expandGlob('src/**\/*.ts') // → ['src/a.ts', 'src/b/c.ts', ...]
202
+ * await expandGlob('foo.txt') // → ['foo.txt']
203
+ */
204
+ declare function expandGlob(pattern: string): Promise<string[]>;
205
+
206
+ declare function compileGlob(pattern: string): RegExp;
207
+ declare function matchGlob(pattern: string, input: string): boolean;
208
+ declare function matchAny(patterns: string[], input: string): boolean;
209
+
210
+ /**
211
+ * Shared IP-address guards for SSRF protection.
212
+ *
213
+ * Exported so `fetch.ts` (tools), `web-search/index.ts` (plugins), and any
214
+ * other package that needs to validate IPs can all consume the same logic.
215
+ * Any future additions (e.g. extra CIDR blocks) need only be made here.
216
+ */
217
+ /**
218
+ * True if `addr` is in a private / loopback / link-local / reserved / CGNAT /
219
+ * multicast range. `net.isIP` is called by the caller first so `addr` is
220
+ * guaranteed to be a canonical dotted-quad at this point.
221
+ */
222
+ declare function isPrivateIPv4(addr: string): boolean;
223
+ /**
224
+ * True if `raw` (an IPv6 literal, already lowercased) is loopback / unique-local /
225
+ * link-local / unspecified / IPv4-mapped-private.
226
+ */
227
+ declare function isPrivateIPv6(raw: string): boolean;
228
+ /**
229
+ * Expand an IPv6 string into exactly 8 16-bit numbers. Handles `::` compression.
230
+ * Returns null on malformed input — caller should treat that as "block".
231
+ */
232
+ declare function expandIPv6(addr: string): number[] | null;
233
+ /**
234
+ * Convenience: throw if `hostname` resolves to a private / loopback IP.
235
+ * Use as a pre-flight check before opening a socket.
236
+ *
237
+ * ⚠️ This is not sufficient alone — connections must also use a pinned
238
+ * dispatcher (so the OS re-uses the already-resolved address) or the same
239
+ * check must be applied after every redirect hop. See `guardedLookup` in
240
+ * `fetch.ts` for the connection-level enforcement.
241
+ */
242
+ declare function assertNotPrivateHost(hostname: string): Promise<void>;
243
+
244
+ /**
245
+ * Attempt to close an incomplete JSON object string by auto-closing braces
246
+ * and completing any unclosed double-quoted string values.
247
+ *
248
+ * Strategy:
249
+ * 1. Compute origOpen from the ORIGINAL input (how many braces are unclosed).
250
+ * 2. Add that many closing braces. If result is now valid JSON → return it.
251
+ * 3. If still invalid: trim trailing whitespace, strip trailing backslash.
252
+ * 4. Walk backwards to detect an unclosed string value.
253
+ * - Quote followed by `:` → key-name, skip
254
+ * - Quote followed by `,` `}` or end-of-string → toggle in/out of string
255
+ * 5. If we end INSIDE a string (unclosed opening `"`), append `"` + origOpen `}`.
256
+ *
257
+ * Known limitations:
258
+ * - Strings whose content ends with a `"` character cannot be repaired
259
+ * (algorithm can't distinguish content-`"` from string-terminator `"`).
260
+ * - Input ending in bare `:` (incomplete value expression) can't be meaningfully repaired.
261
+ * - Bare `{` returns unchanged.
262
+ * - If origOpen=0 (braces balanced) but string is unclosed, repair is skipped
263
+ * (the input would be valid JSON per JSON.parse, so it's returned as-is).
264
+ */
265
+ declare function completePartialObject(s: string): string;
266
+
267
+ /**
268
+ * Minimal JSON Schema validator — covers the subset needed for plugin
269
+ * configSchema validation and tool inputSchema sanity checks. Intentionally
270
+ * small (~80 lines, zero deps) and tolerant: unknown keywords are ignored so
271
+ * authors can mix in non-standard extensions without breaking validation.
272
+ *
273
+ * NOT for full JSON Schema 2020-12 conformance. If a plugin needs $ref,
274
+ * conditional schemas, format validation, or anything else exotic, it should
275
+ * bring its own ajv-based validator and call this only for the cheap path.
276
+ */
277
+
278
+ interface ValidationError {
279
+ path: string;
280
+ message: string;
281
+ }
282
+ interface ValidationResult {
283
+ ok: boolean;
284
+ errors: ValidationError[];
285
+ }
286
+ declare function validateAgainstSchema(value: unknown, schema: JSONSchema): ValidationResult;
287
+
288
+ /**
289
+ * Merge per-provider `customModels` into top-level `configModels`.
290
+ *
291
+ * Keys present in `configModels` always win over `providerCustomModels`
292
+ * when the same model id appears in both places. This lets the user
293
+ * override provider-attached definitions from the top-level config.
294
+ *
295
+ * Pure: never mutates its inputs.
296
+ */
297
+ declare function mergeCustomModelDefs(providerCustomModels: Record<string, CustomModelDefinition> | undefined, configModels: Record<string, CustomModelDefinition> | undefined): Record<string, CustomModelDefinition> | undefined;
298
+
299
+ /**
300
+ * Deep-merge a curated `overlay` payload on top of a `base` payload (both in
301
+ * the models.dev `api.json` shape). The overlay always wins: it can add
302
+ * providers/models the base lacks and override fields the base gets wrong.
303
+ *
304
+ * Precedence rules:
305
+ * - Provider present in both → scalar fields (`name`, `npm`, `api`, `env`,
306
+ * `doc`) come from the overlay when set; `models` maps merge by model id.
307
+ * - Provider only in the overlay → added wholesale.
308
+ * - Model present in both → overlay model fields override base model fields
309
+ * (`{ ...base, ...overlay }`), with the nested `limit` / `cost` /
310
+ * `modalities` objects merged one level deeper so an overlay can fix just
311
+ * `limit.context` without restating the rest of the model.
312
+ * - Model only in the overlay → added.
313
+ *
314
+ * Pure: never mutates its inputs.
315
+ */
316
+ declare function mergeModelsPayload(base: ModelsDevPayload, overlay: ModelsDevPayload): ModelsDevPayload;
317
+
318
+ interface MessageRepairReport {
319
+ changed: boolean;
320
+ removedToolUses: string[];
321
+ removedToolResults: string[];
322
+ removedMessages: number;
323
+ }
324
+ interface MessageRepairResult {
325
+ messages: Message[];
326
+ report: MessageRepairReport;
327
+ }
328
+ /**
329
+ * Repair provider-level tool-call adjacency invariants.
330
+ *
331
+ * Anthropic requires every assistant `tool_use` block to have a matching
332
+ * `tool_result` block in the immediately following user message. Manual
333
+ * context surgery (summary/prune) can cut through the middle of such an
334
+ * exchange. This function removes only the now-orphaned protocol blocks,
335
+ * preserving surrounding text/images/thinking blocks where possible.
336
+ */
337
+ declare function repairToolUseAdjacency(messages: Message[]): MessageRepairResult;
338
+
339
+ type NewlineStyle = 'lf' | 'crlf' | 'cr';
340
+ declare function detectNewlineStyle(text: string): NewlineStyle;
341
+ declare function toStyle(text: string, style: NewlineStyle): string;
342
+ declare function normalizeToLf(text: string): string;
343
+
344
+ /**
345
+ * Compile a user-supplied regex with conservative bounds against ReDoS.
346
+ *
347
+ * Duplicated from @wrongstack/tools/_regex.ts to avoid a circular
348
+ * dependency (tools depends on core, not vice versa). Keep both copies
349
+ * in sync if the heuristics change.
350
+ *
351
+ * V8's regex engine is backtracking-based and cannot interrupt a
352
+ * synchronous match — a pattern like `(a+)+$` against a sufficiently
353
+ * long line will pin a worker for seconds.
354
+ */
355
+ interface CompileResult {
356
+ ok: true;
357
+ regex: RegExp;
15
358
  }
16
- interface FileLockOptions {
17
- timeoutMs?: number | undefined;
18
- staleMs?: number | undefined;
359
+ interface CompileFail {
360
+ ok: false;
361
+ reason: string;
19
362
  }
20
- declare function atomicWrite(targetPath: string, content: string | Uint8Array, opts?: AtomicWriteOptions): Promise<void>;
21
- declare function ensureDir(dir: string): Promise<void>;
22
- declare function withFileLock<T>(targetPath: string, fn: () => Promise<T>, opts?: FileLockOptions): Promise<T>;
363
+ declare function compileUserRegex(pattern: string, flags: string): CompileResult | CompileFail;
23
364
 
24
365
  interface SafeParseResult<T> {
25
366
  ok: boolean;
@@ -40,30 +381,19 @@ declare function safeStringify(value: unknown, pretty?: boolean): string;
40
381
  */
41
382
  declare function sanitizeJsonString(s: string): string | null;
42
383
 
43
- type NewlineStyle = 'lf' | 'crlf' | 'cr';
44
- declare function detectNewlineStyle(text: string): NewlineStyle;
45
- declare function toStyle(text: string, style: NewlineStyle): string;
46
- declare function normalizeToLf(text: string): string;
384
+ /** Resolve a promise after `ms` milliseconds. Prefer this over raw
385
+ * `setTimeout` wrappers so all delay sites use a single implementation
386
+ * and an abortable variant can be introduced without a codebase-wide hunt. */
387
+ declare function sleep(ms: number): Promise<void>;
47
388
 
48
- declare const color: {
49
- reset: (s: string) => string;
50
- bold: (s: string) => string;
51
- dim: (s: string) => string;
52
- italic: (s: string) => string;
53
- underline: (s: string) => string;
54
- red: (s: string) => string;
55
- green: (s: string) => string;
56
- yellow: (s: string) => string;
57
- blue: (s: string) => string;
58
- magenta: (s: string) => string;
59
- cyan: (s: string) => string;
60
- gray: (s: string) => string;
61
- amber: (s: string) => string;
62
- pink: (s: string) => string;
63
- bgRed: (s: string) => string;
64
- bgGreen: (s: string) => string;
65
- };
66
- declare function stripAnsi(s: string): string;
389
+ /**
390
+ * String utilities shared across the WrongStack codebase.
391
+ */
392
+ /**
393
+ * Truncate a string to at most `max` characters, appending an ellipsis if it
394
+ * was longer. Returns the original string unchanged when it fits.
395
+ */
396
+ declare function truncate(s: string, max: number): string;
67
397
 
68
398
  /**
69
399
  * TTY detection helpers — the single source of truth for "is this process
@@ -142,232 +472,81 @@ declare function setRawMode(input: NodeJS.ReadStream, mode: boolean): boolean;
142
472
  * through — so agent-turn output (spinner, renderer) is untouched.
143
473
  */
144
474
  interface OutputLineGuard {
145
- /** Clear the current input row right before an out-of-band write. */
146
- suspend(): void;
147
- /** Repaint the prompt + in-progress draft right after the write. */
148
- resume(): void;
149
- }
150
- /**
151
- * Register (or clear, with `null`) the guard that brackets out-of-band
152
- * writes. Installed by {@link writeOut}/{@link writeErr} consumers — in
153
- * practice the CLI's readline input reader — only while a prompt is live.
154
- * Idempotent; the most recent caller wins.
155
- */
156
- declare function setOutputLineGuard(guard: OutputLineGuard | null): void;
157
- /**
158
- * Write `s` to `stream` (defaults to `process.stdout`). Returns `false`
159
- * when the stream is missing or doesn't expose `write` so callers can
160
- * degrade silently under hostile host environments (closed pipe, mock
161
- * injects `null`, test replaces the stream with a stub).
162
- *
163
- * Why a helper:
164
- * 1. **Single seam for output capture in tests** — stub `writeOut` once
165
- * and assert on what the rest of the codebase intended to print,
166
- * without spying on `process.stdout.write` (which is brittle and
167
- * leaks across parallel test files).
168
- * 2. **Stream swap without grep** — routing the CLI's output to a
169
- * logger or `out.log` becomes a one-line change at process boot.
170
- * 3. **Defensive default** — closes the "what if `process.stdout` is
171
- * `null`" gap that currently exists at ~50 call sites that just
172
- * call `process.stdout.write(s)` and crash on certain Windows
173
- * redirect invocations.
174
- *
175
- * Call-site migration is staged: this commit introduces the helper, a
176
- * follow-up commit replaces the 50+ `process.stdout.write(...)` sites
177
- * with `writeOut(...)`. Until that migration lands, both forms coexist
178
- * and `writeOut` is the preferred form for new code.
179
- */
180
- declare function writeOut(s: string, stream?: NodeJS.WriteStream): boolean;
181
- /**
182
- * Symmetric partner of `writeOut` for the standard error stream. Same shape,
183
- * same defensive contract, same single-seam-for-tests story — just defaults to
184
- * `process.stderr` instead of `process.stdout`.
185
- *
186
- * Use this in code paths that emit error/diagnostic/warning text. Keeping
187
- * these two helpers split (rather than a single `writeTo(s, stream)`) means
188
- * the call site reads as a clear intent signal: "I am writing an error" vs.
189
- * "I am writing a result" — which matters for callers that decide between
190
- * stdout/stderr routing (e.g. `--quiet` flags, log-level filtering,
191
- * structured-log rewriters that fork on stream).
192
- *
193
- * Stderr writes from the core logger (see `infrastructure/logger.ts`) and from
194
- * the TUI guard (see `tui/run-tui.ts`) used to call `process.stderr.write`
195
- * directly. Routing them through this helper lets tests stub the stream at
196
- * one boundary and lets future logging middleware (e.g. a JSON-line rewriter)
197
- * swap the destination for the entire process in one place.
198
- */
199
- declare function writeErr(s: string, stream?: NodeJS.WriteStream): boolean;
200
-
201
- /**
202
- * Canonical text rendering of the live todo list, shared by the CLI's
203
- * `/todos` slash command and the TUI's auto-echo (which prints the same
204
- * snapshot to chat history each time the `todo` tool mutates the list).
205
- *
206
- * Layout: a header line with the `done/total done` count, then one row
207
- * per item — `[ ]` pending, `[~]` in-progress, `[x]` completed. In-
208
- * progress rows prefer `activeForm` ("Building the project") over the
209
- * imperative `content` ("Build the project") when present.
210
- *
211
- * Returned as a single newline-joined string so callers can hand it
212
- * straight to a history dispatcher or stdout.
213
- */
214
- declare function formatTodosList(todos: TodoItem[]): string;
215
-
216
- /**
217
- * String utilities shared across the WrongStack codebase.
218
- */
219
- /**
220
- * Truncate a string to at most `max` characters, appending an ellipsis if it
221
- * was longer. Returns the original string unchanged when it fits.
222
- */
223
- declare function truncate(s: string, max: number): string;
224
-
225
- declare function compileGlob(pattern: string): RegExp;
226
- declare function matchGlob(pattern: string, input: string): boolean;
227
- declare function matchAny(patterns: string[], input: string): boolean;
228
-
229
- /**
230
- * Myers diff with unified-format output. No external dependencies.
231
- * Operates on arrays of lines (newline-terminated or stripped).
232
- */
233
- interface UnifiedDiffOptions {
234
- context?: number | undefined;
235
- fromFile?: string | undefined;
236
- toFile?: string | undefined;
237
- }
238
- declare function unifiedDiff(oldText: string, newText: string, opts?: UnifiedDiffOptions): string;
239
-
240
- /**
241
- * Build a sanitized child-process environment.
242
- *
243
- * The bash/exec tools and MCP stdio transports execute LLM-generated or
244
- * configured commands. The parent process carries provider API keys
245
- * (ANTHROPIC_API_KEY, OPENAI_API_KEY, ...), VCS tokens (GITHUB_TOKEN),
246
- * and cloud credentials. Forwarding those to a child is an exfiltration
247
- * vector even with `permission: 'confirm'` — a compromised MCP server
248
- * or a cleverly composed shell pipeline can leak secrets.
249
- *
250
- * Strategy: copy a small, explicit allowlist of variables that real builds
251
- * need, then copy anything else that does NOT look secret-bearing. This
252
- * preserves user-friendly behavior (locale, terminal, npm config) while
253
- * blocking the obvious leak channels.
254
- *
255
- * Override with `WRONGSTACK_CHILD_ENV_PASSTHROUGH=1` to forward the full
256
- * parent environment unchanged (opt-in for advanced users who understand
257
- * the risk).
258
- */
259
- interface BuildChildEnvOptions {
260
- /** Session ID to inject as WRONGSTACK_SESSION_ID. */
261
- sessionId?: string | undefined;
262
- /** Additional env vars to merge (takes priority over filtered parent env). */
263
- extra?: NodeJS.ProcessEnv | undefined;
264
- }
265
- /**
266
- * Build a filtered child-process environment suitable for bash, exec, and
267
- * MCP server subprocesses. Strips API keys, tokens, and other credentials
268
- * while preserving system/tooling variables.
269
- */
270
- declare function buildChildEnv(optsOrSessionId?: BuildChildEnvOptions | string): NodeJS.ProcessEnv;
271
-
272
- /** Resolve a promise after `ms` milliseconds. Prefer this over raw
273
- * `setTimeout` wrappers so all delay sites use a single implementation
274
- * and an abortable variant can be introduced without a codebase-wide hunt. */
275
- declare function sleep(ms: number): Promise<void>;
276
-
277
- /**
278
- * Exhaustiveness check for discriminated union switches.
279
- * Place in the `default` branch of a switch over a union type
280
- * to get a compile-time error when a new variant is added.
281
- *
282
- * @example
283
- * switch (block.type) {
284
- * case 'text': return renderText(block);
285
- * case 'tool_use': return renderToolUse(block);
286
- * default: return assertNever(block);
287
- * }
288
- */
289
- declare function assertNever(x: never, message?: string): never;
290
-
291
- /**
292
- * Deep merge utility — safely merges nested objects with configurable
293
- * conflict resolution, array merging, and prototype-pollution guarding.
294
- *
295
- * Used by:
296
- * - config-loader (config layer merging with primitive-array concatenation)
297
- * - secret-vault (config patching)
298
- * - json-path (json_merge tool with prefer-base / prefer-patch semantics)
299
- *
300
- * @module utils/deep-merge
301
- */
302
- declare const FORBIDDEN_PROTO_KEYS: Set<string>;
303
- /** True when every element is a primitive or null (no nested objects/arrays). */
304
- declare function isPrimitiveArray(a: unknown[]): boolean;
305
- interface DeepMergeOptions {
306
- /**
307
- * Which side wins on collision for scalars and arrays.
308
- *
309
- * - `'prefer-patch'` (default): patch value replaces base value.
310
- * - `'prefer-base'`: base value is kept, patch value is ignored.
311
- */
312
- conflictResolution?: 'prefer-base' | 'prefer-patch';
313
- /**
314
- * How to handle array values.
315
- *
316
- * - `'replace'` (default): patch array replaces base array entirely.
317
- * - `'concat-primitives'`: when both values are primitive arrays,
318
- * they are concatenated and deduped (via Set). Non-primitive
319
- * arrays still replace the base wholesale.
320
- */
321
- arrayMode?: 'replace' | 'concat-primitives';
322
- /**
323
- * Skip prototype-pollution keys (`__proto__`, `constructor`, etc.).
324
- * Enabled by default. Only disable when you control both inputs
325
- * and the keyset (e.g. when merging trusted JSON schemas).
326
- */
327
- protectProto?: boolean;
328
- /**
329
- * Optional callback fired when a non-primitive (object) array is
330
- * replaced wholesale (only relevant with `arrayMode: 'concat-primitives'`).
331
- * Receives the key name, existing array length, and patch array length.
332
- * Used by config-loader for debug logging.
333
- */
334
- onNonPrimitiveArrayReplace?: (key: string, existingLen: number, patchLen: number) => void;
475
+ /** Clear the current input row right before an out-of-band write. */
476
+ suspend(): void;
477
+ /** Repaint the prompt + in-progress draft right after the write. */
478
+ resume(): void;
335
479
  }
336
480
  /**
337
- * Recursively merge `patch` into `base`, returning a new object.
481
+ * Register (or clear, with `null`) the guard that brackets out-of-band
482
+ * writes. Installed by {@link writeOut}/{@link writeErr} consumers — in
483
+ * practice the CLI's readline input reader — only while a prompt is live.
484
+ * Idempotent; the most recent caller wins.
485
+ */
486
+ declare function setOutputLineGuard(guard: OutputLineGuard | null): void;
487
+ /**
488
+ * Write `s` to `stream` (defaults to `process.stdout`). Returns `false`
489
+ * when the stream is missing or doesn't expose `write` so callers can
490
+ * degrade silently under hostile host environments (closed pipe, mock
491
+ * injects `null`, test replaces the stream with a stub).
338
492
  *
339
- * - Nested plain objects are merged recursively.
340
- * - Arrays are handled per `options.arrayMode`.
341
- * - Scalar collisions are resolved per `options.conflictResolution`.
342
- * - `null` and non-object values in `patch` replace the base value
343
- * (unless `conflictResolution` is `'prefer-base'`).
344
- * - Keys in `base` that are absent from `patch` are preserved.
345
- * - `FORBIDDEN_PROTO_KEYS` are skipped in the patch (unless
346
- * `options.protectProto` is set to `false`).
493
+ * Why a helper:
494
+ * 1. **Single seam for output capture in tests** — stub `writeOut` once
495
+ * and assert on what the rest of the codebase intended to print,
496
+ * without spying on `process.stdout.write` (which is brittle and
497
+ * leaks across parallel test files).
498
+ * 2. **Stream swap without grep** routing the CLI's output to a
499
+ * logger or `out.log` becomes a one-line change at process boot.
500
+ * 3. **Defensive default** closes the "what if `process.stdout` is
501
+ * `null`" gap that currently exists at ~50 call sites that just
502
+ * call `process.stdout.write(s)` and crash on certain Windows
503
+ * redirect invocations.
347
504
  *
348
- * The function is generic over `T extends Record<string, unknown>` for
349
- * callers that pass typed config objects, but the runtime signature
350
- * also accepts `unknown` inputs (used by the json-path plugin).
505
+ * Call-site migration is staged: this commit introduces the helper, a
506
+ * follow-up commit replaces the 50+ `process.stdout.write(...)` sites
507
+ * with `writeOut(...)`. Until that migration lands, both forms coexist
508
+ * and `writeOut` is the preferred form for new code.
351
509
  */
352
- declare function deepMerge<T extends Record<string, unknown>>(base: T, patch: Record<string, unknown>, options?: DeepMergeOptions): T;
353
- declare function deepMerge(base: unknown, patch: unknown, options?: DeepMergeOptions): unknown;
510
+ declare function writeOut(s: string, stream?: NodeJS.WriteStream): boolean;
511
+ /**
512
+ * Symmetric partner of `writeOut` for the standard error stream. Same shape,
513
+ * same defensive contract, same single-seam-for-tests story — just defaults to
514
+ * `process.stderr` instead of `process.stdout`.
515
+ *
516
+ * Use this in code paths that emit error/diagnostic/warning text. Keeping
517
+ * these two helpers split (rather than a single `writeTo(s, stream)`) means
518
+ * the call site reads as a clear intent signal: "I am writing an error" vs.
519
+ * "I am writing a result" — which matters for callers that decide between
520
+ * stdout/stderr routing (e.g. `--quiet` flags, log-level filtering,
521
+ * structured-log rewriters that fork on stream).
522
+ *
523
+ * Stderr writes from the core logger (see `infrastructure/logger.ts`) and from
524
+ * the TUI guard (see `tui/run-tui.ts`) used to call `process.stderr.write`
525
+ * directly. Routing them through this helper lets tests stub the stream at
526
+ * one boundary and lets future logging middleware (e.g. a JSON-line rewriter)
527
+ * swap the destination for the entire process in one place.
528
+ */
529
+ declare function writeErr(s: string, stream?: NodeJS.WriteStream): boolean;
354
530
 
355
531
  /**
356
- * Tool output serialization utilities.
357
- * Extracted from Agent.executeTools to allow reuse and consistent output handling.
532
+ * Canonical text rendering of the live todo list, shared by the CLI's
533
+ * `/todos` slash command and the TUI's auto-echo (which prints the same
534
+ * snapshot to chat history each time the `todo` tool mutates the list).
535
+ *
536
+ * Layout: a header line with the `done/total done` count, then one row
537
+ * per item — `[ ]` pending, `[~]` in-progress, `[x]` completed. In-
538
+ * progress rows prefer `activeForm` ("Building the project") over the
539
+ * imperative `content` ("Build the project") when present.
540
+ *
541
+ * Returned as a single newline-joined string so callers can hand it
542
+ * straight to a history dispatcher or stdout.
358
543
  */
359
- interface ToolOutputSerializerOptions {
360
- perIterationOutputCapBytes?: number | undefined;
361
- estimator?: ((text: string) => number) | undefined;
362
- }
363
- declare function createToolOutputSerializer(opts?: ToolOutputSerializerOptions): {
364
- serialize: (value: unknown) => string;
365
- enforceCap: (text: string, remainingBudget: number) => {
366
- text: string;
367
- newBudget: number;
368
- };
369
- capBytes: number;
370
- };
544
+ declare function formatTodosList(todos: TodoItem[]): string;
545
+
546
+ declare function escapeGlobSubject(value: string): string;
547
+ declare function normalizePathSubject(value: string): string;
548
+ declare function isPathSubjectKey(subjectKey: string): boolean;
549
+ declare function subjectForToolInput(toolName: string, input: unknown, subjectKey?: string): string | undefined;
371
550
 
372
551
  /**
373
552
  * Estimate tokens for a tool_use block input.
@@ -480,164 +659,52 @@ declare function estimateRequestTokensCalibrated(messages: unknown, systemPrompt
480
659
  */
481
660
  declare function resetCalibration(calibrationKey?: string): void;
482
661
 
483
- interface MessageRepairReport {
484
- changed: boolean;
485
- removedToolUses: string[];
486
- removedToolResults: string[];
487
- removedMessages: number;
488
- }
489
- interface MessageRepairResult {
490
- messages: Message[];
491
- report: MessageRepairReport;
492
- }
493
- /**
494
- * Repair provider-level tool-call adjacency invariants.
495
- *
496
- * Anthropic requires every assistant `tool_use` block to have a matching
497
- * `tool_result` block in the immediately following user message. Manual
498
- * context surgery (summary/prune) can cut through the middle of such an
499
- * exchange. This function removes only the now-orphaned protocol blocks,
500
- * preserving surrounding text/images/thinking blocks where possible.
501
- */
502
- declare function repairToolUseAdjacency(messages: Message[]): MessageRepairResult;
503
-
504
662
  /**
505
- * Minimal JSON Schema validator — covers the subset needed for plugin
506
- * configSchema validation and tool inputSchema sanity checks. Intentionally
507
- * small (~80 lines, zero deps) and tolerant: unknown keywords are ignored so
508
- * authors can mix in non-standard extensions without breaking validation.
509
- *
510
- * NOT for full JSON Schema 2020-12 conformance. If a plugin needs $ref,
511
- * conditional schemas, format validation, or anything else exotic, it should
512
- * bring its own ajv-based validator and call this only for the cheap path.
663
+ * Tool output serialization utilities.
664
+ * Extracted from Agent.executeTools to allow reuse and consistent output handling.
513
665
  */
514
-
515
- interface ValidationError {
516
- path: string;
517
- message: string;
666
+ interface ToolOutputSerializerOptions {
667
+ perIterationOutputCapBytes?: number | undefined;
668
+ estimator?: ((text: string) => number) | undefined;
518
669
  }
519
- interface ValidationResult {
520
- ok: boolean;
521
- errors: ValidationError[];
670
+ interface ToolOutputSerializeContext {
671
+ toolName?: string | undefined;
672
+ input?: unknown;
522
673
  }
523
- declare function validateAgainstSchema(value: unknown, schema: JSONSchema): ValidationResult;
674
+ declare function createToolOutputSerializer(opts?: ToolOutputSerializerOptions): {
675
+ serialize: (value: unknown, context?: ToolOutputSerializeContext) => string;
676
+ enforceCap: (text: string, remainingBudget: number) => {
677
+ text: string;
678
+ newBudget: number;
679
+ };
680
+ capBytes: number;
681
+ };
524
682
 
525
- /**
526
- * Compile a user-supplied regex with conservative bounds against ReDoS.
527
- *
528
- * Duplicated from @wrongstack/tools/_regex.ts to avoid a circular
529
- * dependency (tools depends on core, not vice versa). Keep both copies
530
- * in sync if the heuristics change.
531
- *
532
- * V8's regex engine is backtracking-based and cannot interrupt a
533
- * synchronous match — a pattern like `(a+)+$` against a sufficiently
534
- * long line will pin a worker for seconds.
535
- */
536
- interface CompileResult {
537
- ok: true;
538
- regex: RegExp;
683
+ interface ToolWireDefinitionLike {
684
+ name: string;
685
+ description?: string | undefined;
686
+ inputSchema: unknown;
539
687
  }
540
- interface CompileFail {
541
- ok: false;
542
- reason: string;
688
+ interface CompactToolDefinitionForWireOptions {
689
+ /** Top-level tool description budget. */
690
+ descriptionMaxChars?: number | undefined;
691
+ /** Per-JSON-Schema `description` annotation budget. */
692
+ schemaDescriptionMaxChars?: number | undefined;
693
+ }
694
+ interface CompactWireToolDefinition {
695
+ name: string;
696
+ description: string;
697
+ inputSchema: Record<string, unknown>;
543
698
  }
544
- declare function compileUserRegex(pattern: string, flags: string): CompileResult | CompileFail;
545
-
546
- /**
547
- * Resolve `pattern` to the set of concrete file paths it matches.
548
- * Literal paths (no glob chars) are returned as-is.
549
- *
550
- * @example
551
- * await expandGlob('src/**\/*.ts') // → ['src/a.ts', 'src/b/c.ts', ...]
552
- * await expandGlob('foo.txt') // → ['foo.txt']
553
- */
554
- declare function expandGlob(pattern: string): Promise<string[]>;
555
-
556
- /**
557
- * Attempt to close an incomplete JSON object string by auto-closing braces
558
- * and completing any unclosed double-quoted string values.
559
- *
560
- * Strategy:
561
- * 1. Compute origOpen from the ORIGINAL input (how many braces are unclosed).
562
- * 2. Add that many closing braces. If result is now valid JSON → return it.
563
- * 3. If still invalid: trim trailing whitespace, strip trailing backslash.
564
- * 4. Walk backwards to detect an unclosed string value.
565
- * - Quote followed by `:` → key-name, skip
566
- * - Quote followed by `,` `}` or end-of-string → toggle in/out of string
567
- * 5. If we end INSIDE a string (unclosed opening `"`), append `"` + origOpen `}`.
568
- *
569
- * Known limitations:
570
- * - Strings whose content ends with a `"` character cannot be repaired
571
- * (algorithm can't distinguish content-`"` from string-terminator `"`).
572
- * - Input ending in bare `:` (incomplete value expression) can't be meaningfully repaired.
573
- * - Bare `{` returns unchanged.
574
- * - If origOpen=0 (braces balanced) but string is unclosed, repair is skipped
575
- * (the input would be valid JSON per JSON.parse, so it's returned as-is).
576
- */
577
- declare function completePartialObject(s: string): string;
578
-
579
- /**
580
- * Deep-merge a curated `overlay` payload on top of a `base` payload (both in
581
- * the models.dev `api.json` shape). The overlay always wins: it can add
582
- * providers/models the base lacks and override fields the base gets wrong.
583
- *
584
- * Precedence rules:
585
- * - Provider present in both → scalar fields (`name`, `npm`, `api`, `env`,
586
- * `doc`) come from the overlay when set; `models` maps merge by model id.
587
- * - Provider only in the overlay → added wholesale.
588
- * - Model present in both → overlay model fields override base model fields
589
- * (`{ ...base, ...overlay }`), with the nested `limit` / `cost` /
590
- * `modalities` objects merged one level deeper so an overlay can fix just
591
- * `limit.context` without restating the rest of the model.
592
- * - Model only in the overlay → added.
593
- *
594
- * Pure: never mutates its inputs.
595
- */
596
- declare function mergeModelsPayload(base: ModelsDevPayload, overlay: ModelsDevPayload): ModelsDevPayload;
597
-
598
- /**
599
- * Merge per-provider `customModels` into top-level `configModels`.
600
- *
601
- * Keys present in `configModels` always win over `providerCustomModels`
602
- * when the same model id appears in both places. This lets the user
603
- * override provider-attached definitions from the top-level config.
604
- *
605
- * Pure: never mutates its inputs.
606
- */
607
- declare function mergeCustomModelDefs(providerCustomModels: Record<string, CustomModelDefinition> | undefined, configModels: Record<string, CustomModelDefinition> | undefined): Record<string, CustomModelDefinition> | undefined;
608
-
609
- /**
610
- * Shared IP-address guards for SSRF protection.
611
- *
612
- * Exported so `fetch.ts` (tools), `web-search/index.ts` (plugins), and any
613
- * other package that needs to validate IPs can all consume the same logic.
614
- * Any future additions (e.g. extra CIDR blocks) need only be made here.
615
- */
616
- /**
617
- * True if `addr` is in a private / loopback / link-local / reserved / CGNAT /
618
- * multicast range. `net.isIP` is called by the caller first so `addr` is
619
- * guaranteed to be a canonical dotted-quad at this point.
620
- */
621
- declare function isPrivateIPv4(addr: string): boolean;
622
- /**
623
- * True if `raw` (an IPv6 literal, already lowercased) is loopback / unique-local /
624
- * link-local / unspecified / IPv4-mapped-private.
625
- */
626
- declare function isPrivateIPv6(raw: string): boolean;
627
- /**
628
- * Expand an IPv6 string into exactly 8 16-bit numbers. Handles `::` compression.
629
- * Returns null on malformed input — caller should treat that as "block".
630
- */
631
- declare function expandIPv6(addr: string): number[] | null;
632
699
  /**
633
- * Convenience: throw if `hostname` resolves to a private / loopback IP.
634
- * Use as a pre-flight check before opening a socket.
700
+ * Return the provider-wire version of a tool definition.
635
701
  *
636
- * ⚠️ This is not sufficient alone connections must also use a pinned
637
- * dispatcher (so the OS re-uses the already-resolved address) or the same
638
- * check must be applied after every redirect hop. See `guardedLookup` in
639
- * `fetch.ts` for the connection-level enforcement.
702
+ * Tool schemas remain structurally intact: validation keywords, property
703
+ * names, required fields, enum values, and nested shapes are preserved. The
704
+ * only reduction is on human prose annotations (`description`), which are the
705
+ * largest repeated cost in provider tool declarations.
640
706
  */
641
- declare function assertNotPrivateHost(hostname: string): Promise<void>;
707
+ declare function compactToolDefinitionForWire(tool: ToolWireDefinitionLike, opts?: CompactToolDefinitionForWireOptions): CompactWireToolDefinition;
708
+ declare function compactSchemaDescriptions(schema: unknown, maxDescriptionChars?: number): Record<string, unknown>;
642
709
 
643
- export { type AtomicWriteOptions, type BuildChildEnvOptions, type CompileFail, type CompileResult, type DeepMergeOptions, FORBIDDEN_PROTO_KEYS, type FileLockOptions, type MessageRepairReport, type MessageRepairResult, type NewlineStyle, type OutputLineGuard, type RequestTokenBreakdown, type SafeParseResult, type ToolOutputSerializerOptions, type UnifiedDiffOptions, type ValidationError, type ValidationResult, assertNever, assertNotPrivateHost, atomicWrite, buildChildEnv, color, compileGlob, compileUserRegex, completePartialObject, computeMessageTokens, createToolOutputSerializer, deepMerge, detectNewlineStyle, ensureDir, estimateMessageTokens, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, expandGlob, expandIPv6, formatTodosList, getCalibrationState, getTermSize, isInteractive, isPrimitiveArray, isPrivateIPv4, isPrivateIPv6, isStdinTTY, isStdoutTTY, matchAny, matchGlob, mergeCustomModelDefs, mergeModelsPayload, normalizeToLf, onResize, recordActualUsage, repairToolUseAdjacency, resetCalibration, safeParse, safeStringify, sanitizeJsonString, setOutputLineGuard, setRawMode, sleep, stripAnsi, toStyle, truncate, unifiedDiff, validateAgainstSchema, withFileLock, writeErr, writeOut };
710
+ export { type AtomicWriteOptions, type BuildChildEnvOptions, type CompactToolDefinitionForWireOptions, type CompactWireToolDefinition, type CompileFail, type CompileResult, type DeepMergeOptions, FORBIDDEN_PROTO_KEYS, type FileLockOptions, type JsonObject, type JsonPath, type JsonPathSegment, type MessageRepairReport, type MessageRepairResult, type NewlineStyle, type OutputLineGuard, type RecordToolOutputEvidenceInput, type RequestTokenBreakdown, type SafeParseResult, type ToolOutputSerializerOptions, type ToolWireDefinitionLike, type UnifiedDiffOptions, type ValidationError, type ValidationResult, assertNever, assertNotPrivateHost, atomicWrite, buildChildEnv, buildContextEvidenceDigest, color, compactSchemaDescriptions, compactToolDefinitionForWire, compileGlob, compileUserRegex, completePartialObject, computeMessageTokens, createContextEvidenceState, createToolOutputSerializer, deepMerge, detectNewlineStyle, ensureDir, escapeGlobSubject, estimateMessageTokens, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, expandGlob, expandIPv6, formatTodosList, getCalibrationState, getJsonPath, getTermSize, isInteractive, isJsonObject, isPathSubjectKey, isPrimitiveArray, isPrivateIPv4, isPrivateIPv6, isStdinTTY, isStdoutTTY, jsonObjectFileExists, markAssistantReferencedEvidence, matchAny, matchGlob, mergeCustomModelDefs, mergeModelsPayload, normalizePathSubject, normalizeToLf, onResize, readJsonObjectFile, recordActualUsage, recordToolOutputEvidence, recordUserIntentEvidence, removeJsonPath, removeJsonPathInFile, repairToolUseAdjacency, repeatedReadPressure, resetCalibration, safeParse, safeStringify, sanitizeJsonString, setJsonPath, setJsonPathInFile, setOutputLineGuard, setRawMode, sleep, stripAnsi, subjectForToolInput, toStyle, truncate, unifiedDiff, updateJsonObjectFile, validateAgainstSchema, withFileLock, writeErr, writeJsonObjectFile, writeOut };