cool-workflow 0.1.80 → 0.1.82
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +42 -2
- package/apps/architecture-review/app.json +1 -1
- package/apps/architecture-review-fast/app.json +1 -1
- package/apps/end-to-end-golden-path/app.json +1 -1
- package/apps/pr-review-fix-ci/app.json +1 -1
- package/apps/release-cut/app.json +1 -1
- package/apps/research-synthesis/app.json +1 -1
- package/dist/agent-config.js +21 -7
- package/dist/candidate-scoring.js +62 -48
- package/dist/capability-core.js +152 -96
- package/dist/capability-registry.js +160 -174
- package/dist/cli.js +90 -100
- package/dist/collaboration.js +5 -6
- package/dist/commit.js +232 -209
- package/dist/compare.js +18 -0
- package/dist/coordinator/classify.js +45 -0
- package/dist/coordinator/paths.js +42 -0
- package/dist/coordinator/util.js +126 -0
- package/dist/coordinator.js +127 -300
- package/dist/dispatch.js +46 -3
- package/dist/drive.js +7 -7
- package/dist/error-feedback.js +8 -4
- package/dist/evidence-reasoning.js +5 -2
- package/dist/execution-backend/agent.js +294 -0
- package/dist/execution-backend/probes.js +96 -0
- package/dist/execution-backend/util.js +47 -0
- package/dist/execution-backend.js +77 -450
- package/dist/gates.js +48 -0
- package/dist/mcp-server.js +34 -173
- package/dist/multi-agent/graph.js +84 -0
- package/dist/multi-agent/helpers.js +141 -0
- package/dist/multi-agent/ids.js +20 -0
- package/dist/multi-agent/paths.js +22 -0
- package/dist/multi-agent-eval/format.js +194 -0
- package/dist/multi-agent-eval/normalize.js +51 -0
- package/dist/multi-agent-eval.js +63 -242
- package/dist/multi-agent-host.js +53 -40
- package/dist/multi-agent-operator-ux.js +2 -1
- package/dist/multi-agent-trust.js +5 -5
- package/dist/multi-agent.js +125 -314
- package/dist/node-projection.js +59 -0
- package/dist/node-snapshot.js +11 -21
- package/dist/observability/format.js +61 -0
- package/dist/observability/intake.js +98 -0
- package/dist/observability.js +14 -160
- package/dist/operator-ux/format.js +364 -0
- package/dist/operator-ux.js +22 -363
- package/dist/orchestrator/lifecycle-operations.js +22 -1
- package/dist/orchestrator/report.js +8 -0
- package/dist/orchestrator.js +41 -10
- package/dist/reclamation.js +32 -55
- package/dist/run-export.js +138 -14
- package/dist/run-registry/derive.js +172 -0
- package/dist/run-registry/format.js +124 -0
- package/dist/run-registry/gc.js +251 -0
- package/dist/run-registry/policy.js +16 -0
- package/dist/run-registry/queue.js +116 -0
- package/dist/run-registry.js +78 -593
- package/dist/run-state-schema.js +1 -0
- package/dist/sandbox-profile.js +43 -2
- package/dist/scheduler.js +34 -4
- package/dist/state-explosion/format.js +159 -0
- package/dist/state-explosion/helpers.js +82 -0
- package/dist/state-explosion.js +65 -283
- package/dist/state-node.js +19 -4
- package/dist/telemetry-attestation.js +55 -0
- package/dist/telemetry-demo.js +15 -3
- package/dist/telemetry-ledger.js +60 -15
- package/dist/topology.js +50 -12
- package/dist/triggers.js +33 -14
- package/dist/trust-audit.js +215 -71
- package/dist/validation.js +328 -0
- package/dist/version.js +1 -1
- package/dist/worker-isolation/helpers.js +51 -0
- package/dist/worker-isolation/paths.js +46 -0
- package/dist/worker-isolation.js +182 -173
- package/docs/agent-delegation-drive.7.md +14 -0
- package/docs/cli-mcp-parity.7.md +5 -0
- package/docs/contract-migration-tooling.7.md +3 -0
- package/docs/control-plane-scheduling.7.md +3 -0
- package/docs/dogfood/resume-drive-real-agent-2026-06-14.md +40 -0
- package/docs/durable-state-and-locking.7.md +5 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +3 -0
- package/docs/execution-backends.7.md +3 -0
- package/docs/index.md +1 -0
- package/docs/launch/launch-kit.md +46 -23
- package/docs/launch/pre-launch-checklist.md +14 -14
- package/docs/multi-agent-cli-mcp-surface.7.md +5 -0
- package/docs/multi-agent-eval-replay-harness.7.md +3 -0
- package/docs/multi-agent-operator-ux.7.md +3 -0
- package/docs/multi-agent-trust-policy-audit.7.md +27 -0
- package/docs/node-snapshot-diff-replay.7.md +3 -0
- package/docs/observability-cost-accounting.7.md +3 -0
- package/docs/project-index.md +23 -6
- package/docs/real-execution-backends.7.md +3 -0
- package/docs/release-and-migration.7.md +5 -0
- package/docs/release-tooling.7.md +35 -2
- package/docs/run-registry-control-plane.7.md +55 -8
- package/docs/run-retention-reclamation.7.md +5 -0
- package/docs/state-explosion-management.7.md +3 -0
- package/docs/team-collaboration.7.md +3 -0
- package/docs/trust-model.md +267 -0
- package/docs/vendor-manifest-loadability.7.md +43 -0
- package/docs/web-desktop-workbench.7.md +3 -0
- package/manifest/plugin.manifest.json +1 -1
- package/manifest/source-context-profiles.json +1 -1
- package/package.json +4 -2
- package/scripts/agents/builtin-templates.json +7 -0
- package/scripts/bump-version.js +5 -11
- package/scripts/canonical-apps-list.js +64 -0
- package/scripts/canonical-apps.js +19 -4
- package/scripts/children/batch-delegate-child.js +58 -0
- package/scripts/children/http-delegate-child.js +39 -0
- package/scripts/dogfood-release.js +1 -1
- package/scripts/golden-path.js +4 -4
- package/scripts/parity-check.js +5 -0
- package/scripts/release-check.js +5 -1
- package/scripts/release-flow.js +181 -5
- package/scripts/version-sync-check.js +5 -8
- package/dist/capability-dispatcher.js +0 -86
|
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.BackendError = exports.SANDBOX_DIMENSIONS = exports.DEFAULT_BACKEND_ID = exports.EXECUTION_BACKEND_SCHEMA_VERSION = void 0;
|
|
29
|
+
exports.BackendError = exports.SANDBOX_DIMENSIONS = exports.DEFAULT_BACKEND_ID = exports.EXECUTION_BACKEND_SCHEMA_VERSION = exports.runAgentBatchOutcomes = exports.prepareAgentSpawn = exports.stripSecretArgs = exports.sha256 = void 0;
|
|
30
30
|
exports.registerBackend = registerBackend;
|
|
31
31
|
exports.getBackendDriver = getBackendDriver;
|
|
32
32
|
exports.listBackendDescriptors = listBackendDescriptors;
|
|
@@ -39,21 +39,33 @@ exports.requiredSandboxDimensions = requiredSandboxDimensions;
|
|
|
39
39
|
exports.attestSandbox = attestSandbox;
|
|
40
40
|
exports.probeBackend = probeBackend;
|
|
41
41
|
exports.runBackend = runBackend;
|
|
42
|
-
exports.stripSecretArgs = stripSecretArgs;
|
|
43
|
-
exports.prepareAgentSpawn = prepareAgentSpawn;
|
|
44
|
-
exports.runAgentBatchOutcomes = runAgentBatchOutcomes;
|
|
45
42
|
exports.createExecutionBackend = createExecutionBackend;
|
|
46
43
|
exports.listExecutionBackends = listExecutionBackends;
|
|
47
44
|
exports.backendListPayload = backendListPayload;
|
|
48
45
|
exports.backendShowPayload = backendShowPayload;
|
|
49
46
|
exports.backendProbePayload = backendProbePayload;
|
|
50
47
|
exports.buildChildEnv = buildChildEnv;
|
|
51
|
-
exports.sha256 = sha256;
|
|
52
48
|
exports.clearProbeCache = clearProbeCache;
|
|
53
|
-
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
54
49
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
55
50
|
const node_path_1 = __importDefault(require("node:path"));
|
|
56
51
|
const node_child_process_1 = require("node:child_process");
|
|
52
|
+
// Pure leaf helpers — carved into ./execution-backend/util.ts (god-module carve).
|
|
53
|
+
// `sha256` is re-exported below to keep the public surface byte-identical.
|
|
54
|
+
const util_1 = require("./execution-backend/util");
|
|
55
|
+
// Per-backend readiness probe bodies — carved into ./execution-backend/probes.ts.
|
|
56
|
+
const probes_1 = require("./execution-backend/probes");
|
|
57
|
+
// Agent-delegation pure helpers + concurrent batch — carved into
|
|
58
|
+
// ./execution-backend/agent.ts. The public symbols (stripSecretArgs,
|
|
59
|
+
// AgentSpawnJob, prepareAgentSpawn, runAgentBatchOutcomes) are re-exported below.
|
|
60
|
+
const agent_1 = require("./execution-backend/agent");
|
|
61
|
+
// Re-export the carved public surface so every importer of "./execution-backend"
|
|
62
|
+
// is byte-unchanged (no public signature changed; pure code movement).
|
|
63
|
+
var util_2 = require("./execution-backend/util");
|
|
64
|
+
Object.defineProperty(exports, "sha256", { enumerable: true, get: function () { return util_2.sha256; } });
|
|
65
|
+
var agent_2 = require("./execution-backend/agent");
|
|
66
|
+
Object.defineProperty(exports, "stripSecretArgs", { enumerable: true, get: function () { return agent_2.stripSecretArgs; } });
|
|
67
|
+
Object.defineProperty(exports, "prepareAgentSpawn", { enumerable: true, get: function () { return agent_2.prepareAgentSpawn; } });
|
|
68
|
+
Object.defineProperty(exports, "runAgentBatchOutcomes", { enumerable: true, get: function () { return agent_2.runAgentBatchOutcomes; } });
|
|
57
69
|
exports.EXECUTION_BACKEND_SCHEMA_VERSION = 1;
|
|
58
70
|
exports.DEFAULT_BACKEND_ID = "node";
|
|
59
71
|
exports.SANDBOX_DIMENSIONS = ["read", "write", "command", "network", "env"];
|
|
@@ -204,21 +216,21 @@ function getBackendDescriptor(id) {
|
|
|
204
216
|
// — no central switch to edit. Function declarations below are hoisted, so the
|
|
205
217
|
// closures resolve at call time.
|
|
206
218
|
const BUILTIN_DRIVER_BEHAVIORS = {
|
|
207
|
-
node: { spawnStyle: "direct", runtimeNote: () => "node", probe: probeNodeBackend },
|
|
219
|
+
node: { spawnStyle: "direct", runtimeNote: () => "node", probe: probes_1.probeNodeBackend },
|
|
208
220
|
bun: {
|
|
209
221
|
spawnStyle: "direct",
|
|
210
|
-
runtimeNote: () => (hasExecutable("bun") ? "bun (node-compatible execution)" : "node-compatible (bun not installed)"),
|
|
211
|
-
probe: probeBunBackend
|
|
222
|
+
runtimeNote: () => ((0, util_1.hasExecutable)("bun") ? "bun (node-compatible execution)" : "node-compatible (bun not installed)"),
|
|
223
|
+
probe: probes_1.probeBunBackend
|
|
212
224
|
},
|
|
213
|
-
shell: { spawnStyle: "shell", runtimeNote: () => "posix-shell", probe: probeShellBackend },
|
|
214
|
-
container: { delegateRun: ctxDelegate(runContainer), buildHandle: containerHandle, probe: probeContainerBackend },
|
|
215
|
-
remote: { delegateRun: ctxDelegate(runHttpDelegation), buildHandle: remoteHandle, probe: probeRemoteBackend },
|
|
216
|
-
ci: { delegateRun: ctxDelegate(runHttpDelegation), buildHandle: ciHandle, probe: probeCiBackend },
|
|
225
|
+
shell: { spawnStyle: "shell", runtimeNote: () => "posix-shell", probe: probes_1.probeShellBackend },
|
|
226
|
+
container: { delegateRun: ctxDelegate(runContainer), buildHandle: containerHandle, probe: probes_1.probeContainerBackend },
|
|
227
|
+
remote: { delegateRun: ctxDelegate(runHttpDelegation), buildHandle: remoteHandle, probe: probes_1.probeRemoteBackend },
|
|
228
|
+
ci: { delegateRun: ctxDelegate(runHttpDelegation), buildHandle: ciHandle, probe: probes_1.probeCiBackend },
|
|
217
229
|
agent: {
|
|
218
230
|
delegateRun: ctxDelegate(runAgentProcess),
|
|
219
|
-
buildHandle: agentHandle,
|
|
231
|
+
buildHandle: agent_1.agentHandle,
|
|
220
232
|
commandlessDelegate: true,
|
|
221
|
-
probe: probeAgentBackend
|
|
233
|
+
probe: probes_1.probeAgentBackend
|
|
222
234
|
}
|
|
223
235
|
};
|
|
224
236
|
for (const spec of DRIVER_SPECS) {
|
|
@@ -254,7 +266,7 @@ function resolveBackendSelection(requested, env = process.env) {
|
|
|
254
266
|
return { backendId: exports.DEFAULT_BACKEND_ID, source: "default" };
|
|
255
267
|
}
|
|
256
268
|
function backendSelectionFrom(args, env = process.env) {
|
|
257
|
-
const requested = firstString(args.backend, args.backendId, args.executionBackend);
|
|
269
|
+
const requested = (0, util_1.firstString)(args.backend, args.backendId, args.executionBackend);
|
|
258
270
|
return resolveBackendSelection(requested, env);
|
|
259
271
|
}
|
|
260
272
|
// ---------------------------------------------------------------------------
|
|
@@ -340,77 +352,6 @@ function probeBackend(id, context = {}) {
|
|
|
340
352
|
reason: body.reason
|
|
341
353
|
};
|
|
342
354
|
}
|
|
343
|
-
function probeNodeBackend() {
|
|
344
|
-
const ok = hasExecutable("node");
|
|
345
|
-
return {
|
|
346
|
-
checks: [{ name: "node-runtime", ok, detail: ok ? "node on PATH" : "node not found on PATH" }],
|
|
347
|
-
readiness: ok ? "ready" : "unavailable",
|
|
348
|
-
reason: ok ? undefined : "node runtime not found on PATH"
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
function probeShellBackend() {
|
|
352
|
-
const ok = hasExecutable("sh") || node_fs_1.default.existsSync("/bin/sh");
|
|
353
|
-
return {
|
|
354
|
-
checks: [{ name: "posix-shell", ok, detail: ok ? "sh available" : "no POSIX shell found" }],
|
|
355
|
-
readiness: ok ? "ready" : "unavailable",
|
|
356
|
-
reason: ok ? undefined : "POSIX shell not found"
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
function probeBunBackend() {
|
|
360
|
-
const bun = hasExecutable("bun");
|
|
361
|
-
const node = hasExecutable("node");
|
|
362
|
-
return {
|
|
363
|
-
checks: [
|
|
364
|
-
{ name: "bun-runtime", ok: bun, detail: bun ? "bun on PATH" : "bun not found; node-compatible fallback" },
|
|
365
|
-
{ name: "node-compatible-fallback", ok: node, detail: node ? "node on PATH" : "node not found on PATH" }
|
|
366
|
-
],
|
|
367
|
-
readiness: bun || node ? "ready" : "unavailable",
|
|
368
|
-
reason: !bun && node ? "bun not installed; executing via node-compatible runtime" : !bun && !node ? "neither bun nor node found on PATH" : undefined
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
function probeContainerBackend() {
|
|
372
|
-
const docker = hasExecutable("docker");
|
|
373
|
-
const podman = hasExecutable("podman");
|
|
374
|
-
return {
|
|
375
|
-
checks: [
|
|
376
|
-
{ name: "docker", ok: docker, detail: docker ? "docker on PATH" : "docker not found" },
|
|
377
|
-
{ name: "podman", ok: podman, detail: podman ? "podman on PATH" : "podman not found" }
|
|
378
|
-
],
|
|
379
|
-
readiness: docker || podman ? "ready" : "unavailable",
|
|
380
|
-
reason: docker || podman ? undefined : "no container runtime (docker/podman) found; supply --image to delegate explicitly"
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
function probeRemoteBackend() {
|
|
384
|
-
const endpoint = (process.env.CW_REMOTE_ENDPOINT || "").trim();
|
|
385
|
-
return {
|
|
386
|
-
checks: [{ name: "endpoint", ok: Boolean(endpoint), detail: endpoint ? "CW_REMOTE_ENDPOINT configured" : "CW_REMOTE_ENDPOINT not set" }],
|
|
387
|
-
readiness: endpoint ? "ready" : "unverified",
|
|
388
|
-
reason: endpoint ? undefined : "no remote endpoint configured (set CW_REMOTE_ENDPOINT or pass --endpoint)"
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
function probeCiBackend() {
|
|
392
|
-
const endpoint = (process.env.CW_CI_ENDPOINT || "").trim();
|
|
393
|
-
return {
|
|
394
|
-
checks: [{ name: "ci-endpoint", ok: Boolean(endpoint), detail: endpoint ? "CW_CI_ENDPOINT configured" : "CW_CI_ENDPOINT not set" }],
|
|
395
|
-
readiness: endpoint ? "ready" : "unverified",
|
|
396
|
-
reason: endpoint ? undefined : "no CI job target configured (set CW_CI_ENDPOINT or pass --job)"
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
function probeAgentBackend() {
|
|
400
|
-
// Mirrors remote/ci EXACTLY: unconfigured ⇒ `unverified` (NOT a hard refusal),
|
|
401
|
-
// configured ⇒ `ready`. "Configured" = a command-template or endpoint is set.
|
|
402
|
-
const command = (process.env.CW_AGENT_COMMAND || "").trim();
|
|
403
|
-
const endpoint = (process.env.CW_AGENT_ENDPOINT || "").trim();
|
|
404
|
-
const configured = Boolean(command || endpoint);
|
|
405
|
-
return {
|
|
406
|
-
checks: [
|
|
407
|
-
{ name: "agent-command", ok: Boolean(command), detail: command ? "CW_AGENT_COMMAND configured" : "CW_AGENT_COMMAND not set" },
|
|
408
|
-
{ name: "agent-endpoint", ok: Boolean(endpoint), detail: endpoint ? "CW_AGENT_ENDPOINT configured" : "CW_AGENT_ENDPOINT not set" }
|
|
409
|
-
],
|
|
410
|
-
readiness: configured ? "ready" : "unverified",
|
|
411
|
-
reason: configured ? undefined : "no agent configured (set CW_AGENT_COMMAND or CW_AGENT_ENDPOINT, or pass --agent-command/--agent-endpoint)"
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
355
|
// ---------------------------------------------------------------------------
|
|
415
356
|
// The run entry. Refuses (fail closed) when the sandbox cannot be honored, the
|
|
416
357
|
// command is denied by policy, or the backend is not ready. Local drivers spawn a
|
|
@@ -474,9 +415,9 @@ function executeLocal(descriptor, policy, request, label, attestation) {
|
|
|
474
415
|
? (0, node_child_process_1.spawnSync)([command, ...args].join(" "), { ...options, shell: true })
|
|
475
416
|
: (0, node_child_process_1.spawnSync)(command, args, { ...options, shell: false });
|
|
476
417
|
const exitCode = typeof result.status === "number" ? result.status : null;
|
|
477
|
-
const spawnError = result.error ? messageOf(result.error) : undefined;
|
|
418
|
+
const spawnError = result.error ? (0, util_1.messageOf)(result.error) : undefined;
|
|
478
419
|
const stdout = String(result.stdout || "");
|
|
479
|
-
const digest = sha256(stdout);
|
|
420
|
+
const digest = (0, util_1.sha256)(stdout);
|
|
480
421
|
const status = spawnError ? "failed" : exitCode === 0 ? "completed" : "failed";
|
|
481
422
|
const evidence = [
|
|
482
423
|
`command:${[command, ...args].join(" ")}`,
|
|
@@ -502,7 +443,7 @@ function executeLocal(descriptor, policy, request, label, attestation) {
|
|
|
502
443
|
backendId: descriptor.id,
|
|
503
444
|
locality: descriptor.locality,
|
|
504
445
|
kind: descriptor.kind,
|
|
505
|
-
attestation: { ...attestation, status:
|
|
446
|
+
attestation: { ...attestation, status: attestation.status, notes }
|
|
506
447
|
}
|
|
507
448
|
};
|
|
508
449
|
}
|
|
@@ -544,7 +485,7 @@ function delegate(descriptor, policy, request, label, probe) {
|
|
|
544
485
|
* identical to executeLocal's, so evidence is byte-stable across backends. The
|
|
545
486
|
* handle is recorded in provenance only. */
|
|
546
487
|
function delegatedEnvelope(descriptor, label, handle, attestation, command, args, exitCode, stdout) {
|
|
547
|
-
const digest = sha256(stdout);
|
|
488
|
+
const digest = (0, util_1.sha256)(stdout);
|
|
548
489
|
const status = exitCode === 0 ? "completed" : "failed";
|
|
549
490
|
const evidence = [
|
|
550
491
|
`command:${[command, ...args].join(" ")}`,
|
|
@@ -573,7 +514,7 @@ function delegatedEnvelope(descriptor, label, handle, attestation, command, args
|
|
|
573
514
|
* closed when no runtime is on PATH, the daemon is unreachable, or the runtime
|
|
574
515
|
* itself errors (exit 125) — distinct from the command's own non-zero exit. */
|
|
575
516
|
function runContainer(descriptor, policy, request, label, handle, attestation) {
|
|
576
|
-
const runtime = hasExecutable("docker") ? "docker" : hasExecutable("podman") ? "podman" : undefined;
|
|
517
|
+
const runtime = (0, util_1.hasExecutable)("docker") ? "docker" : (0, util_1.hasExecutable)("podman") ? "podman" : undefined;
|
|
577
518
|
if (!runtime) {
|
|
578
519
|
return refusedEnvelope(descriptor, policy, label, "runtime-unavailable", "no container runtime (docker/podman) on PATH", {
|
|
579
520
|
attestation
|
|
@@ -622,7 +563,7 @@ function runContainer(descriptor, policy, request, label, handle, attestation) {
|
|
|
622
563
|
maxBuffer: 32 * 1024 * 1024
|
|
623
564
|
});
|
|
624
565
|
if (result.error) {
|
|
625
|
-
return refusedEnvelope(descriptor, policy, label, "delegation-failed", `${runtime} run failed: ${messageOf(result.error)}`, {
|
|
566
|
+
return refusedEnvelope(descriptor, policy, label, "delegation-failed", `${runtime} run failed: ${(0, util_1.messageOf)(result.error)}`, {
|
|
626
567
|
attestation
|
|
627
568
|
});
|
|
628
569
|
}
|
|
@@ -638,35 +579,14 @@ function runContainer(descriptor, policy, request, label, handle, attestation) {
|
|
|
638
579
|
}
|
|
639
580
|
return delegatedEnvelope(descriptor, label, handle, attestation, command, args, exitCode, String(result.stdout || ""));
|
|
640
581
|
}
|
|
641
|
-
//
|
|
642
|
-
// JSON job on stdin, POSTs it to the endpoint,
|
|
643
|
-
// and prints `{ exitCode, stdout }` (or
|
|
644
|
-
// fetch, node >=18), so the driver stays
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
const job = JSON.parse((await read()) || "{}");
|
|
650
|
-
const endpoint = process.env.CW_DELEGATE_ENDPOINT;
|
|
651
|
-
if (!endpoint) throw new Error("no endpoint");
|
|
652
|
-
const post = await fetch(endpoint, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(job) });
|
|
653
|
-
if (!post.ok) throw new Error("runner responded " + post.status);
|
|
654
|
-
let data = await post.json();
|
|
655
|
-
// Poll a returned jobId until the runner reports done.
|
|
656
|
-
let guard = 0;
|
|
657
|
-
while (data && data.jobId && data.done !== true && guard++ < 600) {
|
|
658
|
-
await new Promise((r) => setTimeout(r, 1000));
|
|
659
|
-
const poll = await fetch(endpoint + (endpoint.includes("?") ? "&" : "?") + "jobId=" + encodeURIComponent(data.jobId));
|
|
660
|
-
if (!poll.ok) throw new Error("poll responded " + poll.status);
|
|
661
|
-
data = await poll.json();
|
|
662
|
-
}
|
|
663
|
-
if (typeof data.exitCode !== "number") throw new Error("runner did not report an exitCode");
|
|
664
|
-
process.stdout.write(JSON.stringify({ exitCode: data.exitCode, stdout: String(data.stdout || "") }));
|
|
665
|
-
} catch (e) {
|
|
666
|
-
process.stdout.write(JSON.stringify({ error: e && e.message ? e.message : String(e) }));
|
|
667
|
-
}
|
|
668
|
-
})();
|
|
669
|
-
`;
|
|
582
|
+
// The remote/CI delegation child is a real, packaged Node script (not an embedded
|
|
583
|
+
// `node -e` string — F11): it reads a JSON job on stdin, POSTs it to the endpoint,
|
|
584
|
+
// optionally polls a returned jobId, and prints `{ exitCode, stdout }` (or
|
|
585
|
+
// `{ error }`) on stdout. Node-only (global fetch, node >=18), so the driver stays
|
|
586
|
+
// portable and synchronous from CW's view. We spawn it BY PATH (shell:false). The
|
|
587
|
+
// path is resolved from this compiled module (dist/execution-backend.js) up to the
|
|
588
|
+
// package's `scripts/children/` dir, which package.json ships in "files".
|
|
589
|
+
const HTTP_DELEGATE_CHILD_SCRIPT = node_path_1.default.resolve(__dirname, "..", "scripts", "children", "http-delegate-child.js");
|
|
670
590
|
/** remote / ci — real HTTP delegation. POSTs the job to the configured endpoint
|
|
671
591
|
* (and polls a returned jobId) via a Node child, then records the runner's exit +
|
|
672
592
|
* stdout digest as canonical evidence. Fails closed when the endpoint is missing,
|
|
@@ -688,7 +608,7 @@ function runHttpDelegation(descriptor, policy, request, label, handle, attestati
|
|
|
688
608
|
sandboxProfileId: policy.id,
|
|
689
609
|
jobId: handle.jobId
|
|
690
610
|
});
|
|
691
|
-
const child = (0, node_child_process_1.spawnSync)(process.execPath, [
|
|
611
|
+
const child = (0, node_child_process_1.spawnSync)(process.execPath, [HTTP_DELEGATE_CHILD_SCRIPT], {
|
|
692
612
|
input: job,
|
|
693
613
|
env: { ...process.env, CW_DELEGATE_ENDPOINT: endpoint },
|
|
694
614
|
encoding: "utf8",
|
|
@@ -696,7 +616,7 @@ function runHttpDelegation(descriptor, policy, request, label, handle, attestati
|
|
|
696
616
|
maxBuffer: 32 * 1024 * 1024
|
|
697
617
|
});
|
|
698
618
|
if (child.error) {
|
|
699
|
-
return refusedEnvelope(descriptor, policy, label, "delegation-failed", `${descriptor.id} delegation failed: ${messageOf(child.error)}`, {
|
|
619
|
+
return refusedEnvelope(descriptor, policy, label, "delegation-failed", `${descriptor.id} delegation failed: ${(0, util_1.messageOf)(child.error)}`, {
|
|
700
620
|
attestation
|
|
701
621
|
});
|
|
702
622
|
}
|
|
@@ -714,168 +634,31 @@ function runHttpDelegation(descriptor, policy, request, label, handle, attestati
|
|
|
714
634
|
}
|
|
715
635
|
return delegatedEnvelope(descriptor, label, handle, attestation, command, args, parsed.exitCode, String(parsed.stdout || ""));
|
|
716
636
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
const parts = binary.split(/\s+/).filter(Boolean);
|
|
737
|
-
binary = parts[0];
|
|
738
|
-
rawArgs = parts.slice(1);
|
|
739
|
-
}
|
|
740
|
-
return { binary, rawArgs, endpoint, model, timeoutMs: request.timeoutMs };
|
|
741
|
-
}
|
|
742
|
-
const AGENT_SECRET_FLAGS = new Set(["--api-key", "--apikey", "--token", "--key", "--secret", "--password", "--auth", "--bearer"]);
|
|
743
|
-
/** Redact secrets from recorded agent args: a value FOLLOWING a known secret flag,
|
|
744
|
-
* an `--x-key=...` inline value, or a token that LOOKS like a credential. Never
|
|
745
|
-
* record a raw secret in provenance/evidence. Exported so the durable config
|
|
746
|
-
* surface strips the SAME way before persisting/showing a command template. */
|
|
747
|
-
function stripSecretArgs(args) {
|
|
748
|
-
const out = [];
|
|
749
|
-
for (let i = 0; i < args.length; i++) {
|
|
750
|
-
const arg = String(args[i]);
|
|
751
|
-
if (AGENT_SECRET_FLAGS.has(arg.toLowerCase())) {
|
|
752
|
-
out.push(arg);
|
|
753
|
-
if (i + 1 < args.length) {
|
|
754
|
-
out.push("<redacted>");
|
|
755
|
-
i++;
|
|
756
|
-
}
|
|
757
|
-
continue;
|
|
758
|
-
}
|
|
759
|
-
const inline = arg.match(/^(--?[A-Za-z][\w-]*(?:key|token|secret|password|auth|bearer)[\w-]*)=.*/i);
|
|
760
|
-
if (inline) {
|
|
761
|
-
out.push(`${inline[1]}=<redacted>`);
|
|
762
|
-
continue;
|
|
763
|
-
}
|
|
764
|
-
// Bare credential-looking token: a known provider prefix, or a long high-entropy
|
|
765
|
-
// run with NO path separators (so file paths / {{...}} substitutions survive as
|
|
766
|
-
// useful provenance). Over-redaction is safe; leaking a key is not.
|
|
767
|
-
if (/^(sk-|ghp_|gho_|github_pat_|xox[abpr]-|Bearer\s)/.test(arg) || (arg.length >= 32 && /^[A-Za-z0-9_\-]{32,}$/.test(arg))) {
|
|
768
|
-
out.push("<redacted>");
|
|
769
|
-
continue;
|
|
770
|
-
}
|
|
771
|
-
out.push(arg);
|
|
772
|
-
}
|
|
773
|
-
return out;
|
|
774
|
-
}
|
|
775
|
-
/** Best-effort parse of the AGENT-reported model id from its stdout. SOLELY the
|
|
776
|
-
* agent's own report — `unreported` when absent. Never CW_AGENT_MODEL. */
|
|
777
|
-
function parseAgentReport(stdout) {
|
|
778
|
-
const text = String(stdout || "").trim();
|
|
779
|
-
if (!text)
|
|
780
|
-
return {};
|
|
781
|
-
const tryObj = (value) => {
|
|
782
|
-
try {
|
|
783
|
-
const parsed = JSON.parse(value);
|
|
784
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : undefined;
|
|
785
|
-
}
|
|
786
|
-
catch {
|
|
787
|
-
return undefined;
|
|
788
|
-
}
|
|
789
|
-
};
|
|
790
|
-
let obj = tryObj(text);
|
|
791
|
-
if (!obj) {
|
|
792
|
-
const line = text
|
|
793
|
-
.split(/\r?\n/)
|
|
794
|
-
.reverse()
|
|
795
|
-
.find((entry) => entry.trim().startsWith("{") && entry.trim().endsWith("}"));
|
|
796
|
-
if (line)
|
|
797
|
-
obj = tryObj(line.trim());
|
|
798
|
-
}
|
|
799
|
-
if (!obj)
|
|
800
|
-
return {};
|
|
801
|
-
const usage = obj.usage && typeof obj.usage === "object" ? obj.usage : undefined;
|
|
802
|
-
let model = typeof obj.model === "string"
|
|
803
|
-
? obj.model
|
|
804
|
-
: usage && typeof usage.model === "string"
|
|
805
|
-
? usage.model
|
|
806
|
-
: typeof obj.modelId === "string"
|
|
807
|
-
? obj.modelId
|
|
808
|
-
: undefined;
|
|
809
|
-
// Some agents (e.g. `claude -p --output-format json`) report no top-level model;
|
|
810
|
-
// the model id(s) appear as KEYS of a `modelUsage` object. Pick the primary model
|
|
811
|
-
// (the one with the most input tokens). Still SOLELY the agent's own report.
|
|
812
|
-
if (!model && obj.modelUsage && typeof obj.modelUsage === "object" && !Array.isArray(obj.modelUsage)) {
|
|
813
|
-
const entries = Object.entries(obj.modelUsage);
|
|
814
|
-
if (entries.length) {
|
|
815
|
-
const tokensOf = (value) => {
|
|
816
|
-
const record = value && typeof value === "object" ? value : {};
|
|
817
|
-
const input = Number(record.inputTokens ?? record.input_tokens ?? 0);
|
|
818
|
-
return Number.isFinite(input) ? input : 0;
|
|
819
|
-
};
|
|
820
|
-
entries.sort((left, right) => tokensOf(right[1]) - tokensOf(left[1]));
|
|
821
|
-
model = entries[0][0];
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
// Track 1: the executor's detached signature over its usage report, if it signs.
|
|
825
|
-
// SOLELY the agent's own field — CW verifies it later against the trust key.
|
|
826
|
-
const usageSignature = typeof obj.usageSignature === "string"
|
|
827
|
-
? obj.usageSignature
|
|
828
|
-
: typeof obj.usage_signature === "string"
|
|
829
|
-
? obj.usage_signature
|
|
830
|
-
: undefined;
|
|
831
|
-
return { model, usage, usageSignature };
|
|
832
|
-
}
|
|
833
|
-
function agentSubstitutions(request, model) {
|
|
834
|
-
const manifest = request.manifest;
|
|
835
|
-
const workerDir = manifest?.workerDir || request.cwd || "";
|
|
836
|
-
return {
|
|
837
|
-
manifest: manifest?.manifestPath || (workerDir ? node_path_1.default.join(workerDir, "manifest.json") : ""),
|
|
838
|
-
input: manifest?.inputPath || "",
|
|
839
|
-
result: manifest?.resultPath || "",
|
|
840
|
-
workerDir,
|
|
841
|
-
model: model || "",
|
|
842
|
-
prompt: manifest?.prompt || ""
|
|
843
|
-
};
|
|
844
|
-
}
|
|
845
|
-
function substituteAgentArg(arg, subst) {
|
|
846
|
-
return arg.replace(/\{\{(\w+)\}\}/g, (_, key) => (key in subst ? subst[key] : `{{${key}}}`));
|
|
847
|
-
}
|
|
848
|
-
/** Build the recorded process handle for the envelope — secret-stripped + the
|
|
849
|
-
* agent-reported model. Same SHAPE that lands in provenance, never in evidence. */
|
|
850
|
-
function recordedAgentHandle(binary, endpoint, recordedArgs, model, reportedModel, reportedUsage, usageSignature) {
|
|
851
|
-
const ref = binary ? [binary, ...recordedArgs].join(" ") : endpoint || "";
|
|
852
|
-
return {
|
|
853
|
-
kind: "process",
|
|
854
|
-
ref,
|
|
855
|
-
endpoint,
|
|
856
|
-
metadata: {
|
|
857
|
-
mode: binary ? "command" : "endpoint",
|
|
858
|
-
command: binary,
|
|
859
|
-
args: recordedArgs,
|
|
860
|
-
model,
|
|
861
|
-
reportedModel,
|
|
862
|
-
// Telemetry thread-back: the agent's OWN self-reported token usage (parsed
|
|
863
|
-
// from its stdout by parseAgentReport). ATTESTED, never measured by CW —
|
|
864
|
-
// same red-line posture as reportedModel. Lands in provenance, never in the
|
|
865
|
-
// byte-stable evidence triple. Absent when the agent reported no usage.
|
|
866
|
-
...(reportedUsage ? { reportedUsage } : {}),
|
|
867
|
-
// Track 1: the executor's detached signature over its usage report. CW
|
|
868
|
-
// verifies it against the operator trust key at output intake.
|
|
869
|
-
...(usageSignature ? { usageSignature } : {})
|
|
870
|
-
}
|
|
871
|
-
};
|
|
872
|
-
}
|
|
637
|
+
// ---------------------------------------------------------------------------
|
|
638
|
+
// agent — the v0.1.38 delegating driver. Spawns an EXTERNAL agent process per
|
|
639
|
+
// worker (claude -p / codex exec / …) argv-style (shell:false), or POSTs the
|
|
640
|
+
// manifest to a configured HTTP agent endpoint. The agent reads the worker
|
|
641
|
+
// input/manifest and writes the worker's result.md out-of-process; CW captures
|
|
642
|
+
// the agent CHILD's command + exit + stdout digest as the canonical evidence
|
|
643
|
+
// triple (NEVER the result.md — that is the separate recordWorkerOutput layer)
|
|
644
|
+
// and records the kind:process handle + agent-reported model in provenance.
|
|
645
|
+
//
|
|
646
|
+
// THE RED LINE: CW spawns the agent and records its attested output. It NEVER
|
|
647
|
+
// imports a model SDK, holds an API key, or constructs a model API request. Any
|
|
648
|
+
// API key flows from the agent's OWN inherited env; CW never reads or records it.
|
|
649
|
+
// The operator-chosen CW_AGENT_MODEL is interpolated into `{{model}}` as policy
|
|
650
|
+
// and recorded ONLY in secret-stripped args — it is NEVER the attested model id.
|
|
651
|
+
//
|
|
652
|
+
// The pure agent helpers (resolveAgentInvocation, stripSecretArgs, parseAgentReport,
|
|
653
|
+
// agentSubstitutions, substituteAgentArg, recordedAgentHandle, extractEndpointResult,
|
|
654
|
+
// agentHandle) and the concurrent batch live in ./execution-backend/agent.ts; the
|
|
655
|
+
// stateful runners below build the refusal/delegated envelopes and stay here.
|
|
873
656
|
function runAgentProcess(descriptor, policy, request, label, handle, attestation) {
|
|
874
|
-
const resolved = resolveAgentInvocation(request);
|
|
875
|
-
const subst = agentSubstitutions(request, resolved.model);
|
|
657
|
+
const resolved = (0, agent_1.resolveAgentInvocation)(request);
|
|
658
|
+
const subst = (0, agent_1.agentSubstitutions)(request, resolved.model);
|
|
876
659
|
if (resolved.binary) {
|
|
877
|
-
const realArgs = resolved.rawArgs.map((arg) => substituteAgentArg(arg, subst));
|
|
878
|
-
const recordedArgs = stripSecretArgs(realArgs);
|
|
660
|
+
const realArgs = resolved.rawArgs.map((arg) => (0, agent_1.substituteAgentArg)(arg, subst));
|
|
661
|
+
const recordedArgs = (0, agent_1.stripSecretArgs)(realArgs);
|
|
879
662
|
// Spawn the agent argv-style — shell:false, never a shell-interpreted string.
|
|
880
663
|
// The agent inherits the host env so ITS OWN credentials resolve; CW neither
|
|
881
664
|
// reads nor records them. CW enforces only the exact argv it spawns.
|
|
@@ -901,22 +684,22 @@ function runAgentProcess(descriptor, policy, request, label, handle, attestation
|
|
|
901
684
|
stdio: ["ignore", "pipe", streamStderr ? "inherit" : "pipe"]
|
|
902
685
|
});
|
|
903
686
|
outcome = {
|
|
904
|
-
...(child.error ? { spawnError: messageOf(child.error) } : {}),
|
|
687
|
+
...(child.error ? { spawnError: (0, util_1.messageOf)(child.error) } : {}),
|
|
905
688
|
exitCode: typeof child.status === "number" ? child.status : null,
|
|
906
689
|
stdout: String(child.stdout || "")
|
|
907
690
|
};
|
|
908
691
|
}
|
|
909
692
|
if (outcome.spawnError) {
|
|
910
|
-
const handleOut = recordedAgentHandle(resolved.binary, undefined, recordedArgs, resolved.model, "unreported");
|
|
693
|
+
const handleOut = (0, agent_1.recordedAgentHandle)(resolved.binary, undefined, recordedArgs, resolved.model, "unreported");
|
|
911
694
|
return refusedEnvelope(descriptor, policy, label, "delegation-failed", `agent process failed to spawn: ${outcome.spawnError}`, {
|
|
912
695
|
attestation: { ...attestation, handle: handleOut }
|
|
913
696
|
});
|
|
914
697
|
}
|
|
915
698
|
const exitCode = outcome.exitCode;
|
|
916
699
|
const stdout = outcome.stdout;
|
|
917
|
-
const report = parseAgentReport(stdout);
|
|
700
|
+
const report = (0, agent_1.parseAgentReport)(stdout);
|
|
918
701
|
const reportedModel = report.model && report.model.trim() ? report.model.trim() : "unreported";
|
|
919
|
-
const handleOut = recordedAgentHandle(resolved.binary, undefined, recordedArgs, resolved.model, reportedModel, report.usage, report.usageSignature);
|
|
702
|
+
const handleOut = (0, agent_1.recordedAgentHandle)(resolved.binary, undefined, recordedArgs, resolved.model, reportedModel, report.usage, report.usageSignature);
|
|
920
703
|
if (exitCode === null) {
|
|
921
704
|
// No exit code (timeout/killed) ⇒ fail closed, never a fabricated completion.
|
|
922
705
|
return refusedEnvelope(descriptor, policy, label, "delegation-failed", `agent process returned no exit code (timed out or killed)`, {
|
|
@@ -934,94 +717,6 @@ function runAgentProcess(descriptor, policy, request, label, handle, attestation
|
|
|
934
717
|
attestation
|
|
935
718
|
});
|
|
936
719
|
}
|
|
937
|
-
/** Resolve a request to a spawn-style batch job, or undefined when the agent is
|
|
938
|
-
* endpoint-configured/unconfigured (those settle through the serial path). */
|
|
939
|
-
function prepareAgentSpawn(request) {
|
|
940
|
-
const resolved = resolveAgentInvocation(request);
|
|
941
|
-
if (!resolved.binary)
|
|
942
|
-
return undefined;
|
|
943
|
-
const subst = agentSubstitutions(request, resolved.model);
|
|
944
|
-
return {
|
|
945
|
-
binary: resolved.binary,
|
|
946
|
-
args: resolved.rawArgs.map((arg) => substituteAgentArg(arg, subst)),
|
|
947
|
-
cwd: request.cwd,
|
|
948
|
-
timeoutMs: resolved.timeoutMs || 600000
|
|
949
|
-
};
|
|
950
|
-
}
|
|
951
|
-
// Reads jobs JSON on stdin, spawns ALL concurrently (shell:false, inherited env —
|
|
952
|
-
// the agent's own credentials resolve; CW never reads them), per-job SIGTERM at
|
|
953
|
-
// timeoutMs + SIGKILL at +5s, caps each captured stdout at 32MB, and prints the
|
|
954
|
-
// outcome array when every job has settled. stderr is drained (a full pipe must
|
|
955
|
-
// never wedge a child). A kill yields exitCode null — the no-exit-code refusal.
|
|
956
|
-
const BATCH_DELEGATE_CHILD = `
|
|
957
|
-
const { spawn } = require("node:child_process");
|
|
958
|
-
let raw = "";
|
|
959
|
-
process.stdin.setEncoding("utf8");
|
|
960
|
-
process.stdin.on("data", (d) => (raw += d));
|
|
961
|
-
process.stdin.on("end", () => {
|
|
962
|
-
const jobs = JSON.parse(raw);
|
|
963
|
-
if (!jobs.length) { process.stdout.write("[]"); return; }
|
|
964
|
-
const out = new Array(jobs.length);
|
|
965
|
-
let pending = jobs.length;
|
|
966
|
-
const CAP = 32 * 1024 * 1024;
|
|
967
|
-
jobs.forEach((job, i) => {
|
|
968
|
-
let stdout = "";
|
|
969
|
-
let settled = false;
|
|
970
|
-
const settle = (o) => {
|
|
971
|
-
if (settled) return;
|
|
972
|
-
settled = true;
|
|
973
|
-
out[i] = o;
|
|
974
|
-
if (--pending === 0) process.stdout.write(JSON.stringify(out));
|
|
975
|
-
};
|
|
976
|
-
let child;
|
|
977
|
-
try {
|
|
978
|
-
child = spawn(job.binary, job.args, { cwd: job.cwd, env: process.env, shell: false });
|
|
979
|
-
} catch (error) {
|
|
980
|
-
settle({ spawnError: String((error && error.message) || error), exitCode: null, stdout: "" });
|
|
981
|
-
return;
|
|
982
|
-
}
|
|
983
|
-
const term = setTimeout(() => { try { child.kill("SIGTERM"); } catch {} }, job.timeoutMs);
|
|
984
|
-
const kill = setTimeout(() => { try { child.kill("SIGKILL"); } catch {} }, job.timeoutMs + 5000);
|
|
985
|
-
child.stdout.on("data", (d) => { if (stdout.length < CAP) stdout += d; });
|
|
986
|
-
child.stderr.on("data", () => {});
|
|
987
|
-
child.on("error", (error) => {
|
|
988
|
-
clearTimeout(term); clearTimeout(kill);
|
|
989
|
-
settle({ spawnError: String((error && error.message) || error), exitCode: null, stdout });
|
|
990
|
-
});
|
|
991
|
-
child.on("close", (code) => {
|
|
992
|
-
clearTimeout(term); clearTimeout(kill);
|
|
993
|
-
settle({ exitCode: typeof code === "number" ? code : null, stdout });
|
|
994
|
-
});
|
|
995
|
-
});
|
|
996
|
-
});
|
|
997
|
-
`;
|
|
998
|
-
/** Run a batch of agent spawns concurrently; outcomes index-align with jobs. The
|
|
999
|
-
* parent backstop timeout (max job timeout + 30s) means even a wedged delegate
|
|
1000
|
-
* child cannot deadlock the drive: on any batch-level failure EVERY job settles
|
|
1001
|
-
* as a fail-closed spawn refusal — never a fabricated completion, never a hang. */
|
|
1002
|
-
function runAgentBatchOutcomes(jobs) {
|
|
1003
|
-
if (!jobs.length)
|
|
1004
|
-
return [];
|
|
1005
|
-
const maxTimeout = Math.max(...jobs.map((job) => job.timeoutMs));
|
|
1006
|
-
const child = (0, node_child_process_1.spawnSync)(process.execPath, ["-e", BATCH_DELEGATE_CHILD], {
|
|
1007
|
-
input: JSON.stringify(jobs),
|
|
1008
|
-
encoding: "utf8",
|
|
1009
|
-
maxBuffer: 33 * 1024 * 1024 * jobs.length,
|
|
1010
|
-
timeout: maxTimeout + 30000
|
|
1011
|
-
});
|
|
1012
|
-
if (!child.error && typeof child.status === "number" && child.status === 0) {
|
|
1013
|
-
try {
|
|
1014
|
-
const parsed = JSON.parse(String(child.stdout || ""));
|
|
1015
|
-
if (Array.isArray(parsed) && parsed.length === jobs.length)
|
|
1016
|
-
return parsed;
|
|
1017
|
-
}
|
|
1018
|
-
catch {
|
|
1019
|
-
// fall through to the fail-closed mapping below
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
const reason = child.error ? messageOf(child.error) : `batch delegate exited ${child.status === null ? "without an exit code (timed out or killed)" : `with ${child.status}`}`;
|
|
1023
|
-
return jobs.map(() => ({ spawnError: `batch delegate failed: ${reason}`, exitCode: null, stdout: "" }));
|
|
1024
|
-
}
|
|
1025
720
|
/** Agent HTTP endpoint variant — POSTs the worker manifest/prompt to a configured
|
|
1026
721
|
* agent endpoint via the shared Node delegate child; if the endpoint returns a
|
|
1027
722
|
* `result` body, CW writes it to the worker's result.md (the endpoint agent is the
|
|
@@ -1037,16 +732,16 @@ function runAgentEndpoint(descriptor, policy, request, label, resolved, attestat
|
|
|
1037
732
|
resultPath: manifest?.resultPath,
|
|
1038
733
|
sandboxProfileId: policy.id
|
|
1039
734
|
});
|
|
1040
|
-
const child = (0, node_child_process_1.spawnSync)(process.execPath, [
|
|
735
|
+
const child = (0, node_child_process_1.spawnSync)(process.execPath, [HTTP_DELEGATE_CHILD_SCRIPT], {
|
|
1041
736
|
input: job,
|
|
1042
737
|
env: { ...process.env, CW_DELEGATE_ENDPOINT: endpoint },
|
|
1043
738
|
encoding: "utf8",
|
|
1044
739
|
timeout: resolved.timeoutMs || 600000,
|
|
1045
740
|
maxBuffer: 32 * 1024 * 1024
|
|
1046
741
|
});
|
|
1047
|
-
const baseHandle = recordedAgentHandle(undefined, endpoint, [], resolved.model, "unreported");
|
|
742
|
+
const baseHandle = (0, agent_1.recordedAgentHandle)(undefined, endpoint, [], resolved.model, "unreported");
|
|
1048
743
|
if (child.error) {
|
|
1049
|
-
return refusedEnvelope(descriptor, policy, label, "delegation-failed", `agent endpoint delegation failed: ${messageOf(child.error)}`, {
|
|
744
|
+
return refusedEnvelope(descriptor, policy, label, "delegation-failed", `agent endpoint delegation failed: ${(0, util_1.messageOf)(child.error)}`, {
|
|
1050
745
|
attestation: { ...attestation, handle: baseHandle }
|
|
1051
746
|
});
|
|
1052
747
|
}
|
|
@@ -1067,9 +762,9 @@ function runAgentEndpoint(descriptor, policy, request, label, resolved, attestat
|
|
|
1067
762
|
const stdout = String(parsed.stdout || "");
|
|
1068
763
|
// If the endpoint agent returned the result body, CW (as transport) writes it to
|
|
1069
764
|
// the worker's result.md for the separate recordWorkerOutput layer to accept.
|
|
1070
|
-
const report = parseAgentReport(stdout);
|
|
765
|
+
const report = (0, agent_1.parseAgentReport)(stdout);
|
|
1071
766
|
if (manifest?.resultPath && report.usage === undefined) {
|
|
1072
|
-
const body = extractEndpointResult(stdout);
|
|
767
|
+
const body = (0, agent_1.extractEndpointResult)(stdout);
|
|
1073
768
|
if (body && !node_fs_1.default.existsSync(manifest.resultPath)) {
|
|
1074
769
|
try {
|
|
1075
770
|
node_fs_1.default.writeFileSync(manifest.resultPath, body, "utf8");
|
|
@@ -1080,28 +775,9 @@ function runAgentEndpoint(descriptor, policy, request, label, resolved, attestat
|
|
|
1080
775
|
}
|
|
1081
776
|
}
|
|
1082
777
|
const reportedModel = report.model && report.model.trim() ? report.model.trim() : "unreported";
|
|
1083
|
-
const handleOut = recordedAgentHandle(undefined, endpoint, [], resolved.model, reportedModel, report.usage, report.usageSignature);
|
|
778
|
+
const handleOut = (0, agent_1.recordedAgentHandle)(undefined, endpoint, [], resolved.model, reportedModel, report.usage, report.usageSignature);
|
|
1084
779
|
return delegatedEnvelope(descriptor, label, handleOut, { ...attestation, handle: handleOut }, "agent-endpoint", [endpoint], parsed.exitCode, stdout);
|
|
1085
780
|
}
|
|
1086
|
-
function extractEndpointResult(stdout) {
|
|
1087
|
-
const text = String(stdout || "").trim();
|
|
1088
|
-
if (!text)
|
|
1089
|
-
return undefined;
|
|
1090
|
-
try {
|
|
1091
|
-
const parsed = JSON.parse(text);
|
|
1092
|
-
if (parsed && typeof parsed === "object") {
|
|
1093
|
-
if (typeof parsed.result === "string")
|
|
1094
|
-
return parsed.result;
|
|
1095
|
-
if (typeof parsed.resultMarkdown === "string")
|
|
1096
|
-
return parsed.resultMarkdown;
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
catch {
|
|
1100
|
-
/* not JSON — treat the raw text as the result body */
|
|
1101
|
-
return text;
|
|
1102
|
-
}
|
|
1103
|
-
return undefined;
|
|
1104
|
-
}
|
|
1105
781
|
function delegationHandle(descriptor, request) {
|
|
1106
782
|
return getBackendDriver(descriptor.id)?.buildHandle?.(request);
|
|
1107
783
|
}
|
|
@@ -1132,28 +808,6 @@ function ciHandle(request) {
|
|
|
1132
808
|
const ref = endpoint && jobId ? `${endpoint}#${jobId}` : jobId || endpoint || "";
|
|
1133
809
|
return { kind: "ci", ref, endpoint, jobId };
|
|
1134
810
|
}
|
|
1135
|
-
function agentHandle(request) {
|
|
1136
|
-
// The agent invocation is POLICY-as-DATA, resolved flags(delegation) > env. The
|
|
1137
|
-
// handle records ONLY secret-stripped provenance; the raw template is re-resolved
|
|
1138
|
-
// inside runAgentProcess for substitution + spawning so no secret ever lands in
|
|
1139
|
-
// a recorded handle/evidence entry.
|
|
1140
|
-
const resolved = resolveAgentInvocation(request);
|
|
1141
|
-
if (!resolved.binary && !resolved.endpoint)
|
|
1142
|
-
return undefined;
|
|
1143
|
-
const strippedArgs = stripSecretArgs(resolved.rawArgs);
|
|
1144
|
-
const ref = resolved.binary ? [resolved.binary, ...strippedArgs].join(" ") : resolved.endpoint || "";
|
|
1145
|
-
return {
|
|
1146
|
-
kind: "process",
|
|
1147
|
-
ref,
|
|
1148
|
-
endpoint: resolved.endpoint,
|
|
1149
|
-
metadata: {
|
|
1150
|
-
mode: resolved.binary ? "command" : "endpoint",
|
|
1151
|
-
command: resolved.binary,
|
|
1152
|
-
args: strippedArgs,
|
|
1153
|
-
model: resolved.model
|
|
1154
|
-
}
|
|
1155
|
-
};
|
|
1156
|
-
}
|
|
1157
811
|
function refusedEnvelope(descriptor, policy, label, code, reason, options = {}) {
|
|
1158
812
|
const attestation = options.attestation
|
|
1159
813
|
? { ...options.attestation, status: "refused", notes: [...(options.attestation.notes || []), `refused: ${code}`] }
|
|
@@ -1242,33 +896,6 @@ function commandDenied(policy, command) {
|
|
|
1242
896
|
function runtimeNote(descriptor) {
|
|
1243
897
|
return getBackendDriver(descriptor.id)?.runtimeNote?.() ?? "node";
|
|
1244
898
|
}
|
|
1245
|
-
function hasExecutable(name) {
|
|
1246
|
-
const dirs = (process.env.PATH || "").split(node_path_1.default.delimiter).filter(Boolean);
|
|
1247
|
-
for (const dir of dirs) {
|
|
1248
|
-
const candidate = node_path_1.default.join(dir, name);
|
|
1249
|
-
try {
|
|
1250
|
-
if (node_fs_1.default.existsSync(candidate) && node_fs_1.default.statSync(candidate).isFile())
|
|
1251
|
-
return true;
|
|
1252
|
-
}
|
|
1253
|
-
catch {
|
|
1254
|
-
// ignore unreadable PATH entries
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
return false;
|
|
1258
|
-
}
|
|
1259
|
-
function sha256(value) {
|
|
1260
|
-
return `sha256:${node_crypto_1.default.createHash("sha256").update(value, "utf8").digest("hex")}`;
|
|
1261
|
-
}
|
|
1262
|
-
function firstString(...values) {
|
|
1263
|
-
for (const value of values) {
|
|
1264
|
-
if (typeof value === "string" && value.trim())
|
|
1265
|
-
return value.trim();
|
|
1266
|
-
}
|
|
1267
|
-
return undefined;
|
|
1268
|
-
}
|
|
1269
|
-
function messageOf(error) {
|
|
1270
|
-
return error instanceof Error ? error.message : String(error);
|
|
1271
|
-
}
|
|
1272
899
|
// ---- Probe cache (v0.1.60) — mechanism, not policy -----------------------
|
|
1273
900
|
const _probeCache = new Map();
|
|
1274
901
|
const PROBE_CACHE_TTL_MS = 60_000; // 60s
|