@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
@@ -0,0 +1,285 @@
1
+ /**
2
+ * Compiles one path segment of a glob (no `/` in it) to the regular expression
3
+ * the file walk tests names with, in the same dialect the sandbox guest runs.
4
+ * It covers `*`, `?`, `[...]` classes (with `!`/`^` negation, ranges and POSIX
5
+ * classes), the five extglob forms and `\` escapes. A segment with no magic in
6
+ * it comes back as the plain name to compare against.
7
+ */
8
+
9
+ // Written for the `u` flag, which every compiled expression carries.
10
+ const POSIX_CLASSES: Record<string, string> = {
11
+ alnum: '\\p{L}\\p{Nl}\\p{Nd}',
12
+ alpha: '\\p{L}\\p{Nl}',
13
+ ascii: '\\x00-\\x7f',
14
+ blank: '\\p{Zs}\\t',
15
+ cntrl: '\\p{Cc}',
16
+ digit: '\\p{Nd}',
17
+ graph: '\\x21-\\x7e',
18
+ lower: '\\p{Ll}',
19
+ print: '\\x20-\\x7e',
20
+ punct: '\\p{P}\\p{S}',
21
+ space: '\\s',
22
+ upper: '\\p{Lu}',
23
+ word: '\\p{L}\\p{Nl}\\p{Nd}_',
24
+ xdigit: '0-9a-fA-F',
25
+ }
26
+
27
+ export interface CompiledSegment {
28
+ /** Set when the segment has no magic: the exact name it matches. */
29
+ literal: string | null
30
+ /** Source of an expression anchored with `^` and `$`, for a segment with magic. */
31
+ source: string
32
+ }
33
+
34
+ /**
35
+ * Raised for a segment that repeats a group (`*( )` or `+( )`) which could match
36
+ * the same text more than one way. Such a group backtracks exponentially on a
37
+ * name that nearly matches, and glob patterns come from untrusted input.
38
+ */
39
+ export class GlobTooComplexError extends Error {
40
+ readonly segment: string
41
+ constructor(segment: string, reason: 'ambiguous' | 'stretch' = 'ambiguous') {
42
+ super(
43
+ reason === 'stretch'
44
+ ? `The pattern '${segment}' has more than three wildcards that can stretch; split it across folders or use fewer *.`
45
+ : `The pattern '${segment}' repeats a group that can match the same text more than one way; use ** or {a,b} instead.`,
46
+ )
47
+ this.name = 'GlobTooComplexError'
48
+ this.segment = segment
49
+ }
50
+ }
51
+
52
+ const escapeRegex = (text: string) => text.replace(/[\\^$.*+?()[\]{}|/]/g, '\\$&')
53
+ const escapeInClass = (text: string) => text.replace(/[\\^\]\[/-]/g, '\\$&')
54
+
55
+ interface Parsed {
56
+ source: string
57
+ literal: string
58
+ magic: boolean
59
+ /** Unbounded runs: each `*` (consecutive ones collapsed), `*( )` and `+( )`. */
60
+ runs: number
61
+ }
62
+
63
+ /** Index of the `)` closing the group opened just before `from`, or -1. */
64
+ function closeGroup(text: string, from: number): number {
65
+ let depth = 1
66
+ for (let index = from; index < text.length; index += 1) {
67
+ const ch = text[index]
68
+ if (ch === '\\') index += 1
69
+ else if (ch === '[') {
70
+ const end = classEnd(text, index)
71
+ if (end > 0) index = end
72
+ } else if (ch === '(') depth += 1
73
+ else if (ch === ')' && --depth === 0) return index
74
+ }
75
+ return -1
76
+ }
77
+
78
+ /** Index of the `]` closing the class opened at `start`, or -1 when it never closes. */
79
+ function classEnd(text: string, start: number): number {
80
+ let index = start + 1
81
+ if (text[index] === '!' || text[index] === '^') index += 1
82
+ if (text[index] === ']') index += 1
83
+ for (; index < text.length; index += 1) {
84
+ const ch = text[index]
85
+ if (ch === '\\') index += 1
86
+ else if (ch === '[' && text[index + 1] === ':') {
87
+ const end = text.indexOf(':]', index + 2)
88
+ if (end > 0 && POSIX_CLASSES[text.slice(index + 2, end)]) index = end + 1
89
+ } else if (ch === ']') return index
90
+ }
91
+ return -1
92
+ }
93
+
94
+ function splitAlternatives(body: string): string[] {
95
+ const parts: string[] = []
96
+ let depth = 0
97
+ let current = ''
98
+ for (let index = 0; index < body.length; index += 1) {
99
+ const ch = body[index] as string
100
+ if (ch === '\\') {
101
+ current += ch + (body[index + 1] ?? '')
102
+ index += 1
103
+ continue
104
+ }
105
+ if (ch === '[') {
106
+ const end = classEnd(body, index)
107
+ if (end > 0) {
108
+ current += body.slice(index, end + 1)
109
+ index = end
110
+ continue
111
+ }
112
+ }
113
+ if (ch === '(') depth += 1
114
+ if (ch === ')') depth -= 1
115
+ if (ch === '|' && depth === 0) {
116
+ parts.push(current)
117
+ current = ''
118
+ } else current += ch
119
+ }
120
+ parts.push(current)
121
+ return parts
122
+ }
123
+
124
+ function classSource(body: string): string {
125
+ let index = 0
126
+ let negate = false
127
+ if (body[0] === '!' || body[0] === '^') {
128
+ negate = true
129
+ index = 1
130
+ }
131
+ let out = ''
132
+ for (; index < body.length; index += 1) {
133
+ const ch = body[index] as string
134
+ if (ch === '[' && body[index + 1] === ':') {
135
+ const end = body.indexOf(':]', index + 2)
136
+ const name = end > 0 ? body.slice(index + 2, end) : ''
137
+ if (POSIX_CLASSES[name]) {
138
+ out += POSIX_CLASSES[name]
139
+ index = end + 1
140
+ continue
141
+ }
142
+ }
143
+ if (ch === '\\' && index + 1 < body.length) {
144
+ index += 1
145
+ out += escapeInClass(body[index] as string)
146
+ } else if (ch === '-' && out !== '' && index + 1 < body.length) out += '-'
147
+ else out += escapeInClass(ch)
148
+ }
149
+ const source = `[${negate ? '^' : ''}${out}]`
150
+ try {
151
+ new RegExp(source, 'u')
152
+ } catch {
153
+ // An out-of-order range such as `[z-a]` matches no name rather than failing the walk.
154
+ return '(?!)'
155
+ }
156
+ return source
157
+ }
158
+
159
+ /** `guard`, when set, is put before each alternative of a group at the very start that does not itself spell out a leading `.`. */
160
+ function parse(text: string, guard = ''): Parsed {
161
+ let source = ''
162
+ let literal = ''
163
+ let magic = false
164
+ let runs = 0
165
+ for (let index = 0; index < text.length; index += 1) {
166
+ const ch = text[index] as string
167
+ const next = text[index + 1]
168
+ if (ch === '\\') {
169
+ if (next === undefined) {
170
+ source += '\\\\'
171
+ literal += '\\'
172
+ } else {
173
+ source += escapeRegex(next)
174
+ literal += next
175
+ index += 1
176
+ }
177
+ } else if (ch === '*' && next !== '(') {
178
+ while (text[index + 1] === '*') index += 1
179
+ source += '[^/]*?'
180
+ runs += 1
181
+ magic = true
182
+ } else if (ch === '?' && next !== '(') {
183
+ source += '[^/]'
184
+ magic = true
185
+ } else if (ch === '[') {
186
+ const end = classEnd(text, index)
187
+ if (end < 0) {
188
+ source += '\\['
189
+ literal += '['
190
+ } else {
191
+ source += classSource(text.slice(index + 1, end))
192
+ magic = true
193
+ index = end
194
+ }
195
+ } else if ('?*+@!'.includes(ch) && next === '(') {
196
+ const end = closeGroup(text, index + 2)
197
+ if (end < 0) {
198
+ source += escapeRegex(ch)
199
+ literal += ch
200
+ continue
201
+ }
202
+ const parts = splitAlternatives(text.slice(index + 2, end)).map((a) => ({
203
+ a,
204
+ parsed: parse(a),
205
+ }))
206
+ // A repeated group is linear only when each repetition can be read one way:
207
+ // plain literal alternatives, none a prefix of another (and none empty).
208
+ if (ch === '*' || ch === '+') {
209
+ const literals = parts.map((part) => part.parsed.literal)
210
+ // A lone class is one character wide, so each repetition has one reading.
211
+ const lone = parts.length === 1 && isOneClass(parts[0]?.a ?? '')
212
+ const plain = lone || parts.every((part) => !part.parsed.magic)
213
+ const prefixFree =
214
+ lone ||
215
+ literals.every(
216
+ (one, i) =>
217
+ one !== '' && literals.every((other, j) => i === j || !other.startsWith(one)),
218
+ )
219
+ if (!plain || !prefixFree) throw new GlobTooComplexError(text)
220
+ }
221
+ const alternatives = parts.map(
222
+ ({ a, parsed }) => (index === 0 && !startsWithDot(a) ? guard : '') + parsed.source,
223
+ )
224
+ const group = `(?:${alternatives.join('|')})`
225
+ runs +=
226
+ parts.reduce((sum, part) => sum + part.parsed.runs, 0) + (ch === '*' || ch === '+' ? 1 : 0)
227
+ magic = true
228
+ if (ch === '!') {
229
+ const tail = parse(text.slice(end + 1))
230
+ const rest = tail.source
231
+ source += `(?:(?!${group}${rest}$)[^/]*?)${rest}`
232
+ return { source, literal, magic, runs: runs + 1 + tail.runs }
233
+ }
234
+ source += ch === '@' ? group : `${group}${ch === '?' ? '?' : ch === '*' ? '*' : '+'}`
235
+ index = end
236
+ } else {
237
+ source += escapeRegex(ch)
238
+ literal += ch
239
+ }
240
+ }
241
+ return { source, literal, magic, runs }
242
+ }
243
+
244
+ /** Whether the text is exactly one closed `[...]` class. */
245
+ function isOneClass(text: string): boolean {
246
+ return text.startsWith('[') && classEnd(text, 0) === text.length - 1
247
+ }
248
+
249
+ /** Whether every name this segment can match begins with a literal `.` the pattern spells out. */
250
+ function startsWithDot(text: string): boolean {
251
+ if (text.startsWith('.') || text.startsWith('\\.')) return true
252
+ if (text.startsWith('[')) {
253
+ const end = classEnd(text, 0)
254
+ return end > 0 && text.slice(1, end) === '.'
255
+ }
256
+ return false
257
+ }
258
+
259
+ /** Whether the segment is a single `?(`, `*(`, `@(` or `+(` group, whose alternatives carry the leading-dot guard themselves. */
260
+ function isOneGroup(text: string): boolean {
261
+ return (
262
+ '?*@+'.includes(text[0] ?? '') && text[1] === '(' && closeGroup(text, 2) === text.length - 1
263
+ )
264
+ }
265
+
266
+ /**
267
+ * `dot` lets a wildcard match a name that starts with `.`; without it only a
268
+ * segment that itself begins with a literal `.` can.
269
+ */
270
+ export function compileSegment(segment: string, dot: boolean): CompiledSegment {
271
+ const guard = dot ? '' : '(?!\\.)'
272
+ let parsed: Parsed
273
+ try {
274
+ parsed = parse(segment, guard)
275
+ } catch (error) {
276
+ throw error instanceof GlobTooComplexError
277
+ ? new GlobTooComplexError(segment, 'ambiguous')
278
+ : error
279
+ }
280
+ if (parsed.runs > 3) throw new GlobTooComplexError(segment, 'stretch')
281
+ if (!parsed.magic) return { literal: parsed.literal, source: `^${parsed.source}$` }
282
+ const grouped = (segment.startsWith('@(') || segment.startsWith('+(')) && !dot
283
+ const lead = grouped || dot || startsWithDot(segment) || isOneGroup(segment) ? '' : guard
284
+ return { literal: null, source: `^${lead}(?:${parsed.source})$` }
285
+ }
@@ -50,6 +50,42 @@ export interface TokenUsage {
50
50
  * unreported.
51
51
  */
52
52
  speed?: import('../provider/chat.js').InferenceSpeed
53
+
54
+ /**
55
+ * Hosted web searches the provider ran for this call and bills per request.
56
+ * Unlike the per-call facts below it IS a count of work done, so it is
57
+ * summed across calls. Absent means the provider did not report one.
58
+ */
59
+ webSearchRequests?: number
60
+
61
+ /**
62
+ * The service tier the provider says served this call (`standard`,
63
+ * `priority`, `batch`). A per-call fact, never summed. Absent means
64
+ * unreported.
65
+ */
66
+ serviceTier?: string
67
+
68
+ /** The region the provider says ran this call, when it says. Per call, never summed. */
69
+ inferenceGeo?: string
70
+
71
+ /**
72
+ * Why the provider's prompt cache did not serve a prefix it had before, when
73
+ * it explains. Per call, never summed. Absent means no miss was explained: the
74
+ * provider reported nothing, a pending answer, or a hit.
75
+ */
76
+ cacheMiss?: CacheMiss
77
+ }
78
+
79
+ /** A prompt-cache miss as the provider explains it. See {@link TokenUsage.cacheMiss}. */
80
+ export interface CacheMiss {
81
+ /**
82
+ * The provider's own reason, e.g. `model_changed`, `system_changed`,
83
+ * `tools_changed`, `messages_changed`, `previous_message_not_found`,
84
+ * `unavailable`.
85
+ */
86
+ reason: string
87
+ /** Input tokens that missed the cache because of it, when the provider counts them. */
88
+ missedTokens?: number
53
89
  }
54
90
 
55
91
  export function accumulateTokenUsage(current: TokenUsage, addition: TokenUsage): TokenUsage {
@@ -66,8 +102,12 @@ export function accumulateTokenUsage(current: TokenUsage, addition: TokenUsage):
66
102
  ...(current.reasoningTokens !== undefined || addition.reasoningTokens !== undefined
67
103
  ? { reasoningTokens: (current.reasoningTokens ?? 0) + (addition.reasoningTokens ?? 0) }
68
104
  : {}),
69
- // `cacheTtlMs` and `cacheWriteTtlTokens` are per-call facts and are
70
- // deliberately not carried into a sum.
105
+ ...(current.webSearchRequests !== undefined || addition.webSearchRequests !== undefined
106
+ ? { webSearchRequests: (current.webSearchRequests ?? 0) + (addition.webSearchRequests ?? 0) }
107
+ : {}),
108
+ // `cacheTtlMs`, `cacheWriteTtlTokens`, `speed`, `serviceTier`,
109
+ // `inferenceGeo` and `cacheMiss` are per-call facts and are deliberately
110
+ // not carried into a sum.
71
111
  }
72
112
  }
73
113
 
@@ -112,6 +152,18 @@ export function mergeTokenUsage(current: TokenUsage, next: TokenUsage): TokenUsa
112
152
  ...(next.speed !== undefined || current.speed !== undefined
113
153
  ? { speed: next.speed ?? current.speed }
114
154
  : {}),
155
+ ...(next.webSearchRequests !== undefined || current.webSearchRequests !== undefined
156
+ ? { webSearchRequests: Math.max(current.webSearchRequests ?? 0, next.webSearchRequests ?? 0) }
157
+ : {}),
158
+ ...(next.serviceTier !== undefined || current.serviceTier !== undefined
159
+ ? { serviceTier: next.serviceTier ?? current.serviceTier }
160
+ : {}),
161
+ ...(next.inferenceGeo !== undefined || current.inferenceGeo !== undefined
162
+ ? { inferenceGeo: next.inferenceGeo ?? current.inferenceGeo }
163
+ : {}),
164
+ ...(next.cacheMiss !== undefined || current.cacheMiss !== undefined
165
+ ? { cacheMiss: next.cacheMiss ?? current.cacheMiss }
166
+ : {}),
115
167
  }
116
168
  }
117
169
 
@@ -926,6 +926,13 @@ type CoreSessionEvent =
926
926
  * absent means unknown, and a host must not assume one.
927
927
  */
928
928
  cache?: { ttlMs: number; touched: boolean }
929
+ /**
930
+ * Why the provider says the latest model call did not read the cache
931
+ * prefix it had before, when it explains one. Absent means no miss was
932
+ * explained: nothing reported, a pending answer, or a hit. A host that
933
+ * shows it should do so quietly; a miss after an edit is ordinary.
934
+ */
935
+ cacheMiss?: { reason: string; missedTokens?: number }
929
936
  /**
930
937
  * How large the CONTEXT is right now, and how large it may get.
931
938
  *
package/src/utils/cost.ts CHANGED
@@ -48,6 +48,12 @@ export interface ModelPricing {
48
48
  * charged at the standard rate, which would under-report it.
49
49
  */
50
50
  fastMultiplier?: number
51
+ /**
52
+ * What the vendor charges per 1,000 hosted web searches
53
+ * (`TokenUsage.webSearchRequests`). Absent means no published search price:
54
+ * searches are then not part of the total, as before this field existed.
55
+ */
56
+ webSearchCostPer1000?: number
51
57
  }
52
58
 
53
59
  export { ZERO_COST }
@@ -61,6 +67,26 @@ interface Priced {
61
67
  }
62
68
 
63
69
  function priceUsage(usage: TokenUsage, pricing: ModelPricing): Priced {
70
+ const tokens = priceTokens(usage, pricing)
71
+ const searches = usage.webSearchRequests ?? 0
72
+ if (searches === 0 || pricing.webSearchCostPer1000 === undefined) return tokens
73
+ // Per request, so no speed or tier multiple applies to it.
74
+ return { ...tokens, cost: tokens.cost + (searches / 1000) * pricing.webSearchCostPer1000 }
75
+ }
76
+
77
+ /**
78
+ * Tiers with a published per-token price (anything but `priority` and `batch`; a compatible endpoint may report `default` or `auto`). The vendor lists none for `priority`
79
+ * (a capacity commitment) or `batch` (a different endpoint), so tokens served
80
+ * on one are unpriced rather than charged at the standard rate.
81
+ */
82
+ function isStandardTier(tier: string | undefined): boolean {
83
+ return tier !== 'priority' && tier !== 'batch'
84
+ }
85
+
86
+ function priceTokens(usage: TokenUsage, pricing: ModelPricing): Priced {
87
+ if (!isStandardTier(usage.serviceTier)) {
88
+ return { cost: 0, cacheDiscount: 0, unpriced: usage.totalTokens }
89
+ }
64
90
  if (usage.speed === 'fast') {
65
91
  const multiplier = pricing.fastMultiplier
66
92
  if (multiplier === undefined) {