@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
@@ -1,179 +1,2 @@
1
- import { createAssistantMessageEventStream, } from '@earendil-works/pi-ai';
2
- import { streamSimple as streamClaude } from '@earendil-works/pi-ai/api/anthropic-messages';
3
- import { randomUUID } from 'node:crypto';
4
- import { digest, textDigest } from './protocol.js';
5
- import { getClaudeModel, validateClaudePayload } from './provider-config.js';
6
- export function registerClaude(pi, route, frame, tools, bridge, fail) {
7
- const catalog = getClaudeModel(route.model);
8
- if (!catalog)
9
- throw new Error('Pinned Claude model missing');
10
- const key = process.env.ANTHROPIC_API_KEY;
11
- if (!key)
12
- throw new Error('Claude API key unavailable to worker');
13
- pi.registerProvider('anthropic', {
14
- baseUrl: 'https://api.anthropic.com',
15
- apiKey: '$ANTHROPIC_API_KEY',
16
- api: 'anthropic-messages',
17
- models: [{ ...catalog, maxTokens: route.max_tokens }],
18
- streamSimple(model, context, options) {
19
- const output = createAssistantMessageEventStream();
20
- const source = 'salaam-request:' + randomUUID();
21
- let dispatched = false, gateFailed = false, status;
22
- let last;
23
- const normalize = (message) => ({
24
- ...message,
25
- responseId: source,
26
- });
27
- void (async () => {
28
- try {
29
- if (model.provider !== route.provider || model.id !== route.model)
30
- throw new Error('Unapproved Claude route');
31
- const native = {
32
- ...catalog,
33
- provider: 'anthropic',
34
- api: 'anthropic-messages',
35
- baseUrl: 'https://api.anthropic.com',
36
- compat: {
37
- ...catalog.compat,
38
- supportsMidConvoSystemMessages: false,
39
- supportsMidConvoToolChanges: false,
40
- supportsMidConvoEffort: false,
41
- },
42
- };
43
- const headers = {};
44
- for (const [name, value] of Object.entries(options?.headers ?? {}))
45
- headers[name.toLowerCase()] = value;
46
- delete headers.authorization;
47
- delete headers['x-api-key'];
48
- delete headers['anthropic-workspace-id'];
49
- if (route.workspace_id)
50
- headers['anthropic-workspace-id'] = route.workspace_id;
51
- let approvedDigest;
52
- const upstream = streamClaude(native, context, {
53
- ...options,
54
- apiKey: key,
55
- env: {},
56
- headers,
57
- maxTokens: route.max_tokens,
58
- reasoning: 'low',
59
- maxRetries: 0,
60
- timeoutMs: 120000,
61
- onPayload: async (payload, rawModel) => {
62
- const replacement = await options?.onPayload?.(payload, rawModel);
63
- const actual = replacement === undefined ? payload : replacement;
64
- try {
65
- validateClaudePayload(actual, route, frame(), tools);
66
- approvedDigest = digest(actual);
67
- }
68
- catch {
69
- gateFailed = true;
70
- throw new Error('Claude payload denied');
71
- }
72
- return actual;
73
- },
74
- onResponse: async (response, rawModel) => {
75
- status = response.status;
76
- await options?.onResponse?.(response, rawModel);
77
- },
78
- fetch: async (input, init) => {
79
- try {
80
- const url = new URL(input instanceof Request ? input.url : String(input));
81
- if (url.href !== 'https://api.anthropic.com/v1/messages?beta=true' &&
82
- url.href !== 'https://api.anthropic.com/v1/messages')
83
- throw new Error('Unapproved Claude endpoint');
84
- if (init?.method !== 'POST' || typeof init.body !== 'string' || dispatched)
85
- throw new Error('Claude request replay denied');
86
- const sentHeaders = new Headers(init.headers);
87
- if (sentHeaders.get('x-api-key') !== key || sentHeaders.has('authorization'))
88
- throw new Error('Unapproved Claude authentication');
89
- const payload = JSON.parse(init.body);
90
- validateClaudePayload(payload, route, frame(), tools);
91
- if (approvedDigest !== digest(payload))
92
- throw new Error('Claude payload changed after validation');
93
- dispatched = true;
94
- await bridge('model', {
95
- source_key: source,
96
- provider: 'anthropic',
97
- model: route.model,
98
- context_digest: textDigest(frame()),
99
- payload_digest: approvedDigest,
100
- payload,
101
- });
102
- }
103
- catch {
104
- gateFailed = true;
105
- throw new Error('Claude request denied by Salaam');
106
- }
107
- return globalThis.fetch(input, { ...init, redirect: 'error' });
108
- },
109
- });
110
- for await (const event of upstream) {
111
- if ('partial' in event)
112
- last = normalize(event.partial);
113
- if (event.type === 'done') {
114
- const message = normalize(event.message);
115
- last = message;
116
- await bridge('usage', {
117
- source_key: source,
118
- usage: message.usage,
119
- provider_response_id: event.message.responseId ?? null,
120
- });
121
- output.push({ ...event, message });
122
- }
123
- else if (event.type === 'error') {
124
- const message = normalize(event.error);
125
- const code = /^(?:Error:\s*)?(\d{3})\b/.exec(message.errorMessage ?? '')?.[1];
126
- status = status ?? (code ? Number(code) : undefined);
127
- delete message.diagnostics;
128
- message.errorMessage = options?.signal?.aborted
129
- ? 'Claude request cancelled'
130
- : `Claude request failed${status ? ` (HTTP ${status})` : ''}`;
131
- message.content = [];
132
- last = message;
133
- output.push({ ...event, error: message });
134
- }
135
- else
136
- output.push(('partial' in event
137
- ? { ...event, partial: last }
138
- : event));
139
- }
140
- output.end();
141
- if (gateFailed)
142
- await fail();
143
- }
144
- catch {
145
- const error = {
146
- role: 'assistant',
147
- content: [],
148
- api: 'anthropic-messages',
149
- provider: 'anthropic',
150
- model: route.model,
151
- timestamp: Date.now(),
152
- stopReason: options?.signal?.aborted ? 'aborted' : 'error',
153
- usage: last?.usage ?? {
154
- input: 0,
155
- output: 0,
156
- cacheRead: 0,
157
- cacheWrite: 0,
158
- totalTokens: 0,
159
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
160
- },
161
- responseId: source,
162
- errorMessage: options?.signal?.aborted
163
- ? 'Claude request cancelled'
164
- : 'Claude required control failed',
165
- };
166
- output.push({
167
- type: 'error',
168
- reason: error.stopReason === 'aborted' ? 'aborted' : 'error',
169
- error,
170
- });
171
- output.end();
172
- if (!options?.signal?.aborted)
173
- await fail();
174
- }
175
- })();
176
- return output;
177
- },
178
- });
179
- }
1
+ // Kept as an internal compatibility entry point for existing extensions.
2
+ export { registerLiveProvider as registerClaude } from './live-provider.js';
@@ -1,4 +1,58 @@
1
1
  const commands = {
2
+ a2a: `salaam a2a credential PEER_ID --file NEW_OWNER_ONLY_FILE
3
+ salaam a2a discover --peer ID --endpoint URL --card-url URL --credential-file FILE --addresses IP[,IP] [--local]
4
+ salaam a2a pin --peer ID --endpoint URL --card-url URL --card-file REVIEWED_JSON --credential-file FILE --addresses IP[,IP] --skill SKILL [--tenant TENANT --files PATH[,PATH] --local]
5
+ salaam a2a peers
6
+ salaam a2a grant --assignment ID --peer ID --credential-file OWNER_ONLY_JSON [--tenant TENANT --skill SKILL] [--peer-client openclaw-2026.10.5]
7
+ salaam a2a inspect CHILD_ID
8
+ salaam a2a reconcile CHILD_ID --run-id EXACT_RUN_ID
9
+
10
+ Use --a2a-config FILE for outbound peers and --a2a-listen-config FILE for private
11
+ inbound one-use analysis grants. Peer credentials grant no human/operator powers.
12
+ Remote internal model usage and execution remain unobserved.`,
13
+ harness: `salaam harness probe --pin FILE
14
+ salaam harness prepare --runtime-root NEW_DIR --arch linux-arm64|linux-x64
15
+ salaam harness config --target TARGET --runtime-root PREPARED_DIR [--id ID]
16
+ salaam harness list
17
+ salaam harness inspect CHILD_ID
18
+ salaam harness reconcile CHILD_ID --run-id RUN_ID
19
+
20
+ Negotiate an operator-pinned external harness in a disposable empty home, with no
21
+ repository, credentials or model request. FILE contains target, executable,
22
+ executable_digest and version. Targets: gemini-acp, claude-acp, codex-app-server.
23
+ Advertised capabilities are separate from enforced authority. A probe grants no
24
+ file, command or worker-dispatch permission. A service started with an owner-only
25
+ --harness-config FILE runs fresh Docker readiness proofs before admitting configured
26
+ targets. Reconcile confirms stopped resources; it never resends a prompt.`,
27
+ workers: `salaam workers proposal CHILD_ID [--offset N]
28
+ salaam workers integrate CHILD_ID --revision N --root-digest SHA256 --patch-digest SHA256
29
+ salaam workers integration RECEIPT_ID
30
+ salaam workers resolve RECEIPT_ID --revision N --root-digest SHA256
31
+
32
+ Writing specialists use private drafts. Review every proposal page and the root digest before integration.
33
+ Integration does not verify a task. Resolution explicitly admits a new apply only against its unchanged original root.
34
+ Mutations accept --request-id ID and --takeover.`,
35
+ team: `salaam team show TASK_ID
36
+ salaam team assign TASK_ID --step STEP_ID --profile PROFILE_ID --requests N --revision N
37
+ salaam team assign TASK_ID --step STEP_ID --profile PROFILE_ID --runtime RUNTIME_ID --invocations 1 --revision N
38
+ salaam team run TASK_ID --assignment ID --generation N --revision N [--instruction TEXT --continues ATTEMPT_ID]
39
+ salaam team send TASK_ID --to ASSIGNMENT_ID --text TEXT
40
+ salaam team receive TASK_ID --assignment ID
41
+ salaam team ack TASK_ID --assignment ID --message MESSAGE_ID
42
+ salaam team reject TASK_ID --assignment ID --generation N --revision N --reason TEXT
43
+
44
+ Mutations accept --request-id ID and --takeover. Claims bind the plan, admitted profile and generation.
45
+ Messages never wake workers. Use run for an explicit new claim or follow-up.
46
+ External assignments require a task with explicit limits.invocations and
47
+ token_units:null. Their internal model usage is reported as unknown.`,
48
+ agents: `salaam agents list --session SESSION_ID
49
+ salaam agents inspect PROFILE_ID --session SESSION_ID
50
+ salaam agents admit PROFILE_ID --session SESSION_ID --digest REVIEWED_DIGEST [--facts ID,ID]
51
+ salaam agents revoke GRANT_ID --session SESSION_ID
52
+
53
+ Profiles are data in .salaam/agents/*.md with JSON front matter and a prompt body.
54
+ Inspect the effective tools and digest before admission. Project definitions cannot
55
+ grant themselves authority. Mutations accept --request-id and --takeover.`,
2
56
  tui: `salaam tui [--session SESSION_ID] [--observe|--takeover] [--theme night|paper]
3
57
  [--cwd DIR] [--coding-root PRIVATE_DIR] [--coding-socket DOCKER_SOCKET]
4
58
 
@@ -110,6 +164,8 @@ salaam tasks cancel TASK_ID --revision N
110
164
  Mutations accept --request-id ID and --takeover. Reuse the same request ID and input
111
165
  when retrying a lost response. One task per session. Automatic progression is opt-in
112
166
  and bounded by step, request, runtime, concurrency and declared token limits.
167
+ Plans may explicitly set limits.token_units to null to disable token budgeting;
168
+ request/runtime limits and approvals still apply. Unsupported output caps are rejected.
113
169
  Success requires fresh command receipts and deliverables at the exact workspace revision.
114
170
  Operator exceptions are reported separately. Checkpoints preserve conflicting human edits.
115
171
  Legacy task facts remain under the singular task command.`,
@@ -123,18 +179,33 @@ Updates are explicit and versioned. Corrections retain the original fact and its
123
179
  Read bounded evidence/history pages, including entries preceding compaction.`,
124
180
  compactions: `salaam compactions inspect COMPACTION_ID
125
181
  salaam compactions cancel COMPACTION_ID [--takeover] [--request-id ID]`,
126
- auth: `salaam auth set anthropic
182
+ providers: `salaam providers list
183
+
184
+ List every provider in the pinned Pi registry, its chat APIs and auth methods.
185
+ Catalog support does not imply live account access or verification.`,
186
+ models: `salaam models list [PROVIDER]
187
+
188
+ List pinned chat model IDs without network requests or credentials.`,
189
+ auth: `salaam auth set PROVIDER
190
+ salaam auth login PROVIDER [--oauth]
191
+ salaam auth status [PROVIDER]
192
+ salaam auth logout PROVIDER
127
193
 
128
- Enter a Claude API key at a hidden prompt. It is saved privately under
129
- ~/.config/salaam/credentials/anthropic. ANTHROPIC_API_KEY takes precedence.`,
130
- service: `salaam service start [--foreground] [--provider offline|anthropic]
194
+ Set an API key at a hidden prompt (or pipe it on stdin). Login uses Pi's provider-owned
195
+ setup, including cloud configuration and browser OAuth. Credentials are private under
196
+ ~/.config/salaam/credentials; refresh is serialized across processes.
197
+ Status prints metadata only. Existing Anthropic keys remain compatible.`,
198
+ service: `salaam service start [--foreground] [--provider PROVIDER]
131
199
  [--model MODEL] [--max-output-tokens N] [--workspace-id ID]
132
200
  [--effect-socket PATH] [--effect-opaque]
133
201
  [--coding-root PRIVATE_DIR] [--coding-socket DOCKER_SOCKET]
202
+ [--harness-config OWNER_ONLY_FILE] [--a2a-config OWNER_ONLY_FILE]
203
+ [--a2a-listen-config OWNER_ONLY_FILE]
134
204
  salaam service status
135
205
  salaam service stop
136
206
 
137
- The default provider is offline. Claude defaults to claude-sonnet-5-5.
207
+ The default provider is offline. Anthropic defaults to claude-sonnet-5-5.
208
+ Other providers require --model; use salaam providers list and salaam models list PROVIDER.
138
209
  Start with --foreground for process-supervisor use; otherwise the service detaches.`,
139
210
  session: `salaam session create [--cwd DIR] [--request-id ID] [--coding]
140
211
  salaam session list
@@ -193,7 +264,9 @@ Usage: salaam <command> [options]
193
264
  Commands:
194
265
  tui Open the Salaam terminal interface (also bare sal)
195
266
  remote Open the same session over SSH
196
- auth Configure the Claude API key locally
267
+ providers List pinned Pi providers and capabilities
268
+ models List pinned chat models
269
+ auth Configure provider credentials or OAuth
197
270
  service Start, inspect or stop the session service
198
271
  session Create, inspect, submit, resume or cancel work
199
272
  attach Open an interactive terminal on a session
@@ -212,6 +285,11 @@ Commands:
212
285
  compactions Inspect or cancel a managed checkpoint
213
286
  execution Prove the container boundary with disposable fixtures
214
287
  workspace Inspect coding copies, review diffs or acknowledge human edits
288
+ agents Inspect and admit specialist profiles
289
+ team Assign task steps and inspect the durable team inbox
290
+ workers Inspect and explicitly integrate isolated coding proposals
291
+ harness Prepare, inspect and reconcile pinned external workers
292
+ a2a Configure bounded peer analysis and inspect remote work
215
293
  commands Start, inspect, cancel or reconcile isolated commands
216
294
 
217
295
  ${globalOptions}
@@ -256,6 +334,28 @@ const valueOptions = new Set([
256
334
  '--run-id',
257
335
  '--coding-root',
258
336
  '--coding-socket',
337
+ '--harness-config',
338
+ '--a2a-config',
339
+ '--a2a-listen-config',
340
+ '--assignment',
341
+ '--peer',
342
+ '--peer-client',
343
+ '--file',
344
+ '--endpoint',
345
+ '--card-url',
346
+ '--card-file',
347
+ '--addresses',
348
+ '--files',
349
+ '--retention',
350
+ '--credential-file',
351
+ '--tenant',
352
+ '--skill',
353
+ '--runtime-root',
354
+ '--arch',
355
+ '--target',
356
+ '--id',
357
+ '--runtime',
358
+ '--invocations',
259
359
  '--step-key',
260
360
  '--command',
261
361
  '--plan',
@@ -275,6 +375,7 @@ const valueOptions = new Set([
275
375
  '--status',
276
376
  '--manifest',
277
377
  '--config',
378
+ '--pin',
278
379
  '--platform',
279
380
  ]);
280
381
  /** Skip option values so a prompt containing "--help" stays a prompt. */