@namzu/sdk 62.5.0 → 62.6.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 (67) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +1 -1
  3. package/dist/agents/file-definitions.d.ts +1 -1
  4. package/dist/agents/file-definitions.js +1 -1
  5. package/dist/agents/runAgent.d.ts +1 -1
  6. package/dist/agents/runAgent.js +1 -1
  7. package/dist/compaction/model-summary.d.ts.map +1 -1
  8. package/dist/compaction/model-summary.js +2 -1
  9. package/dist/compaction/model-summary.js.map +1 -1
  10. package/dist/compaction/verifier.d.ts.map +1 -1
  11. package/dist/compaction/verifier.js +2 -1
  12. package/dist/compaction/verifier.js.map +1 -1
  13. package/dist/eval/judge.d.ts.map +1 -1
  14. package/dist/eval/judge.js +2 -1
  15. package/dist/eval/judge.js.map +1 -1
  16. package/dist/pricing/catalogue.generated.d.ts.map +1 -1
  17. package/dist/pricing/catalogue.generated.js +19 -0
  18. package/dist/pricing/catalogue.generated.js.map +1 -1
  19. package/dist/runtime/query/auto-review/classifier.d.ts.map +1 -1
  20. package/dist/runtime/query/auto-review/classifier.js +2 -1
  21. package/dist/runtime/query/auto-review/classifier.js.map +1 -1
  22. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  23. package/dist/runtime/query/iteration/index.js +3 -0
  24. package/dist/runtime/query/iteration/index.js.map +1 -1
  25. package/dist/runtime/query/iteration/request-repair.d.ts +9 -1
  26. package/dist/runtime/query/iteration/request-repair.d.ts.map +1 -1
  27. package/dist/runtime/query/iteration/request-repair.js +29 -0
  28. package/dist/runtime/query/iteration/request-repair.js.map +1 -1
  29. package/dist/runtime/query/iteration/side-call.d.ts +16 -0
  30. package/dist/runtime/query/iteration/side-call.d.ts.map +1 -0
  31. package/dist/runtime/query/iteration/side-call.js +50 -0
  32. package/dist/runtime/query/iteration/side-call.js.map +1 -0
  33. package/dist/sandbox/file-walk.d.ts.map +1 -1
  34. package/dist/sandbox/file-walk.js +34 -17
  35. package/dist/sandbox/file-walk.js.map +1 -1
  36. package/dist/sandbox/glob-segment.d.ts +28 -0
  37. package/dist/sandbox/glob-segment.d.ts.map +1 -0
  38. package/dist/sandbox/glob-segment.js +281 -0
  39. package/dist/sandbox/glob-segment.js.map +1 -0
  40. package/dist/types/common/index.d.ts +31 -0
  41. package/dist/types/common/index.d.ts.map +1 -1
  42. package/dist/types/common/index.js +18 -2
  43. package/dist/types/common/index.js.map +1 -1
  44. package/dist/types/session/events.d.ts +10 -0
  45. package/dist/types/session/events.d.ts.map +1 -1
  46. package/dist/types/session/events.js.map +1 -1
  47. package/dist/utils/cost.d.ts +6 -0
  48. package/dist/utils/cost.d.ts.map +1 -1
  49. package/dist/utils/cost.js +19 -0
  50. package/dist/utils/cost.js.map +1 -1
  51. package/package.json +3 -3
  52. package/src/agents/file-definitions.ts +1 -1
  53. package/src/agents/runAgent.ts +1 -1
  54. package/src/compaction/model-summary.ts +2 -1
  55. package/src/compaction/verifier.ts +2 -1
  56. package/src/eval/judge.ts +2 -1
  57. package/src/pricing/catalogue.generated.ts +19 -0
  58. package/src/pricing/rates.source.json +7 -0
  59. package/src/runtime/query/auto-review/classifier.ts +2 -1
  60. package/src/runtime/query/iteration/index.ts +3 -0
  61. package/src/runtime/query/iteration/request-repair.ts +42 -1
  62. package/src/runtime/query/iteration/side-call.ts +60 -0
  63. package/src/sandbox/file-walk.ts +38 -17
  64. package/src/sandbox/glob-segment.ts +285 -0
  65. package/src/types/common/index.ts +54 -2
  66. package/src/types/session/events.ts +7 -0
  67. package/src/utils/cost.ts +26 -0
@@ -1,6 +1,7 @@
1
1
  import type { CompactionConfig } from '../config/runtime.js'
2
2
  import { collectChatCompletion } from '../provider/collect-chat-completion.js'
3
3
  import { resolveStreamIdleTimeoutMs, withStreamIdleTimeout } from '../provider/idle-timeout.js'
4
+ import { chatStreamWithSamplingRepair } from '../runtime/query/iteration/side-call.js'
4
5
  import { isOperatorUserMessage } from '../runtime/query/steering.js'
5
6
  import {
6
7
  generateRenderNonce,
@@ -347,7 +348,7 @@ export async function requestModelSummary(input: ModelSummaryInput): Promise<str
347
348
  try {
348
349
  const response = await deadline.race(
349
350
  collectChatCompletion(
350
- provider.chatStream({
351
+ chatStreamWithSamplingRepair(provider, {
351
352
  model: route.model,
352
353
  messages: [
353
354
  { role: 'system', content: SUMMARY_SYSTEM_PROMPT },
@@ -2,6 +2,7 @@ import type { CompactionConfig } from '../config/runtime.js'
2
2
  import { SECTION_HEADERS } from '../constants/compaction/index.js'
3
3
  import { collectChatCompletion } from '../provider/collect-chat-completion.js'
4
4
  import { resolveStreamIdleTimeoutMs, withStreamIdleTimeout } from '../provider/idle-timeout.js'
5
+ import { chatStreamWithSamplingRepair } from '../runtime/query/iteration/side-call.js'
5
6
  import {
6
7
  generateRenderNonce,
7
8
  nonceClosureStatement,
@@ -306,7 +307,7 @@ async function buildVerifiedSummaryWithProvider(
306
307
  ]
307
308
  try {
308
309
  response = await collectChatCompletion(
309
- provider.chatStream({
310
+ chatStreamWithSamplingRepair(provider, {
310
311
  model: model ?? '',
311
312
  messages: verificationMessages,
312
313
  maxTokens: config.llmVerificationMaxTokens,
package/src/eval/judge.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { collectChatCompletion } from '../provider/collect-chat-completion.js'
2
2
  import { resolveStreamIdleTimeoutMs, withStreamIdleTimeout } from '../provider/idle-timeout.js'
3
+ import { chatStreamWithSamplingRepair } from '../runtime/query/iteration/side-call.js'
3
4
  import type { LLMProvider } from '../types/provider/interface.js'
4
5
  import type { EvalCase, EvalTurn, Score, Scorer } from './types.js'
5
6
 
@@ -221,7 +222,7 @@ export function judgeScorer(config: JudgeScorerConfig): Scorer {
221
222
  name: config.name ?? 'judge',
222
223
  async score(turn: EvalTurn, evalCase: EvalCase, signal?: AbortSignal): Promise<Score> {
223
224
  const response = await collectChatCompletion(
224
- provider.chatStream({
225
+ chatStreamWithSamplingRepair(provider, {
225
226
  model: config.model,
226
227
  messages: [{ role: 'user', content: buildPrompt(config, turn, evalCase, scale) }],
227
228
  // The same turn must grade the same way twice, or a
@@ -33,6 +33,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
33
33
  inputCostPer1M: 10,
34
34
  outputCostPer1M: 50,
35
35
  cache: { promptIncludesCacheReads: false, readCostPer1M: 1, writeCostPer1M: 12.5 },
36
+ webSearchCostPer1000: 10,
36
37
  },
37
38
  ],
38
39
  [
@@ -41,6 +42,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
41
42
  inputCostPer1M: 10,
42
43
  outputCostPer1M: 50,
43
44
  cache: { promptIncludesCacheReads: false, readCostPer1M: 0.25, writeCostPer1M: 12.5 },
45
+ webSearchCostPer1000: 10,
44
46
  },
45
47
  ],
46
48
  [
@@ -50,6 +52,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
50
52
  outputCostPer1M: 40,
51
53
  cache: { promptIncludesCacheReads: false, readCostPer1M: 0.2, writeCostPer1M: 10 },
52
54
  fastMultiplier: 2,
55
+ webSearchCostPer1000: 10,
53
56
  },
54
57
  ],
55
58
  [
@@ -58,6 +61,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
58
61
  inputCostPer1M: 1,
59
62
  outputCostPer1M: 5,
60
63
  cache: { promptIncludesCacheReads: false, readCostPer1M: 0.1, writeCostPer1M: 1.25 },
64
+ webSearchCostPer1000: 10,
61
65
  },
62
66
  ],
63
67
  [
@@ -66,6 +70,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
66
70
  inputCostPer1M: 10,
67
71
  outputCostPer1M: 50,
68
72
  cache: { promptIncludesCacheReads: false, readCostPer1M: 1, writeCostPer1M: 12.5 },
73
+ webSearchCostPer1000: 10,
69
74
  },
70
75
  ],
71
76
  [
@@ -74,6 +79,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
74
79
  inputCostPer1M: 10,
75
80
  outputCostPer1M: 50,
76
81
  cache: { promptIncludesCacheReads: false, readCostPer1M: 0.25, writeCostPer1M: 12.5 },
82
+ webSearchCostPer1000: 10,
77
83
  },
78
84
  ],
79
85
  [
@@ -83,6 +89,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
83
89
  outputCostPer1M: 40,
84
90
  cache: { promptIncludesCacheReads: false, readCostPer1M: 0.2, writeCostPer1M: 10 },
85
91
  fastMultiplier: 2,
92
+ webSearchCostPer1000: 10,
86
93
  },
87
94
  ],
88
95
  [
@@ -91,6 +98,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
91
98
  inputCostPer1M: 15,
92
99
  outputCostPer1M: 75,
93
100
  cache: { promptIncludesCacheReads: false, readCostPer1M: 1.5, writeCostPer1M: 18.75 },
101
+ webSearchCostPer1000: 10,
94
102
  },
95
103
  ],
96
104
  [
@@ -99,6 +107,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
99
107
  inputCostPer1M: 15,
100
108
  outputCostPer1M: 75,
101
109
  cache: { promptIncludesCacheReads: false, readCostPer1M: 1.5, writeCostPer1M: 18.75 },
110
+ webSearchCostPer1000: 10,
102
111
  },
103
112
  ],
104
113
  [
@@ -107,6 +116,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
107
116
  inputCostPer1M: 5,
108
117
  outputCostPer1M: 25,
109
118
  cache: { promptIncludesCacheReads: false, readCostPer1M: 0.5, writeCostPer1M: 6.25 },
119
+ webSearchCostPer1000: 10,
110
120
  },
111
121
  ],
112
122
  [
@@ -115,6 +125,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
115
125
  inputCostPer1M: 5,
116
126
  outputCostPer1M: 25,
117
127
  cache: { promptIncludesCacheReads: false, readCostPer1M: 0.5, writeCostPer1M: 6.25 },
128
+ webSearchCostPer1000: 10,
118
129
  },
119
130
  ],
120
131
  [
@@ -124,6 +135,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
124
135
  outputCostPer1M: 25,
125
136
  cache: { promptIncludesCacheReads: false, readCostPer1M: 0.5, writeCostPer1M: 6.25 },
126
137
  fastMultiplier: 2,
138
+ webSearchCostPer1000: 10,
127
139
  },
128
140
  ],
129
141
  [
@@ -133,6 +145,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
133
145
  outputCostPer1M: 25,
134
146
  cache: { promptIncludesCacheReads: false, readCostPer1M: 0.5, writeCostPer1M: 6.25 },
135
147
  fastMultiplier: 2,
148
+ webSearchCostPer1000: 10,
136
149
  },
137
150
  ],
138
151
  [
@@ -142,6 +155,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
142
155
  outputCostPer1M: 20,
143
156
  cache: { promptIncludesCacheReads: false, readCostPer1M: 0.2, writeCostPer1M: 5 },
144
157
  fastMultiplier: 2,
158
+ webSearchCostPer1000: 10,
145
159
  },
146
160
  ],
147
161
  [
@@ -150,6 +164,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
150
164
  inputCostPer1M: 3,
151
165
  outputCostPer1M: 15,
152
166
  cache: { promptIncludesCacheReads: false, readCostPer1M: 0.3, writeCostPer1M: 3.75 },
167
+ webSearchCostPer1000: 10,
153
168
  },
154
169
  ],
155
170
  [
@@ -158,6 +173,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
158
173
  inputCostPer1M: 3,
159
174
  outputCostPer1M: 15,
160
175
  cache: { promptIncludesCacheReads: false, readCostPer1M: 0.3, writeCostPer1M: 3.75 },
176
+ webSearchCostPer1000: 10,
161
177
  },
162
178
  ],
163
179
  [
@@ -166,6 +182,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
166
182
  inputCostPer1M: 3,
167
183
  outputCostPer1M: 15,
168
184
  cache: { promptIncludesCacheReads: false, readCostPer1M: 0.3, writeCostPer1M: 3.75 },
185
+ webSearchCostPer1000: 10,
169
186
  },
170
187
  ],
171
188
  [
@@ -174,6 +191,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
174
191
  inputCostPer1M: 2,
175
192
  outputCostPer1M: 10,
176
193
  cache: { promptIncludesCacheReads: false, readCostPer1M: 0.2, writeCostPer1M: 2.5 },
194
+ webSearchCostPer1000: 10,
177
195
  },
178
196
  ],
179
197
  [
@@ -182,6 +200,7 @@ export const VENDOR_RATES: readonly VendorRates[] = [
182
200
  inputCostPer1M: 2,
183
201
  outputCostPer1M: 10,
184
202
  cache: { promptIncludesCacheReads: false, readCostPer1M: 0.1, writeCostPer1M: 2.5 },
203
+ webSearchCostPer1000: 10,
185
204
  },
186
205
  ],
187
206
  ]),
@@ -24,6 +24,12 @@
24
24
  "2x on the full context window, stacking with cache pricing). A model without it",
25
25
  "has no published fast price, so a fast request on it is reported as unpriced.",
26
26
  "",
27
+ "webSearchPer1000 (optional, per vendor) is the price in USD of 1,000 hosted web",
28
+ "searches, billed per request on top of tokens (verified 2026-11-10 against the",
29
+ "pricing page's web search section: $10 per 1,000, a failed search is not billed).",
30
+ "It is copied onto every model row of that vendor. Absent means searches are not",
31
+ "part of the total.",
32
+ "",
27
33
  "Matching is EXACT on a normalised id (lowercased, one trailing -YYYYMMDD or",
28
34
  "@YYYYMMDD snapshot suffix removed). Deliberately not prefix or substring:",
29
35
  "within one vendor here, ids one character apart differ in price by 4x, so a",
@@ -64,6 +70,7 @@
64
70
  "verified": "2026-11-10",
65
71
  "promptIncludesCacheReads": false,
66
72
  "reportsCacheWrites": true,
73
+ "webSearchPer1000": 10,
67
74
  "note": "The last two rows exist because the driver's own offline catalogue offers those models and this table did not price them — a lookup-key gap that reads as `cost unknown` and is invisible to the generator's own check, which only proves the module matches this file. `providers/anthropic` asserts the two lists agree. Vendor list prices, every row checked against its pricing page on the `verified` date. Cache read is the vendor's published 0.1x of the input rate, except on the six models it prices differently: 0.05x on claude-opus-5-5 and claude-sonnet-5-5 and 0.025x on claude-fable-5-1, claude-mythos-5-1, claude-fable-5-2 and claude-mythos-5-2, entered at the published rate rather than derived. claude-haiku-5-5 has no row on purpose: the vendor prices it by prompt length ($0.10 in / $0.50 out up to 100,000 prompt tokens, $0.50 / $2.50 above, cache counted in the length), this table holds one rate per model, and either single rate would be wrong for the other half of requests, so its cost reports unknown. Cache write is its published 1.25x for the five-minute entry, which is the default TTL and the one the driver's `cacheControl: { type: 'auto' }` asks for. claude-sonnet-5 launched at an introductory $2/$10 with $3/$15 scheduled from 2026-09-01, and this table recorded the scheduled rate; the vendor has since made $2/$10 the standard price and cancelled the increase. The table has no time dimension, so a future promotional window is still recorded at the list rate and over-reported rather than mis-modelled.",
68
75
  "models": [
69
76
  {
@@ -38,6 +38,7 @@ import {
38
38
  } from '../../../utils/backoff.js'
39
39
  import { SCOPE_ATTRIBUTE } from '../../../utils/log/types.js'
40
40
  import { type Logger, resolveLogger } from '../../../utils/logger.js'
41
+ import { chatStreamWithSamplingRepair } from '../iteration/side-call.js'
41
42
  import {
42
43
  STAGE_ONE_INSTRUCTION,
43
44
  STAGE_TWO_INSTRUCTION,
@@ -209,7 +210,7 @@ export function createAutoReviewClassifier(
209
210
  // ignores it would otherwise hold the call open past its deadline, and
210
211
  // a turn cancelled while it waits would wait with it.
211
212
  const collected = collectChatCompletion(
212
- options.provider.chatStream({
213
+ chatStreamWithSamplingRepair(options.provider, {
213
214
  model: options.model,
214
215
  messages,
215
216
  maxTokens: stage === 1 ? 64 : 8_192,
@@ -1383,6 +1383,9 @@ export class IterationOrchestrator {
1383
1383
  },
1384
1384
  }
1385
1385
  : {}),
1386
+ ...(response.usage.cacheMiss !== undefined
1387
+ ? { cacheMiss: response.usage.cacheMiss }
1388
+ : {}),
1386
1389
  ...contextFigures,
1387
1390
  })
1388
1391
 
@@ -39,7 +39,13 @@ import { isProviderRejectedImageError } from './provider-rejected-image.js'
39
39
  * compaction summaries, do not pass through it.
40
40
  */
41
41
 
42
- export type RejectedParameter = 'effort' | 'thinking' | 'temperature' | 'topP' | 'responseFormat'
42
+ export type RejectedParameter =
43
+ | 'effort'
44
+ | 'thinking'
45
+ | 'temperature'
46
+ | 'topP'
47
+ | 'topK'
48
+ | 'responseFormat'
43
49
 
44
50
  export interface MediaRemoval {
45
51
  readonly count: number
@@ -110,6 +116,7 @@ const PARAMETER_NAMES: Record<RejectedParameter, string> = {
110
116
  thinking: 'thinking',
111
117
  temperature: 'temperature',
112
118
  topP: 'top[_ ]?p',
119
+ topK: 'top[_ ]?k',
113
120
  responseFormat:
114
121
  '(?:response_format|output_config(?:\\.format)?|json_schema|json_object|structured\\s+output)',
115
122
  }
@@ -124,6 +131,10 @@ const PARAMETER_PATTERNS = Object.fromEntries(
124
131
  ]),
125
132
  ) as Record<RejectedParameter, { named: RegExp; refused: RegExp }>
126
133
 
134
+ const SAMPLING = ['temperature', 'topP', 'topK'] as const satisfies readonly RejectedParameter[]
135
+ const SAMPLING_GROUP =
136
+ /\btemperature\b.{0,80}\btop[_ ]?[pk]\b|\btop[_ ]?[pk]\b.{0,80}\btemperature\b/i
137
+
127
138
  const BOTH_SAMPLING =
128
139
  /temperature.{0,60}top[_ ]?p.{0,80}(?:cannot|can['’]t|may\s+not|should\s+not).{0,20}(?:both|together)/i
129
140
 
@@ -156,6 +167,7 @@ function rejectedParameters(
156
167
  thinking: params.thinking !== undefined,
157
168
  temperature: params.temperature !== undefined,
158
169
  topP: params.topP !== undefined,
170
+ topK: params.topK !== undefined,
159
171
  responseFormat: params.responseFormat !== undefined,
160
172
  } satisfies Record<RejectedParameter, boolean>
161
173
  const found: RejectedParameter[] = []
@@ -165,6 +177,12 @@ function rejectedParameters(
165
177
  if (structured ? pattern.named.test(text) : pattern.refused.test(text)) found.push(name)
166
178
  }
167
179
  if (found.length === 0 && set.topP && BOTH_SAMPLING.test(text)) found.push('topP')
180
+ // The vendor words a refusal of sampling controls as a group ("Omit
181
+ // temperature, top_p, and top_k"), so naming one of them in a refusal that
182
+ // names the others is enough for whichever of them was sent.
183
+ if (found.length === 0 && SAMPLING_GROUP.test(text)) {
184
+ for (const name of SAMPLING) if (set[name]) found.push(name)
185
+ }
168
186
  return found
169
187
  }
170
188
 
@@ -726,6 +744,7 @@ function withoutParameters(
726
744
  if (names.has('thinking')) next.thinking = undefined
727
745
  if (names.has('temperature')) next.temperature = undefined
728
746
  if (names.has('topP')) next.topP = undefined
747
+ if (names.has('topK')) next.topK = undefined
729
748
  if (names.has('responseFormat')) next.responseFormat = undefined
730
749
  return next
731
750
  }
@@ -741,3 +760,25 @@ function isHandledElsewhere(error: unknown, params: ChatCompletionParams): boole
741
760
  }
742
761
  return isImageInputRefusal(error)
743
762
  }
763
+
764
+ /**
765
+ * Which of `temperature`, `top_p` and `top_k` a provider refused on this
766
+ * request, from the same reading of its rejection the main turn uses. Empty
767
+ * for any other failure, so a caller can strip exactly these and nothing else.
768
+ */
769
+ export function rejectedSamplingParameters(
770
+ error: unknown,
771
+ params: ChatCompletionParams,
772
+ ): RejectedParameter[] {
773
+ const rejection = classifyRequestRejection(error, params)
774
+ if (rejection?.kind !== 'parameter') return []
775
+ return rejection.parameters.filter((name) => (SAMPLING as readonly string[]).includes(name))
776
+ }
777
+
778
+ /** The request without the named sampling parameters. */
779
+ export function withoutSamplingParameters(
780
+ params: ChatCompletionParams,
781
+ names: ReadonlySet<RejectedParameter>,
782
+ ): ChatCompletionParams {
783
+ return withoutParameters(params, names)
784
+ }
@@ -0,0 +1,60 @@
1
+ import type {
2
+ ChatCompletionParams,
3
+ LLMProvider,
4
+ StreamChunk,
5
+ } from '../../../types/provider/index.js'
6
+ import {
7
+ type RejectedParameter,
8
+ rejectedSamplingParameters,
9
+ withoutSamplingParameters,
10
+ } from './request-repair.js'
11
+
12
+ /**
13
+ * What each model was found to refuse, for the life of the provider object.
14
+ * Weakly keyed so a dropped provider takes its entry with it.
15
+ */
16
+ const refused = new WeakMap<object, Map<string, Set<RejectedParameter>>>()
17
+
18
+ /**
19
+ * A model call made outside the turn loop (a compaction summary, its verifier,
20
+ * the auto-review classifier) that asks for a deterministic answer with
21
+ * `temperature: 0`. Current models refuse sampling parameters outright, and
22
+ * the turn loop already recovers from that; this gives side calls the same
23
+ * recovery instead of letting the call fail and the feature with it.
24
+ *
25
+ * The request goes out as asked. If the provider refuses a sampling parameter
26
+ * BEFORE any output arrived, it is resent without exactly those parameters,
27
+ * and the finding is kept for the model so later calls omit them up front.
28
+ * Nothing is kept unless the resend was accepted, and any other failure is
29
+ * thrown unchanged.
30
+ */
31
+ export async function* chatStreamWithSamplingRepair(
32
+ provider: LLMProvider,
33
+ params: ChatCompletionParams,
34
+ ): AsyncGenerator<StreamChunk> {
35
+ const perModel = refused.get(provider) ?? new Map<string, Set<RejectedParameter>>()
36
+ refused.set(provider, perModel)
37
+ const known = perModel.get(params.model)
38
+ let request = known ? withoutSamplingParameters(params, known) : params
39
+ let stripped: Set<RejectedParameter> | undefined
40
+ for (;;) {
41
+ let yielded = false
42
+ try {
43
+ for await (const chunk of provider.chatStream(request)) {
44
+ yielded = true
45
+ yield chunk
46
+ }
47
+ if (stripped) {
48
+ const merged = new Set([...(perModel.get(params.model) ?? []), ...stripped])
49
+ perModel.set(params.model, merged)
50
+ }
51
+ return
52
+ } catch (error) {
53
+ if (yielded || stripped) throw error
54
+ const names = rejectedSamplingParameters(error, request)
55
+ if (names.length === 0) throw error
56
+ stripped = new Set(names)
57
+ request = withoutSamplingParameters(request, stripped)
58
+ }
59
+ }
60
+ }
@@ -1,6 +1,5 @@
1
1
  import { isAbsolute, join, posix } from 'node:path'
2
2
  import braceExpansion from 'brace-expansion'
3
- import { GLOBSTAR, Minimatch } from 'minimatch'
4
3
  import type {
5
4
  SandboxExecOptions,
6
5
  SandboxExecResult,
@@ -10,11 +9,15 @@ import type {
10
9
  } from '../types/sandbox/index.js'
11
10
  import { subscribeToAbort } from '../utils/abort.js'
12
11
  import { FILE_WALK_PROGRAM } from './file-walk-program.js'
12
+ import { compileSegment } from './glob-segment.js'
13
13
  import { VCS_NAMES, createIgnoreStack } from './ignore-matcher.js'
14
14
 
15
15
  /** Entries a walk examines before it stops with what it has: a safety valve, not a budget. */
16
16
  export const DEFAULT_MAX_VISITED_ENTRIES = 500_000
17
17
 
18
+ const GLOBSTAR = Symbol('globstar')
19
+ type Segment = string | { source: string; flags: string } | typeof GLOBSTAR
20
+
18
21
  interface WalkPlan {
19
22
  root: string
20
23
  prefix: string[]
@@ -62,22 +65,26 @@ function planWalk(root: string, options: SandboxWalkFilesOptions): WalkPlan {
62
65
  // that cap can silently truncate before our extra branch proves incompleteness.
63
66
  const branches = expandBraces(pattern, { max: 257, maxLength: Number.POSITIVE_INFINITY })
64
67
  if (branches.length > 256) throw new RangeError('File walk pattern exceeds 256 brace expansions')
65
- const matchers = branches.map((branch) => {
68
+ const dot = options.includeHidden ?? false
69
+ const sets = branches.map((branch) => {
66
70
  if (branch.startsWith('/') || /^[a-z]:/i.test(branch) || branch.split('/').includes('..')) {
67
71
  throw new Error('Expanded file walk pattern escapes its root')
68
72
  }
69
- return new Minimatch(branch, {
70
- nobrace: true,
71
- nonegate: true,
72
- nocomment: true,
73
- dot: options.includeHidden ?? false,
74
- platform: 'linux',
75
- })
73
+ return branch
74
+ .split('/')
75
+ .filter(
76
+ (segment, index, all) => segment !== '.' && (segment !== '' || index === all.length - 1),
77
+ )
78
+ .reduce<Segment[]>((set, segment) => {
79
+ if (segment === '**') {
80
+ if (set[set.length - 1] !== GLOBSTAR) set.push(GLOBSTAR)
81
+ return set
82
+ }
83
+ const { literal, source } = compileSegment(segment, dot)
84
+ set.push(literal !== null ? literal : { source, flags: 'u' })
85
+ return set
86
+ }, [])
76
87
  })
77
- const sets = matchers.flatMap((matcher) => matcher.set)
78
- if (sets.some((set) => set.some((segment) => segment === '..'))) {
79
- throw new Error('Expanded file walk pattern escapes its root')
80
- }
81
88
  const patternDepth = Math.max(
82
89
  1,
83
90
  ...sets.map((set) => (set.includes(GLOBSTAR) ? Number.POSITIVE_INFINITY : set.length)),
@@ -100,10 +107,24 @@ function planWalk(root: string, options: SandboxWalkFilesOptions): WalkPlan {
100
107
  break
101
108
  prefix.push(segment)
102
109
  }
103
- const expressions = matchers.flatMap((matcher) => {
104
- const expression = matcher.makeRe()
105
- return expression ? [{ source: expression.source, flags: expression.flags }] : []
106
- })
110
+ const hiddenGuard = dot ? '' : '(?!\\.)'
111
+ const expressions = sets.map((set) => ({
112
+ source: `^${set
113
+ .map((segment, index) => {
114
+ const last = index === set.length - 1
115
+ if (segment === GLOBSTAR) {
116
+ const run = `(?:${hiddenGuard}[^/]+/)*`
117
+ return last ? `${run}${hiddenGuard}[^/]+` : run
118
+ }
119
+ const body =
120
+ typeof segment === 'string'
121
+ ? segment.replace(/[\\^$.*+?()[\]{}|/]/g, '\\$&')
122
+ : segment.source.slice(1, -1)
123
+ return last ? `(?:${body})` : `(?:${body})/`
124
+ })
125
+ .join('')}$`,
126
+ flags: 'u',
127
+ }))
107
128
  return {
108
129
  root,
109
130
  prefix,