brainclaw 1.26.2 → 1.28.0
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/README.md +13 -0
- package/dist/brainclaw-vscode.vsix +0 -0
- package/dist/cli/register-coordination.js +65 -1
- package/dist/commands/attempt-authority.js +80 -0
- package/dist/commands/harvest.js +140 -61
- package/dist/commands/loop.js +34 -0
- package/dist/commands/loops-handlers.js +143 -15
- package/dist/commands/mcp-catalog.js +52 -18
- package/dist/commands/mcp-schemas.generated.js +64 -0
- package/dist/commands/mcp-write-claims.js +128 -1
- package/dist/commands/mcp-write-coordination.js +149 -76
- package/dist/core/agent-capability.js +1 -1
- package/dist/core/agentrun-reconciler.js +148 -22
- package/dist/core/agentruns.js +254 -29
- package/dist/core/assignment-request-schema.js +7 -0
- package/dist/core/assignment-sweeper.js +5 -3
- package/dist/core/assignments.js +131 -33
- package/dist/core/claim-request-schema.js +7 -0
- package/dist/core/claims.js +53 -2
- package/dist/core/dispatch-status.js +16 -6
- package/dist/core/dispatcher.js +51 -51
- package/dist/core/entity-operations.js +20 -0
- package/dist/core/events.js +4 -0
- package/dist/core/execution-adapters.js +189 -14
- package/dist/core/execution-contract.js +345 -0
- package/dist/core/execution.js +130 -16
- package/dist/core/facade-schema.js +3 -0
- package/dist/core/harness-adapters/base.js +150 -0
- package/dist/core/harness-adapters/claude.js +39 -0
- package/dist/core/harness-adapters/codex.js +57 -0
- package/dist/core/harness-adapters/harvest.js +109 -0
- package/dist/core/harness-adapters/index.js +8 -0
- package/dist/core/harness-adapters/prompt-only.js +13 -0
- package/dist/core/harness-adapters/registry.js +48 -0
- package/dist/core/harness-adapters/result.js +33 -0
- package/dist/core/harness-adapters/types.js +2 -0
- package/dist/core/ideation-loop-close.js +25 -2
- package/dist/core/instruction-templates.js +3 -2
- package/dist/core/loop-turn-dispatch.js +235 -0
- package/dist/core/loops/artifact-contract.js +11 -0
- package/dist/core/loops/attempt-authority.js +496 -0
- package/dist/core/loops/attempt-generations.js +509 -0
- package/dist/core/loops/attempt-reservation.js +197 -35
- package/dist/core/loops/attempt-rollout.js +404 -0
- package/dist/core/loops/attempt-takeover.js +155 -0
- package/dist/core/loops/bootstrap-acquire.js +7 -3
- package/dist/core/loops/brief-assembly.js +21 -4
- package/dist/core/loops/evidence.js +188 -0
- package/dist/core/loops/facade-schema.js +75 -11
- package/dist/core/loops/gate-policy.js +533 -0
- package/dist/core/loops/impl-bind.js +91 -81
- package/dist/core/loops/index.js +9 -0
- package/dist/core/loops/iteration-engine.js +31 -19
- package/dist/core/loops/kind-policies.js +90 -0
- package/dist/core/loops/lock.js +71 -13
- package/dist/core/loops/reconcile-turn.js +237 -18
- package/dist/core/loops/result-reducers.js +113 -10
- package/dist/core/loops/store.js +34 -3
- package/dist/core/loops/turn-execution.js +480 -0
- package/dist/core/loops/types.js +127 -3
- package/dist/core/loops/verbs.js +335 -99
- package/dist/core/loops/verify-command.js +105 -20
- package/dist/core/loops/workspace-digest.js +54 -0
- package/dist/core/review-loop-close.js +25 -3
- package/dist/core/review-loop-turn-dispatch.js +210 -161
- package/dist/core/runtime-signals.js +62 -25
- package/dist/core/schema.js +40 -0
- package/dist/core/spawn-check.js +3 -2
- package/dist/core/upgrades/backup.js +27 -4
- package/dist/facts.js +9 -8
- package/dist/facts.json +8 -7
- package/docs/cli.md +49 -1
- package/docs/concepts/attempt-authority.md +407 -0
- package/docs/concepts/evidence-attestations.md +135 -0
- package/docs/concepts/execution-contract.md +166 -0
- package/docs/concepts/harness-adapters.md +166 -0
- package/docs/concepts/ideation-loop.md +5 -4
- package/docs/concepts/loop-engine.md +302 -113
- package/docs/index.md +4 -1
- package/docs/integrations/codex.md +3 -3
- package/docs/integrations/mcp.md +59 -5
- package/docs/loops/debug.md +144 -0
- package/docs/loops/ideation.md +158 -0
- package/docs/loops/implementation.md +174 -0
- package/docs/loops/research.md +136 -0
- package/docs/loops/review.md +200 -0
- package/docs/mcp-schema-changelog.md +18 -5
- package/package.json +1 -1
|
@@ -5,6 +5,56 @@ import { buildClaimEnvPrefix, buildWorkerIdentityEnv } from './execution-profile
|
|
|
5
5
|
import { getCapabilityProfile } from './agent-capability.js';
|
|
6
6
|
import { nowISO } from './ids.js';
|
|
7
7
|
import { ensureRuntimeDirs, getRuntimeLogPath, getRuntimeSignalPath, } from './runtime-signals.js';
|
|
8
|
+
const CONTRACT_BOOTSTRAP_SOURCE = `'use strict';
|
|
9
|
+
const fs = require('node:fs');
|
|
10
|
+
const [ackPath, turnId, runId, nonce, expectedContractHash, expectedSnapshotHash, attemptEpoch, workspaceDigest, expectedWorkspaceB64] = process.argv.slice(2);
|
|
11
|
+
const contractHash = process.env.BRAINCLAW_EXECUTION_CONTRACT_HASH || '';
|
|
12
|
+
const snapshotHash = process.env.BRAINCLAW_CAPABILITY_SNAPSHOT_HASH || '';
|
|
13
|
+
const normalize = (value) => {
|
|
14
|
+
let resolved = fs.realpathSync.native(value);
|
|
15
|
+
if (process.platform === 'win32') resolved = resolved.toLowerCase();
|
|
16
|
+
return resolved;
|
|
17
|
+
};
|
|
18
|
+
const expectedWorkspace = Buffer.from(expectedWorkspaceB64 || '', 'base64url').toString('utf8');
|
|
19
|
+
const actualCwd = normalize(process.cwd());
|
|
20
|
+
const accepted = contractHash === expectedContractHash
|
|
21
|
+
&& snapshotHash === expectedSnapshotHash
|
|
22
|
+
&& expectedWorkspace !== ''
|
|
23
|
+
&& actualCwd === normalize(expectedWorkspace);
|
|
24
|
+
let fd;
|
|
25
|
+
try {
|
|
26
|
+
fd = fs.openSync(ackPath, 'wx');
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error && error.code === 'EEXIST') process.exit(79);
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
fs.writeFileSync(fd, JSON.stringify({
|
|
33
|
+
status: accepted ? 'accepted' : 'rejected',
|
|
34
|
+
turn_id: turnId,
|
|
35
|
+
run_id: runId,
|
|
36
|
+
nonce,
|
|
37
|
+
...(attemptEpoch ? { attempt_epoch: Number(attemptEpoch) } : {}),
|
|
38
|
+
...(workspaceDigest ? { workspace_digest: workspaceDigest } : {}),
|
|
39
|
+
contract_hash: contractHash,
|
|
40
|
+
capability_snapshot_hash: snapshotHash,
|
|
41
|
+
cwd: actualCwd,
|
|
42
|
+
}));
|
|
43
|
+
} finally {
|
|
44
|
+
fs.closeSync(fd);
|
|
45
|
+
}
|
|
46
|
+
if (!accepted) process.exitCode = 78;
|
|
47
|
+
`;
|
|
48
|
+
/** Materialize the tiny child bootstrap used by both cmd.exe and POSIX shells. */
|
|
49
|
+
export function writeContractBootstrapScript(scriptPath) {
|
|
50
|
+
fs.mkdirSync(path.dirname(scriptPath), { recursive: true });
|
|
51
|
+
try {
|
|
52
|
+
if (fs.readFileSync(scriptPath, 'utf8') === CONTRACT_BOOTSTRAP_SOURCE)
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
catch { /* create or replace below */ }
|
|
56
|
+
fs.writeFileSync(scriptPath, CONTRACT_BOOTSTRAP_SOURCE, 'utf8');
|
|
57
|
+
}
|
|
8
58
|
// The turn-echo values are raw-embedded into a shell one-liner (see marker()),
|
|
9
59
|
// so the `[A-Za-z0-9_-]` safety invariant documented on TurnEcho is LOAD-BEARING,
|
|
10
60
|
// not cosmetic. A stray `"` desyncs cmd.exe quote-parity (no sentinel file is
|
|
@@ -18,7 +68,9 @@ const TURN_ECHO_SAFE = /^[A-Za-z0-9_-]+$/;
|
|
|
18
68
|
export function buildAckWrapCommand(bashCommand, paths, isWin32, turnEcho) {
|
|
19
69
|
if (turnEcho) {
|
|
20
70
|
for (const [field, value] of Object.entries(turnEcho)) {
|
|
21
|
-
if (
|
|
71
|
+
if (value === undefined)
|
|
72
|
+
continue;
|
|
73
|
+
if (!TURN_ECHO_SAFE.test(String(value))) {
|
|
22
74
|
throw new Error(`buildAckWrapCommand: turnEcho.${field} must match ${TURN_ECHO_SAFE} to be shell-safe for the completion sentinel (got ${JSON.stringify(value)})`);
|
|
23
75
|
}
|
|
24
76
|
}
|
|
@@ -32,12 +84,42 @@ export function buildAckWrapCommand(bashCommand, paths, isWin32, turnEcho) {
|
|
|
32
84
|
const marker = (p, status) => {
|
|
33
85
|
if (!turnEcho)
|
|
34
86
|
return touch(p);
|
|
35
|
-
const body = JSON.stringify({
|
|
87
|
+
const body = JSON.stringify({
|
|
88
|
+
turn_id: turnEcho.turn_id,
|
|
89
|
+
run_id: turnEcho.run_id,
|
|
90
|
+
nonce: turnEcho.nonce,
|
|
91
|
+
...(turnEcho.contract_hash ? { contract_hash: turnEcho.contract_hash } : {}),
|
|
92
|
+
...(turnEcho.capability_snapshot_hash ? { capability_snapshot_hash: turnEcho.capability_snapshot_hash } : {}),
|
|
93
|
+
...(turnEcho.attempt_epoch !== undefined ? { attempt_epoch: turnEcho.attempt_epoch } : {}),
|
|
94
|
+
...(turnEcho.workspace_digest ? { workspace_digest: turnEcho.workspace_digest } : {}),
|
|
95
|
+
status,
|
|
96
|
+
});
|
|
36
97
|
return isWin32 ? `echo ${body}>"${p}"` : `printf '%s' '${body}' > "${p}"`;
|
|
37
98
|
};
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
99
|
+
const ack = (() => {
|
|
100
|
+
if (!turnEcho?.contract_hash || !turnEcho.capability_snapshot_hash)
|
|
101
|
+
return touch(paths.ackPath);
|
|
102
|
+
if (!paths.contractBootstrapPath) {
|
|
103
|
+
throw new Error('buildAckWrapCommand: contracted turn requires contractBootstrapPath');
|
|
104
|
+
}
|
|
105
|
+
// This child reads the EFFECTIVE environment it received. It writes the
|
|
106
|
+
// actual values and exits non-zero on mismatch, so `&&` prevents worker exec.
|
|
107
|
+
if (!paths.expectedWorkspacePath) {
|
|
108
|
+
throw new Error('buildAckWrapCommand: contracted turn requires expectedWorkspacePath');
|
|
109
|
+
}
|
|
110
|
+
const expectedWorkspaceB64 = Buffer.from(paths.expectedWorkspacePath, 'utf8').toString('base64url');
|
|
111
|
+
return `"${process.execPath}" "${paths.contractBootstrapPath}" "${paths.ackPath}" "${turnEcho.turn_id}" "${turnEcho.run_id}" "${turnEcho.nonce}" "${turnEcho.contract_hash}" "${turnEcho.capability_snapshot_hash}" "${turnEcho.attempt_epoch ?? ''}" "${turnEcho.workspace_digest ?? ''}" "${expectedWorkspaceB64}"`;
|
|
112
|
+
})();
|
|
113
|
+
const stdinRedirect = paths.stdinFilePath ? ` < "${paths.stdinFilePath}"` : '';
|
|
114
|
+
const redirected = `${bashCommand}${stdinRedirect} > "${paths.stdoutLog}" 2> "${paths.stderrLog}"`;
|
|
115
|
+
const cleanup = paths.stdinFilePath
|
|
116
|
+
? isWin32
|
|
117
|
+
? ` & del /q "${paths.stdinFilePath}" > nul 2>&1`
|
|
118
|
+
: `; rm -f -- "${paths.stdinFilePath}"`
|
|
119
|
+
: '';
|
|
120
|
+
return (`${ack} && ` +
|
|
121
|
+
`( ${redirected} && ${marker(paths.completedPath, 'completed')} || ${marker(paths.failedPath, 'failed')} )` +
|
|
122
|
+
cleanup);
|
|
41
123
|
}
|
|
42
124
|
/**
|
|
43
125
|
* Check if a binary is resolvable on the system PATH.
|
|
@@ -96,6 +178,33 @@ function buildManualEnvPrefix(claimId) {
|
|
|
96
178
|
// wrapper for symmetry with the dispatcher's buildEnvPrefix.
|
|
97
179
|
return buildClaimEnvPrefix(claimId);
|
|
98
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* Make the isolated worktree the explicit Codex workspace root. Relying only
|
|
183
|
+
* on child_process.cwd is insufficient for non-interactive Windows launches:
|
|
184
|
+
* the Codex sandbox can retain the coordinator workspace and apply_patch then
|
|
185
|
+
* refuses writes in ~/.brainclaw/worktrees even when NTFS grants access.
|
|
186
|
+
* `--cd` defines the primary root. Do not redundantly add the same path with
|
|
187
|
+
* `--add-dir`: the unelevated Windows sandbox cannot enforce split writable
|
|
188
|
+
* root sets and refuses to prepare its wrapper in that configuration.
|
|
189
|
+
*/
|
|
190
|
+
export function withCodexWorkspaceRoot(invoke, agent, worktreePath, isWin32 = process.platform === 'win32') {
|
|
191
|
+
const executableName = path.win32.basename(invoke.executable).replace(/\.(?:cmd|exe|bat|com)$/i, '').toLowerCase();
|
|
192
|
+
if (agent.trim().toLowerCase() !== 'codex' || executableName !== 'codex' || !worktreePath)
|
|
193
|
+
return invoke;
|
|
194
|
+
const args = [...invoke.args];
|
|
195
|
+
const subcommandIndex = args.indexOf('exec');
|
|
196
|
+
const insertAt = subcommandIndex >= 0 ? subcommandIndex : 0;
|
|
197
|
+
args.splice(insertAt, 0, '--cd', worktreePath);
|
|
198
|
+
const quote = (value) => isWin32
|
|
199
|
+
? `"${value.replace(/"/g, '""')}"`
|
|
200
|
+
: `'${value.replace(/'/g, `'\\''`)}'`;
|
|
201
|
+
const flags = `--cd ${quote(worktreePath)}`;
|
|
202
|
+
const prefix = invoke.executable;
|
|
203
|
+
const suffix = invoke.bashCommand.startsWith(`${prefix} `)
|
|
204
|
+
? invoke.bashCommand.slice(prefix.length + 1)
|
|
205
|
+
: invoke.bashCommand;
|
|
206
|
+
return { ...invoke, args, bashCommand: `${prefix} ${flags} ${suffix}` };
|
|
207
|
+
}
|
|
99
208
|
export class CliExecutionAdapter {
|
|
100
209
|
id = 'cli';
|
|
101
210
|
canSpawn(agentName) {
|
|
@@ -115,14 +224,57 @@ export class CliExecutionAdapter {
|
|
|
115
224
|
return { canSpawn: true, reason: 'agent has spawnable profile' };
|
|
116
225
|
}
|
|
117
226
|
prepareManualCommand(invoke, options) {
|
|
227
|
+
const isWin32 = process.platform === 'win32';
|
|
228
|
+
invoke = withCodexWorkspaceRoot(invoke, options.agent, options.worktreePath, isWin32);
|
|
229
|
+
const shell = isWin32 ? 'cmd' : (invoke.shell ? 'bash' : 'sh');
|
|
230
|
+
if (options.turnEcho?.contract_hash
|
|
231
|
+
&& options.turnEcho.capability_snapshot_hash
|
|
232
|
+
&& options.assignmentId
|
|
233
|
+
&& (options.ackRoot ?? options.worktreePath)) {
|
|
234
|
+
const signalRoot = options.ackRoot ?? options.worktreePath;
|
|
235
|
+
ensureRuntimeDirs(signalRoot);
|
|
236
|
+
const runtimeRunId = options.turnEcho?.run_id;
|
|
237
|
+
const ackPath = getRuntimeSignalPath(signalRoot, options.assignmentId, 'ack', runtimeRunId);
|
|
238
|
+
const contractBootstrapPath = `${ackPath}.bootstrap.cjs`;
|
|
239
|
+
writeContractBootstrapScript(contractBootstrapPath);
|
|
240
|
+
const stdinFilePath = isWin32 && invoke.promptDelivery === 'stdin_pipe' && invoke.promptText
|
|
241
|
+
? `${ackPath}.stdin`
|
|
242
|
+
: undefined;
|
|
243
|
+
if (stdinFilePath && invoke.promptText) {
|
|
244
|
+
fs.writeFileSync(stdinFilePath, invoke.promptText, { encoding: 'utf8', mode: 0o600 });
|
|
245
|
+
}
|
|
246
|
+
const wrapped = buildAckWrapCommand(invoke.bashCommand, {
|
|
247
|
+
ackPath,
|
|
248
|
+
completedPath: getRuntimeSignalPath(signalRoot, options.assignmentId, 'completed', runtimeRunId),
|
|
249
|
+
failedPath: getRuntimeSignalPath(signalRoot, options.assignmentId, 'failed', runtimeRunId),
|
|
250
|
+
stdoutLog: getRuntimeLogPath(signalRoot, options.assignmentId, 'stdout', runtimeRunId),
|
|
251
|
+
stderrLog: getRuntimeLogPath(signalRoot, options.assignmentId, 'stderr', runtimeRunId),
|
|
252
|
+
stdinFilePath,
|
|
253
|
+
contractBootstrapPath,
|
|
254
|
+
expectedWorkspacePath: options.worktreePath,
|
|
255
|
+
}, isWin32, options.turnEcho);
|
|
256
|
+
const contractEnv = isWin32
|
|
257
|
+
? [
|
|
258
|
+
options.claimId && options.claimId !== '(dry-run)' ? `set BRAINCLAW_CLAIM_ID=${options.claimId}` : undefined,
|
|
259
|
+
`set BRAINCLAW_EXECUTION_CONTRACT_HASH=${options.turnEcho.contract_hash}`,
|
|
260
|
+
`set BRAINCLAW_CAPABILITY_SNAPSHOT_HASH=${options.turnEcho.capability_snapshot_hash}`,
|
|
261
|
+
].filter((item) => Boolean(item)).join(' && ') + ' && '
|
|
262
|
+
: `export ${[
|
|
263
|
+
options.claimId && options.claimId !== '(dry-run)' ? `BRAINCLAW_CLAIM_ID="${options.claimId}"` : undefined,
|
|
264
|
+
`BRAINCLAW_EXECUTION_CONTRACT_HASH="${options.turnEcho.contract_hash}"`,
|
|
265
|
+
`BRAINCLAW_CAPABILITY_SNAPSHOT_HASH="${options.turnEcho.capability_snapshot_hash}"`,
|
|
266
|
+
].filter((item) => Boolean(item)).join(' ')}; `;
|
|
267
|
+
return { command: `${contractEnv}${wrapped}`, shell, contractWrapped: true };
|
|
268
|
+
}
|
|
118
269
|
const envPrefix = buildManualEnvPrefix(options.claimId);
|
|
119
270
|
return {
|
|
120
271
|
command: `${envPrefix}${invoke.bashCommand}`,
|
|
121
|
-
shell
|
|
272
|
+
shell,
|
|
122
273
|
};
|
|
123
274
|
}
|
|
124
275
|
start(invoke, options) {
|
|
125
276
|
const isWin32 = process.platform === 'win32';
|
|
277
|
+
invoke = withCodexWorkspaceRoot(invoke, options.agent, options.worktreePath, isWin32);
|
|
126
278
|
// F7 (trp_0e5150d3): route worker env through buildWorkerIdentityEnv so the
|
|
127
279
|
// worker is an independent agent — coordinator identity (BRAINCLAW_AGENT*,
|
|
128
280
|
// SESSION_ID, PROJECT) is scrubbed LAST and cannot be reintroduced by
|
|
@@ -131,7 +283,16 @@ export class CliExecutionAdapter {
|
|
|
131
283
|
const env = buildWorkerIdentityEnv(process.env, {
|
|
132
284
|
agent: options.agent,
|
|
133
285
|
claimId: options.claimId,
|
|
134
|
-
extraEnv: {
|
|
286
|
+
extraEnv: {
|
|
287
|
+
...buildGitAttributionEnv(options.agent),
|
|
288
|
+
...(invoke.env ?? {}),
|
|
289
|
+
// Contract identity wins over any agent/invoke-provided environment.
|
|
290
|
+
// The child bootstrap independently verifies these effective values.
|
|
291
|
+
...(options.turnEcho?.contract_hash ? { BRAINCLAW_EXECUTION_CONTRACT_HASH: options.turnEcho.contract_hash } : {}),
|
|
292
|
+
...(options.turnEcho?.capability_snapshot_hash
|
|
293
|
+
? { BRAINCLAW_CAPABILITY_SNAPSHOT_HASH: options.turnEcho.capability_snapshot_hash }
|
|
294
|
+
: {}),
|
|
295
|
+
},
|
|
135
296
|
});
|
|
136
297
|
if (invoke.promptDelivery === 'temp_file' && invoke.tempFilePath && invoke.promptText) {
|
|
137
298
|
const dir = path.dirname(invoke.tempFilePath);
|
|
@@ -156,7 +317,8 @@ export class CliExecutionAdapter {
|
|
|
156
317
|
// process just ignores stdout/stderr here. stdin stays a pipe when the
|
|
157
318
|
// prompt is delivered that way (the grouped agent command inherits it).
|
|
158
319
|
const useAckWrap = !!(options.assignmentId && (options.ackRoot ?? options.worktreePath));
|
|
159
|
-
const
|
|
320
|
+
const useWindowsStdinFile = isWin32 && useAckWrap && Boolean(needsStdin);
|
|
321
|
+
const stdinTarget = needsStdin && !useWindowsStdinFile ? 'pipe' : 'ignore';
|
|
160
322
|
const stdio = [stdinTarget, 'ignore', 'ignore'];
|
|
161
323
|
// pln#476 + pln#520 step 4: wrap the spawn so the worker shell touches the
|
|
162
324
|
// pre-exec `ack` sentinel, redirects logs at the shell level, and emits a
|
|
@@ -168,12 +330,25 @@ export class CliExecutionAdapter {
|
|
|
168
330
|
if (useAckWrap) {
|
|
169
331
|
const signalRoot = options.ackRoot ?? options.worktreePath;
|
|
170
332
|
ensureRuntimeDirs(signalRoot);
|
|
333
|
+
const runtimeRunId = options.turnEcho?.run_id;
|
|
334
|
+
const ackPath = getRuntimeSignalPath(signalRoot, options.assignmentId, 'ack', runtimeRunId);
|
|
335
|
+
const contractBootstrapPath = `${ackPath}.bootstrap.cjs`;
|
|
336
|
+
if (options.turnEcho?.contract_hash && options.turnEcho.capability_snapshot_hash) {
|
|
337
|
+
writeContractBootstrapScript(contractBootstrapPath);
|
|
338
|
+
}
|
|
339
|
+
const stdinFilePath = useWindowsStdinFile ? `${ackPath}.stdin` : undefined;
|
|
340
|
+
if (stdinFilePath) {
|
|
341
|
+
fs.writeFileSync(stdinFilePath, invoke.promptText, { encoding: 'utf8', mode: 0o600 });
|
|
342
|
+
}
|
|
171
343
|
const wrappedCmd = buildAckWrapCommand(invoke.bashCommand, {
|
|
172
|
-
ackPath
|
|
173
|
-
completedPath: getRuntimeSignalPath(signalRoot, options.assignmentId, 'completed'),
|
|
174
|
-
failedPath: getRuntimeSignalPath(signalRoot, options.assignmentId, 'failed'),
|
|
175
|
-
stdoutLog: getRuntimeLogPath(signalRoot, options.assignmentId, 'stdout'),
|
|
176
|
-
stderrLog: getRuntimeLogPath(signalRoot, options.assignmentId, 'stderr'),
|
|
344
|
+
ackPath,
|
|
345
|
+
completedPath: getRuntimeSignalPath(signalRoot, options.assignmentId, 'completed', runtimeRunId),
|
|
346
|
+
failedPath: getRuntimeSignalPath(signalRoot, options.assignmentId, 'failed', runtimeRunId),
|
|
347
|
+
stdoutLog: getRuntimeLogPath(signalRoot, options.assignmentId, 'stdout', runtimeRunId),
|
|
348
|
+
stderrLog: getRuntimeLogPath(signalRoot, options.assignmentId, 'stderr', runtimeRunId),
|
|
349
|
+
stdinFilePath,
|
|
350
|
+
contractBootstrapPath,
|
|
351
|
+
expectedWorkspacePath: options.worktreePath,
|
|
177
352
|
}, isWin32, options.turnEcho);
|
|
178
353
|
child = spawn(wrappedCmd, [], {
|
|
179
354
|
detached: !isWin32,
|
|
@@ -201,7 +376,7 @@ export class CliExecutionAdapter {
|
|
|
201
376
|
// On Windows shell:true: this never fires for ENOENT (cmd.exe succeeds);
|
|
202
377
|
// the isBinaryOnPath pre-check above catches that case instead.
|
|
203
378
|
child.on('error', () => { });
|
|
204
|
-
if (needsStdin && child.stdin) {
|
|
379
|
+
if (needsStdin && !useWindowsStdinFile && child.stdin) {
|
|
205
380
|
child.stdin.write(invoke.promptText);
|
|
206
381
|
child.stdin.end();
|
|
207
382
|
}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getCapabilityProfile, dispatchCanCommit, } from './agent-capability.js';
|
|
4
|
+
import { ExpectedArtifactSchema } from './loops/artifact-contract.js';
|
|
5
|
+
export const EXECUTION_CONTRACT_VERSION = 1;
|
|
6
|
+
export const EXECUTION_CONTRACT_PROTOCOL_VERSION = 1;
|
|
7
|
+
const RoleCapabilitySchema = z.enum(['execute', 'coordinate', 'review', 'consult']);
|
|
8
|
+
const ExecutionSurfaceSchema = z.enum(['cli', 'ide', 'extension', 'remote']);
|
|
9
|
+
export const CapabilityRequirementSchema = z.object({
|
|
10
|
+
roles: z.array(RoleCapabilitySchema).default(['execute']),
|
|
11
|
+
required_surfaces: z.array(z.enum([
|
|
12
|
+
'mcp',
|
|
13
|
+
'hooks',
|
|
14
|
+
'skills',
|
|
15
|
+
'rules',
|
|
16
|
+
'auto_approve',
|
|
17
|
+
'cli_spawn',
|
|
18
|
+
'commit',
|
|
19
|
+
'inbox',
|
|
20
|
+
])).default([]),
|
|
21
|
+
execution_surfaces: z.array(ExecutionSurfaceSchema).default([]),
|
|
22
|
+
model: z.string().min(1).optional(),
|
|
23
|
+
required_tools: z.array(z.string().min(1)).default([]),
|
|
24
|
+
});
|
|
25
|
+
export const CapabilityResolutionReasonSchema = z.object({
|
|
26
|
+
code: z.enum([
|
|
27
|
+
'agent_profile_missing',
|
|
28
|
+
'role_unsupported',
|
|
29
|
+
'surface_unsupported',
|
|
30
|
+
'execution_surface_mismatch',
|
|
31
|
+
'model_unsupported',
|
|
32
|
+
'tool_catalog_unattested',
|
|
33
|
+
]),
|
|
34
|
+
requirement: z.string().min(1),
|
|
35
|
+
expected: z.string().optional(),
|
|
36
|
+
actual: z.string().optional(),
|
|
37
|
+
});
|
|
38
|
+
export const HarnessCapabilityBindingSchema = z.object({
|
|
39
|
+
adapter_id: z.string().min(1),
|
|
40
|
+
adapter_version: z.string().min(1),
|
|
41
|
+
requested_model: z.string().min(1).optional(),
|
|
42
|
+
resolved_model: z.string().min(1).optional(),
|
|
43
|
+
model_resolution: z.enum(['exact', 'defaulted', 'unattested']),
|
|
44
|
+
});
|
|
45
|
+
export const CapabilitySnapshotSchema = z.object({
|
|
46
|
+
schema_version: z.literal(1),
|
|
47
|
+
agent: z.string().min(1),
|
|
48
|
+
agent_id: z.string().min(1).optional(),
|
|
49
|
+
profile_name: z.string().min(1).optional(),
|
|
50
|
+
accepted: z.boolean(),
|
|
51
|
+
requested: CapabilityRequirementSchema,
|
|
52
|
+
resolved: z.object({
|
|
53
|
+
roles: z.array(RoleCapabilitySchema),
|
|
54
|
+
surfaces: z.array(z.string().min(1)),
|
|
55
|
+
execution_surface: ExecutionSurfaceSchema.optional(),
|
|
56
|
+
model: z.string().min(1).optional(),
|
|
57
|
+
invoke_binary: z.string().min(1).optional(),
|
|
58
|
+
tool_catalog_attested: z.boolean(),
|
|
59
|
+
harness: HarnessCapabilityBindingSchema.optional(),
|
|
60
|
+
}),
|
|
61
|
+
reasons: z.array(CapabilityResolutionReasonSchema),
|
|
62
|
+
});
|
|
63
|
+
export const ExecutionContractSchema = z.object({
|
|
64
|
+
schema_version: z.literal(EXECUTION_CONTRACT_VERSION),
|
|
65
|
+
minimum_reader_version: z.number().int().positive().max(EXECUTION_CONTRACT_VERSION).default(1),
|
|
66
|
+
identity: z.object({
|
|
67
|
+
loop_id: z.string().min(1),
|
|
68
|
+
turn_id: z.string().min(1),
|
|
69
|
+
logical_attempt_epoch: z.number().int().nonnegative(),
|
|
70
|
+
assignment_id: z.string().min(1),
|
|
71
|
+
run_id: z.string().min(1),
|
|
72
|
+
kind: z.enum(['review', 'ideation', 'implementation', 'research', 'debug']),
|
|
73
|
+
phase: z.string().min(1),
|
|
74
|
+
iteration: z.number().int().nonnegative(),
|
|
75
|
+
}),
|
|
76
|
+
artifact_contract: z.object({
|
|
77
|
+
completion_mode: z.enum(['file', 'mcp', 'either']),
|
|
78
|
+
expected_artifacts: z.array(ExpectedArtifactSchema),
|
|
79
|
+
}),
|
|
80
|
+
capability_requirement: CapabilityRequirementSchema,
|
|
81
|
+
workspace_policy: z.object({
|
|
82
|
+
scope: z.string().min(1),
|
|
83
|
+
cwd: z.string().min(1),
|
|
84
|
+
worktree_path: z.string().min(1).optional(),
|
|
85
|
+
isolation: z.enum(['worktree', 'shared_checkout', 'none']),
|
|
86
|
+
write_access: z.enum(['read_only', 'workspace', 'unrestricted']),
|
|
87
|
+
}),
|
|
88
|
+
timeout_policy: z.object({
|
|
89
|
+
dispatch_lease_ms: z.number().int().positive(),
|
|
90
|
+
grant_lease_ms: z.number().int().positive(),
|
|
91
|
+
}),
|
|
92
|
+
evidence_policy: z.object({
|
|
93
|
+
require_turn_id: z.literal(true),
|
|
94
|
+
require_run_id: z.literal(true),
|
|
95
|
+
require_nonce: z.literal(true),
|
|
96
|
+
artifact_hash: z.enum(['required', 'optional']),
|
|
97
|
+
}),
|
|
98
|
+
protocol: z.object({
|
|
99
|
+
name: z.literal('attempt-authority'),
|
|
100
|
+
minimum_version: z.number().int().positive().max(EXECUTION_CONTRACT_PROTOCOL_VERSION),
|
|
101
|
+
}),
|
|
102
|
+
});
|
|
103
|
+
export const ExecutionContractRefSchema = z.object({
|
|
104
|
+
version: z.literal(EXECUTION_CONTRACT_VERSION),
|
|
105
|
+
hash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
106
|
+
snapshot_hash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
107
|
+
turn_id: z.string().min(1),
|
|
108
|
+
});
|
|
109
|
+
export const RuntimeCapabilityObservationSchema = z.object({
|
|
110
|
+
contract_hash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
111
|
+
capability_snapshot_hash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
112
|
+
adapter_id: z.string().min(1).optional(),
|
|
113
|
+
adapter_version: z.string().min(1).optional(),
|
|
114
|
+
observed_surfaces: z.array(z.string().min(1)).default([]),
|
|
115
|
+
observed_model: z.string().min(1).optional(),
|
|
116
|
+
accepted_contract_hash: z.string().regex(/^[a-f0-9]{64}$/).optional(),
|
|
117
|
+
accepted_capability_snapshot_hash: z.string().regex(/^[a-f0-9]{64}$/).optional(),
|
|
118
|
+
});
|
|
119
|
+
function canonicalValue(value) {
|
|
120
|
+
if (typeof value === 'string')
|
|
121
|
+
return value.normalize('NFC');
|
|
122
|
+
if (Array.isArray(value))
|
|
123
|
+
return value.map(canonicalValue);
|
|
124
|
+
if (value && typeof value === 'object') {
|
|
125
|
+
const normalized = Object.entries(value)
|
|
126
|
+
.filter(([, item]) => item !== undefined)
|
|
127
|
+
.map(([key, item]) => [key.normalize('NFC'), canonicalValue(item)])
|
|
128
|
+
.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
129
|
+
if (new Set(normalized.map(([key]) => key)).size !== normalized.length) {
|
|
130
|
+
throw new Error('canonical execution contract contains duplicate NFC-normalized keys');
|
|
131
|
+
}
|
|
132
|
+
return Object.fromEntries(normalized);
|
|
133
|
+
}
|
|
134
|
+
return value;
|
|
135
|
+
}
|
|
136
|
+
export function canonicalExecutionContract(contract) {
|
|
137
|
+
return JSON.stringify(canonicalValue(ExecutionContractSchema.parse(contract)));
|
|
138
|
+
}
|
|
139
|
+
export function executionContractHash(contract) {
|
|
140
|
+
return crypto.createHash('sha256').update(canonicalExecutionContract(contract), 'utf8').digest('hex');
|
|
141
|
+
}
|
|
142
|
+
export function capabilitySnapshotHash(snapshot) {
|
|
143
|
+
const canonical = JSON.stringify(canonicalValue(CapabilitySnapshotSchema.parse(snapshot)));
|
|
144
|
+
return crypto.createHash('sha256').update(canonical, 'utf8').digest('hex');
|
|
145
|
+
}
|
|
146
|
+
function profileSurfaces(profile) {
|
|
147
|
+
const surfaces = [
|
|
148
|
+
profile.hasMcp && 'mcp',
|
|
149
|
+
profile.hasHooks && 'hooks',
|
|
150
|
+
profile.hasSkills && 'skills',
|
|
151
|
+
profile.hasRules && 'rules',
|
|
152
|
+
profile.hasAutoApprove && 'auto_approve',
|
|
153
|
+
profile.runtime.canBeSpawnedCli && 'cli_spawn',
|
|
154
|
+
profile.runtime.inbox && 'inbox',
|
|
155
|
+
dispatchCanCommit(profile) && 'commit',
|
|
156
|
+
].filter((value) => Boolean(value));
|
|
157
|
+
return [...new Set(surfaces)].sort();
|
|
158
|
+
}
|
|
159
|
+
function resolvedModel(profile, requested) {
|
|
160
|
+
if (!requested)
|
|
161
|
+
return profile.default_model;
|
|
162
|
+
if (profile.model_flag || profile.default_model === requested)
|
|
163
|
+
return requested;
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
export function resolveCapabilitySnapshot(agent, requirementInput, agentId, harness) {
|
|
167
|
+
const requested = CapabilityRequirementSchema.parse(requirementInput);
|
|
168
|
+
const harnessBinding = harness ? HarnessCapabilityBindingSchema.parse(harness) : undefined;
|
|
169
|
+
const profile = getCapabilityProfile(agent);
|
|
170
|
+
const reasons = [];
|
|
171
|
+
if (!profile) {
|
|
172
|
+
reasons.push({ code: 'agent_profile_missing', requirement: 'agent_profile', expected: agent });
|
|
173
|
+
return CapabilitySnapshotSchema.parse({
|
|
174
|
+
schema_version: 1,
|
|
175
|
+
agent,
|
|
176
|
+
agent_id: agentId,
|
|
177
|
+
accepted: false,
|
|
178
|
+
requested,
|
|
179
|
+
resolved: { roles: [], surfaces: [], tool_catalog_attested: false },
|
|
180
|
+
reasons,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
const surfaces = profileSurfaces(profile);
|
|
184
|
+
for (const role of requested.roles) {
|
|
185
|
+
if (!profile.role_capabilities.includes(role)) {
|
|
186
|
+
reasons.push({
|
|
187
|
+
code: 'role_unsupported',
|
|
188
|
+
requirement: `role:${role}`,
|
|
189
|
+
expected: role,
|
|
190
|
+
actual: profile.role_capabilities.join(','),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
for (const surface of requested.required_surfaces) {
|
|
195
|
+
if (!surfaces.includes(surface)) {
|
|
196
|
+
reasons.push({
|
|
197
|
+
code: 'surface_unsupported',
|
|
198
|
+
requirement: `surface:${surface}`,
|
|
199
|
+
expected: surface,
|
|
200
|
+
actual: surfaces.join(','),
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (requested.execution_surfaces.length > 0
|
|
205
|
+
&& !requested.execution_surfaces.includes(profile.execution_env.surface)) {
|
|
206
|
+
reasons.push({
|
|
207
|
+
code: 'execution_surface_mismatch',
|
|
208
|
+
requirement: 'execution_surface',
|
|
209
|
+
expected: requested.execution_surfaces.join(','),
|
|
210
|
+
actual: profile.execution_env.surface,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
const model = resolvedModel(profile, requested.model);
|
|
214
|
+
if (requested.model && !model) {
|
|
215
|
+
reasons.push({
|
|
216
|
+
code: 'model_unsupported',
|
|
217
|
+
requirement: 'model',
|
|
218
|
+
expected: requested.model,
|
|
219
|
+
actual: profile.default_model,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
// Current profiles attest transport surfaces, not per-tool catalogs. Refuse
|
|
223
|
+
// named tools rather than guessing from hasMcp/hasSkills.
|
|
224
|
+
if (requested.required_tools.length > 0) {
|
|
225
|
+
reasons.push({
|
|
226
|
+
code: 'tool_catalog_unattested',
|
|
227
|
+
requirement: 'required_tools',
|
|
228
|
+
expected: requested.required_tools.join(','),
|
|
229
|
+
actual: 'unattested',
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
return CapabilitySnapshotSchema.parse({
|
|
233
|
+
schema_version: 1,
|
|
234
|
+
agent,
|
|
235
|
+
agent_id: agentId,
|
|
236
|
+
profile_name: profile.name,
|
|
237
|
+
accepted: reasons.length === 0,
|
|
238
|
+
requested,
|
|
239
|
+
resolved: {
|
|
240
|
+
roles: profile.role_capabilities,
|
|
241
|
+
surfaces,
|
|
242
|
+
execution_surface: profile.execution_env.surface,
|
|
243
|
+
model,
|
|
244
|
+
invoke_binary: profile.invoke_binary,
|
|
245
|
+
tool_catalog_attested: false,
|
|
246
|
+
harness: harnessBinding,
|
|
247
|
+
},
|
|
248
|
+
reasons,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
export function executionContractRef(contract, snapshot) {
|
|
252
|
+
return {
|
|
253
|
+
version: contract.schema_version,
|
|
254
|
+
hash: executionContractHash(contract),
|
|
255
|
+
snapshot_hash: capabilitySnapshotHash(snapshot),
|
|
256
|
+
turn_id: contract.identity.turn_id,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
export function assertExecutionContractIntegrity(contract, ref, snapshot, selectedAgent) {
|
|
260
|
+
const parsedContract = ExecutionContractSchema.parse(contract);
|
|
261
|
+
const parsedRef = ExecutionContractRefSchema.parse(ref);
|
|
262
|
+
const parsedSnapshot = CapabilitySnapshotSchema.parse(snapshot);
|
|
263
|
+
if (parsedRef.turn_id !== parsedContract.identity.turn_id) {
|
|
264
|
+
throw new Error(`execution contract turn mismatch: ${parsedRef.turn_id} != ${parsedContract.identity.turn_id}`);
|
|
265
|
+
}
|
|
266
|
+
const actualHash = executionContractHash(parsedContract);
|
|
267
|
+
if (parsedRef.hash !== actualHash) {
|
|
268
|
+
throw new Error(`execution contract hash mismatch: ${parsedRef.hash} != ${actualHash}`);
|
|
269
|
+
}
|
|
270
|
+
const actualSnapshotHash = capabilitySnapshotHash(parsedSnapshot);
|
|
271
|
+
if (parsedRef.snapshot_hash !== actualSnapshotHash) {
|
|
272
|
+
throw new Error(`capability snapshot hash mismatch: ${parsedRef.snapshot_hash} != ${actualSnapshotHash}`);
|
|
273
|
+
}
|
|
274
|
+
if (!parsedSnapshot.accepted) {
|
|
275
|
+
throw new Error('execution contract capability snapshot was not accepted');
|
|
276
|
+
}
|
|
277
|
+
if (JSON.stringify(parsedSnapshot.requested) !== JSON.stringify(parsedContract.capability_requirement)) {
|
|
278
|
+
throw new Error('execution contract capability requirement differs from its resolved snapshot');
|
|
279
|
+
}
|
|
280
|
+
if (selectedAgent && parsedSnapshot.agent !== selectedAgent.agent) {
|
|
281
|
+
throw new Error(`capability snapshot agent mismatch: ${parsedSnapshot.agent} != ${selectedAgent.agent}`);
|
|
282
|
+
}
|
|
283
|
+
if (selectedAgent && parsedSnapshot.agent_id !== selectedAgent.agent_id) {
|
|
284
|
+
throw new Error(`capability snapshot agent_id mismatch: ${parsedSnapshot.agent_id ?? 'none'} != ${selectedAgent.agent_id ?? 'none'}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Pre-crossing acceptance emitted by the frozen harness adapter. The child
|
|
289
|
+
* process still confirms the effective environment in its bootstrap ACK after
|
|
290
|
+
* spawn; this attestation proves, before authority crosses, that the selected
|
|
291
|
+
* adapter accepted the exact immutable contract it is about to deliver.
|
|
292
|
+
*/
|
|
293
|
+
export function attestHarnessContractAcceptance(expectedRef, snapshot, binding) {
|
|
294
|
+
const parsedSnapshot = CapabilitySnapshotSchema.parse(snapshot);
|
|
295
|
+
const parsedBinding = HarnessCapabilityBindingSchema.parse(binding);
|
|
296
|
+
if (!parsedSnapshot.accepted) {
|
|
297
|
+
throw new Error('harness cannot accept a rejected capability snapshot');
|
|
298
|
+
}
|
|
299
|
+
const frozen = parsedSnapshot.resolved.harness;
|
|
300
|
+
if (!frozen)
|
|
301
|
+
throw new Error('capability snapshot has no frozen harness binding');
|
|
302
|
+
if (frozen.adapter_id !== parsedBinding.adapter_id
|
|
303
|
+
|| frozen.adapter_version !== parsedBinding.adapter_version
|
|
304
|
+
|| frozen.requested_model !== parsedBinding.requested_model
|
|
305
|
+
|| frozen.resolved_model !== parsedBinding.resolved_model) {
|
|
306
|
+
throw new Error(`harness acceptance mismatch: frozen ${frozen.adapter_id}@${frozen.adapter_version}, `
|
|
307
|
+
+ `selected ${parsedBinding.adapter_id}@${parsedBinding.adapter_version}`);
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
contract_hash: expectedRef.hash,
|
|
311
|
+
capability_snapshot_hash: expectedRef.snapshot_hash,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Deterministic capability selection/reselection. Input order is deliberately
|
|
316
|
+
* irrelevant so replaying the same candidate set produces the same worker.
|
|
317
|
+
* Callers can exclude a failed pre-cross candidate and run the resolver again.
|
|
318
|
+
*/
|
|
319
|
+
export function resolveExecutionCandidate(candidates, requirementInput, options = {}) {
|
|
320
|
+
const requirement = CapabilityRequirementSchema.parse(requirementInput);
|
|
321
|
+
const excluded = new Set((options.exclude ?? []).map(({ agent, agent_id }) => `${agent.normalize('NFC')}\0${(agent_id ?? '').normalize('NFC')}`));
|
|
322
|
+
const ordered = candidates
|
|
323
|
+
.filter(({ agent, agent_id }) => !excluded.has(`${agent.normalize('NFC')}\0${(agent_id ?? '').normalize('NFC')}`))
|
|
324
|
+
.map((candidate) => ({ ...candidate, agent: candidate.agent.normalize('NFC'), agent_id: candidate.agent_id?.normalize('NFC') }))
|
|
325
|
+
.sort((left, right) => (right.preference ?? 0) - (left.preference ?? 0)
|
|
326
|
+
|| left.agent.localeCompare(right.agent, 'en')
|
|
327
|
+
|| (left.agent_id ?? '').localeCompare(right.agent_id ?? '', 'en'));
|
|
328
|
+
const evaluated = ordered.map((candidate) => ({
|
|
329
|
+
...candidate,
|
|
330
|
+
snapshot: resolveCapabilitySnapshot(candidate.agent, requirement, candidate.agent_id),
|
|
331
|
+
}));
|
|
332
|
+
const selected = evaluated.find((candidate) => candidate.snapshot.accepted);
|
|
333
|
+
return selected ? { kind: 'selected', selected, evaluated } : { kind: 'rejected', evaluated };
|
|
334
|
+
}
|
|
335
|
+
export function validateWorkerContractAcceptance(expectedRef, acceptedRef, launchStatus) {
|
|
336
|
+
const expected = { contract_hash: expectedRef.hash, capability_snapshot_hash: expectedRef.snapshot_hash };
|
|
337
|
+
if (expected.contract_hash === acceptedRef.contract_hash
|
|
338
|
+
&& expected.capability_snapshot_hash === acceptedRef.capability_snapshot_hash)
|
|
339
|
+
return { kind: 'accepted' };
|
|
340
|
+
if (launchStatus === 'crossed') {
|
|
341
|
+
return { kind: 'post_crossing_anomaly', expected, accepted: acceptedRef, respawn: false };
|
|
342
|
+
}
|
|
343
|
+
return { kind: 'abort_and_reselect', expected, accepted: acceptedRef };
|
|
344
|
+
}
|
|
345
|
+
//# sourceMappingURL=execution-contract.js.map
|