@zenera/neo 1.1.24 → 1.1.26

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 (42) hide show
  1. package/dist/agent.js +2 -1
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.js +3 -0
  4. package/dist/inspect/client.d.ts +1 -1
  5. package/dist/inspect/client.js +82 -11
  6. package/dist/inspect/css.d.ts +1 -1
  7. package/dist/inspect/css.js +1 -0
  8. package/dist/inspect/index.d.ts +1 -1
  9. package/dist/inspect/report.d.ts +9 -0
  10. package/dist/inspect/report.js +1 -0
  11. package/dist/lock.d.ts +27 -0
  12. package/dist/lock.js +66 -0
  13. package/dist/match.d.ts +44 -0
  14. package/dist/match.js +100 -0
  15. package/dist/memory/diff.d.ts +39 -0
  16. package/dist/memory/diff.js +95 -0
  17. package/dist/memory/files.d.ts +1 -1
  18. package/dist/memory/grep.d.ts +1 -1
  19. package/dist/memory/grep.js +2 -2
  20. package/dist/memory/instructions.d.ts +15 -0
  21. package/dist/memory/instructions.js +103 -0
  22. package/dist/memory/merge.js +1 -31
  23. package/dist/memory/store.d.ts +1 -5
  24. package/dist/memory/store.js +7 -53
  25. package/dist/memory/twin.d.ts +6 -0
  26. package/dist/memory/twin.js +40 -0
  27. package/dist/memory-stores/factory.d.ts +15 -0
  28. package/dist/memory-stores/factory.js +29 -0
  29. package/dist/memory-stores/file.d.ts +37 -0
  30. package/dist/memory-stores/file.js +174 -0
  31. package/dist/memory-stores/in-memory.d.ts +17 -0
  32. package/dist/memory-stores/in-memory.js +80 -0
  33. package/dist/memory-stores/index.d.ts +4 -0
  34. package/dist/memory-stores/index.js +5 -0
  35. package/dist/memory.d.ts +115 -0
  36. package/dist/memory.js +213 -0
  37. package/dist/models/factory.d.ts +24 -0
  38. package/dist/models/factory.js +153 -14
  39. package/dist/project/config.d.ts +9 -9
  40. package/dist/runner.js +7 -0
  41. package/dist/tools/sandbox.js +40 -2
  42. package/package.json +1 -1
@@ -0,0 +1,115 @@
1
+ import { type AnyTool } from './types.ts';
2
+ export interface MemoryRecord {
3
+ id: string;
4
+ /** memory space this record belongs to */
5
+ scope: string;
6
+ /** app-defined: 'fact' | 'preference' | 'episode' | … */
7
+ kind: string;
8
+ text: string;
9
+ metadata?: Record<string, unknown>;
10
+ createdAt: string;
11
+ updatedAt: string;
12
+ /** optimistic-concurrency token */
13
+ revision: number;
14
+ }
15
+ export interface MemoryDraft {
16
+ kind?: string;
17
+ text: string;
18
+ metadata?: Record<string, unknown>;
19
+ }
20
+ export interface MemoryPatch {
21
+ kind?: string;
22
+ text?: string;
23
+ metadata?: Record<string, unknown>;
24
+ /** when given, the update fails unless the stored revision matches */
25
+ expectedRevision?: number;
26
+ }
27
+ export interface MemoryQuery {
28
+ /** semantic query; omitted ⇒ pure filter listing */
29
+ text?: string;
30
+ filter?: Record<string, unknown>;
31
+ kind?: string;
32
+ limit?: number;
33
+ minScore?: number;
34
+ }
35
+ export interface MemoryHit {
36
+ record: MemoryRecord;
37
+ /** 0..1, backend-normalized */
38
+ score: number;
39
+ }
40
+ /**
41
+ * Embedding generation is the store's business, not the kernel's: a pgvector
42
+ * implementation embeds inside `write`/`search`, the in-memory one below falls
43
+ * back to token overlap. The kernel never sees vectors.
44
+ */
45
+ export interface MemoryStore {
46
+ readonly id: string;
47
+ search(scope: string, q: MemoryQuery): Promise<MemoryHit[]>;
48
+ get(scope: string, id: string): Promise<MemoryRecord | undefined>;
49
+ /** `opId` makes writes idempotent under retry/replay */
50
+ write(scope: string, rec: MemoryDraft, opId: string): Promise<MemoryRecord>;
51
+ update(scope: string, id: string, patch: MemoryPatch, opId: string): Promise<MemoryRecord>;
52
+ delete(scope: string, id: string, opId: string): Promise<void>;
53
+ }
54
+ export interface MemoryBinding<TCtx = unknown> {
55
+ /** MemoryStore id */
56
+ store: string;
57
+ /** namespace; default `agent:${agent.name}`. A function is resolved per run. */
58
+ scope?: string | ((ctx: TCtx) => string);
59
+ access: 'read' | 'read-write';
60
+ /** inject top-k matches before an LLM call */
61
+ autoRecall?: {
62
+ query: 'last_user_input' | 'none';
63
+ limit: number;
64
+ };
65
+ }
66
+ /** A binding with its scope already resolved against the run context. */
67
+ export interface ResolvedBinding {
68
+ store: string;
69
+ scope: string;
70
+ access: 'read' | 'read-write';
71
+ autoRecall?: {
72
+ query: 'last_user_input' | 'none';
73
+ limit: number;
74
+ };
75
+ }
76
+ export interface MemoryRecallSpec {
77
+ kind: 'recall';
78
+ store: string;
79
+ scope: string;
80
+ query: MemoryQuery;
81
+ hits: {
82
+ id: string;
83
+ score: number;
84
+ revision: number;
85
+ }[];
86
+ /** the rendered block the model actually saw */
87
+ content: string;
88
+ }
89
+ export interface MemoryOpSpec {
90
+ kind: 'op';
91
+ op: 'write' | 'update' | 'delete';
92
+ store: string;
93
+ scope: string;
94
+ /** sha256(runId, callId) — deterministic, so replay re-issues the same write */
95
+ opId: string;
96
+ recordId: string;
97
+ revision: number;
98
+ before?: string;
99
+ after?: string;
100
+ }
101
+ export declare function memoryOpId(runId: string, callId: string): string;
102
+ export declare function renderMemories(hits: MemoryHit[]): string;
103
+ /**
104
+ * Filtering plus token-overlap scoring, shared by the backends that have no
105
+ * embeddings of their own. A vector store ignores this and ranks in the
106
+ * database instead — the kernel only ever sees `MemoryHit[]`.
107
+ */
108
+ export declare function rankMemories(records: Iterable<MemoryRecord>, q: MemoryQuery): MemoryHit[];
109
+ /**
110
+ * Memory is reachable both explicitly (these tools) and implicitly (auto-recall
111
+ * in the runner). Both land in the trajectory; only the explicit path is
112
+ * visible to the model as a tool.
113
+ */
114
+ export declare function memoryTools<TCtx>(bindings: ResolvedBinding[]): AnyTool<TCtx>[];
115
+ //# sourceMappingURL=memory.d.ts.map
package/dist/memory.js ADDED
@@ -0,0 +1,213 @@
1
+ import { hash } from "./payload.js";
2
+ import { MEMORY_DELETE_TOOL, MEMORY_SEARCH_TOOL, MEMORY_UPDATE_TOOL, MEMORY_WRITE_TOOL, tool, withEffects, } from "./types.js";
3
+ export function memoryOpId(runId, callId) {
4
+ return hash(`${runId}\u0000${callId}`);
5
+ }
6
+ export function renderMemories(hits) {
7
+ if (!hits.length) {
8
+ return '';
9
+ }
10
+ const lines = hits.map((h) => `- [${h.record.id}] (${h.record.kind}, score ${h.score.toFixed(2)}) ${h.record.text}`);
11
+ return `Relevant memories:\n${lines.join('\n')}`;
12
+ }
13
+ // ---------------------------------------------------------------------------
14
+ // Shared ranking
15
+ // ---------------------------------------------------------------------------
16
+ /**
17
+ * Filtering plus token-overlap scoring, shared by the backends that have no
18
+ * embeddings of their own. A vector store ignores this and ranks in the
19
+ * database instead — the kernel only ever sees `MemoryHit[]`.
20
+ */
21
+ export function rankMemories(records, q) {
22
+ const limit = q.limit ?? 8;
23
+ const terms = tokenize(q.text ?? '');
24
+ const hits = [];
25
+ for (const rec of records) {
26
+ if (q.kind && rec.kind !== q.kind) {
27
+ continue;
28
+ }
29
+ if (q.filter && !matches(rec.metadata, q.filter)) {
30
+ continue;
31
+ }
32
+ const score = terms.length ? overlap(terms, tokenize(rec.text)) : 1;
33
+ if (score <= 0 || (q.minScore !== undefined && score < q.minScore)) {
34
+ continue;
35
+ }
36
+ hits.push({ record: rec, score });
37
+ }
38
+ return hits.sort((a, b) => b.score - a.score).slice(0, limit);
39
+ }
40
+ function tokenize(v) {
41
+ return v
42
+ .toLowerCase()
43
+ .split(/[^a-z0-9]+/)
44
+ .filter((t) => t.length > 2);
45
+ }
46
+ function overlap(a, b) {
47
+ const set = new Set(b);
48
+ const shared = a.filter((t) => set.has(t)).length;
49
+ return a.length ? shared / a.length : 0;
50
+ }
51
+ function matches(meta, filter) {
52
+ return Object.entries(filter).every(([k, v]) => meta?.[k] === v);
53
+ }
54
+ // ---------------------------------------------------------------------------
55
+ // Built-in memory tools
56
+ // ---------------------------------------------------------------------------
57
+ function spaceParam(bindings) {
58
+ return bindings.length > 1
59
+ ? { type: 'string', enum: bindings.map((b) => b.scope), description: 'memory space' }
60
+ : undefined;
61
+ }
62
+ function pick(bindings, space) {
63
+ const b = space ? bindings.find((x) => x.scope === space) : bindings[0];
64
+ if (!b) {
65
+ throw new Error(`unknown memory space: ${space ?? '(default)'}`);
66
+ }
67
+ return b;
68
+ }
69
+ /**
70
+ * Memory is reachable both explicitly (these tools) and implicitly (auto-recall
71
+ * in the runner). Both land in the trajectory; only the explicit path is
72
+ * visible to the model as a tool.
73
+ */
74
+ export function memoryTools(bindings) {
75
+ if (!bindings.length) {
76
+ return [];
77
+ }
78
+ const writable = bindings.filter((b) => b.access === 'read-write');
79
+ const space = spaceParam(bindings);
80
+ const withSpace = (props) => (space ? { ...props, space } : props);
81
+ const tools = [
82
+ tool({
83
+ name: MEMORY_SEARCH_TOOL,
84
+ description: 'Search long-lived memories.',
85
+ parameters: {
86
+ type: 'object',
87
+ properties: withSpace({
88
+ query: { type: 'string' },
89
+ kind: { type: 'string' },
90
+ limit: { type: 'integer' },
91
+ }),
92
+ required: ['query'],
93
+ additionalProperties: false,
94
+ },
95
+ execute: async (args, tc) => {
96
+ const b = pick(bindings, args.space);
97
+ const store = tc.services.memoryStore(b.store);
98
+ const hits = await store.search(b.scope, {
99
+ text: args.query,
100
+ kind: args.kind,
101
+ limit: args.limit,
102
+ });
103
+ return hits.map((h) => ({
104
+ id: h.record.id,
105
+ kind: h.record.kind,
106
+ text: h.record.text,
107
+ revision: h.record.revision,
108
+ score: Number(h.score.toFixed(3)),
109
+ }));
110
+ },
111
+ }),
112
+ ];
113
+ if (!writable.length) {
114
+ return tools;
115
+ }
116
+ const wSpace = spaceParam(writable);
117
+ const withWSpace = (props) => wSpace ? { ...props, space: wSpace } : props;
118
+ tools.push(tool({
119
+ name: MEMORY_WRITE_TOOL,
120
+ description: 'Store a new long-lived memory.',
121
+ parameters: {
122
+ type: 'object',
123
+ properties: withWSpace({ text: { type: 'string' }, kind: { type: 'string' } }),
124
+ required: ['text'],
125
+ additionalProperties: false,
126
+ },
127
+ execute: async (args, tc) => {
128
+ const b = pick(writable, args.space);
129
+ const opId = memoryOpId(tc.state.runId, tc.callId);
130
+ const rec = await tc.services
131
+ .memoryStore(b.store)
132
+ .write(b.scope, { text: args.text, kind: args.kind }, opId);
133
+ return withEffects(`stored memory ${rec.id}`, {
134
+ kind: 'memory_op',
135
+ spec: {
136
+ kind: 'op',
137
+ op: 'write',
138
+ store: b.store,
139
+ scope: b.scope,
140
+ opId,
141
+ recordId: rec.id,
142
+ revision: rec.revision,
143
+ after: rec.text,
144
+ },
145
+ });
146
+ },
147
+ }), tool({
148
+ name: MEMORY_UPDATE_TOOL,
149
+ description: 'Update an existing memory by id.',
150
+ parameters: {
151
+ type: 'object',
152
+ properties: withWSpace({
153
+ id: { type: 'string' },
154
+ text: { type: 'string' },
155
+ kind: { type: 'string' },
156
+ }),
157
+ required: ['id'],
158
+ additionalProperties: false,
159
+ },
160
+ execute: async (args, tc) => {
161
+ const b = pick(writable, args.space);
162
+ const store = tc.services.memoryStore(b.store);
163
+ const before = await store.get(b.scope, args.id);
164
+ const opId = memoryOpId(tc.state.runId, tc.callId);
165
+ const rec = await store.update(b.scope, args.id, { text: args.text, kind: args.kind }, opId);
166
+ return withEffects(`updated memory ${rec.id}`, {
167
+ kind: 'memory_op',
168
+ spec: {
169
+ kind: 'op',
170
+ op: 'update',
171
+ store: b.store,
172
+ scope: b.scope,
173
+ opId,
174
+ recordId: rec.id,
175
+ revision: rec.revision,
176
+ before: before?.text,
177
+ after: rec.text,
178
+ },
179
+ });
180
+ },
181
+ }), tool({
182
+ name: MEMORY_DELETE_TOOL,
183
+ description: 'Delete a memory by id.',
184
+ parameters: {
185
+ type: 'object',
186
+ properties: withWSpace({ id: { type: 'string' } }),
187
+ required: ['id'],
188
+ additionalProperties: false,
189
+ },
190
+ execute: async (args, tc) => {
191
+ const b = pick(writable, args.space);
192
+ const store = tc.services.memoryStore(b.store);
193
+ const before = await store.get(b.scope, args.id);
194
+ const opId = memoryOpId(tc.state.runId, tc.callId);
195
+ await store.delete(b.scope, args.id, opId);
196
+ return withEffects(`deleted memory ${args.id}`, {
197
+ kind: 'memory_op',
198
+ spec: {
199
+ kind: 'op',
200
+ op: 'delete',
201
+ store: b.store,
202
+ scope: b.scope,
203
+ opId,
204
+ recordId: args.id,
205
+ revision: before?.revision ?? 0,
206
+ before: before?.text,
207
+ },
208
+ });
209
+ },
210
+ }));
211
+ return tools;
212
+ }
213
+ //# sourceMappingURL=memory.js.map
@@ -150,6 +150,15 @@ export interface ModelRequirement {
150
150
  }
151
151
  export declare const providerKinds: ProviderKind[];
152
152
  export declare function isProviderKind(name: string): name is ProviderKind;
153
+ /**
154
+ * Vertex serves three request formats behind one account, and the model id
155
+ * says which: Google's own models through the GenAI API, Claude through
156
+ * Anthropic's Messages format at `rawPredict`, and every other publisher's
157
+ * (`xai/grok-4.1-fast-reasoning`, `qwen/qwen3-…-maas`) through the
158
+ * OpenAI-compatible endpoint, which names its models `publisher/model`.
159
+ */
160
+ export type VertexRoute = 'gemini' | 'anthropic' | 'openai';
161
+ export declare function vertexRoute(model: string): VertexRoute;
153
162
  /**
154
163
  * Which knobs each adapter actually reads.
155
164
  *
@@ -240,6 +249,21 @@ export declare class ModelRegistry {
240
249
  /** A declared kind wins; otherwise a built-in name is its own kind. */
241
250
  kindOf(name: string): ProviderKind;
242
251
  }
252
+ export interface VertexEndpoint {
253
+ project: string;
254
+ location: string;
255
+ /** `https://…/v1/projects/<project>/locations/<location>` */
256
+ base: string;
257
+ /** a fresh access token from Application Default Credentials, or `spec.token` */
258
+ token: () => Promise<string>;
259
+ }
260
+ /**
261
+ * Where a Vertex provider's partner models are served, and how to authenticate
262
+ * there. The GenAI SDK does both for Gemini; nothing does them for Claude or
263
+ * the OpenAI-compatible endpoint, so they are resolved here the same way:
264
+ * `project`, then GOOGLE_CLOUD_PROJECT, then the service-account file.
265
+ */
266
+ export declare function vertexEndpoint(spec?: ProviderSpec, name?: string): VertexEndpoint;
243
267
  /**
244
268
  * The registry behind the bare `createModel`. An application that never
245
269
  * declares a provider still gets shared clients through it, because the
@@ -114,6 +114,20 @@ export const providerKinds = Object.keys(KINDS);
114
114
  export function isProviderKind(name) {
115
115
  return Object.hasOwn(KINDS, name);
116
116
  }
117
+ export function vertexRoute(model) {
118
+ if (/^(anthropic\/)?claude-/.test(model)) {
119
+ return 'anthropic';
120
+ }
121
+ return model.includes('/') ? 'openai' : 'gemini';
122
+ }
123
+ /** The protocol one model on one kind is spoken in. Only Vertex has more than one. */
124
+ function protocolOf(kind, model) {
125
+ return kind === 'vertex' ? vertexRoute(model) : KINDS[kind].protocol;
126
+ }
127
+ /** The OpenAI-compatible endpoint on Vertex has chat completions and nothing else. */
128
+ function apisOf(kind, protocol) {
129
+ return kind === 'vertex' && protocol === 'openai' ? ['chat'] : KINDS[kind].apis;
130
+ }
117
131
  // ---------------------------------------------------------------------------
118
132
  // Tuning knobs
119
133
  // ---------------------------------------------------------------------------
@@ -214,7 +228,11 @@ export class ModelRegistry {
214
228
  this.#providers.set(name, spec);
215
229
  // Re-declaring after a client was handed out would otherwise leave the
216
230
  // old credentials in service for the rest of the process.
217
- this.#clients.delete(name);
231
+ for (const key of this.#clients.keys()) {
232
+ if (key === name || key.startsWith(`${name}\0`)) {
233
+ this.#clients.delete(key);
234
+ }
235
+ }
218
236
  this.#limiters.delete(name);
219
237
  return this;
220
238
  }
@@ -247,6 +265,17 @@ export class ModelRegistry {
247
265
  this.#clients.set(name, built);
248
266
  return built;
249
267
  }
268
+ /** A Vertex provider's Claude or partner client: built once, beside its GenAI one. */
269
+ #partner(name, protocol, spec) {
270
+ const key = `${name}\0${protocol}`;
271
+ const existing = this.#clients.get(key);
272
+ if (existing) {
273
+ return existing;
274
+ }
275
+ const built = buildVertexPartner(name, protocol, spec);
276
+ this.#clients.set(key, built);
277
+ return built;
278
+ }
250
279
  /**
251
280
  * The pacing shared by every embedder on a provider, so that what one of
252
281
  * them learns from a 429 the others do not have to learn again — and so
@@ -275,14 +304,16 @@ export class ModelRegistry {
275
304
  */
276
305
  knobs(ref) {
277
306
  const spec = typeof ref === 'string' ? this.parse(ref) : ref;
278
- const defaults = KINDS[this.kindOf(spec.provider ?? this.#default)];
279
- if (defaults.protocol !== 'openai') {
280
- return unreadKnobs(spec, defaults.protocol);
307
+ const kind = this.kindOf(spec.provider ?? this.#default);
308
+ const protocol = protocolOf(kind, spec.model);
309
+ if (protocol !== 'openai') {
310
+ return unreadKnobs(spec, protocol);
281
311
  }
282
312
  // An api this vendor does not speak already has an error of its own;
283
313
  // answering about its knobs would only bury it.
284
- const api = spec.api ?? defaults.apis[0];
285
- return api && defaults.apis.includes(api) ? unreadKnobs(spec, `openai/${api}`) : [];
314
+ const apis = apisOf(kind, protocol);
315
+ const api = spec.api ?? apis[0];
316
+ return api && apis.includes(api) ? unreadKnobs(spec, `openai/${api}`) : [];
286
317
  }
287
318
  /** Turns a shorthand or a spec into a `Model`. */
288
319
  model(ref) {
@@ -290,7 +321,7 @@ export class ModelRegistry {
290
321
  const name = spec.provider ?? this.#default;
291
322
  const provider = this.#spec(name);
292
323
  const kind = this.kindOf(name);
293
- const defaults = KINDS[kind];
324
+ const protocol = protocolOf(kind, spec.model);
294
325
  // Before the client, so a knob for the wrong vendor is reported as the
295
326
  // config mistake it is rather than behind a missing credential.
296
327
  const [stray] = this.knobs(spec);
@@ -302,30 +333,35 @@ export class ModelRegistry {
302
333
  // different connection, and handing back the memoized one would
303
334
  // silently ignore them.
304
335
  const client = spec.client ??
305
- (hasCredentials(spec) ? buildClient(name, kind, spec, provider) : this.client(name));
336
+ (protocol !== KINDS[kind].protocol
337
+ ? this.#partner(name, protocol, provider)
338
+ : hasCredentials(spec)
339
+ ? buildClient(name, kind, spec, provider)
340
+ : this.client(name));
306
341
  // The *resolved* name, not `spec.provider`: a bare ref carries none,
307
342
  // and a failure that cannot say which connection refused it is the
308
343
  // thing this is here to prevent.
309
344
  const options = { ...spec, provider: name };
310
345
  // Every protocol but OpenAI's has exactly one API, so naming one is a
311
346
  // mistake worth reporting rather than a field to ignore.
312
- if (defaults.protocol !== 'openai') {
347
+ if (protocol !== 'openai') {
313
348
  if (spec.api) {
314
349
  throw new TypeError(`provider "${name}" (${kind}) has one api, so "${spec.api}" means nothing here`);
315
350
  }
316
- switch (defaults.protocol) {
351
+ switch (protocol) {
317
352
  case 'anthropic':
318
- return new AnthropicModel(spec.model, client, options);
353
+ return new AnthropicModel(spec.model.replace(/^anthropic\//, ''), client, options);
319
354
  case 'gemini':
320
355
  return new GeminiModel(spec.model, client, options);
321
356
  case 'openrouter':
322
357
  return new OpenRouterModel(spec.model, client, options);
323
358
  }
324
359
  }
325
- const api = spec.api ?? defaults.apis[0];
326
- if (!defaults.apis.includes(api)) {
360
+ const apis = apisOf(kind, protocol);
361
+ const api = spec.api ?? apis[0];
362
+ if (!apis.includes(api)) {
327
363
  throw new TypeError(`provider "${name}" (${kind}) does not speak the "${api}" api ` +
328
- `(supported: ${defaults.apis.join(', ')})`);
364
+ `(supported: ${apis.join(', ')})`);
329
365
  }
330
366
  return api === 'responses'
331
367
  ? new OpenAIResponsesModel(spec.model, client, options)
@@ -658,6 +694,109 @@ function projectFromKeyFile() {
658
694
  return undefined;
659
695
  }
660
696
  }
697
+ // ---------------------------------------------------------------------------
698
+ // Vertex partner models
699
+ // ---------------------------------------------------------------------------
700
+ /** Gemini has these; the partner endpoints do not, and answer them with a bare 404. */
701
+ const MULTI_REGIONS = new Set(['us', 'eu']);
702
+ /**
703
+ * Where a Vertex provider's partner models are served, and how to authenticate
704
+ * there. The GenAI SDK does both for Gemini; nothing does them for Claude or
705
+ * the OpenAI-compatible endpoint, so they are resolved here the same way:
706
+ * `project`, then GOOGLE_CLOUD_PROJECT, then the service-account file.
707
+ */
708
+ export function vertexEndpoint(spec = {}, name = 'vertex') {
709
+ const where = `provider "${name}"`;
710
+ const project = expand(spec.project, `${where}: project`) ??
711
+ fromEnv('GOOGLE_CLOUD_PROJECT') ??
712
+ projectFromKeyFile();
713
+ if (!project) {
714
+ throw new Error(`${where}: Claude and partner models on vertex need \`project\`, or ` +
715
+ `GOOGLE_CLOUD_PROJECT, and a service account — an express-mode key reaches Gemini only`);
716
+ }
717
+ const named = expand(spec.location, `${where}: location`) ?? fromEnv('GOOGLE_CLOUD_LOCATION') ?? 'global';
718
+ const location = MULTI_REGIONS.has(named) ? 'global' : named;
719
+ const host = location === 'global'
720
+ ? 'https://aiplatform.googleapis.com'
721
+ : `https://${location}-aiplatform.googleapis.com`;
722
+ const token = spec.token;
723
+ return {
724
+ project,
725
+ location,
726
+ base: `${host}/v1/projects/${project}/locations/${location}`,
727
+ token: token ? async () => token() : adcToken(),
728
+ };
729
+ }
730
+ /** Mints and refreshes tokens the way the GenAI SDK does for Gemini. */
731
+ function adcToken() {
732
+ let auth;
733
+ return async () => {
734
+ if (!auth) {
735
+ // `@google/genai`'s own dependency, so it is resolved from there rather than declared twice.
736
+ const genai = sdkPath('@google/genai', 'vertex');
737
+ const { GoogleAuth } = createRequire(genai)('google-auth-library');
738
+ auth = new GoogleAuth({ scopes: ['https://www.googleapis.com/auth/cloud-platform'] });
739
+ }
740
+ const token = await auth.getAccessToken();
741
+ if (!token) {
742
+ throw new Error('vertex: Application Default Credentials gave no access token');
743
+ }
744
+ return token;
745
+ };
746
+ }
747
+ function sdkPath(pkg, kind) {
748
+ try {
749
+ return requirePeer.resolve(pkg);
750
+ }
751
+ catch (cause) {
752
+ throw new Error(`provider kind "${kind}" needs ${pkg}, which is not installed — run: npm i ${pkg}`, { cause });
753
+ }
754
+ }
755
+ function buildVertexPartner(name, protocol, opts) {
756
+ const at = vertexEndpoint(opts, name);
757
+ const common = {
758
+ apiKey: OAUTH_PLACEHOLDER,
759
+ defaultHeaders: expandHeaders(opts.headers, `provider "${name}"`),
760
+ timeout: opts.timeoutMs,
761
+ maxRetries: retriesOf(opts),
762
+ };
763
+ if (protocol === 'anthropic') {
764
+ const { Anthropic } = sdk('@anthropic-ai/sdk', 'vertex');
765
+ return new Anthropic({
766
+ ...common,
767
+ baseURL: at.base,
768
+ fetch: funded(rawPredictFetch(at.base, at.token)),
769
+ });
770
+ }
771
+ const { OpenAI } = sdk('openai', 'vertex');
772
+ return new OpenAI({
773
+ ...common,
774
+ baseURL: `${at.base}/endpoints/openapi`,
775
+ fetch: funded(bearerFetch(at.token)),
776
+ });
777
+ }
778
+ /**
779
+ * Anthropic's client posts to `/v1/messages`; Vertex wants the model in the
780
+ * path and its own `anthropic_version` in the body instead.
781
+ */
782
+ function rawPredictFetch(base, token) {
783
+ const authed = bearerFetch(token);
784
+ return async (input, init) => {
785
+ const url = input instanceof Request ? input.url : String(input);
786
+ if (!new URL(url).pathname.endsWith('/v1/messages') || typeof init?.body !== 'string') {
787
+ return authed(input, init);
788
+ }
789
+ const { model, ...body } = JSON.parse(init.body);
790
+ const headers = new Headers(init.headers);
791
+ headers.delete('x-api-key');
792
+ const verb = body.stream ? 'streamRawPredict' : 'rawPredict';
793
+ return authed(`${base}/publishers/anthropic/models/${model}:${verb}`, {
794
+ ...init,
795
+ headers,
796
+ body: JSON.stringify({ ...body, anthropic_version: 'vertex-2023-10-16' }),
797
+ });
798
+ };
799
+ }
661
800
  // Both SDKs refuse to construct without a key. When a token callback owns the
662
801
  // Authorization header this value is never sent, so it only has to be non-empty.
663
802
  const OAUTH_PLACEHOLDER = 'oauth';
@@ -48,10 +48,10 @@ declare const modelSpec: z.ZodObject<{
48
48
  thinkingBudgetTokens: z.ZodOptional<z.ZodInt>;
49
49
  thinkingBudget: z.ZodOptional<z.ZodInt>;
50
50
  thinkingLevel: z.ZodOptional<z.ZodEnum<{
51
+ minimal: "minimal";
51
52
  low: "low";
52
53
  medium: "medium";
53
54
  high: "high";
54
- minimal: "minimal";
55
55
  }>>;
56
56
  includeThoughts: z.ZodOptional<z.ZodBoolean>;
57
57
  routing: z.ZodOptional<z.ZodObject<{
@@ -163,15 +163,15 @@ declare const sandbox: z.ZodObject<{
163
163
  cpus: z.ZodOptional<z.ZodNumber>;
164
164
  memory: z.ZodOptional<z.ZodInt>;
165
165
  network: z.ZodOptional<z.ZodEnum<{
166
+ bridge: "bridge";
166
167
  none: "none";
167
168
  host: "host";
168
- bridge: "bridge";
169
169
  }>>;
170
170
  workdir: z.ZodOptional<z.ZodString>;
171
171
  timeout: z.ZodOptional<z.ZodInt>;
172
172
  hardening: z.ZodOptional<z.ZodEnum<{
173
- strict: "strict";
174
173
  standard: "standard";
174
+ strict: "strict";
175
175
  }>>;
176
176
  user: z.ZodOptional<z.ZodString>;
177
177
  persist: z.ZodOptional<z.ZodBoolean>;
@@ -220,10 +220,10 @@ export declare const projectSchema: z.ZodObject<{
220
220
  thinkingBudgetTokens: z.ZodOptional<z.ZodInt>;
221
221
  thinkingBudget: z.ZodOptional<z.ZodInt>;
222
222
  thinkingLevel: z.ZodOptional<z.ZodEnum<{
223
+ minimal: "minimal";
223
224
  low: "low";
224
225
  medium: "medium";
225
226
  high: "high";
226
- minimal: "minimal";
227
227
  }>>;
228
228
  includeThoughts: z.ZodOptional<z.ZodBoolean>;
229
229
  routing: z.ZodOptional<z.ZodObject<{
@@ -293,15 +293,15 @@ export declare const projectSchema: z.ZodObject<{
293
293
  cpus: z.ZodOptional<z.ZodNumber>;
294
294
  memory: z.ZodOptional<z.ZodInt>;
295
295
  network: z.ZodOptional<z.ZodEnum<{
296
+ bridge: "bridge";
296
297
  none: "none";
297
298
  host: "host";
298
- bridge: "bridge";
299
299
  }>>;
300
300
  workdir: z.ZodOptional<z.ZodString>;
301
301
  timeout: z.ZodOptional<z.ZodInt>;
302
302
  hardening: z.ZodOptional<z.ZodEnum<{
303
- strict: "strict";
304
303
  standard: "standard";
304
+ strict: "strict";
305
305
  }>>;
306
306
  user: z.ZodOptional<z.ZodString>;
307
307
  persist: z.ZodOptional<z.ZodBoolean>;
@@ -325,8 +325,8 @@ export declare const projectSchema: z.ZodObject<{
325
325
  provider: z.ZodOptional<z.ZodString>;
326
326
  discovery: z.ZodDefault<z.ZodEnum<{
327
327
  none: "none";
328
- search: "search";
329
328
  index: "index";
329
+ search: "search";
330
330
  }>>;
331
331
  allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
332
332
  preload: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -357,15 +357,15 @@ export declare const projectSchema: z.ZodObject<{
357
357
  cpus: z.ZodOptional<z.ZodNumber>;
358
358
  memory: z.ZodOptional<z.ZodInt>;
359
359
  network: z.ZodOptional<z.ZodEnum<{
360
+ bridge: "bridge";
360
361
  none: "none";
361
362
  host: "host";
362
- bridge: "bridge";
363
363
  }>>;
364
364
  workdir: z.ZodOptional<z.ZodString>;
365
365
  timeout: z.ZodOptional<z.ZodInt>;
366
366
  hardening: z.ZodOptional<z.ZodEnum<{
367
- strict: "strict";
368
367
  standard: "standard";
368
+ strict: "strict";
369
369
  }>>;
370
370
  user: z.ZodOptional<z.ZodString>;
371
371
  persist: z.ZodOptional<z.ZodBoolean>;