@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
@@ -0,0 +1,418 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { AnthropicHarnessGateway } from './harness-model-gateway.js';
3
+ import type { GatewayRequest, GatewayResponse } from './harness-container.js';
4
+ import { ServiceError } from './protocol.js';
5
+
6
+ type Json = Record<string, any>;
7
+ function invalid(): never {
8
+ throw new ServiceError(
9
+ 'harness_wire_unsupported',
10
+ 'Payload exceeds the admitted text/custom-tool wire subset',
11
+ );
12
+ }
13
+ const object = (value: unknown): Json =>
14
+ value && typeof value === 'object' && !Array.isArray(value) ? (value as Json) : invalid();
15
+ const list = (value: unknown): Json[] =>
16
+ Array.isArray(value) && value.length <= 512 ? value.map(object) : invalid();
17
+ const text = (value: unknown): string => (typeof value === 'string' ? value : invalid());
18
+ const id = (value: unknown): string =>
19
+ typeof value === 'string' && /^[a-zA-Z0-9_-]{1,128}$/.test(value) ? value : invalid();
20
+ interface Transcript {
21
+ system: string[];
22
+ messages: Json[];
23
+ tools: Json[];
24
+ }
25
+ /** Explicit protocol translation to the operator-declared Claude backend, never a Google/OpenAI account claim. */
26
+ export class HarnessWireBridge {
27
+ constructor(
28
+ readonly backend: AnthropicHarnessGateway,
29
+ readonly wire: { protocol: 'google' | 'responses'; model: string; tools: readonly string[] },
30
+ readonly onTool?: (intent: { id: string; name: string; arguments: Json }) => void,
31
+ ) {
32
+ if (
33
+ !wire.model ||
34
+ !wire.tools.length ||
35
+ wire.tools.some((n) => !/^[a-zA-Z0-9_-]{1,128}$/.test(n))
36
+ )
37
+ throw Error('Invalid frozen harness wire policy');
38
+ }
39
+ private tool(name: unknown): string {
40
+ const n = text(name);
41
+ if (!this.wire.tools.includes(n)) invalid();
42
+ return n;
43
+ }
44
+ private append(t: Transcript, role: string, content: Json[]): void {
45
+ if (!content.length) return;
46
+ const previous = t.messages.at(-1);
47
+ if (previous?.role === role) previous.content.push(...content);
48
+ else t.messages.push({ role, content });
49
+ }
50
+ private google(body: Json): Transcript {
51
+ if (
52
+ Object.keys(body).some(
53
+ (k) =>
54
+ !['contents', 'systemInstruction', 'tools', 'generationConfig', 'toolConfig'].includes(k),
55
+ )
56
+ )
57
+ invalid();
58
+ const t: Transcript = { system: [], messages: [], tools: [] },
59
+ pending = new Map<string, string>(),
60
+ byName = new Map<string, string[]>();
61
+ if (body.systemInstruction)
62
+ for (const part of list(object(body.systemInstruction).parts)) t.system.push(text(part.text));
63
+ for (const tool of list(body.tools ?? []))
64
+ for (const declaration of list(tool.functionDeclarations ?? [])) {
65
+ const name = text(declaration.name);
66
+ if (!this.wire.tools.includes(name)) continue;
67
+ t.tools.push({
68
+ name,
69
+ description: typeof declaration.description === 'string' ? declaration.description : '',
70
+ input_schema: object(declaration.parametersJsonSchema ?? declaration.parameters),
71
+ });
72
+ }
73
+ for (const [position, message] of list(body.contents).entries()) {
74
+ if (!['user', 'model'].includes(message.role)) invalid();
75
+ const parts: Json[] = [];
76
+ for (const [index, part] of list(message.parts).entries()) {
77
+ if (typeof part.text === 'string') {
78
+ parts.push({ type: 'text', text: part.text });
79
+ continue;
80
+ }
81
+ if (part.functionCall && message.role === 'model') {
82
+ const call = object(part.functionCall),
83
+ name = this.tool(call.name),
84
+ callId = call.id === undefined ? 'google_' + position + '_' + index : id(call.id);
85
+ if (pending.has(callId)) invalid();
86
+ pending.set(callId, name);
87
+ byName.set(name, [...(byName.get(name) ?? []), callId]);
88
+ parts.push({ type: 'tool_use', id: callId, name, input: object(call.args) });
89
+ continue;
90
+ }
91
+ if (part.functionResponse && message.role === 'user') {
92
+ const reply = object(part.functionResponse),
93
+ name = this.tool(reply.name),
94
+ options = byName.get(name) ?? [],
95
+ callId =
96
+ reply.id === undefined
97
+ ? options.length === 1
98
+ ? options[0]!
99
+ : invalid()
100
+ : id(reply.id);
101
+ if (pending.get(callId) !== name) invalid();
102
+ pending.delete(callId);
103
+ byName.set(
104
+ name,
105
+ options.filter((n) => n !== callId),
106
+ );
107
+ parts.push({
108
+ type: 'tool_result',
109
+ tool_use_id: callId,
110
+ content: JSON.stringify(reply.response),
111
+ });
112
+ continue;
113
+ }
114
+ invalid();
115
+ }
116
+ this.append(t, message.role === 'model' ? 'assistant' : 'user', parts);
117
+ }
118
+ if (pending.size) invalid();
119
+ return t;
120
+ }
121
+ private responses(body: Json): Transcript {
122
+ if (body.model !== this.wire.model || body.previous_response_id) invalid();
123
+ const t: Transcript = { system: [], messages: [], tools: [] },
124
+ pending = new Set<string>();
125
+ if (body.instructions !== undefined) t.system.push(text(body.instructions));
126
+ for (const tool of list(body.tools ?? [])) {
127
+ if (tool.type !== 'function' || !this.wire.tools.includes(tool.name)) continue;
128
+ t.tools.push({
129
+ name: this.tool(tool.name),
130
+ description: typeof tool.description === 'string' ? tool.description : '',
131
+ input_schema: object(tool.parameters),
132
+ });
133
+ }
134
+ for (const item of list(body.input)) {
135
+ if (item.type === 'message' || item.role) {
136
+ if (!['user', 'assistant', 'system', 'developer'].includes(item.role)) invalid();
137
+ const parts =
138
+ typeof item.content === 'string'
139
+ ? [{ text: item.content }]
140
+ : list(item.content).map((p) => {
141
+ if (!['input_text', 'output_text'].includes(p.type)) invalid();
142
+ return { text: text(p.text) };
143
+ });
144
+ if (['system', 'developer'].includes(item.role))
145
+ t.system.push(parts.map((p) => p.text).join('\n'));
146
+ else
147
+ this.append(
148
+ t,
149
+ item.role,
150
+ parts.map((p) => ({ type: 'text', text: p.text })),
151
+ );
152
+ continue;
153
+ }
154
+ if (item.type === 'function_call') {
155
+ const callId = id(item.call_id);
156
+ if (pending.has(callId)) invalid();
157
+ pending.add(callId);
158
+ let input: Json;
159
+ try {
160
+ input = object(JSON.parse(text(item.arguments)));
161
+ } catch {
162
+ invalid();
163
+ }
164
+ this.append(t, 'assistant', [
165
+ { type: 'tool_use', id: callId, name: this.tool(item.name), input },
166
+ ]);
167
+ continue;
168
+ }
169
+ if (item.type === 'function_call_output') {
170
+ const callId = id(item.call_id);
171
+ if (!pending.delete(callId)) invalid();
172
+ const output = typeof item.output === 'string' ? item.output : JSON.stringify(item.output);
173
+ this.append(t, 'user', [{ type: 'tool_result', tool_use_id: callId, content: output }]);
174
+ continue;
175
+ }
176
+ // A foreign provider's opaque thinking signature is never forwarded as Claude authority.
177
+ if (item.type === 'reasoning') continue;
178
+ invalid();
179
+ }
180
+ if (pending.size) invalid();
181
+ return t;
182
+ }
183
+ async request(request: GatewayRequest): Promise<GatewayResponse> {
184
+ if (request.method !== 'POST' || request.body.length > 2 * 1024 * 1024) invalid();
185
+ const expected =
186
+ this.wire.protocol === 'google'
187
+ ? '/v1beta/models/' + this.wire.model + ':streamGenerateContent?alt=sse'
188
+ : '/v1/responses';
189
+ if (request.path !== expected) invalid();
190
+ let raw: Json;
191
+ try {
192
+ raw = object(JSON.parse(request.body.toString('utf8')));
193
+ } catch {
194
+ invalid();
195
+ }
196
+ const transcript = this.wire.protocol === 'google' ? this.google(raw) : this.responses(raw);
197
+ const requested =
198
+ this.wire.protocol === 'google'
199
+ ? raw.generationConfig?.maxOutputTokens
200
+ : raw.max_output_tokens;
201
+ if (requested !== undefined && (!Number.isSafeInteger(requested) || requested < 1)) invalid();
202
+ const body = {
203
+ model: this.backend.policy.model,
204
+ max_tokens: Math.min(requested ?? this.backend.policy.output, this.backend.policy.output),
205
+ stream: false,
206
+ system: transcript.system.join('\n\n'),
207
+ messages: transcript.messages,
208
+ ...(transcript.tools.length
209
+ ? {
210
+ tools: transcript.tools,
211
+ tool_choice: { type: 'auto', disable_parallel_tool_use: true },
212
+ }
213
+ : {}),
214
+ };
215
+ const result = await this.backend.request({
216
+ ...request,
217
+ path: '/v1/messages',
218
+ body: Buffer.from(JSON.stringify(body)),
219
+ });
220
+ if (result.status !== 200)
221
+ return {
222
+ status: result.status,
223
+ content_type: 'application/json',
224
+ body: Buffer.from(
225
+ JSON.stringify({
226
+ error: {
227
+ code: result.status,
228
+ message: 'Declared model backend rejected this request',
229
+ status: 'FAILED_PRECONDITION',
230
+ },
231
+ }),
232
+ ),
233
+ };
234
+ let bytes: Buffer;
235
+ if (Buffer.isBuffer(result.body)) bytes = result.body;
236
+ else {
237
+ const chunks: Buffer[] = [];
238
+ let length = 0;
239
+ for await (const chunk of result.body) {
240
+ length += chunk.length;
241
+ if (length > 8 * 1024 * 1024) invalid();
242
+ chunks.push(Buffer.from(chunk));
243
+ }
244
+ bytes = Buffer.concat(chunks);
245
+ }
246
+ const response = object(JSON.parse(bytes.toString('utf8'))),
247
+ content = list(response.content),
248
+ usage = object(response.usage ?? {});
249
+ for (const part of content)
250
+ if (part.type === 'tool_use') {
251
+ this.tool(part.name);
252
+ this.onTool?.({
253
+ id: id(part.id),
254
+ name: this.tool(part.name),
255
+ arguments: structuredClone(object(part.input)),
256
+ });
257
+ } else if (!['text', 'thinking', 'redacted_thinking'].includes(part.type)) invalid();
258
+ if (this.wire.protocol === 'google') {
259
+ const parts = content.flatMap<Json>((p) =>
260
+ p.type === 'text'
261
+ ? [{ text: text(p.text) }]
262
+ : p.type === 'tool_use'
263
+ ? [{ functionCall: { id: id(p.id), name: this.tool(p.name), args: object(p.input) } }]
264
+ : [],
265
+ );
266
+ const payload = {
267
+ candidates: [
268
+ {
269
+ index: 0,
270
+ content: { role: 'model', parts },
271
+ finishReason: response.stop_reason === 'max_tokens' ? 'MAX_TOKENS' : 'STOP',
272
+ },
273
+ ],
274
+ usageMetadata: {
275
+ promptTokenCount: usage.input_tokens ?? 0,
276
+ candidatesTokenCount: usage.output_tokens ?? 0,
277
+ totalTokenCount: (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0),
278
+ },
279
+ modelVersion: this.wire.model,
280
+ responseId: response.id,
281
+ };
282
+ return {
283
+ status: 200,
284
+ content_type: 'text/event-stream',
285
+ body: Buffer.from('data: ' + JSON.stringify(payload) + '\n\n'),
286
+ };
287
+ }
288
+ const responseId = 'resp_' + randomUUID().replaceAll('-', ''),
289
+ output: Json[] = content.flatMap<Json>((p) =>
290
+ p.type === 'text'
291
+ ? [
292
+ {
293
+ id: 'msg_' + randomUUID().replaceAll('-', ''),
294
+ type: 'message',
295
+ status: 'completed',
296
+ role: 'assistant',
297
+ content: [{ type: 'output_text', text: text(p.text), annotations: [] }],
298
+ },
299
+ ]
300
+ : p.type === 'tool_use'
301
+ ? [
302
+ {
303
+ id: 'fc_' + id(p.id),
304
+ type: 'function_call',
305
+ status: 'completed',
306
+ call_id: id(p.id),
307
+ name: this.tool(p.name),
308
+ arguments: JSON.stringify(object(p.input)),
309
+ },
310
+ ]
311
+ : [],
312
+ );
313
+ const complete = {
314
+ id: responseId,
315
+ object: 'response',
316
+ created_at: Math.floor(Date.now() / 1000),
317
+ status: response.stop_reason === 'max_tokens' ? 'incomplete' : 'completed',
318
+ error: null,
319
+ incomplete_details:
320
+ response.stop_reason === 'max_tokens' ? { reason: 'max_output_tokens' } : null,
321
+ model: this.wire.model,
322
+ output,
323
+ usage: {
324
+ input_tokens: usage.input_tokens ?? 0,
325
+ output_tokens: usage.output_tokens ?? 0,
326
+ total_tokens: (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0),
327
+ input_tokens_details: { cached_tokens: usage.cache_read_input_tokens ?? 0 },
328
+ output_tokens_details: { reasoning_tokens: 0 },
329
+ },
330
+ parallel_tool_calls: false,
331
+ };
332
+ if (raw.stream !== true)
333
+ return {
334
+ status: 200,
335
+ content_type: 'application/json',
336
+ body: Buffer.from(JSON.stringify(complete)),
337
+ };
338
+ let serial = 0;
339
+ const events: Json[] = [
340
+ { type: 'response.created', response: { ...complete, status: 'in_progress', output: [] } },
341
+ ];
342
+ for (const [index, item] of output.entries()) {
343
+ events.push({
344
+ type: 'response.output_item.added',
345
+ output_index: index,
346
+ item: {
347
+ ...item,
348
+ status: 'in_progress',
349
+ ...(item.type === 'function_call' ? { arguments: '' } : { content: [] }),
350
+ },
351
+ });
352
+ if (item.type === 'function_call')
353
+ events.push(
354
+ {
355
+ type: 'response.function_call_arguments.delta',
356
+ item_id: item.id,
357
+ output_index: index,
358
+ delta: item.arguments,
359
+ },
360
+ {
361
+ type: 'response.function_call_arguments.done',
362
+ item_id: item.id,
363
+ output_index: index,
364
+ arguments: item.arguments,
365
+ },
366
+ );
367
+ else {
368
+ events.push(
369
+ {
370
+ type: 'response.content_part.added',
371
+ item_id: item.id,
372
+ output_index: index,
373
+ content_index: 0,
374
+ part: { type: 'output_text', text: '', annotations: [] },
375
+ },
376
+ {
377
+ type: 'response.output_text.delta',
378
+ item_id: item.id,
379
+ output_index: index,
380
+ content_index: 0,
381
+ delta: item.content[0].text,
382
+ },
383
+ {
384
+ type: 'response.output_text.done',
385
+ item_id: item.id,
386
+ output_index: index,
387
+ content_index: 0,
388
+ text: item.content[0].text,
389
+ },
390
+ {
391
+ type: 'response.content_part.done',
392
+ item_id: item.id,
393
+ output_index: index,
394
+ content_index: 0,
395
+ part: item.content[0],
396
+ },
397
+ );
398
+ }
399
+ events.push({ type: 'response.output_item.done', output_index: index, item });
400
+ }
401
+ events.push({
402
+ type: complete.status === 'completed' ? 'response.completed' : 'response.incomplete',
403
+ response: complete,
404
+ });
405
+ return {
406
+ status: 200,
407
+ content_type: 'text/event-stream',
408
+ body: Buffer.from(
409
+ events
410
+ .map((event) => {
411
+ const value = { ...event, sequence_number: serial++ };
412
+ return 'event: ' + event.type + '\ndata: ' + JSON.stringify(value) + '\n\n';
413
+ })
414
+ .join(''),
415
+ ),
416
+ };
417
+ }
418
+ }
package/src/host.ts CHANGED
@@ -16,6 +16,8 @@ import {
16
16
  import { join, resolve, isAbsolute, sep } from 'node:path';
17
17
  import { ROOT, bundleDigest } from './config.js';
18
18
  import { digest, object, string, ServiceError } from './protocol.js';
19
+ import { bindProviderConfiguration } from './provider-model.js';
20
+ import { resolveProviderAuth } from './provider-auth.js';
19
21
  import { providerRoute, type ProviderRoute } from './provider-config.js';
20
22
  import { engineRequest } from './execution-boundary.js';
21
23
  import { DockerCodingExecutor } from './coding-executor.js';
@@ -78,6 +80,14 @@ export async function planHost(input: unknown): Promise<HostManifest> {
78
80
  mkdirSync(coding_root, { recursive: true, mode: 0o700 });
79
81
  const executor = new DockerCodingExecutor(engineRequest(coding_socket));
80
82
  await executor.prepare(coding_root);
83
+ let route = providerRoute(
84
+ c.provider === undefined ? 'offline' : String(c.provider),
85
+ c.model as string | undefined,
86
+ c.max_output_tokens as number | undefined,
87
+ c.workspace_id as string | undefined,
88
+ );
89
+ if (route.provider !== 'salaam-proof')
90
+ route = bindProviderConfiguration(route, await resolveProviderAuth(route));
81
91
  return {
82
92
  schema: 1,
83
93
  node: realpathSync(process.execPath),
@@ -87,12 +97,7 @@ export async function planHost(input: unknown): Promise<HostManifest> {
87
97
  state,
88
98
  coding_root: realpathSync(coding_root),
89
99
  coding_socket,
90
- provider: providerRoute(
91
- c.provider === undefined ? 'offline' : String(c.provider),
92
- c.model as string | undefined,
93
- c.max_output_tokens as number | undefined,
94
- c.workspace_id as string | undefined,
95
- ),
100
+ provider: route,
96
101
  profile_digest: executor.profileDigest,
97
102
  engine_id: executor.engineId,
98
103
  };