@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.
- package/CHANGELOG.md +15 -0
- package/README.md +25 -17
- package/dist/src/a2a-backend.js +225 -0
- package/dist/src/a2a-inbound.js +487 -0
- package/dist/src/a2a-peer.js +370 -0
- package/dist/src/a2a-protocol.js +250 -0
- package/dist/src/agent-profiles.js +289 -0
- package/dist/src/children.js +132 -26
- package/dist/src/claude-provider.js +2 -179
- package/dist/src/cli-help.js +107 -6
- package/dist/src/cli.js +477 -13
- package/dist/src/config.js +26 -3
- package/dist/src/credentials.js +7 -2
- package/dist/src/external-runtime.js +1 -0
- package/dist/src/external-work.js +583 -0
- package/dist/src/harness-backend.js +466 -0
- package/dist/src/harness-container.js +450 -0
- package/dist/src/harness-model-gateway.js +104 -0
- package/dist/src/harness-prepare.js +88 -0
- package/dist/src/harness-probe.js +111 -0
- package/dist/src/harness-protocol.js +325 -0
- package/dist/src/harness-readiness.js +50 -0
- package/dist/src/harness-rpc.js +206 -0
- package/dist/src/harness-tool-identity.js +87 -0
- package/dist/src/harness-wire-bridge.js +387 -0
- package/dist/src/host.js +6 -1
- package/dist/src/house-extension.js +147 -64
- package/dist/src/live-provider.js +301 -0
- package/dist/src/managed.js +33 -3
- package/dist/src/protocol.js +9 -5
- package/dist/src/provider-auth.js +300 -0
- package/dist/src/provider-config.js +65 -17
- package/dist/src/provider-model.js +78 -0
- package/dist/src/provider-payload.js +264 -0
- package/dist/src/provider-transport.js +36 -0
- package/dist/src/provider-usage.js +59 -0
- package/dist/src/repository-snapshots.js +219 -0
- package/dist/src/service.js +274 -9
- package/dist/src/store.js +44 -2
- package/dist/src/supervisor.js +246 -35
- package/dist/src/task-budget.js +17 -8
- package/dist/src/task-context.js +15 -0
- package/dist/src/task-driver.js +38 -14
- package/dist/src/task-policy.js +20 -1
- package/dist/src/task-verification.js +3 -0
- package/dist/src/task-work.js +31 -0
- package/dist/src/team-work.js +378 -0
- package/dist/src/tui-view.js +65 -10
- package/dist/src/tui.js +4 -2
- package/dist/src/usage.js +18 -0
- package/dist/src/worker-boundary.js +298 -0
- package/dist/src/worker-workspaces.js +274 -0
- package/dist/src/workspaces.js +6 -2
- package/harness-runtime/codex-pins.json +10 -0
- package/harness-runtime/package-lock.json +560 -0
- package/harness-runtime/package.json +9 -0
- package/npm-shrinkwrap.json +57 -2
- package/package.json +2 -1
- package/src/a2a-backend.ts +254 -0
- package/src/a2a-inbound.ts +585 -0
- package/src/a2a-peer.ts +441 -0
- package/src/a2a-protocol.ts +286 -0
- package/src/agent-profiles.ts +379 -0
- package/src/children.ts +168 -27
- package/src/claude-provider.ts +2 -189
- package/src/cli-help.ts +107 -6
- package/src/cli.ts +602 -12
- package/src/config.ts +34 -3
- package/src/credentials.ts +9 -2
- package/src/external-runtime.ts +54 -0
- package/src/external-work.ts +663 -0
- package/src/harness-backend.ts +557 -0
- package/src/harness-container.ts +543 -0
- package/src/harness-model-gateway.ts +138 -0
- package/src/harness-prepare.ts +113 -0
- package/src/harness-probe.ts +150 -0
- package/src/harness-protocol.ts +388 -0
- package/src/harness-readiness.ts +71 -0
- package/src/harness-rpc.ts +244 -0
- package/src/harness-tool-identity.ts +91 -0
- package/src/harness-wire-bridge.ts +418 -0
- package/src/host.ts +11 -6
- package/src/house-extension.ts +165 -66
- package/src/live-provider.ts +347 -0
- package/src/managed.ts +43 -4
- package/src/protocol.ts +36 -8
- package/src/provider-auth.ts +336 -0
- package/src/provider-config.ts +89 -18
- package/src/provider-model.ts +86 -0
- package/src/provider-payload.ts +263 -0
- package/src/provider-transport.ts +51 -0
- package/src/provider-usage.ts +64 -0
- package/src/repository-snapshots.ts +259 -0
- package/src/service.ts +446 -11
- package/src/store.ts +60 -4
- package/src/supervisor.ts +294 -41
- package/src/task-budget.ts +24 -11
- package/src/task-context.ts +17 -0
- package/src/task-driver.ts +51 -15
- package/src/task-policy.ts +23 -3
- package/src/task-verification.ts +2 -0
- package/src/task-work.ts +46 -1
- package/src/team-work.ts +554 -0
- package/src/tui-client.ts +5 -0
- package/src/tui-view.ts +164 -12
- package/src/tui.ts +5 -2
- package/src/usage.ts +17 -0
- package/src/worker-boundary.ts +395 -0
- package/src/worker-workspaces.ts +390 -0
- package/src/workspaces.ts +4 -2
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { readFileSync, lstatSync, realpathSync, readdirSync, readlinkSync, createReadStream, mkdirSync, writeFileSync, existsSync, } from 'node:fs';
|
|
3
|
+
import { join, relative, dirname, isAbsolute } from 'node:path';
|
|
4
|
+
import { DockerHarnessBoundary } from './harness-container.js';
|
|
5
|
+
import { engineRequest, engineJson } from './execution-boundary.js';
|
|
6
|
+
import { HarnessProtocol } from './harness-protocol.js';
|
|
7
|
+
import { HARNESS_TARGETS } from './harness-probe.js';
|
|
8
|
+
import { AnthropicHarnessGateway } from './harness-model-gateway.js';
|
|
9
|
+
import { HarnessWireBridge } from './harness-wire-bridge.js';
|
|
10
|
+
import { harnessToolIds, matchesCodexCommandScope } from './harness-tool-identity.js';
|
|
11
|
+
import { proveHarnessReadiness } from './harness-readiness.js';
|
|
12
|
+
import { ProviderCredentialStore, resolveProviderAuth } from './provider-auth.js';
|
|
13
|
+
import { providerRoute } from './provider-config.js';
|
|
14
|
+
import { readTree, validateTree } from './workspaces.js';
|
|
15
|
+
import { repositoryPath } from './repository-snapshots.js';
|
|
16
|
+
import { digest, ServiceError } from './protocol.js';
|
|
17
|
+
import { hostDigests } from './host.js';
|
|
18
|
+
export async function harnessRuntimeDigest(root) {
|
|
19
|
+
const entries = [];
|
|
20
|
+
async function walk(dir) {
|
|
21
|
+
for (const name of readdirSync(dir).sort()) {
|
|
22
|
+
const p = join(dir, name), s = lstatSync(p);
|
|
23
|
+
if (s.isDirectory())
|
|
24
|
+
await walk(p);
|
|
25
|
+
else if (s.isSymbolicLink()) {
|
|
26
|
+
if (!realpathSync(p).startsWith(root + '/'))
|
|
27
|
+
throw Error('Runtime symlink escapes prepared artifact');
|
|
28
|
+
entries.push({ path: relative(root, p), link: readlinkSync(p) });
|
|
29
|
+
}
|
|
30
|
+
else if (s.isFile()) {
|
|
31
|
+
const hash = createHash('sha256');
|
|
32
|
+
for await (const chunk of createReadStream(p))
|
|
33
|
+
hash.update(chunk);
|
|
34
|
+
entries.push({
|
|
35
|
+
path: relative(root, p),
|
|
36
|
+
hash: hash.digest('hex'),
|
|
37
|
+
executable: Boolean(s.mode & 0o111),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
else
|
|
41
|
+
throw Error('Unsupported harness runtime artifact');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
await walk(root);
|
|
45
|
+
return digest(entries);
|
|
46
|
+
}
|
|
47
|
+
export function assertHarnessArtifactRoot(root) {
|
|
48
|
+
const allowed = [
|
|
49
|
+
'node_modules',
|
|
50
|
+
'codex',
|
|
51
|
+
'package.json',
|
|
52
|
+
'package-lock.json',
|
|
53
|
+
'runtime-manifest.json',
|
|
54
|
+
];
|
|
55
|
+
if (!isAbsolute(root) ||
|
|
56
|
+
realpathSync(root) !== root ||
|
|
57
|
+
readdirSync(root).some((name) => !allowed.includes(name)))
|
|
58
|
+
throw Error('Select a dedicated prepared artifact root containing only pinned runtime packages; source, state and user configuration are excluded');
|
|
59
|
+
}
|
|
60
|
+
/** Host-owned registry and fixed launch templates. No project-controlled commands, inherited env or real keys enter containers. */
|
|
61
|
+
export class DockerHarnessBackend {
|
|
62
|
+
path;
|
|
63
|
+
root;
|
|
64
|
+
socket;
|
|
65
|
+
executor;
|
|
66
|
+
docker;
|
|
67
|
+
pins;
|
|
68
|
+
fileDigest;
|
|
69
|
+
boundary;
|
|
70
|
+
descriptors = [];
|
|
71
|
+
protocols = new Map();
|
|
72
|
+
receipts = new Map();
|
|
73
|
+
constructor(path, root, socket, executor, docker = process.platform === 'darwin'
|
|
74
|
+
? '/opt/homebrew/bin/docker'
|
|
75
|
+
: '/usr/bin/docker') {
|
|
76
|
+
this.path = path;
|
|
77
|
+
this.root = root;
|
|
78
|
+
this.socket = socket;
|
|
79
|
+
this.executor = executor;
|
|
80
|
+
this.docker = docker;
|
|
81
|
+
const stat = lstatSync(path), bytes = readFileSync(path);
|
|
82
|
+
if (!stat.isFile() ||
|
|
83
|
+
stat.isSymbolicLink() ||
|
|
84
|
+
stat.uid !== process.getuid?.() ||
|
|
85
|
+
(stat.mode & 0o077) !== 0 ||
|
|
86
|
+
bytes.length > 65536)
|
|
87
|
+
throw Error('Harness config must be an owner-only regular file, at most 64 KiB');
|
|
88
|
+
this.fileDigest = digest(bytes.toString());
|
|
89
|
+
const value = JSON.parse(bytes.toString());
|
|
90
|
+
if (value.schema !== 1 ||
|
|
91
|
+
Object.keys(value).some((k) => !['schema', 'runtimes'].includes(k)) ||
|
|
92
|
+
!Array.isArray(value.runtimes) ||
|
|
93
|
+
value.runtimes.length > 8)
|
|
94
|
+
throw Error('Unsupported harness registry');
|
|
95
|
+
const ids = new Set();
|
|
96
|
+
this.pins = value.runtimes.map((p) => {
|
|
97
|
+
if (Object.keys(p).some((k) => ![
|
|
98
|
+
'id',
|
|
99
|
+
'target',
|
|
100
|
+
'version',
|
|
101
|
+
'runtime_root',
|
|
102
|
+
'runtime_digest',
|
|
103
|
+
'model',
|
|
104
|
+
'output',
|
|
105
|
+
'requests',
|
|
106
|
+
].includes(k)) ||
|
|
107
|
+
!/^[a-zA-Z0-9_-]{1,64}$/.test(p.id) ||
|
|
108
|
+
ids.has(p.id) ||
|
|
109
|
+
!Object.hasOwn(HARNESS_TARGETS, p.target) ||
|
|
110
|
+
p.version !== HARNESS_TARGETS[p.target].version ||
|
|
111
|
+
!isAbsolute(p.runtime_root) ||
|
|
112
|
+
realpathSync(p.runtime_root) !== p.runtime_root ||
|
|
113
|
+
!/^[a-f0-9]{64}$/.test(p.runtime_digest) ||
|
|
114
|
+
p.model !== 'claude-sonnet-4-6' ||
|
|
115
|
+
!Number.isSafeInteger(p.output) ||
|
|
116
|
+
p.output < 1 ||
|
|
117
|
+
p.output > 8192 ||
|
|
118
|
+
!Number.isSafeInteger(p.requests) ||
|
|
119
|
+
p.requests < 1 ||
|
|
120
|
+
p.requests > 32)
|
|
121
|
+
throw Error('Select exact reviewed harness artifacts and the tested Sonnet 4.6 route');
|
|
122
|
+
ids.add(p.id);
|
|
123
|
+
assertHarnessArtifactRoot(p.runtime_root);
|
|
124
|
+
return structuredClone(p);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
async prepare() {
|
|
128
|
+
if (this.executor.evidence.probe !== 'passed')
|
|
129
|
+
throw Error('Harness requires a proven real executor');
|
|
130
|
+
mkdirSync(this.root, { recursive: true, mode: 0o700 });
|
|
131
|
+
for (const p of this.pins) {
|
|
132
|
+
if ((await harnessRuntimeDigest(p.runtime_root)) !== p.runtime_digest)
|
|
133
|
+
throw Error('Harness runtime artifact changed');
|
|
134
|
+
if (p.target !== 'codex-app-server') {
|
|
135
|
+
const pkg = JSON.parse(readFileSync(join(p.runtime_root, 'node_modules', p.target === 'gemini-acp'
|
|
136
|
+
? '@google/gemini-cli/package.json'
|
|
137
|
+
: '@zed-industries/claude-agent-acp/package.json'), 'utf8'));
|
|
138
|
+
if (pkg.version !== p.version)
|
|
139
|
+
throw Error('Harness package version differs from its pin');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const executable = realpathSync(this.docker);
|
|
143
|
+
this.boundary = new DockerHarnessBoundary(engineRequest(this.socket), this.executor.engineId, {
|
|
144
|
+
executable,
|
|
145
|
+
digest: createHash('sha256').update(readFileSync(executable)).digest('hex'),
|
|
146
|
+
socket: this.socket,
|
|
147
|
+
});
|
|
148
|
+
await proveHarnessReadiness(this.boundary, this.root);
|
|
149
|
+
this.descriptors = this.pins.map((p) => ({
|
|
150
|
+
id: p.id,
|
|
151
|
+
kind: 'harness',
|
|
152
|
+
target: p.target,
|
|
153
|
+
version: p.version,
|
|
154
|
+
config_digest: digest(p),
|
|
155
|
+
adapter_digest: hostDigests().build_digest,
|
|
156
|
+
execution_identity: {
|
|
157
|
+
version: String(this.executor.evidence.engine_version),
|
|
158
|
+
kernel: String(this.executor.evidence.kernel),
|
|
159
|
+
arch: String(this.executor.evidence.arch),
|
|
160
|
+
},
|
|
161
|
+
boundary_digest: this.boundary.profileDigest,
|
|
162
|
+
engine_id: this.executor.engineId,
|
|
163
|
+
evidence: 'proven',
|
|
164
|
+
model_usage: 'unknown',
|
|
165
|
+
provider_route: {
|
|
166
|
+
provider: 'anthropic',
|
|
167
|
+
model: p.model,
|
|
168
|
+
wire: p.target === 'claude-acp'
|
|
169
|
+
? 'anthropic'
|
|
170
|
+
: p.target === 'gemini-acp'
|
|
171
|
+
? 'google'
|
|
172
|
+
: 'responses',
|
|
173
|
+
wire_model: p.target === 'gemini-acp' ? 'gemini-2.5-flash' : p.model,
|
|
174
|
+
},
|
|
175
|
+
output_cap: p.output,
|
|
176
|
+
max_model_requests: p.requests,
|
|
177
|
+
files: true,
|
|
178
|
+
commands: true,
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
runtimes() {
|
|
182
|
+
return structuredClone(this.descriptors);
|
|
183
|
+
}
|
|
184
|
+
validate(runtime) {
|
|
185
|
+
if (!this.boundary ||
|
|
186
|
+
digest(readFileSync(this.path, 'utf8')) !== this.fileDigest ||
|
|
187
|
+
!this.descriptors.some((r) => digest(r) === digest(runtime)))
|
|
188
|
+
throw new ServiceError('worker_binding_changed', 'Operator runtime configuration or proven boundary changed');
|
|
189
|
+
}
|
|
190
|
+
assertAuthority(runtime, tools) {
|
|
191
|
+
if (runtime.target === 'codex-app-server' && !tools.includes('salaam_command'))
|
|
192
|
+
throw new ServiceError('runtime_role_unsupported', 'The pinned Codex route requires an explicitly command-enabled profile');
|
|
193
|
+
}
|
|
194
|
+
runRoot(run) {
|
|
195
|
+
if (!/^[0-9a-f-]{36}$/.test(run))
|
|
196
|
+
throw Error('Invalid owned run ID');
|
|
197
|
+
return join(this.root, run);
|
|
198
|
+
}
|
|
199
|
+
async execute(binding, run, prompt, tree, hooks) {
|
|
200
|
+
this.validate(binding.external);
|
|
201
|
+
const pin = this.pins.find((p) => p.id === binding.external.id);
|
|
202
|
+
if ((await harnessRuntimeDigest(pin.runtime_root)) !== pin.runtime_digest)
|
|
203
|
+
throw Error('Harness runtime changed before launch');
|
|
204
|
+
hooks.assertActive();
|
|
205
|
+
const dir = this.runRoot(run), work = join(dir, 'workspace'), home = join(dir, 'home'), token = randomUUID() + randomUUID();
|
|
206
|
+
if (existsSync(dir))
|
|
207
|
+
throw new ServiceError('harness_replay', 'Owned runtime directory already exists; reconcile without replay');
|
|
208
|
+
for (const p of [dir, work, home])
|
|
209
|
+
mkdirSync(p, { mode: 0o700 });
|
|
210
|
+
validateTree(tree);
|
|
211
|
+
for (const [p, file] of Object.entries(tree)) {
|
|
212
|
+
repositoryPath(p);
|
|
213
|
+
mkdirSync(dirname(join(work, p)), { recursive: true, mode: 0o700 });
|
|
214
|
+
writeFileSync(join(work, p), Buffer.from(file.content, 'base64'), {
|
|
215
|
+
mode: file.executable ? 0o700 : 0o600,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
const codex = pin.target === 'codex-app-server', claude = pin.target === 'claude-acp';
|
|
219
|
+
const files = binding.tools.includes('salaam_files'), commands = binding.tools.includes('salaam_command');
|
|
220
|
+
const tools = codex
|
|
221
|
+
? commands
|
|
222
|
+
? ['exec_command']
|
|
223
|
+
: []
|
|
224
|
+
: claude
|
|
225
|
+
? [
|
|
226
|
+
'Read',
|
|
227
|
+
'Glob',
|
|
228
|
+
'Grep',
|
|
229
|
+
...(files ? ['Edit', 'Write'] : []),
|
|
230
|
+
...(commands ? ['Bash'] : []),
|
|
231
|
+
]
|
|
232
|
+
: [
|
|
233
|
+
'list_directory',
|
|
234
|
+
'read_file',
|
|
235
|
+
'grep_search',
|
|
236
|
+
'glob',
|
|
237
|
+
...(files ? ['replace', 'write_file'] : []),
|
|
238
|
+
...(commands ? ['run_shell_command'] : []),
|
|
239
|
+
];
|
|
240
|
+
let args, env;
|
|
241
|
+
if (codex) {
|
|
242
|
+
mkdirSync(join(home, '.codex'));
|
|
243
|
+
writeFileSync(join(home, '.codex/config.toml'), `model = "${pin.model}"\nmodel_provider = "salaam"\n[model_providers.salaam]\nname = "Salaam explicit Claude gateway"\nbase_url = "http://127.0.0.1:47123/v1"\nenv_key = "ANTHROPIC_API_KEY"\nwire_api = "responses"\nrequires_openai_auth = false\n`, { mode: 0o600 });
|
|
244
|
+
const arch = this.executor.evidence.arch === 'arm64' ? 'aarch64' : 'x86_64';
|
|
245
|
+
const binary = '/runtime/codex/vendor/' + arch + '-unknown-linux-musl/bin/codex';
|
|
246
|
+
const wrapper = `const cp=require('node:child_process');if(cp.execFileSync(${JSON.stringify(binary)},['--version'],{encoding:'utf8'}).trim()!=='codex-cli ${pin.version}')process.exit(2);const c=cp.spawn(${JSON.stringify(binary)},['app-server'],{stdio:'inherit',env:process.env});c.on('exit',n=>process.exit(n??1));c.on('error',()=>process.exit(2));`;
|
|
247
|
+
args = ['/usr/local/bin/node', '-e', wrapper];
|
|
248
|
+
env = { CODEX_HOME: '/home/agent/.codex', ANTHROPIC_API_KEY: token };
|
|
249
|
+
}
|
|
250
|
+
else if (claude) {
|
|
251
|
+
args = [
|
|
252
|
+
'/usr/local/bin/node',
|
|
253
|
+
'/runtime/node_modules/@zed-industries/claude-agent-acp/dist/index.js',
|
|
254
|
+
];
|
|
255
|
+
env = {
|
|
256
|
+
ANTHROPIC_BASE_URL: 'http://127.0.0.1:47123',
|
|
257
|
+
ANTHROPIC_API_KEY: token,
|
|
258
|
+
ANTHROPIC_MODEL: pin.model,
|
|
259
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: pin.model,
|
|
260
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: pin.model,
|
|
261
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: pin.model,
|
|
262
|
+
DISABLE_TELEMETRY: '1',
|
|
263
|
+
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1',
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
args = [
|
|
268
|
+
'/usr/local/bin/node',
|
|
269
|
+
'/runtime/node_modules/@google/gemini-cli/bundle/gemini.js',
|
|
270
|
+
'--acp',
|
|
271
|
+
'--model',
|
|
272
|
+
'gemini-2.5-flash',
|
|
273
|
+
];
|
|
274
|
+
env = {
|
|
275
|
+
GEMINI_API_KEY: token,
|
|
276
|
+
GOOGLE_GEMINI_BASE_URL: 'http://127.0.0.1:47123',
|
|
277
|
+
GEMINI_CLI_NO_RELAUNCH: 'true',
|
|
278
|
+
DISABLE_TELEMETRY: '1',
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
const receipt = await this.boundary.prepare(run, work, pin.runtime_root, home, args, env);
|
|
282
|
+
this.receipts.set(run, receipt);
|
|
283
|
+
hooks.receipt(receipt);
|
|
284
|
+
const intents = new Map();
|
|
285
|
+
const checkBoundary = async () => {
|
|
286
|
+
try {
|
|
287
|
+
hooks.assertActive();
|
|
288
|
+
await this.boundary.assert(receipt);
|
|
289
|
+
const info = engineJson(await engineRequest(this.socket)('GET', '/info'));
|
|
290
|
+
const identity = binding.external.execution_identity;
|
|
291
|
+
if (info.ServerVersion !== identity.version || info.KernelVersion !== identity.kernel)
|
|
292
|
+
throw Error('Harness engine version or kernel changed');
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
try {
|
|
296
|
+
hooks.failed(error);
|
|
297
|
+
}
|
|
298
|
+
catch { }
|
|
299
|
+
throw error;
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
const gateway = new AnthropicHarnessGateway({
|
|
303
|
+
model: pin.model,
|
|
304
|
+
output: pin.output,
|
|
305
|
+
token,
|
|
306
|
+
credential: async () => {
|
|
307
|
+
const auth = await resolveProviderAuth(providerRoute('anthropic', pin.model, pin.output), new ProviderCredentialStore(), {});
|
|
308
|
+
if (!auth.auth.apiKey)
|
|
309
|
+
throw Error('No operator-configured Claude credential');
|
|
310
|
+
return auth.auth.apiKey;
|
|
311
|
+
},
|
|
312
|
+
admit: hooks.modelRequest,
|
|
313
|
+
});
|
|
314
|
+
const bridge = claude
|
|
315
|
+
? null
|
|
316
|
+
: new HarnessWireBridge(gateway, {
|
|
317
|
+
protocol: codex ? 'responses' : 'google',
|
|
318
|
+
model: codex ? pin.model : 'gemini-2.5-flash',
|
|
319
|
+
tools,
|
|
320
|
+
}, (intent) => {
|
|
321
|
+
for (const id of harnessToolIds(pin.target, pin.version, intent))
|
|
322
|
+
intents.set(id, intent);
|
|
323
|
+
});
|
|
324
|
+
await this.boundary.gateway(receipt, async (request) => {
|
|
325
|
+
await checkBoundary();
|
|
326
|
+
if (bridge)
|
|
327
|
+
return bridge.request(request);
|
|
328
|
+
const body = JSON.parse(request.body.toString());
|
|
329
|
+
if (Array.isArray(body.tools))
|
|
330
|
+
body.tools = body.tools.filter((t) => tools.includes(t.name));
|
|
331
|
+
return gateway.request({ ...request, body: Buffer.from(JSON.stringify(body)) });
|
|
332
|
+
});
|
|
333
|
+
const checkpoint = {
|
|
334
|
+
schema_version: 1,
|
|
335
|
+
dialect: codex ? 'codex' : 'acp',
|
|
336
|
+
worker_id: binding.worker_id,
|
|
337
|
+
attempt_id: binding.attempt_id,
|
|
338
|
+
state: 'prepared',
|
|
339
|
+
session_id: null,
|
|
340
|
+
turn_id: null,
|
|
341
|
+
prompt_digest: null,
|
|
342
|
+
cancel_requested: false,
|
|
343
|
+
result: null,
|
|
344
|
+
load_session: false,
|
|
345
|
+
};
|
|
346
|
+
hooks.checkpoint(checkpoint);
|
|
347
|
+
const protocol = new HarnessProtocol(await this.boundary.launchHarness(receipt, checkpoint.dialect), checkpoint, {
|
|
348
|
+
save: hooks.checkpoint,
|
|
349
|
+
permission: async (owned) => {
|
|
350
|
+
await checkBoundary();
|
|
351
|
+
const params = owned.params, call = params.toolCall, known = intents.get(codex ? params.itemId : call?.toolCallId);
|
|
352
|
+
let scope;
|
|
353
|
+
if (codex) {
|
|
354
|
+
if (matchesCodexCommandScope(pin.version, known, params))
|
|
355
|
+
scope = {
|
|
356
|
+
id: params.itemId,
|
|
357
|
+
kind: 'command',
|
|
358
|
+
arguments: { cmd: known.arguments.cmd, cwd: params.cwd },
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
const input = (claude ? call?.rawInput : known?.arguments);
|
|
363
|
+
const name = claude ? call?.title : known?.name;
|
|
364
|
+
if (input && typeof input.command === 'string')
|
|
365
|
+
scope = { id: call.toolCallId, kind: 'command', arguments: input };
|
|
366
|
+
else if (input &&
|
|
367
|
+
typeof input.file_path === 'string' &&
|
|
368
|
+
(claude || ['replace', 'write_file'].includes(String(name)))) {
|
|
369
|
+
if (input.file_path.startsWith('/workspace/'))
|
|
370
|
+
repositoryPath(input.file_path.slice('/workspace/'.length));
|
|
371
|
+
else
|
|
372
|
+
return 'deny';
|
|
373
|
+
scope = { id: call.toolCallId, kind: 'write', arguments: input };
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
if (!scope)
|
|
377
|
+
return 'deny';
|
|
378
|
+
const decision = await hooks.permission(owned.method, {
|
|
379
|
+
scope,
|
|
380
|
+
upstream_session: owned.session_id,
|
|
381
|
+
upstream_turn: owned.turn_id,
|
|
382
|
+
request_digest: digest(params),
|
|
383
|
+
});
|
|
384
|
+
await checkBoundary();
|
|
385
|
+
return decision;
|
|
386
|
+
},
|
|
387
|
+
event: (method, params) => {
|
|
388
|
+
const p = params;
|
|
389
|
+
if (!codex &&
|
|
390
|
+
method === 'session/update' &&
|
|
391
|
+
p.update?.sessionUpdate === 'tool_call_update' &&
|
|
392
|
+
['completed', 'failed'].includes(p.update.status))
|
|
393
|
+
hooks.toolResult(p.update.toolCallId, p.update.status === 'completed', p.update);
|
|
394
|
+
if (codex &&
|
|
395
|
+
method === 'item/completed' &&
|
|
396
|
+
['commandExecution', 'fileChange'].includes(p.item?.type))
|
|
397
|
+
hooks.toolResult(p.item.id, p.item.status === 'completed' &&
|
|
398
|
+
(p.item.exitCode === undefined || p.item.exitCode === 0), p.item);
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
this.protocols.set(run, protocol);
|
|
402
|
+
await protocol.initialize(!codex);
|
|
403
|
+
if (!codex)
|
|
404
|
+
await protocol.rpc.request('authenticate', {
|
|
405
|
+
methodId: 'gateway',
|
|
406
|
+
_meta: {
|
|
407
|
+
...(claude ? {} : { 'api-key': token }),
|
|
408
|
+
gateway: { baseUrl: 'http://127.0.0.1:47123', headers: { 'x-api-key': token } },
|
|
409
|
+
},
|
|
410
|
+
}, 30000);
|
|
411
|
+
await protocol.create(codex
|
|
412
|
+
? {
|
|
413
|
+
cwd: '/workspace',
|
|
414
|
+
model: pin.model,
|
|
415
|
+
modelProvider: 'salaam',
|
|
416
|
+
sandbox: 'danger-full-access',
|
|
417
|
+
approvalPolicy: 'untrusted',
|
|
418
|
+
}
|
|
419
|
+
: {
|
|
420
|
+
cwd: '/workspace',
|
|
421
|
+
mcpServers: [],
|
|
422
|
+
...(claude
|
|
423
|
+
? {
|
|
424
|
+
_meta: {
|
|
425
|
+
claudeCode: {
|
|
426
|
+
options: {
|
|
427
|
+
model: pin.model,
|
|
428
|
+
settingSources: [],
|
|
429
|
+
plugins: [],
|
|
430
|
+
tools,
|
|
431
|
+
disallowedTools: [
|
|
432
|
+
'Skill',
|
|
433
|
+
'Task',
|
|
434
|
+
'Agent',
|
|
435
|
+
'WebFetch',
|
|
436
|
+
'WebSearch',
|
|
437
|
+
'AskUserQuestion',
|
|
438
|
+
],
|
|
439
|
+
maxTurns: pin.requests,
|
|
440
|
+
maxThinkingTokens: 1024,
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
}
|
|
445
|
+
: {}),
|
|
446
|
+
});
|
|
447
|
+
return { result: await protocol.prompt(prompt) };
|
|
448
|
+
}
|
|
449
|
+
async stop(run, runtime, _receipt) {
|
|
450
|
+
if (!this.boundary ||
|
|
451
|
+
runtime.engine_id !== this.executor.engineId ||
|
|
452
|
+
runtime.boundary_digest !== this.boundary.profileDigest)
|
|
453
|
+
throw Error('Cannot establish the original harness engine/profile for cleanup');
|
|
454
|
+
try {
|
|
455
|
+
await this.protocols.get(run)?.close();
|
|
456
|
+
}
|
|
457
|
+
finally {
|
|
458
|
+
this.protocols.delete(run);
|
|
459
|
+
await this.boundary.cleanupRun(run);
|
|
460
|
+
this.receipts.delete(run);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
collect(run, _runtime) {
|
|
464
|
+
return readTree(join(this.runRoot(run), 'workspace'));
|
|
465
|
+
}
|
|
466
|
+
}
|