@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
package/CHANGELOG.md CHANGED
@@ -6,6 +6,21 @@ are forward-only: keep a stopped-daemon backup before upgrading.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.0.2] - 2026-10-19
10
+
11
+ - Add the complete pinned Pi provider catalog, model listing, API-key and OAuth setup, and cloud credentials.
12
+ - Validate all ten chat APIs at dispatch, preserve provider-bound recovery and usage, and allow uncapped subscription routes for durable tasks with an explicit token-budget opt-out, while preserving request/runtime limits and rejecting unsupported token caps.
13
+ - Add admitted native specialist profiles, immutable repository evidence, reviewer
14
+ findings, durable task-step assignments and passive team messages.
15
+ - Give coding specialists private drafts with explicit hash-bound integration,
16
+ protected human edits and fresh parent acceptance checks.
17
+ - Add pinned Gemini ACP, Claude ACP and Codex app-server workers with isolated
18
+ execution, concrete shared approvals and durable invocation/recovery records.
19
+ - Add pinned A2A analysis peers, private one-use inbound grants, owned task
20
+ reconciliation and named Hermes/OpenClaw transport interoperability.
21
+ - Show native, harness and peer workers together in the Salaam terminal, with
22
+ saved identities across disconnects and explicit recovery of uncertain work.
23
+
9
24
  ## [0.0.1] - 2026-10-18
10
25
 
11
26
  Initial release preparation. A version entry is not a published release; see GitHub
package/README.md CHANGED
@@ -38,27 +38,30 @@ Use **Node 26.7.0** and npm **11.19.0**. macOS and Linux are supported; Windows
38
38
  not currently tested. Packages include compiled CLI code and pinned production
39
39
  dependencies. No compiler, checkout or database server is needed.
40
40
 
41
- Download `install.sh` from a [published release](https://github.com/SuperJackfruitLabs/salaam/releases),
42
- inspect it, then run:
41
+ Install the latest stable release:
43
42
 
44
43
  ```sh
45
- sh install.sh --version 0.0.1
44
+ curl -fsSL https://salaam.sh/install.sh | sh
46
45
  salaam --version
47
46
  sal --help
48
47
  ```
49
48
 
50
49
  The installer verifies the release tarball's SHA-256 checksum and installs both
51
- executable names. Use `--prefix "$HOME/.local"` for a private prefix and add its
52
- `bin` directory to `PATH`.
50
+ executable names. You can [read the installer](https://salaam.sh/install.sh) before
51
+ running it. To choose a version or private prefix, pass arguments to `sh`:
53
52
 
54
- An npm registry installation is supported **after the package is published**:
53
+ ```sh
54
+ curl -fsSL https://salaam.sh/install.sh | sh -s -- --version 0.0.2 --prefix "$HOME/.local"
55
+ export PATH="$HOME/.local/bin:$PATH"
56
+ ```
57
+
58
+ Or install from npm:
55
59
 
56
60
  ```sh
57
- npm install --global @superjackfruitlabs/salaam@0.0.1 --ignore-scripts
61
+ npm install --global @superjackfruitlabs/salaam@0.0.2 --ignore-scripts
58
62
  ```
59
63
 
60
- Release preparation does not itself publish a version. Check GitHub Releases and
61
- npm for availability. See [installation and upgrades](docs/install.md) for direct
64
+ See [installation and upgrades](docs/install.md) for direct
62
65
  tarball installation, prerequisites and safe state migration.
63
66
 
64
67
  ## Your first session
@@ -75,6 +78,8 @@ sal attach SESSION_ID --state /tmp/salaam-try
75
78
  Replace `SESSION_ID` with the returned ID. Enter an instruction, detach with
76
79
  Ctrl+D, then attach again. Ctrl+K opens actions; Ctrl+O opens approvals.
77
80
 
81
+ Choose a provider with `sal providers list` and a model with `sal models list PROVIDER`. Salaam exposes all 41 chat providers in pinned Pi, including API-key, OAuth and cloud authentication. ChatGPT subscription routes support interactive sessions and durable tasks that explicitly disable token budgeting; these routes cannot enforce an output-token ceiling.
82
+
78
83
  For Claude, use the hidden terminal prompt and a separate provider-bound service:
79
84
 
80
85
  ```sh
@@ -89,14 +94,17 @@ by OS permissions. See [providers and credentials](docs/providers.md).
89
94
 
90
95
  ## What stays with the work
91
96
 
92
- | Capability | What it means |
93
- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
94
- | [Sessions](docs/service-reference.md) | Durable dispatch, client outboxes, controller leases and committed event replay. |
95
- | [Coding](docs/coding.md) | Owned workspace copies, hash-bound patches and isolated commands through a proven Docker backend. |
96
- | [Tasks](docs/durable-tasks.md) | Stable steps, checked plans, budgets, checkpoints and opt-in bounded progression. Model text cannot complete a checked task. |
97
- | [Jobs and schedules](docs/jobs.md) | Occurrence deduplication, immutable recurring templates and explicit missed-run and timezone/DST policies. |
98
- | [Host operation](docs/host-operation.md) | Reviewed runtime/backend manifests, bounded supervisor restart and a shared CLI/TUI/ACP inbox. |
99
- | [Clients](docs/clients.md) | Terminal attachment, SSH and ACP v1 against the same service. |
97
+ | Capability | What it means |
98
+ | ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
99
+ | [Sessions](docs/service-reference.md) | Durable dispatch, client outboxes, controller leases and committed event replay. |
100
+ | [Coding](docs/coding.md) | Owned workspace copies, hash-bound patches and isolated commands through a proven Docker backend. |
101
+ | [Tasks](docs/durable-tasks.md) | Stable steps, checked plans, budgets, checkpoints and opt-in bounded progression. Model text cannot complete a checked task. |
102
+ | [Specialists and teams](docs/agents.md) | Admitted profiles, isolated coding proposals, explicit integration, durable task-step claims and passive messages. |
103
+ | [External harnesses](docs/external-harnesses.md) | Pinned Gemini, Claude ACP and Codex workers with explicit invocation limits and shared approvals. |
104
+ | [A2A peers](docs/a2a.md) | Authenticated, bounded peer analysis, explicit inbound grants and durable task ownership. |
105
+ | [Jobs and schedules](docs/jobs.md) | Occurrence deduplication, immutable recurring templates and explicit missed-run and timezone/DST policies. |
106
+ | [Host operation](docs/host-operation.md) | Reviewed runtime/backend manifests, bounded supervisor restart and a shared CLI/TUI/ACP inbox. |
107
+ | [Clients](docs/clients.md) | Terminal attachment, SSH and ACP v1 against the same service. |
100
108
 
101
109
  ## Read this before trusting it
102
110
 
@@ -0,0 +1,225 @@
1
+ import { canonical, digest, ServiceError } from './protocol.js';
2
+ import { hostDigests } from './host.js';
3
+ import { A2AHttp, A2A_PIN, a2aObject, privateJSON, validatePeer, } from './a2a-peer.js';
4
+ import { A2AInvocation } from './a2a-protocol.js';
5
+ /** Bounded outbound analysis. A peer has no local file/command/controller authority. */
6
+ export class A2ABackend {
7
+ config;
8
+ peers;
9
+ ready = false;
10
+ active = new Map();
11
+ trees = new Map();
12
+ constructor(config) {
13
+ this.config = config;
14
+ this.peers = this.read();
15
+ }
16
+ read() {
17
+ const config = a2aObject(privateJSON(this.config));
18
+ if (Object.keys(config).sort().join(',') !== 'peers,schema' ||
19
+ config.schema !== 1 ||
20
+ !Array.isArray(config.peers) ||
21
+ !config.peers.length ||
22
+ config.peers.length > 16)
23
+ throw Error('Expected bounded A2A peer registry schema 1');
24
+ const peers = config.peers.map(validatePeer);
25
+ if (new Set(peers.map((p) => p.id)).size !== peers.length)
26
+ throw Error('Duplicate peer identity');
27
+ return peers;
28
+ }
29
+ descriptor(p) {
30
+ return {
31
+ id: p.id,
32
+ kind: 'a2a',
33
+ target: 'a2a',
34
+ version: A2A_PIN.wire,
35
+ config_digest: digest(p),
36
+ adapter_digest: hostDigests().build_digest,
37
+ execution_identity: {
38
+ version: A2A_PIN.wire,
39
+ kernel: 'remote-unobserved',
40
+ arch: 'remote-unobserved',
41
+ },
42
+ boundary_digest: digest({ protocol: A2A_PIN, peer: p, authority: 'remote-analysis-only' }),
43
+ engine_id: p.id,
44
+ evidence: 'proven',
45
+ model_usage: 'unknown',
46
+ output_cap: null,
47
+ max_model_requests: null,
48
+ files: false,
49
+ commands: false,
50
+ };
51
+ }
52
+ async prepare() {
53
+ this.peers = this.read();
54
+ this.ready = true;
55
+ }
56
+ async close() {
57
+ this.active.clear();
58
+ this.trees.clear();
59
+ this.ready = false;
60
+ }
61
+ runtimes() {
62
+ return this.ready ? this.peers.map((p) => this.descriptor(p)) : [];
63
+ }
64
+ validate(runtime) {
65
+ const p = this.read().find((p) => p.id === runtime.id);
66
+ if (!this.ready || !p || canonical(this.descriptor(p)) !== canonical(runtime))
67
+ throw Error('A2A peer/config/credential/build admission changed');
68
+ }
69
+ assertAuthority(_runtime, tools) {
70
+ if (tools.some((t) => [
71
+ 'salaam_files',
72
+ 'salaam_command',
73
+ 'salaam_team',
74
+ 'salaam_managed',
75
+ 'salaam_children',
76
+ ].includes(t)))
77
+ throw Error('A2A peers are analysis specialists without local tools');
78
+ }
79
+ async execute(binding, run, prompt, tree, hooks) {
80
+ this.validate(binding.external);
81
+ this.assertAuthority(binding.external, binding.tools);
82
+ hooks.assertActive();
83
+ if (this.active.has(run) || this.trees.has(run))
84
+ throw Error('A2A invocation cannot be replayed');
85
+ const p = this.peers.find((p) => p.id === binding.external.id), http = new A2AHttp(p);
86
+ const files = Object.create(null), disclosure = [];
87
+ for (const path of p.files) {
88
+ const f = tree[path];
89
+ if (!f)
90
+ throw Error('An explicitly disclosed repository file is absent');
91
+ const bytes = Buffer.from(f.content, 'base64'), text = bytes.toString('utf8');
92
+ if (!Buffer.from(text).equals(bytes) || bytes.length > 65536)
93
+ throw Error('Only bounded UTF-8 peer disclosure is supported');
94
+ files[path] = text;
95
+ disclosure.push({ path, digest: digest(f), bytes: bytes.length });
96
+ }
97
+ const data = { skill: p.skill, files }, peer = { id: p.id, tenant: p.tenant, digest: digest(p), cancel: p.cancel, skill: p.skill };
98
+ if (canonical({ prompt, data }).length > 196608)
99
+ throw Error('A2A disclosure exceeds bound');
100
+ const initial = A2AInvocation.prepare(binding.worker_id, binding.attempt_id, peer, prompt, data, disclosure);
101
+ initial.run_id = run;
102
+ // Both identity and disclosure are durable before any SendMessage bytes leave.
103
+ hooks.checkpoint(initial);
104
+ hooks.receipt(initial);
105
+ const invocation = new A2AInvocation(peer, initial, http, (cp) => {
106
+ hooks.checkpoint(cp);
107
+ hooks.receipt(cp);
108
+ return undefined;
109
+ });
110
+ this.active.set(run, invocation);
111
+ this.trees.set(run, structuredClone(tree));
112
+ await http.card();
113
+ hooks.assertActive();
114
+ let task = await invocation.dispatch(prompt, data);
115
+ const end = Date.now() + 180000;
116
+ while (invocation.view().state !== 'terminal') {
117
+ hooks.assertActive();
118
+ this.validate(binding.external);
119
+ if (['TASK_STATE_INPUT_REQUIRED', 'TASK_STATE_AUTH_REQUIRED'].includes(task.status.state))
120
+ throw Error('Peer needs input/authentication; explicit operator resolution required');
121
+ if (Date.now() >= end)
122
+ throw Error('Peer deadline reached; remote work may still run');
123
+ if (p.streaming) {
124
+ try {
125
+ await http.subscribe(invocation.view().task_id, (event) => {
126
+ hooks.assertActive();
127
+ invocation.observe(event);
128
+ });
129
+ }
130
+ catch (error) {
131
+ if (error instanceof ServiceError)
132
+ throw error;
133
+ }
134
+ }
135
+ else
136
+ await new Promise((r) => setTimeout(r, 2500));
137
+ hooks.assertActive();
138
+ task = await invocation.inspect();
139
+ }
140
+ if (task.status.state !== 'TASK_STATE_COMPLETED')
141
+ throw Error('Remote task ended without a completed result');
142
+ return {
143
+ result: {
144
+ peer: p.id,
145
+ tenant: p.tenant,
146
+ task,
147
+ disclosure,
148
+ prompt_digest: digest(prompt),
149
+ verified_success: false,
150
+ remote_execution: 'unobserved',
151
+ },
152
+ };
153
+ }
154
+ collect(run) {
155
+ const tree = this.trees.get(run);
156
+ if (!tree)
157
+ throw Error('No owned local snapshot for remote analysis');
158
+ this.trees.delete(run);
159
+ return tree;
160
+ }
161
+ async stop(run, runtime, receipt) {
162
+ this.validate(runtime);
163
+ let invocation = this.active.get(run);
164
+ if (!invocation) {
165
+ const record = a2aObject(receipt);
166
+ const cp = a2aObject(record.checkpoint ?? record), p = this.peers.find((p) => p.id === runtime.id);
167
+ if (cp.run_id !== run)
168
+ throw Error('Remote cleanup receipt belongs to another local run');
169
+ invocation = new A2AInvocation({ id: p.id, tenant: p.tenant, digest: digest(p), cancel: p.cancel, skill: p.skill }, cp, new A2AHttp(p), () => undefined);
170
+ }
171
+ if (invocation.view().state === 'prepared') {
172
+ this.active.delete(run);
173
+ this.trees.delete(run);
174
+ return { scope: 'never-dispatched', checkpoint: invocation.view() };
175
+ }
176
+ await invocation.cancel();
177
+ if (invocation.view().state !== 'terminal')
178
+ throw Error('Remote terminal receipt is unconfirmed');
179
+ this.active.delete(run);
180
+ if (invocation.view().result?.status.state !== 'TASK_STATE_COMPLETED')
181
+ this.trees.delete(run);
182
+ return { scope: 'remote-terminal-receipt', checkpoint: invocation.view() };
183
+ }
184
+ }
185
+ export class ExternalBackends {
186
+ backends;
187
+ constructor(backends) {
188
+ this.backends = backends;
189
+ }
190
+ async prepare() {
191
+ for (const b of this.backends)
192
+ await b.prepare?.();
193
+ const ids = this.runtimes().map((r) => r.id);
194
+ if (new Set(ids).size !== ids.length)
195
+ throw Error('Runtime IDs must be unique across local and remote backends');
196
+ }
197
+ async close() {
198
+ for (const b of this.backends)
199
+ await b.close?.();
200
+ }
201
+ runtimes() {
202
+ return this.backends.flatMap((b) => b.runtimes());
203
+ }
204
+ owner(runtime) {
205
+ const b = this.backends.find((b) => b.runtimes().some((r) => r.id === runtime.id));
206
+ if (!b)
207
+ throw Error('Runtime is unavailable');
208
+ return b;
209
+ }
210
+ validate(runtime) {
211
+ this.owner(runtime).validate(runtime);
212
+ }
213
+ assertAuthority(runtime, tools) {
214
+ this.owner(runtime).assertAuthority?.(runtime, tools);
215
+ }
216
+ execute(binding, run, prompt, tree, hooks) {
217
+ return this.owner(binding.external).execute(binding, run, prompt, tree, hooks);
218
+ }
219
+ collect(run, runtime) {
220
+ return this.owner(runtime).collect(run, runtime);
221
+ }
222
+ stop(run, runtime, receipt) {
223
+ return this.owner(runtime).stop(run, runtime, receipt);
224
+ }
225
+ }