intentdna 1.9.0 → 1.9.3
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/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +227 -87
- package/dist/cli/commands/run-lifecycle.d.ts +61 -11
- package/dist/cli/commands/run-lifecycle.js +184 -50
- package/dist/cli/commands/run-observer.d.ts +15 -0
- package/dist/cli/commands/run-observer.js +210 -0
- package/dist/cli/commands/run.d.ts +16 -20
- package/dist/cli/commands/run.js +461 -659
- package/dist/cli/commands/sync.js +16 -12
- package/dist/cli/index.js +14 -5
- package/dist/compiler/compile.d.ts +1 -0
- package/dist/compiler/compile.js +1 -1
- package/dist/compiler/controller.d.ts +16 -0
- package/dist/compiler/controller.js +693 -0
- package/dist/compiler/input-resolver.d.ts +2 -0
- package/dist/compiler/input-resolver.js +22 -6
- package/dist/hooks/cli.d.ts +45 -0
- package/dist/hooks/cli.js +263 -31
- package/dist/hooks/host-event.d.ts +57 -0
- package/dist/hooks/host-event.js +187 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/protocol.d.ts +7 -0
- package/dist/hooks/schema.d.ts +1 -0
- package/dist/hooks/schema.js +89 -1
- package/dist/hooks/state.d.ts +23 -1
- package/dist/hooks/state.js +9 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/index.js +2 -0
- package/dist/mcp/tools-run.d.ts +4 -0
- package/dist/mcp/tools-run.js +419 -0
- package/dist/mcp/tools-state.d.ts +1 -1
- package/dist/mcp/tools-state.js +13 -241
- package/dist/runtime/artifact-store.d.ts +146 -0
- package/dist/runtime/artifact-store.js +1436 -0
- package/dist/runtime/canonical-attempt-outcome.d.ts +12 -0
- package/dist/runtime/canonical-attempt-outcome.js +195 -0
- package/dist/runtime/canonical-json.d.ts +10 -0
- package/dist/runtime/canonical-json.js +105 -0
- package/dist/runtime/canonical-run-application.d.ts +93 -0
- package/dist/runtime/canonical-run-application.js +229 -0
- package/dist/runtime/canonical-run-service.d.ts +454 -0
- package/dist/runtime/canonical-run-service.js +4760 -0
- package/dist/runtime/canonical-runtime-composition.d.ts +22 -0
- package/dist/runtime/canonical-runtime-composition.js +34 -0
- package/dist/runtime/canonical-runtime-projection.d.ts +3 -0
- package/dist/runtime/canonical-runtime-projection.js +4 -0
- package/dist/runtime/canonical-target-compiler.d.ts +46 -0
- package/dist/runtime/canonical-target-compiler.js +514 -0
- package/dist/runtime/claude-agent-identity.d.ts +2 -0
- package/dist/runtime/claude-agent-identity.js +21 -0
- package/dist/runtime/executable-run-plan.d.ts +328 -0
- package/dist/runtime/executable-run-plan.js +1485 -0
- package/dist/runtime/execution-authority.d.ts +83 -0
- package/dist/runtime/execution-authority.js +98 -0
- package/dist/runtime/handoff-resolver.js +4 -1
- package/dist/runtime/harness-pull-adapter.d.ts +146 -0
- package/dist/runtime/harness-pull-adapter.js +315 -0
- package/dist/runtime/index.d.ts +39 -14
- package/dist/runtime/index.js +29 -7
- package/dist/runtime/local-execution-authority.d.ts +194 -0
- package/dist/runtime/local-execution-authority.js +2897 -0
- package/dist/runtime/local-execution-reconciliation.d.ts +20 -0
- package/dist/runtime/local-execution-reconciliation.js +107 -0
- package/dist/runtime/local-execution-supervisor-script.d.ts +7 -0
- package/dist/runtime/local-execution-supervisor-script.js +793 -0
- package/dist/runtime/local-provider-sandbox.d.ts +42 -0
- package/dist/runtime/local-provider-sandbox.js +154 -0
- package/dist/runtime/local-verifier-execution.d.ts +24 -0
- package/dist/runtime/local-verifier-execution.js +290 -0
- package/dist/runtime/local-windows-execution-supervisor-script.d.ts +6 -0
- package/dist/runtime/local-windows-execution-supervisor-script.js +788 -0
- package/dist/runtime/plan-store.d.ts +30 -0
- package/dist/runtime/plan-store.js +231 -0
- package/dist/runtime/process-tree.d.ts +9 -0
- package/dist/runtime/process-tree.js +97 -24
- package/dist/runtime/providers/codex.js +2 -2
- package/dist/runtime/push-driver.d.ts +184 -0
- package/dist/runtime/push-driver.js +1002 -0
- package/dist/runtime/result-store.d.ts +34 -3
- package/dist/runtime/result-store.js +1073 -2
- package/dist/runtime/run-binding.d.ts +68 -0
- package/dist/runtime/run-binding.js +278 -0
- package/dist/runtime/run-contracts.d.ts +575 -0
- package/dist/runtime/run-contracts.js +58 -7
- package/dist/runtime/run-controller.d.ts +1 -6
- package/dist/runtime/run-controller.js +0 -41
- package/dist/runtime/run-store.d.ts +85 -7
- package/dist/runtime/run-store.js +2528 -186
- package/dist/runtime/skill-adapter.d.ts +18 -7
- package/dist/runtime/skill-adapter.js +100 -742
- package/dist/runtime/structured-output-validator.d.ts +44 -0
- package/dist/runtime/structured-output-validator.js +171 -0
- package/dist/runtime/verifier-command-binding.d.ts +22 -0
- package/dist/runtime/verifier-command-binding.js +162 -0
- package/dist/runtime/verifier.d.ts +46 -6
- package/dist/runtime/verifier.js +355 -53
- package/dist/runtime/windows-job-keeper.d.ts +47 -0
- package/dist/runtime/windows-job-keeper.js +231 -0
- package/dist/runtime/worker-executor.d.ts +17 -1
- package/dist/runtime/worker-executor.js +26 -5
- package/dist/runtime/workflow-plan-adapter.d.ts +19 -0
- package/dist/runtime/workflow-plan-adapter.js +502 -6
- package/dist/runtime/workflow-runtime-manifest.d.ts +1 -0
- package/dist/runtime/workflow-runtime-manifest.js +5 -0
- package/dist/runtime/workspace-isolation.d.ts +29 -2
- package/dist/runtime/workspace-isolation.js +488 -11
- package/dist/runtime/workspace-observation.d.ts +44 -0
- package/dist/runtime/workspace-observation.js +216 -0
- package/dist/schema/controller-registry.d.ts +0 -7
- package/dist/schema/controller-registry.js +0 -8
- package/dist/schema/types.d.ts +84 -10
- package/dist/schema/types.js +2 -0
- package/dist/schema/validate.js +151 -1
- package/dist/schema/validators/controllers.d.ts +1 -1
- package/dist/schema/validators/controllers.js +126 -41
- package/dist/schema/workflow-authoring-contract.js +2 -0
- package/dist/schema/yaml-parser.js +1 -1
- package/dist/templates/flutter-rewrite-new.dna.yaml +1 -0
- package/dist/templates/flutter-rewrite.dna.yaml +87 -60
- package/dist/templates/safe-refactoring.dna.yaml +1 -0
- package/native/windows-job-keeper/README.md +80 -0
- package/native/windows-job-keeper/bin/aarch64/intentdna-windows-job-keeper.exe +0 -0
- package/native/windows-job-keeper/bin/x86_64/intentdna-windows-job-keeper.exe +0 -0
- package/native/windows-job-keeper/keeper.c +979 -0
- package/native/windows-job-keeper/manifest.json +33 -0
- package/package.json +6 -2
- package/scripts/build-windows-job-keeper.mjs +172 -0
- package/scripts/verify-windows-job-keeper.mjs +184 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ExecutionProvider } from "./execution-provider.js";
|
|
2
|
+
export declare const MACOS_PROVIDER_DENY_ALL_NETWORK_PROFILE: string;
|
|
3
|
+
interface ExecutableIdentity {
|
|
4
|
+
readonly real_path: string;
|
|
5
|
+
readonly device: number;
|
|
6
|
+
readonly inode: number;
|
|
7
|
+
readonly mode: number;
|
|
8
|
+
readonly size: number;
|
|
9
|
+
readonly modified_at_ms: number;
|
|
10
|
+
}
|
|
11
|
+
type SandboxAvailability = {
|
|
12
|
+
readonly available: true;
|
|
13
|
+
readonly executable_path: string;
|
|
14
|
+
readonly executable_identity: ExecutableIdentity;
|
|
15
|
+
} | {
|
|
16
|
+
readonly available: false;
|
|
17
|
+
readonly reason: "unsupported_platform" | "mechanism_unavailable" | "probe_failed";
|
|
18
|
+
};
|
|
19
|
+
export interface LocalProviderNetworkSandboxOptions {
|
|
20
|
+
readonly platform?: NodeJS.Platform;
|
|
21
|
+
readonly sandbox_exec_path?: string;
|
|
22
|
+
readonly probe_timeout_ms?: number;
|
|
23
|
+
}
|
|
24
|
+
export declare class LocalProviderNetworkSandboxError extends Error {
|
|
25
|
+
readonly code: "unsupported";
|
|
26
|
+
constructor(message: string, options?: ErrorOptions);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Wraps a provider launch only when the host proves deny-all network support.
|
|
30
|
+
* Capability discovery is separate so attach can fail before a worker starts.
|
|
31
|
+
*/
|
|
32
|
+
export declare class LocalProviderNetworkSandbox {
|
|
33
|
+
readonly network_policy_enforcement: boolean;
|
|
34
|
+
readonly unavailable_reason: Exclude<SandboxAvailability, {
|
|
35
|
+
available: true;
|
|
36
|
+
}>["reason"] | null;
|
|
37
|
+
private readonly availability;
|
|
38
|
+
private readonly probeTimeoutMs;
|
|
39
|
+
constructor(options?: LocalProviderNetworkSandboxOptions);
|
|
40
|
+
wrap(provider: ExecutionProvider): ExecutionProvider;
|
|
41
|
+
}
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { accessSync, constants, realpathSync, statSync } from "node:fs";
|
|
3
|
+
const DEFAULT_SANDBOX_EXEC_PATH = "/usr/bin/sandbox-exec";
|
|
4
|
+
const DEFAULT_PROBE_TIMEOUT_MS = 5_000;
|
|
5
|
+
const PROBE_PATH = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
|
|
6
|
+
export const MACOS_PROVIDER_DENY_ALL_NETWORK_PROFILE = [
|
|
7
|
+
"(version 1)",
|
|
8
|
+
"(allow default)",
|
|
9
|
+
"(deny network*)",
|
|
10
|
+
"",
|
|
11
|
+
].join("\n");
|
|
12
|
+
const NETWORK_DENIAL_PROBE = [
|
|
13
|
+
"const net=require('node:net');",
|
|
14
|
+
"let socket;",
|
|
15
|
+
"try{socket=net.connect({host:'127.0.0.1',port:9});}",
|
|
16
|
+
"catch(error){process.exit(error&&(error.code==='EPERM'||error.code==='EACCES')?0:72);}",
|
|
17
|
+
"socket.once('connect',()=>process.exit(73));",
|
|
18
|
+
"socket.once('error',error=>process.exit(error&&(error.code==='EPERM'||error.code==='EACCES')?0:74));",
|
|
19
|
+
"setTimeout(()=>process.exit(75),2000);",
|
|
20
|
+
].join("");
|
|
21
|
+
export class LocalProviderNetworkSandboxError extends Error {
|
|
22
|
+
code = "unsupported";
|
|
23
|
+
constructor(message, options) {
|
|
24
|
+
super(message, options);
|
|
25
|
+
this.name = "LocalProviderNetworkSandboxError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function executableIdentity(path) {
|
|
29
|
+
accessSync(path, constants.X_OK);
|
|
30
|
+
const realPath = realpathSync(path);
|
|
31
|
+
const stats = statSync(realPath);
|
|
32
|
+
if (!stats.isFile())
|
|
33
|
+
throw new Error("sandbox executable is not a regular file");
|
|
34
|
+
return {
|
|
35
|
+
real_path: realPath,
|
|
36
|
+
device: stats.dev,
|
|
37
|
+
inode: stats.ino,
|
|
38
|
+
mode: stats.mode,
|
|
39
|
+
size: stats.size,
|
|
40
|
+
modified_at_ms: stats.mtimeMs,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function sameExecutableIdentity(left, right) {
|
|
44
|
+
return left.real_path === right.real_path
|
|
45
|
+
&& left.device === right.device
|
|
46
|
+
&& left.inode === right.inode
|
|
47
|
+
&& left.mode === right.mode
|
|
48
|
+
&& left.size === right.size
|
|
49
|
+
&& left.modified_at_ms === right.modified_at_ms;
|
|
50
|
+
}
|
|
51
|
+
function probeNetworkDenial(sandboxExecPath, timeoutMs) {
|
|
52
|
+
try {
|
|
53
|
+
const result = spawnSync(sandboxExecPath, [
|
|
54
|
+
"-p",
|
|
55
|
+
MACOS_PROVIDER_DENY_ALL_NETWORK_PROFILE,
|
|
56
|
+
process.execPath,
|
|
57
|
+
"-e",
|
|
58
|
+
NETWORK_DENIAL_PROBE,
|
|
59
|
+
], {
|
|
60
|
+
env: { PATH: PROBE_PATH },
|
|
61
|
+
stdio: "ignore",
|
|
62
|
+
timeout: timeoutMs,
|
|
63
|
+
windowsHide: true,
|
|
64
|
+
});
|
|
65
|
+
return result.error === undefined
|
|
66
|
+
&& result.signal === null
|
|
67
|
+
&& result.status === 0;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function detectAvailability(platform, sandboxExecPath, timeoutMs) {
|
|
74
|
+
if (platform !== "darwin") {
|
|
75
|
+
return { available: false, reason: "unsupported_platform" };
|
|
76
|
+
}
|
|
77
|
+
let identity;
|
|
78
|
+
try {
|
|
79
|
+
identity = executableIdentity(sandboxExecPath);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return { available: false, reason: "mechanism_unavailable" };
|
|
83
|
+
}
|
|
84
|
+
if (identity.real_path !== DEFAULT_SANDBOX_EXEC_PATH) {
|
|
85
|
+
return { available: false, reason: "mechanism_unavailable" };
|
|
86
|
+
}
|
|
87
|
+
if (!probeNetworkDenial(identity.real_path, timeoutMs)) {
|
|
88
|
+
return { available: false, reason: "probe_failed" };
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
available: true,
|
|
92
|
+
executable_path: identity.real_path,
|
|
93
|
+
executable_identity: identity,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function sandboxedLaunch(launch, executablePath) {
|
|
97
|
+
return {
|
|
98
|
+
command: executablePath,
|
|
99
|
+
args: [
|
|
100
|
+
"-p",
|
|
101
|
+
MACOS_PROVIDER_DENY_ALL_NETWORK_PROFILE,
|
|
102
|
+
launch.command,
|
|
103
|
+
...launch.args,
|
|
104
|
+
],
|
|
105
|
+
stdin: launch.stdin,
|
|
106
|
+
cwd: launch.cwd,
|
|
107
|
+
...(launch.env ? { env: launch.env } : {}),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Wraps a provider launch only when the host proves deny-all network support.
|
|
112
|
+
* Capability discovery is separate so attach can fail before a worker starts.
|
|
113
|
+
*/
|
|
114
|
+
export class LocalProviderNetworkSandbox {
|
|
115
|
+
network_policy_enforcement;
|
|
116
|
+
unavailable_reason;
|
|
117
|
+
availability;
|
|
118
|
+
probeTimeoutMs;
|
|
119
|
+
constructor(options = {}) {
|
|
120
|
+
this.probeTimeoutMs = options.probe_timeout_ms ?? DEFAULT_PROBE_TIMEOUT_MS;
|
|
121
|
+
if (!Number.isSafeInteger(this.probeTimeoutMs) || this.probeTimeoutMs <= 0) {
|
|
122
|
+
throw new TypeError("probe_timeout_ms must be a positive safe integer");
|
|
123
|
+
}
|
|
124
|
+
this.availability = detectAvailability(options.platform ?? process.platform, options.sandbox_exec_path ?? DEFAULT_SANDBOX_EXEC_PATH, this.probeTimeoutMs);
|
|
125
|
+
this.network_policy_enforcement = this.availability.available;
|
|
126
|
+
this.unavailable_reason = this.availability.available
|
|
127
|
+
? null
|
|
128
|
+
: this.availability.reason;
|
|
129
|
+
}
|
|
130
|
+
wrap(provider) {
|
|
131
|
+
const availability = this.availability;
|
|
132
|
+
if (!availability.available) {
|
|
133
|
+
throw new LocalProviderNetworkSandboxError(`local provider deny_all network enforcement is unavailable: ${availability.reason}`);
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
name: provider.name,
|
|
137
|
+
createLaunch: (packet) => {
|
|
138
|
+
let currentIdentity;
|
|
139
|
+
try {
|
|
140
|
+
currentIdentity = executableIdentity(availability.executable_path);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
throw new LocalProviderNetworkSandboxError("pinned local provider sandbox mechanism is unavailable", { cause: error });
|
|
144
|
+
}
|
|
145
|
+
if (!sameExecutableIdentity(currentIdentity, availability.executable_identity)
|
|
146
|
+
|| !probeNetworkDenial(availability.executable_path, this.probeTimeoutMs)) {
|
|
147
|
+
throw new LocalProviderNetworkSandboxError("pinned local provider sandbox mechanism cannot prove deny_all");
|
|
148
|
+
}
|
|
149
|
+
return sandboxedLaunch(provider.createLaunch(packet), availability.executable_path);
|
|
150
|
+
},
|
|
151
|
+
parseResult: (context) => provider.parseResult(context),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type CanonicalVerifierExecutionAuthorityDescriptor, type CanonicalVerifierExecutionAuthorityReceipt, type CanonicalVerifierExecutionPort, type CanonicalVerifierExecutionRequest } from "./canonical-run-service.js";
|
|
2
|
+
import type { CanonicalResultDigest } from "./run-contracts.js";
|
|
3
|
+
import { type VerifierCommandExecutionResult } from "./verifier.js";
|
|
4
|
+
export interface ProductionLocalVerifierExecutionPortOptions {
|
|
5
|
+
readonly root_directory: string;
|
|
6
|
+
readonly authority_instance_id?: string;
|
|
7
|
+
readonly platform?: NodeJS.Platform;
|
|
8
|
+
readonly architecture?: NodeJS.Architecture;
|
|
9
|
+
readonly helper_path?: string;
|
|
10
|
+
readonly expected_digest?: CanonicalResultDigest;
|
|
11
|
+
readonly resolve_shell_executable?: (platform: NodeJS.Platform) => string;
|
|
12
|
+
}
|
|
13
|
+
export declare class ProductionLocalVerifierExecutionPort implements CanonicalVerifierExecutionPort {
|
|
14
|
+
readonly authority_descriptor: CanonicalVerifierExecutionAuthorityDescriptor;
|
|
15
|
+
private readonly authorityDescriptorDigest;
|
|
16
|
+
private readonly authority;
|
|
17
|
+
private readonly shellExecutable;
|
|
18
|
+
private readonly platform;
|
|
19
|
+
constructor(options: ProductionLocalVerifierExecutionPortOptions);
|
|
20
|
+
execute(request: CanonicalVerifierExecutionRequest): Promise<{
|
|
21
|
+
readonly execution: VerifierCommandExecutionResult;
|
|
22
|
+
readonly authority: CanonicalVerifierExecutionAuthorityReceipt;
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
4
|
+
import { isAbsolute, win32 as win32Path } from "node:path";
|
|
5
|
+
import { canonicalizeJson } from "./canonical-json.js";
|
|
6
|
+
import { CanonicalRunServiceError, } from "./canonical-run-service.js";
|
|
7
|
+
import { LocalAttemptExecutionAuthority, } from "./local-execution-authority.js";
|
|
8
|
+
import { summarizeCommandEvidence } from "./verifier.js";
|
|
9
|
+
const VERIFIER_PATH = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
|
|
10
|
+
const SHA256_DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/u;
|
|
11
|
+
function digest(value) {
|
|
12
|
+
return `sha256:${createHash("sha256").update(canonicalizeJson(value), "utf8").digest("hex")}`;
|
|
13
|
+
}
|
|
14
|
+
function textDigest(value) {
|
|
15
|
+
return `sha256:${createHash("sha256").update(value, "utf8").digest("hex")}`;
|
|
16
|
+
}
|
|
17
|
+
function assertExactKeys(value, required, label) {
|
|
18
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
19
|
+
throw new CanonicalRunServiceError("invalid_request", `${label} must be an object`);
|
|
20
|
+
}
|
|
21
|
+
const expected = [...required].sort();
|
|
22
|
+
const actual = Object.keys(value).sort();
|
|
23
|
+
if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) {
|
|
24
|
+
throw new CanonicalRunServiceError("invalid_request", `${label} has invalid fields`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function assertDigest(value, label) {
|
|
28
|
+
if (typeof value !== "string" || !SHA256_DIGEST_PATTERN.test(value)) {
|
|
29
|
+
throw new CanonicalRunServiceError("invalid_request", `${label} must be a sha256 digest`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function assertCanonicalTimestamp(value, label) {
|
|
33
|
+
if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(value)
|
|
34
|
+
|| !Number.isFinite(Date.parse(value)) || new Date(Date.parse(value)).toISOString() !== value) {
|
|
35
|
+
throw new CanonicalRunServiceError("invalid_request", `${label} must be a canonical UTC timestamp`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function environmentBindingDigest(environment) {
|
|
39
|
+
return digest(Object.fromEntries(Object.entries(environment)
|
|
40
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
41
|
+
.map(([name, value]) => [name, textDigest(value)])));
|
|
42
|
+
}
|
|
43
|
+
function canonicalEqual(left, right) {
|
|
44
|
+
try {
|
|
45
|
+
return canonicalizeJson(left) === canonicalizeJson(right);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function assertExecutionRequest(request, authorityDescriptorDigest, platform) {
|
|
52
|
+
assertExactKeys(request, ["request_digest", "descriptor", "plan_id", "verifier_id", "verifier_definition_digest", "resolved_command", "resolved_command_digest", "cwd", "cwd_identity", "environment", "environment_binding_digest", "timeout_ms", "requested_at", "network_policy", "inherit_parent_environment", "load_startup_files"], "verifier execution request");
|
|
53
|
+
assertExactKeys(request.descriptor, ["schema_version", "plan_id", "verifier_id", "verifier_definition_digest", "raw_command_digest", "cwd", "supplied_environment", "shell", "timeout_ms", "requested_at", "network_mode", "expected_authority_descriptor_digest"], "verifier execution descriptor");
|
|
54
|
+
assertExactKeys(request.descriptor.cwd, ["workspace_id", "workspace_allocation_digest", "relative_working_directory", "absolute_path_digest"], "verifier execution cwd identity");
|
|
55
|
+
assertExactKeys(request.descriptor.supplied_environment, ["binding_digest", "variable_names", "inherit_parent", "startup_files"], "verifier execution supplied environment");
|
|
56
|
+
assertExactKeys(request.descriptor.shell, ["executable", "arguments"], "verifier execution shell");
|
|
57
|
+
assertDigest(request.request_digest, "verifier execution request digest");
|
|
58
|
+
assertDigest(request.plan_id, "verifier execution plan id");
|
|
59
|
+
assertDigest(request.verifier_definition_digest, "verifier definition digest");
|
|
60
|
+
assertDigest(request.resolved_command_digest, "resolved verifier command digest");
|
|
61
|
+
assertDigest(request.environment_binding_digest, "verifier environment binding digest");
|
|
62
|
+
assertDigest(request.cwd_identity.workspace_allocation_digest, "workspace allocation digest");
|
|
63
|
+
assertDigest(request.cwd_identity.absolute_path_digest, "workspace absolute path digest");
|
|
64
|
+
assertCanonicalTimestamp(request.requested_at, "verifier execution requested_at");
|
|
65
|
+
let descriptorDigest;
|
|
66
|
+
let actualEnvironmentDigest;
|
|
67
|
+
try {
|
|
68
|
+
descriptorDigest = digest(request.descriptor);
|
|
69
|
+
canonicalizeJson(request.environment);
|
|
70
|
+
actualEnvironmentDigest = environmentBindingDigest(request.environment);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
throw new CanonicalRunServiceError("invalid_request", "verifier execution request must contain canonical JSON data", { cause: error });
|
|
74
|
+
}
|
|
75
|
+
const environmentNames = Object.keys(request.environment).sort();
|
|
76
|
+
const platformEnvironmentNames = platform === "win32"
|
|
77
|
+
? new Set(["PATH", "PATHEXT", "SystemRoot", "ComSpec"])
|
|
78
|
+
: new Set(["PATH"]);
|
|
79
|
+
const environmentIsValid = Object.entries(request.environment).every(([name, value]) => name.length > 0 && !name.includes("=") && !name.includes("\0") && typeof value === "string" && !value.includes("\0"))
|
|
80
|
+
&& (platform === "win32" || request.environment.PATH === VERIFIER_PATH)
|
|
81
|
+
&& environmentNames.every((name) => (platformEnvironmentNames.has(name)
|
|
82
|
+
|| /^INTENTDNA_VERIFIER_[0-9]+_[A-Z0-9_]+$/u.test(name)
|
|
83
|
+
|| /^INTENTDNA_HANDOFF_[1-9][0-9]*$/u.test(name)));
|
|
84
|
+
const shellArguments = request.descriptor.shell.arguments;
|
|
85
|
+
const checks = [
|
|
86
|
+
["schema_version", request.descriptor.schema_version === "intentdna.verifier_execution_request.v1"],
|
|
87
|
+
["request_digest", request.request_digest === descriptorDigest],
|
|
88
|
+
["authority_descriptor", request.descriptor.expected_authority_descriptor_digest === authorityDescriptorDigest],
|
|
89
|
+
["plan_id", request.plan_id === request.descriptor.plan_id],
|
|
90
|
+
["verifier_id", request.verifier_id === request.descriptor.verifier_id],
|
|
91
|
+
["verifier_definition", request.verifier_definition_digest === request.descriptor.verifier_definition_digest],
|
|
92
|
+
["resolved_command_descriptor", request.resolved_command_digest === request.descriptor.raw_command_digest],
|
|
93
|
+
["resolved_command", request.resolved_command_digest === textDigest(request.resolved_command)],
|
|
94
|
+
["environment_descriptor", request.environment_binding_digest === request.descriptor.supplied_environment.binding_digest],
|
|
95
|
+
["environment", request.environment_binding_digest === actualEnvironmentDigest],
|
|
96
|
+
["timeout", request.timeout_ms === request.descriptor.timeout_ms && Number.isSafeInteger(request.timeout_ms) && request.timeout_ms > 0],
|
|
97
|
+
["requested_at", request.requested_at === request.descriptor.requested_at],
|
|
98
|
+
["network_policy", request.network_policy === request.descriptor.network_mode && (request.network_policy === "allow" || request.network_policy === "deny_all")],
|
|
99
|
+
["process_environment", request.inherit_parent_environment === false && request.load_startup_files === false],
|
|
100
|
+
["descriptor_environment", request.descriptor.supplied_environment.inherit_parent === false && request.descriptor.supplied_environment.startup_files === false],
|
|
101
|
+
["shell", request.descriptor.shell.executable === "bash" && Array.isArray(shellArguments) && canonicalEqual(shellArguments, ["--noprofile", "--norc", "-c"])],
|
|
102
|
+
["verifier_identity", typeof request.verifier_id === "string" && request.verifier_id.length > 0],
|
|
103
|
+
["command_text", typeof request.resolved_command === "string" && !request.resolved_command.includes("\0")],
|
|
104
|
+
["cwd", typeof request.cwd === "string" && isAbsolute(request.cwd)],
|
|
105
|
+
["cwd_digest", request.cwd_identity.absolute_path_digest === textDigest(request.cwd)],
|
|
106
|
+
["cwd_descriptor", canonicalEqual(request.cwd_identity, request.descriptor.cwd)],
|
|
107
|
+
["environment_shape", environmentIsValid && canonicalEqual(request.descriptor.supplied_environment.variable_names, environmentNames)],
|
|
108
|
+
];
|
|
109
|
+
const mismatches = checks.filter(([, passed]) => !passed).map(([name]) => name);
|
|
110
|
+
if (mismatches.length > 0) {
|
|
111
|
+
throw new CanonicalRunServiceError("invalid_request", `verifier execution request does not match its canonical descriptor: ${mismatches.join(", ")}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function frozenAuthorityDescriptor(authorityInstanceId, configurationDigest, platform) {
|
|
115
|
+
return Object.freeze({
|
|
116
|
+
schema_version: "intentdna.verifier_execution_authority.v1",
|
|
117
|
+
authority_instance_id: authorityInstanceId,
|
|
118
|
+
adapter_kind: platform === "win32" ? "local_windows_job_keeper" : "local_posix_managed_process_group",
|
|
119
|
+
adapter_version: "v2",
|
|
120
|
+
configuration_digest: configurationDigest,
|
|
121
|
+
process_tree_containment: true,
|
|
122
|
+
network_policies: Object.freeze(["allow"]),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function resolveWindowsBash() {
|
|
126
|
+
const located = spawnSync("where.exe", ["bash.exe"], {
|
|
127
|
+
encoding: "utf8",
|
|
128
|
+
env: process.env,
|
|
129
|
+
windowsHide: true,
|
|
130
|
+
});
|
|
131
|
+
if (located.status !== 0) {
|
|
132
|
+
throw new CanonicalRunServiceError("unsupported", "Windows verifier execution requires a host-installed bash.exe");
|
|
133
|
+
}
|
|
134
|
+
const candidate = located.stdout.split(/\r?\n/u).map((line) => line.trim()).find(Boolean);
|
|
135
|
+
if (candidate === undefined) {
|
|
136
|
+
throw new CanonicalRunServiceError("unsupported", "Windows verifier bash.exe resolution returned no executable");
|
|
137
|
+
}
|
|
138
|
+
return candidate;
|
|
139
|
+
}
|
|
140
|
+
function pinnedShell(platform, resolver) {
|
|
141
|
+
if (platform !== "win32")
|
|
142
|
+
return { executable: "bash", identity: { logical_name: "bash" } };
|
|
143
|
+
const selected = resolver?.(platform) ?? resolveWindowsBash();
|
|
144
|
+
if (!win32Path.isAbsolute(selected) || !existsSync(selected)) {
|
|
145
|
+
throw new CanonicalRunServiceError("unsupported", "Windows verifier bash.exe must resolve to an existing absolute path");
|
|
146
|
+
}
|
|
147
|
+
const executable = realpathSync(selected);
|
|
148
|
+
const metadata = statSync(executable);
|
|
149
|
+
if (!metadata.isFile())
|
|
150
|
+
throw new CanonicalRunServiceError("unsupported", "Windows verifier bash.exe is not a regular file");
|
|
151
|
+
return {
|
|
152
|
+
executable,
|
|
153
|
+
identity: {
|
|
154
|
+
real_path_digest: textDigest(executable),
|
|
155
|
+
content_digest: `sha256:${createHash("sha256").update(readFileSync(executable)).digest("hex")}`,
|
|
156
|
+
byte_count: metadata.size,
|
|
157
|
+
modified_at_ms: metadata.mtimeMs,
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function verifierIdentity(request) {
|
|
162
|
+
const token = digest({
|
|
163
|
+
schema_version: "intentdna.verifier_execution_identity.v1",
|
|
164
|
+
plan_id: request.plan_id,
|
|
165
|
+
verifier_id: request.verifier_id,
|
|
166
|
+
verifier_definition_digest: request.verifier_definition_digest,
|
|
167
|
+
resolved_command_digest: request.resolved_command_digest,
|
|
168
|
+
cwd_identity: request.cwd_identity,
|
|
169
|
+
environment_binding_digest: request.environment_binding_digest,
|
|
170
|
+
timeout_ms: request.timeout_ms,
|
|
171
|
+
network_policy: request.network_policy,
|
|
172
|
+
}).slice("sha256:".length);
|
|
173
|
+
const namespace = `verifier:${token}`;
|
|
174
|
+
return {
|
|
175
|
+
run_id: `${namespace}:run`,
|
|
176
|
+
step_id: `${namespace}:step`,
|
|
177
|
+
activation_id: `${namespace}:activation`,
|
|
178
|
+
attempt_id: `${namespace}:attempt`,
|
|
179
|
+
execution_claim_epoch: 1,
|
|
180
|
+
worker_session_id: `${namespace}:authority`,
|
|
181
|
+
execution_instance_id: `local-verifier:${token}`,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function terminalOutcome(result, timeoutMs) {
|
|
185
|
+
if (result.timedOut)
|
|
186
|
+
return { kind: "timeout", exit_code: result.status, signal: result.signal, timeout_ms: timeoutMs };
|
|
187
|
+
if (result.error !== undefined)
|
|
188
|
+
return { kind: "launch_error", exit_code: null, signal: null, error: result.error.message };
|
|
189
|
+
if (result.signal !== null)
|
|
190
|
+
return { kind: "signal", exit_code: null, signal: result.signal };
|
|
191
|
+
if (result.status === 0)
|
|
192
|
+
return { kind: "success", exit_code: 0, signal: null };
|
|
193
|
+
return { kind: "non_zero_exit", exit_code: result.status ?? -1, signal: null };
|
|
194
|
+
}
|
|
195
|
+
function verifierResult(result) {
|
|
196
|
+
const outcome = result.timedOut
|
|
197
|
+
? { kind: "timed_out", exitCode: result.status, signal: result.signal, timedOut: true, launchError: null }
|
|
198
|
+
: result.error !== undefined
|
|
199
|
+
? { kind: "launch_error", exitCode: null, signal: null, timedOut: false, launchError: { code: String(result.error.code ?? "SPAWN_ERROR").toUpperCase().replaceAll(/[^A-Z0-9_]/gu, "_") || "SPAWN_ERROR", messageDigest: textDigest(result.error.message) } }
|
|
200
|
+
: result.signal !== null
|
|
201
|
+
? { kind: "signaled", exitCode: null, signal: result.signal, timedOut: false, launchError: null }
|
|
202
|
+
: { kind: "exited", exitCode: result.status ?? -1, signal: null, timedOut: false, launchError: null };
|
|
203
|
+
return {
|
|
204
|
+
outcome,
|
|
205
|
+
processTreeDrained: result.processTreeDrained,
|
|
206
|
+
...(summarizeCommandEvidence(result.stdout, result.stderr) === undefined ? {} : { evidence: summarizeCommandEvidence(result.stdout, result.stderr) }),
|
|
207
|
+
startedAt: result.startedAt,
|
|
208
|
+
completedAt: result.completedAt,
|
|
209
|
+
stdoutDigest: result.stdoutDigest,
|
|
210
|
+
stdoutBytes: result.stdoutBytes,
|
|
211
|
+
stderrDigest: result.stderrDigest,
|
|
212
|
+
stderrBytes: result.stderrBytes,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
export class ProductionLocalVerifierExecutionPort {
|
|
216
|
+
authority_descriptor;
|
|
217
|
+
authorityDescriptorDigest;
|
|
218
|
+
authority;
|
|
219
|
+
shellExecutable;
|
|
220
|
+
platform;
|
|
221
|
+
constructor(options) {
|
|
222
|
+
const platform = options.platform ?? process.platform;
|
|
223
|
+
this.platform = platform;
|
|
224
|
+
const shell = pinnedShell(platform, options.resolve_shell_executable);
|
|
225
|
+
this.shellExecutable = shell.executable;
|
|
226
|
+
this.authority = new LocalAttemptExecutionAuthority({
|
|
227
|
+
root_directory: options.root_directory,
|
|
228
|
+
authority_instance_id: options.authority_instance_id,
|
|
229
|
+
platform,
|
|
230
|
+
architecture: options.architecture,
|
|
231
|
+
helper_path: options.helper_path,
|
|
232
|
+
expected_digest: options.expected_digest,
|
|
233
|
+
});
|
|
234
|
+
const authorityInstanceId = this.authority.authority_instance_id;
|
|
235
|
+
this.authority_descriptor = frozenAuthorityDescriptor(authorityInstanceId, digest({
|
|
236
|
+
schema_version: "intentdna.local_verifier_execution_configuration.v2",
|
|
237
|
+
platform,
|
|
238
|
+
architecture: options.architecture ?? process.arch,
|
|
239
|
+
shell: { executable: "bash", arguments: ["--noprofile", "--norc", "-c"], pinned_identity: shell.identity },
|
|
240
|
+
process_tree_containment: platform === "win32" ? "windows_job_keeper" : "managed_posix_process_group",
|
|
241
|
+
network_policy: "allow",
|
|
242
|
+
}), platform);
|
|
243
|
+
this.authorityDescriptorDigest = digest(this.authority_descriptor);
|
|
244
|
+
}
|
|
245
|
+
async execute(request) {
|
|
246
|
+
assertExecutionRequest(request, this.authorityDescriptorDigest, this.platform);
|
|
247
|
+
if (request.network_policy !== "allow")
|
|
248
|
+
throw new CanonicalRunServiceError("unsupported", "local verifier execution cannot enforce deny_all network policy");
|
|
249
|
+
const authority = this.authority;
|
|
250
|
+
const identity = verifierIdentity(request);
|
|
251
|
+
let binding;
|
|
252
|
+
const startedAt = request.requested_at;
|
|
253
|
+
const recovered = authority.recoverExecutionForPreparation(identity);
|
|
254
|
+
if (recovered !== null) {
|
|
255
|
+
binding = recovered.binding;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
const prepared = authority.recoverPreparedExecution(identity) ?? authority.prepareExecution(identity);
|
|
259
|
+
binding = authority.bindExecution(prepared, { started_at: startedAt });
|
|
260
|
+
}
|
|
261
|
+
const processResult = await binding.executeProcessTree(this.shellExecutable, ["--noprofile", "--norc", "-c", request.resolved_command], {
|
|
262
|
+
cwd: request.cwd,
|
|
263
|
+
env: request.environment,
|
|
264
|
+
timeoutMs: request.timeout_ms,
|
|
265
|
+
});
|
|
266
|
+
if (!processResult.processTreeDrained)
|
|
267
|
+
throw new CanonicalRunServiceError("verification_failed", "local verifier process tree could not be drained");
|
|
268
|
+
await binding.processTreeCompleted({
|
|
269
|
+
started_at: processResult.startedAt,
|
|
270
|
+
completed_at: processResult.completedAt,
|
|
271
|
+
outcome: terminalOutcome(processResult, request.timeout_ms),
|
|
272
|
+
process_id: processResult.processId,
|
|
273
|
+
process_tree_drained: true,
|
|
274
|
+
});
|
|
275
|
+
return {
|
|
276
|
+
execution: verifierResult(processResult),
|
|
277
|
+
authority: {
|
|
278
|
+
authority_instance_id: this.authority_descriptor.authority_instance_id,
|
|
279
|
+
request_digest: request.request_digest,
|
|
280
|
+
authority_descriptor_digest: this.authorityDescriptorDigest,
|
|
281
|
+
launch_descriptor_digest: request.request_digest,
|
|
282
|
+
process_tree_drained: true,
|
|
283
|
+
network_enforced: false,
|
|
284
|
+
enforcement_kind: "not_required",
|
|
285
|
+
enforcement_digest: null,
|
|
286
|
+
enforcement_descriptor: null,
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency-free Windows supervisor. The native keeper owns the Job Object;
|
|
3
|
+
* this process owns durable CAS transitions and the authenticated named pipe.
|
|
4
|
+
*/
|
|
5
|
+
export declare const LOCAL_WINDOWS_EXECUTION_SUPERVISOR_SOURCE: string;
|
|
6
|
+
export declare const LOCAL_WINDOWS_EXECUTION_SUPERVISOR_DIGEST: `sha256:${string}`;
|