@superjackfruitlabs/salaam 0.0.1 → 0.0.2

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 (110) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +25 -17
  3. package/dist/src/a2a-backend.js +225 -0
  4. package/dist/src/a2a-inbound.js +487 -0
  5. package/dist/src/a2a-peer.js +370 -0
  6. package/dist/src/a2a-protocol.js +250 -0
  7. package/dist/src/agent-profiles.js +289 -0
  8. package/dist/src/children.js +132 -26
  9. package/dist/src/claude-provider.js +2 -179
  10. package/dist/src/cli-help.js +107 -6
  11. package/dist/src/cli.js +477 -13
  12. package/dist/src/config.js +26 -3
  13. package/dist/src/credentials.js +7 -2
  14. package/dist/src/external-runtime.js +1 -0
  15. package/dist/src/external-work.js +583 -0
  16. package/dist/src/harness-backend.js +466 -0
  17. package/dist/src/harness-container.js +450 -0
  18. package/dist/src/harness-model-gateway.js +104 -0
  19. package/dist/src/harness-prepare.js +88 -0
  20. package/dist/src/harness-probe.js +111 -0
  21. package/dist/src/harness-protocol.js +325 -0
  22. package/dist/src/harness-readiness.js +50 -0
  23. package/dist/src/harness-rpc.js +206 -0
  24. package/dist/src/harness-tool-identity.js +87 -0
  25. package/dist/src/harness-wire-bridge.js +387 -0
  26. package/dist/src/host.js +6 -1
  27. package/dist/src/house-extension.js +147 -64
  28. package/dist/src/live-provider.js +301 -0
  29. package/dist/src/managed.js +33 -3
  30. package/dist/src/protocol.js +9 -5
  31. package/dist/src/provider-auth.js +300 -0
  32. package/dist/src/provider-config.js +65 -17
  33. package/dist/src/provider-model.js +78 -0
  34. package/dist/src/provider-payload.js +264 -0
  35. package/dist/src/provider-transport.js +36 -0
  36. package/dist/src/provider-usage.js +59 -0
  37. package/dist/src/repository-snapshots.js +219 -0
  38. package/dist/src/service.js +274 -9
  39. package/dist/src/store.js +44 -2
  40. package/dist/src/supervisor.js +246 -35
  41. package/dist/src/task-budget.js +17 -8
  42. package/dist/src/task-context.js +15 -0
  43. package/dist/src/task-driver.js +38 -14
  44. package/dist/src/task-policy.js +20 -1
  45. package/dist/src/task-verification.js +3 -0
  46. package/dist/src/task-work.js +31 -0
  47. package/dist/src/team-work.js +378 -0
  48. package/dist/src/tui-view.js +65 -10
  49. package/dist/src/tui.js +4 -2
  50. package/dist/src/usage.js +18 -0
  51. package/dist/src/worker-boundary.js +298 -0
  52. package/dist/src/worker-workspaces.js +274 -0
  53. package/dist/src/workspaces.js +6 -2
  54. package/harness-runtime/codex-pins.json +10 -0
  55. package/harness-runtime/package-lock.json +560 -0
  56. package/harness-runtime/package.json +9 -0
  57. package/npm-shrinkwrap.json +57 -2
  58. package/package.json +2 -1
  59. package/src/a2a-backend.ts +254 -0
  60. package/src/a2a-inbound.ts +585 -0
  61. package/src/a2a-peer.ts +441 -0
  62. package/src/a2a-protocol.ts +286 -0
  63. package/src/agent-profiles.ts +379 -0
  64. package/src/children.ts +168 -27
  65. package/src/claude-provider.ts +2 -189
  66. package/src/cli-help.ts +107 -6
  67. package/src/cli.ts +602 -12
  68. package/src/config.ts +34 -3
  69. package/src/credentials.ts +9 -2
  70. package/src/external-runtime.ts +54 -0
  71. package/src/external-work.ts +663 -0
  72. package/src/harness-backend.ts +557 -0
  73. package/src/harness-container.ts +543 -0
  74. package/src/harness-model-gateway.ts +138 -0
  75. package/src/harness-prepare.ts +113 -0
  76. package/src/harness-probe.ts +150 -0
  77. package/src/harness-protocol.ts +388 -0
  78. package/src/harness-readiness.ts +71 -0
  79. package/src/harness-rpc.ts +244 -0
  80. package/src/harness-tool-identity.ts +91 -0
  81. package/src/harness-wire-bridge.ts +418 -0
  82. package/src/host.ts +11 -6
  83. package/src/house-extension.ts +165 -66
  84. package/src/live-provider.ts +347 -0
  85. package/src/managed.ts +43 -4
  86. package/src/protocol.ts +36 -8
  87. package/src/provider-auth.ts +336 -0
  88. package/src/provider-config.ts +89 -18
  89. package/src/provider-model.ts +86 -0
  90. package/src/provider-payload.ts +263 -0
  91. package/src/provider-transport.ts +51 -0
  92. package/src/provider-usage.ts +64 -0
  93. package/src/repository-snapshots.ts +259 -0
  94. package/src/service.ts +446 -11
  95. package/src/store.ts +60 -4
  96. package/src/supervisor.ts +294 -41
  97. package/src/task-budget.ts +24 -11
  98. package/src/task-context.ts +17 -0
  99. package/src/task-driver.ts +51 -15
  100. package/src/task-policy.ts +23 -3
  101. package/src/task-verification.ts +2 -0
  102. package/src/task-work.ts +46 -1
  103. package/src/team-work.ts +554 -0
  104. package/src/tui-client.ts +5 -0
  105. package/src/tui-view.ts +164 -12
  106. package/src/tui.ts +5 -2
  107. package/src/usage.ts +17 -0
  108. package/src/worker-boundary.ts +395 -0
  109. package/src/worker-workspaces.ts +390 -0
  110. package/src/workspaces.ts +4 -2
@@ -34,6 +34,7 @@ interface WorkerConfig {
34
34
  child?: boolean;
35
35
  route?: ProviderRoute;
36
36
  coding?: CodingBinding;
37
+ worker_binding?: import('./worker-boundary.js').WorkerBoundaryView;
37
38
  }
38
39
  export default function house(pi: ExtensionAPI): void {
39
40
  const config = JSON.parse(process.env.SALAAM_WORKER_CONFIG ?? 'null') as WorkerConfig | null;
@@ -46,7 +47,7 @@ export default function house(pi: ExtensionAPI): void {
46
47
  `/worker/${c.session}/${method}`,
47
48
  { epoch: c.epoch, ...(data as object) },
48
49
  c.token,
49
- 2000,
50
+ method === 'provider.auth' ? 120000 : 2000,
50
51
  );
51
52
  let timer: NodeJS.Timeout | undefined;
52
53
  let shutdown: (() => void) | undefined;
@@ -58,28 +59,33 @@ export default function house(pi: ExtensionAPI): void {
58
59
  }
59
60
  shutdown?.();
60
61
  };
61
- pi.registerTool({
62
- name: 'salaam_probe',
63
- label: 'Salaam readiness probe',
64
- description: 'Verify the managed tool gate without external effects.',
65
- parameters: Type.Object({ nested: Type.Optional(Type.Boolean()) }),
66
- annotations: { readOnlyHint: true, openWorldHint: false },
67
- async execute(toolCallId, params, signal, _update, ctx) {
68
- if (params.nested) {
69
- const outcome = await ctx.executeTool('salaam_probe', {}, { signal });
70
- if (outcome.isError) {
71
- await fail();
72
- throw new Error('Nested managed probe failed');
62
+ if (
63
+ workerTools(!!c.effects, !!c.child, c.coding, c.worker_binding?.binding.tools).includes(
64
+ 'salaam_probe',
65
+ )
66
+ )
67
+ pi.registerTool({
68
+ name: 'salaam_probe',
69
+ label: 'Salaam readiness probe',
70
+ description: 'Verify the managed tool gate without external effects.',
71
+ parameters: Type.Object({ nested: Type.Optional(Type.Boolean()) }),
72
+ annotations: { readOnlyHint: true, openWorldHint: false },
73
+ async execute(toolCallId, params, signal, _update, ctx) {
74
+ if (params.nested) {
75
+ const outcome = await ctx.executeTool('salaam_probe', {}, { signal });
76
+ if (outcome.isError) {
77
+ await fail();
78
+ throw new Error('Nested managed probe failed');
79
+ }
80
+ return { ...outcome.result, isError: outcome.isError };
73
81
  }
74
- return { ...outcome.result, isError: outcome.isError };
75
- }
76
- await bridge('tool', { tool: 'salaam_probe', tool_call_id: toolCallId });
77
- return {
78
- content: [{ type: 'text', text: 'managed probe granted' }],
79
- details: { granted: true },
80
- };
81
- },
82
- });
82
+ await bridge('tool', { tool: 'salaam_probe', tool_call_id: toolCallId });
83
+ return {
84
+ content: [{ type: 'text', text: 'managed probe granted' }],
85
+ details: { granted: true },
86
+ };
87
+ },
88
+ });
83
89
  const managedTool = (name: 'salaam_review' | 'salaam_effect', kind: 'review' | 'effect') =>
84
90
  pi.registerTool({
85
91
  name,
@@ -178,6 +184,7 @@ export default function house(pi: ExtensionAPI): void {
178
184
  prompt: Type.Optional(Type.String({ maxLength: 32768 })),
179
185
  budget: Type.Optional(Type.Integer({ minimum: 1, maximum: 8 })),
180
186
  authority: Type.Optional(Type.Array(Type.String())),
187
+ profile: Type.Optional(Type.String({ maxLength: 128 })),
181
188
  tasks: Type.Optional(
182
189
  Type.Array(Type.Object({ step_key: Type.String(), prompt: Type.String() }), {
183
190
  maxItems: 2,
@@ -194,7 +201,8 @@ export default function house(pi: ExtensionAPI): void {
194
201
  bridge<import('./protocol.js').ChildTask>('child.spawn', {
195
202
  ...task,
196
203
  budget: params.budget ?? 2,
197
- authority: params.authority ?? ['salaam_probe'],
204
+ authority: params.authority ?? (params.profile ? [] : ['salaam_probe']),
205
+ ...(params.profile ? { profile: params.profile } : {}),
198
206
  }),
199
207
  ),
200
208
  );
@@ -243,7 +251,96 @@ export default function house(pi: ExtensionAPI): void {
243
251
  managedTool('salaam_review', 'review');
244
252
  }
245
253
  if (c.effects) managedTool('salaam_effect', 'effect');
246
- if (c.coding && !c.child) {
254
+ if (c.worker_binding?.binding.tools.includes('salaam_repository')) {
255
+ pi.registerTool({
256
+ name: 'salaam_repository',
257
+ label: 'Repository snapshot',
258
+ description:
259
+ 'Inspect, list, search or read the exact admitted immutable repository snapshot. Reviewers may also inspect its diff. Paths and line references refer to this snapshot; no host file access, edits or commands are available.',
260
+ parameters: Type.Object({
261
+ action: Type.Union(
262
+ ['inspect', 'list', 'search', 'read', 'diff'].map((x) => Type.Literal(x)),
263
+ ),
264
+ path: Type.Optional(Type.String({ maxLength: 512 })),
265
+ query: Type.Optional(Type.String({ maxLength: 256 })),
266
+ offset: Type.Optional(Type.Integer({ minimum: 0 })),
267
+ }),
268
+ async execute(id, args) {
269
+ try {
270
+ const result = await bridge('specialist.read', { tool_call_id: id, args });
271
+ return {
272
+ content: [{ type: 'text' as const, text: JSON.stringify(result) }],
273
+ details: result,
274
+ };
275
+ } catch (error) {
276
+ if (
277
+ error instanceof ServiceError &&
278
+ ['not_found', 'binary_file', 'line_limit', 'unsafe_path', 'invalid_field'].includes(
279
+ error.code,
280
+ )
281
+ )
282
+ return {
283
+ content: [{ type: 'text' as const, text: error.message }],
284
+ details: { error: error.code },
285
+ isError: true,
286
+ };
287
+ await fail();
288
+ throw error;
289
+ }
290
+ },
291
+ });
292
+ }
293
+ if (c.worker_binding?.binding.tools.includes('salaam_team')) {
294
+ pi.registerTool({
295
+ name: 'salaam_team',
296
+ label: 'Team inbox',
297
+ description:
298
+ 'Send, receive or acknowledge durable messages within this task. Messages never start or continue workers; delivery and acknowledgement are distinct.',
299
+ parameters: Type.Object({
300
+ action: Type.Union(['send', 'receive', 'ack'].map((a) => Type.Literal(a))),
301
+ request_id: Type.String({ maxLength: 128 }),
302
+ recipient: Type.Optional(Type.String()),
303
+ text: Type.Optional(Type.String({ maxLength: 4096 })),
304
+ message_id: Type.Optional(Type.String()),
305
+ }),
306
+ async execute(_id, args) {
307
+ const { action, ...body } = args;
308
+ const result = await bridge('team.' + action, body);
309
+ return {
310
+ content: [{ type: 'text' as const, text: JSON.stringify(result) }],
311
+ details: result,
312
+ };
313
+ },
314
+ });
315
+ }
316
+ if (c.worker_binding?.binding.tools.includes('salaam_findings')) {
317
+ pi.registerTool({
318
+ name: 'salaam_findings',
319
+ label: 'Record review findings',
320
+ description:
321
+ 'Publish final structured analysis over the admitted snapshot. Locations and source evidence are checked. Analysis cannot complete or verify a task.',
322
+ parameters: Type.Object({
323
+ findings: Type.Array(
324
+ Type.Object({
325
+ path: Type.String({ maxLength: 512 }),
326
+ line: Type.Integer({ minimum: 1 }),
327
+ end_line: Type.Optional(Type.Integer({ minimum: 1 })),
328
+ severity: Type.Union(['critical', 'high', 'medium', 'low'].map((x) => Type.Literal(x))),
329
+ message: Type.String({ maxLength: 2048 }),
330
+ }),
331
+ { maxItems: 32 },
332
+ ),
333
+ }),
334
+ async execute(_id, args) {
335
+ const result = await bridge('specialist.report', args);
336
+ return {
337
+ content: [{ type: 'text' as const, text: JSON.stringify(result) }],
338
+ details: result,
339
+ };
340
+ },
341
+ });
342
+ }
343
+ if (c.coding) {
247
344
  const normal = (error: unknown) =>
248
345
  error instanceof ServiceError &&
249
346
  [
@@ -261,46 +358,47 @@ export default function house(pi: ExtensionAPI): void {
261
358
  'command_limit',
262
359
  'invalid_cursor',
263
360
  ].includes(error.code);
264
- pi.registerTool({
265
- name: 'salaam_files',
266
- label: 'Guarded workspace files',
267
- description:
268
- 'Inspect/list/search/read an owned workspace or patch one UTF-8 file. Read returns one-based line references and a file hash. Before patching, inspect/read current revision and hash. expected_hash=null creates a new file, content=null deletes one. The source checkout and Git metadata stay outside this workspace.',
269
- parameters: Type.Object({
270
- action: Type.Union(
271
- ['inspect', 'list', 'search', 'read', 'patch'].map((value) => Type.Literal(value)),
272
- ),
273
- path: Type.Optional(Type.String({ maxLength: 512 })),
274
- query: Type.Optional(Type.String({ maxLength: 256 })),
275
- offset: Type.Optional(Type.Integer({ minimum: 0 })),
276
- expected_revision: Type.Optional(Type.Integer({ minimum: 0 })),
277
- expected_hash: Type.Optional(Type.Union([Type.String(), Type.Null()])),
278
- content: Type.Optional(Type.Union([Type.String({ maxLength: 262144 }), Type.Null()])),
279
- }),
280
- async execute(id, params) {
281
- try {
282
- const result = await bridge<Record<string, unknown>>('coding.files', {
283
- tool_call_id: id,
284
- args: params,
285
- });
286
- return {
287
- content: [{ type: 'text', text: JSON.stringify(result) }],
288
- details: result,
289
- isError: params.action === 'patch' && result.state !== 'applied',
290
- };
291
- } catch (error) {
292
- if (!normal(error)) {
293
- await fail();
294
- throw error;
361
+ if (c.coding.files)
362
+ pi.registerTool({
363
+ name: 'salaam_files',
364
+ label: 'Guarded workspace files',
365
+ description:
366
+ 'Inspect/list/search/read an owned workspace or patch one UTF-8 file. Read returns one-based line references and a file hash. Before patching, inspect/read current revision and hash. expected_hash=null creates a new file, content=null deletes one. The source checkout and Git metadata stay outside this workspace.',
367
+ parameters: Type.Object({
368
+ action: Type.Union(
369
+ ['inspect', 'list', 'search', 'read', 'patch'].map((value) => Type.Literal(value)),
370
+ ),
371
+ path: Type.Optional(Type.String({ maxLength: 512 })),
372
+ query: Type.Optional(Type.String({ maxLength: 256 })),
373
+ offset: Type.Optional(Type.Integer({ minimum: 0 })),
374
+ expected_revision: Type.Optional(Type.Integer({ minimum: 0 })),
375
+ expected_hash: Type.Optional(Type.Union([Type.String(), Type.Null()])),
376
+ content: Type.Optional(Type.Union([Type.String({ maxLength: 262144 }), Type.Null()])),
377
+ }),
378
+ async execute(id, params) {
379
+ try {
380
+ const result = await bridge<Record<string, unknown>>('coding.files', {
381
+ tool_call_id: id,
382
+ args: params,
383
+ });
384
+ return {
385
+ content: [{ type: 'text', text: JSON.stringify(result) }],
386
+ details: result,
387
+ isError: params.action === 'patch' && result.state !== 'applied',
388
+ };
389
+ } catch (error) {
390
+ if (!normal(error)) {
391
+ await fail();
392
+ throw error;
393
+ }
394
+ return {
395
+ content: [{ type: 'text', text: (error as Error).message }],
396
+ details: { error: (error as ServiceError).code },
397
+ isError: true,
398
+ };
295
399
  }
296
- return {
297
- content: [{ type: 'text', text: (error as Error).message }],
298
- details: { error: (error as ServiceError).code },
299
- isError: true,
300
- };
301
- }
302
- },
303
- });
400
+ },
401
+ });
304
402
  if (c.coding.commands)
305
403
  pi.registerTool({
306
404
  name: 'salaam_command',
@@ -445,12 +543,12 @@ export default function house(pi: ExtensionAPI): void {
445
543
  ],
446
544
  };
447
545
  });
448
- if (route.provider === 'anthropic')
546
+ if (route.provider !== 'salaam-proof')
449
547
  registerClaude(
450
548
  pi,
451
549
  route,
452
550
  () => c.frame,
453
- workerTools(!!c.effects, !!c.child, c.coding),
551
+ workerTools(!!c.effects, !!c.child, c.coding, c.worker_binding?.binding.tools),
454
552
  bridge,
455
553
  fail,
456
554
  );
@@ -668,7 +766,7 @@ export default function house(pi: ExtensionAPI): void {
668
766
  session_id: c.session,
669
767
  epoch: c.epoch,
670
768
  pi_session_id: ctx.sessionManager.getSessionId(),
671
- capabilities: workerCapabilities(c.coding),
769
+ capabilities: workerCapabilities(c.coding, c.worker_binding?.binding.tools),
672
770
  ...(c.coding ? { coding: c.coding } : {}),
673
771
  tools: pi
674
772
  .getAllTools()
@@ -679,6 +777,7 @@ export default function house(pi: ExtensionAPI): void {
679
777
  provider: route.provider,
680
778
  route,
681
779
  context_digest: textDigest(c.frame),
780
+ ...(c.worker_binding ? { worker_binding: c.worker_binding } : {}),
682
781
  });
683
782
  timer = setInterval(() => {
684
783
  void bridge('heartbeat').catch(() => {
@@ -0,0 +1,347 @@
1
+ import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
2
+ import {
3
+ createAssistantMessageEventStream,
4
+ type AssistantMessage,
5
+ type AssistantMessageEvent,
6
+ type AuthResult,
7
+ } from '@earendil-works/pi-ai';
8
+ import { zstdDecompressSync } from 'node:zlib';
9
+ import { randomUUID } from 'node:crypto';
10
+ import { digest, object, textDigest, canonical } from './protocol.js';
11
+ import {
12
+ getProvider,
13
+ getProviderModel,
14
+ providerHasOutputCap,
15
+ type ProviderRoute,
16
+ } from './provider-config.js';
17
+ import { ProviderUsageReceipt } from './provider-usage.js';
18
+ import { materialize, generationOptions } from './provider-model.js';
19
+ import { validateProviderPayload } from './provider-payload.js';
20
+ import { installProviderTransport, withProviderTransport } from './provider-transport.js';
21
+
22
+ function freezeModel<T>(value: T): T {
23
+ const copy = structuredClone(value);
24
+ const freeze = (item: unknown) => {
25
+ if (item && typeof item === 'object' && !Object.isFrozen(item)) {
26
+ Object.values(item).forEach(freeze);
27
+ Object.freeze(item);
28
+ }
29
+ };
30
+ freeze(copy);
31
+ return copy;
32
+ }
33
+ function jsonBody(value: unknown, encoding?: string | null): unknown {
34
+ if (encoding) {
35
+ if (encoding !== 'zstd' || !(value instanceof Uint8Array))
36
+ throw new Error('Unsupported provider body encoding');
37
+ value = zstdDecompressSync(value, { maxOutputLength: 1048576 });
38
+ }
39
+ if (typeof value === 'string') return JSON.parse(value);
40
+ if (value instanceof Uint8Array) return JSON.parse(Buffer.from(value).toString('utf8'));
41
+ throw new Error('Provider JSON body missing');
42
+ }
43
+ export function registerLiveProvider(
44
+ pi: ExtensionAPI,
45
+ route: ProviderRoute,
46
+ frame: () => string,
47
+ tools: string[],
48
+ bridge: (method: string, data: unknown) => Promise<unknown>,
49
+ fail: () => Promise<void>,
50
+ ) {
51
+ const catalog = getProviderModel(route),
52
+ provider = getProvider(route.provider);
53
+ if (!catalog || !provider) throw new Error('Pinned provider model missing');
54
+ const rawFetch = installProviderTransport() ?? globalThis.fetch;
55
+ pi.registerProvider(route.provider, {
56
+ baseUrl: route.connection?.endpoint || catalog.baseUrl || 'https://salaam-configured.invalid',
57
+ apiKey: 'salaam-resolves-auth',
58
+ api: catalog.api,
59
+ models: [
60
+ {
61
+ ...catalog,
62
+ baseUrl:
63
+ route.connection?.endpoint || catalog.baseUrl || 'https://salaam-configured.invalid',
64
+ maxTokens: route.max_tokens,
65
+ },
66
+ ],
67
+ streamSimple(model, context, options) {
68
+ const output = createAssistantMessageEventStream(),
69
+ source = 'salaam-request:' + randomUUID(),
70
+ receipt = new ProviderUsageReceipt();
71
+ let failed = false,
72
+ dispatched = false,
73
+ status: number | undefined,
74
+ last: AssistantMessage | undefined;
75
+ const normalize = (message: AssistantMessage): AssistantMessage => ({
76
+ ...message,
77
+ responseId: source,
78
+ });
79
+ void (async () => {
80
+ try {
81
+ if (model.provider !== route.provider || model.id !== route.model)
82
+ throw new Error('Unapproved provider route');
83
+ const auth = (await bridge('provider.auth', {})) as AuthResult;
84
+ if (route.provider === 'amazon-bedrock') {
85
+ for (const name of Object.keys(process.env))
86
+ if (name.startsWith('AWS_')) delete process.env[name];
87
+ Object.assign(
88
+ process.env,
89
+ Object.fromEntries(
90
+ Object.entries(auth.env ?? {}).filter(([key]) => key.startsWith('AWS_')),
91
+ ),
92
+ );
93
+ }
94
+ const native = freezeModel(
95
+ materialize({ ...catalog, maxTokens: route.max_tokens }, auth),
96
+ );
97
+ let approved: unknown, approvedDigest: string | undefined;
98
+ const validate = (payload: unknown) =>
99
+ validateProviderPayload(
100
+ payload,
101
+ route,
102
+ frame(),
103
+ tools,
104
+ !providerHasOutputCap(route, auth),
105
+ );
106
+ const admit = async (payload: unknown) => {
107
+ if (dispatched) throw new Error('Provider request replay denied');
108
+ validate(payload);
109
+ if (!approvedDigest) throw new Error('Provider payload not approved');
110
+ dispatched = true;
111
+ await bridge('model', {
112
+ source_key: source,
113
+ provider: route.provider,
114
+ model: route.model,
115
+ context_digest: textDigest(frame()),
116
+ payload_digest: digest(payload),
117
+ payload,
118
+ });
119
+ };
120
+ const endpoint = (url: URL) => {
121
+ const base = new URL(native.baseUrl);
122
+ if (url.origin !== base.origin || url.username || url.password || url.hash)
123
+ throw new Error('Unapproved provider endpoint');
124
+ const prefix = base.pathname.replace(/\/$/, '');
125
+ if (native.api === 'google-generative-ai' || native.api === 'google-vertex') {
126
+ if (
127
+ !url.pathname.startsWith(prefix + '/') ||
128
+ !url.pathname.endsWith('/' + route.model + ':streamGenerateContent') ||
129
+ url.searchParams.get('alt') !== 'sse'
130
+ )
131
+ throw new Error('Unapproved Google endpoint');
132
+ } else if (native.api === 'bedrock-converse-stream') {
133
+ if (decodeURIComponent(url.pathname) !== '/model/' + route.model + '/converse-stream')
134
+ throw new Error('Unapproved Bedrock endpoint');
135
+ } else {
136
+ const suffixes =
137
+ native.api === 'anthropic-messages'
138
+ ? ['/v1/messages', '/messages']
139
+ : native.api === 'mistral-conversations'
140
+ ? ['/v1/chat/completions']
141
+ : native.api === 'openai-completions'
142
+ ? ['/chat/completions']
143
+ : native.api === 'pi-messages'
144
+ ? ['/messages']
145
+ : native.api === 'openai-codex-responses'
146
+ ? ['/codex/responses']
147
+ : ['/responses'];
148
+ if (!suffixes.some((suffix) => url.pathname === prefix + suffix))
149
+ throw new Error('Unapproved provider path');
150
+ }
151
+ };
152
+ const wirePayload = (payload: unknown): unknown => {
153
+ if (native.api === 'google-generative-ai' || native.api === 'google-vertex') {
154
+ const p = object(payload);
155
+ if (p.cachedContent !== undefined) throw new Error('Cached Google context disabled');
156
+ return {
157
+ model: route.model,
158
+ contents: p.contents,
159
+ config: { ...object(p.generationConfig), tools: p.tools, toolConfig: p.toolConfig },
160
+ };
161
+ }
162
+ if (native.api === 'bedrock-converse-stream')
163
+ return { ...object(payload), modelId: route.model };
164
+ return payload;
165
+ };
166
+ const transport = {
167
+ fetch: async (input: string | URL | Request, init?: RequestInit) => {
168
+ try {
169
+ const url = new URL(input instanceof Request ? input.url : String(input));
170
+ endpoint(url);
171
+ const request = input instanceof Request ? input : undefined;
172
+ if ((init?.method ?? request?.method) !== 'POST')
173
+ throw new Error('Provider POST required');
174
+ const raw = init?.body ?? (request ? await request.clone().text() : undefined);
175
+ const payload = jsonBody(
176
+ raw,
177
+ new Headers(init?.headers ?? request?.headers).get('content-encoding'),
178
+ );
179
+ validate(wirePayload(payload));
180
+ const expectedWire =
181
+ native.api === 'anthropic-messages'
182
+ ? Object.fromEntries(
183
+ Object.entries(object(approved)).filter(([key]) => key !== 'betas'),
184
+ )
185
+ : approved;
186
+ if (
187
+ !['google-generative-ai', 'google-vertex', 'mistral-conversations'].includes(
188
+ native.api,
189
+ ) &&
190
+ digest(payload) !== digest(expectedWire)
191
+ )
192
+ throw new Error('Provider payload changed after validation');
193
+ // The SDK translates Google/Mistral parameters. Validate the final wire controls too.
194
+ await admit(approved);
195
+ } catch {
196
+ failed = true;
197
+ throw new Error('Provider request denied by Salaam');
198
+ }
199
+ return rawFetch(input, { ...init, redirect: 'error' });
200
+ },
201
+ bedrock: async (request: {
202
+ protocol: string;
203
+ hostname: string;
204
+ path: string;
205
+ method: string;
206
+ body?: unknown;
207
+ }) => {
208
+ try {
209
+ endpoint(new URL(request.protocol + '//' + request.hostname + request.path));
210
+ if (request.method !== 'POST') throw new Error('Provider POST required');
211
+ const payload = wirePayload(jsonBody(request.body));
212
+ validate(payload);
213
+ // AWS serializes undefined fields away. Compare the canonical JSON representation.
214
+ if (canonical(payload) !== canonical(JSON.parse(JSON.stringify(approved))))
215
+ throw new Error('Bedrock payload changed after validation');
216
+ await admit(approved);
217
+ } catch {
218
+ failed = true;
219
+ throw new Error('Provider request denied by Salaam');
220
+ }
221
+ },
222
+ };
223
+ const headers = { ...options?.headers };
224
+ for (const name of Object.keys(headers))
225
+ if (
226
+ [
227
+ 'authorization',
228
+ 'x-api-key',
229
+ 'api-key',
230
+ 'x-goog-api-key',
231
+ 'cf-aig-authorization',
232
+ 'anthropic-workspace-id',
233
+ ].includes(name.toLowerCase())
234
+ )
235
+ delete headers[name];
236
+ Object.assign(headers, auth.auth.headers);
237
+ if (route.workspace_id) headers['anthropic-workspace-id'] = route.workspace_id;
238
+ const upstream = withProviderTransport(transport, () =>
239
+ provider.streamSimple(native, context, {
240
+ ...options,
241
+ headers,
242
+ apiKey: auth.auth.apiKey,
243
+ env: auth.env ?? {},
244
+ ...generationOptions(native, context, route.max_tokens),
245
+ transport: 'sse',
246
+ maxRetries: 0,
247
+ timeoutMs: 120000,
248
+ fetch: ['google-generative-ai', 'google-vertex', 'bedrock-converse-stream'].includes(
249
+ native.api,
250
+ )
251
+ ? undefined
252
+ : transport.fetch,
253
+ onPayload: async (payload, rawModel) => {
254
+ try {
255
+ const replacement = await options?.onPayload?.(payload, rawModel);
256
+ const actual = replacement === undefined ? payload : replacement;
257
+ validate(actual);
258
+ if (native.api === 'google-generative-ai' || native.api === 'google-vertex')
259
+ object(actual).config = {
260
+ ...object(object(actual).config),
261
+ httpOptions: { retryOptions: { attempts: 1 }, timeout: 120000 },
262
+ };
263
+ approved = JSON.parse(JSON.stringify(actual));
264
+ approvedDigest = digest(approved);
265
+ return actual;
266
+ } catch {
267
+ failed = true;
268
+ throw new Error('Provider payload denied');
269
+ }
270
+ },
271
+ onProviderStreamEvent: async (event, rawModel) => {
272
+ receipt.observe(catalog.api, event);
273
+ await options?.onProviderStreamEvent?.(event, rawModel);
274
+ },
275
+ onResponse: async (response, rawModel) => {
276
+ status = response.status;
277
+ await options?.onResponse?.(response, rawModel);
278
+ },
279
+ }),
280
+ );
281
+ for await (const event of upstream) {
282
+ if ('partial' in event) last = normalize(event.partial);
283
+ if (event.type === 'done') {
284
+ if (!dispatched) throw new Error('Provider returned without admission');
285
+ const message = normalize(event.message);
286
+ last = message;
287
+ if (receipt.complete)
288
+ await bridge('usage', {
289
+ source_key: source,
290
+ usage: message.usage,
291
+ provider_response_id: event.message.responseId ?? null,
292
+ });
293
+ output.push({ ...event, message });
294
+ } else if (event.type === 'error') {
295
+ const message = normalize(event.error);
296
+ const code = /^(?:Error:\s*)?(\d{3})\b/.exec(message.errorMessage ?? '')?.[1];
297
+ status ??= code ? Number(code) : undefined;
298
+ delete message.diagnostics;
299
+ message.content = [];
300
+ message.errorMessage = options?.signal?.aborted
301
+ ? 'Provider request cancelled'
302
+ : `Provider request failed${status ? ` (HTTP ${status})` : ''}`;
303
+ output.push({ ...event, error: message });
304
+ } else
305
+ output.push(
306
+ ('partial' in event
307
+ ? { ...event, partial: last! }
308
+ : event) as AssistantMessageEvent,
309
+ );
310
+ }
311
+ output.end();
312
+ if (failed) await fail();
313
+ } catch {
314
+ const error: AssistantMessage = {
315
+ role: 'assistant',
316
+ content: [],
317
+ api: catalog.api,
318
+ provider: route.provider,
319
+ model: route.model,
320
+ timestamp: Date.now(),
321
+ responseId: source,
322
+ stopReason: options?.signal?.aborted ? 'aborted' : 'error',
323
+ usage: last?.usage ?? {
324
+ input: 0,
325
+ output: 0,
326
+ cacheRead: 0,
327
+ cacheWrite: 0,
328
+ totalTokens: 0,
329
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
330
+ },
331
+ errorMessage: options?.signal?.aborted
332
+ ? 'Provider request cancelled'
333
+ : 'Provider required control failed',
334
+ };
335
+ output.push({
336
+ type: 'error',
337
+ reason: error.stopReason === 'aborted' ? 'aborted' : 'error',
338
+ error,
339
+ });
340
+ output.end();
341
+ if (!options?.signal?.aborted) await fail();
342
+ }
343
+ })();
344
+ return output;
345
+ },
346
+ });
347
+ }