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,44 @@
|
|
|
1
|
+
import type { CanonicalJsonValue } from "./canonical-json.js";
|
|
2
|
+
import type { ExecutableRunPlanPayload } from "./executable-run-plan.js";
|
|
3
|
+
export type StructuredOutputSchema = {
|
|
4
|
+
readonly type: "null";
|
|
5
|
+
} | {
|
|
6
|
+
readonly type: "boolean";
|
|
7
|
+
} | {
|
|
8
|
+
readonly type: "number";
|
|
9
|
+
readonly enum?: readonly number[];
|
|
10
|
+
} | {
|
|
11
|
+
readonly type: "string";
|
|
12
|
+
readonly enum?: readonly string[];
|
|
13
|
+
} | {
|
|
14
|
+
readonly type: "array";
|
|
15
|
+
readonly items: StructuredOutputSchema;
|
|
16
|
+
readonly minItems?: number;
|
|
17
|
+
readonly maxItems?: number;
|
|
18
|
+
} | {
|
|
19
|
+
readonly type: "object";
|
|
20
|
+
readonly properties: Readonly<Record<string, StructuredOutputSchema>>;
|
|
21
|
+
readonly required?: readonly string[];
|
|
22
|
+
readonly additionalProperties?: boolean;
|
|
23
|
+
};
|
|
24
|
+
export interface StructuredOutputSchemaRegistry {
|
|
25
|
+
readonly schemas: Readonly<Record<string, StructuredOutputSchema>>;
|
|
26
|
+
}
|
|
27
|
+
export declare const DEFAULT_STRUCTURED_OUTPUT_SCHEMA_REGISTRY: StructuredOutputSchemaRegistry;
|
|
28
|
+
export type StructuredOutputValidationFailureCode = "output_not_declared" | "output_not_structured" | "schema_ref_mismatch" | "schema_ref_unsupported" | "schema_validation_failed";
|
|
29
|
+
export type StructuredOutputValidationResult = {
|
|
30
|
+
readonly valid: true;
|
|
31
|
+
} | {
|
|
32
|
+
readonly valid: false;
|
|
33
|
+
readonly code: StructuredOutputValidationFailureCode;
|
|
34
|
+
readonly path: string;
|
|
35
|
+
readonly message: string;
|
|
36
|
+
};
|
|
37
|
+
export interface ValidatePinnedStructuredOutputInput {
|
|
38
|
+
readonly plan: ExecutableRunPlanPayload;
|
|
39
|
+
readonly node_id: string;
|
|
40
|
+
readonly output_name: string;
|
|
41
|
+
readonly schema_ref: string | null;
|
|
42
|
+
readonly value: CanonicalJsonValue;
|
|
43
|
+
}
|
|
44
|
+
export declare function validatePinnedStructuredOutput(input: ValidatePinnedStructuredOutputInput, registry: StructuredOutputSchemaRegistry): StructuredOutputValidationResult;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
export const DEFAULT_STRUCTURED_OUTPUT_SCHEMA_REGISTRY = {
|
|
2
|
+
schemas: {
|
|
3
|
+
"intentdna.controller.diagnosis-source.v1": {
|
|
4
|
+
type: "object",
|
|
5
|
+
properties: {
|
|
6
|
+
mode: { type: "string", enum: ["fresh", "legacy", "canonical"] },
|
|
7
|
+
},
|
|
8
|
+
required: ["mode"],
|
|
9
|
+
},
|
|
10
|
+
"intentdna.controller.diagnosis-origin.v1": {
|
|
11
|
+
type: "object",
|
|
12
|
+
properties: {
|
|
13
|
+
mode: { type: "string", enum: ["fresh", "legacy", "canonical"] },
|
|
14
|
+
source_node_id: { type: "string" },
|
|
15
|
+
source_result_id: { type: "string" },
|
|
16
|
+
},
|
|
17
|
+
required: ["mode", "source_node_id", "source_result_id"],
|
|
18
|
+
},
|
|
19
|
+
"intentdna.frw.diagnosis-review.v1": {
|
|
20
|
+
type: "object",
|
|
21
|
+
properties: {
|
|
22
|
+
verdict: { type: "string", enum: ["APPROVE", "REQUEST_REANALYSIS"] },
|
|
23
|
+
contract_valid: { type: "boolean" },
|
|
24
|
+
contract_artifact_reviewed: { type: "string" },
|
|
25
|
+
artifact_reviewed: { type: "string" },
|
|
26
|
+
contract_snapshot: { type: "object", properties: {} },
|
|
27
|
+
updated_at: { type: "string" },
|
|
28
|
+
},
|
|
29
|
+
required: [
|
|
30
|
+
"verdict",
|
|
31
|
+
"contract_valid",
|
|
32
|
+
"contract_artifact_reviewed",
|
|
33
|
+
"artifact_reviewed",
|
|
34
|
+
"contract_snapshot",
|
|
35
|
+
"updated_at",
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
"intentdna.frw.fix-progress.v1": {
|
|
39
|
+
type: "object",
|
|
40
|
+
properties: {
|
|
41
|
+
verdict: {
|
|
42
|
+
type: "string",
|
|
43
|
+
enum: [
|
|
44
|
+
"PASS",
|
|
45
|
+
"CONTINUE",
|
|
46
|
+
"REQUEST_CHANGES",
|
|
47
|
+
"BLOCKED",
|
|
48
|
+
"DIAGNOSIS_CONTRACT_INVALID",
|
|
49
|
+
"PROVENANCE_INVALID",
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
"module": { type: "string" },
|
|
53
|
+
round: { type: "number" },
|
|
54
|
+
run_id: { type: "string" },
|
|
55
|
+
diagnosis_artifact: { type: "string" },
|
|
56
|
+
review_artifact: { type: "string" },
|
|
57
|
+
module_complete: { type: "boolean" },
|
|
58
|
+
diagnosis_invalid: { type: "boolean" },
|
|
59
|
+
next_round_focus: { type: "string" },
|
|
60
|
+
failure_reason: { type: "string" },
|
|
61
|
+
updated_at: { type: "string" },
|
|
62
|
+
},
|
|
63
|
+
required: [
|
|
64
|
+
"verdict",
|
|
65
|
+
"module",
|
|
66
|
+
"round",
|
|
67
|
+
"run_id",
|
|
68
|
+
"diagnosis_artifact",
|
|
69
|
+
"review_artifact",
|
|
70
|
+
"module_complete",
|
|
71
|
+
"diagnosis_invalid",
|
|
72
|
+
"next_round_focus",
|
|
73
|
+
"failure_reason",
|
|
74
|
+
"updated_at",
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
function declaredOutput(plan, nodeId, outputName) {
|
|
80
|
+
const node = plan.nodes.find((candidate) => candidate.node_id === nodeId);
|
|
81
|
+
if (node?.kind === "worker") {
|
|
82
|
+
return node.packet_template.output_contract.find((output) => output.name === outputName);
|
|
83
|
+
}
|
|
84
|
+
if (node?.kind === "system") {
|
|
85
|
+
return node.output_contract?.find((output) => output.name === outputName);
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
function fail(code, path, message) {
|
|
90
|
+
return { valid: false, code, path, message };
|
|
91
|
+
}
|
|
92
|
+
function validateValue(value, schema, path) {
|
|
93
|
+
if (schema.type === "null") {
|
|
94
|
+
return value === null ? { valid: true } : fail("schema_validation_failed", path, "must be null");
|
|
95
|
+
}
|
|
96
|
+
if (schema.type === "boolean") {
|
|
97
|
+
return typeof value === "boolean" ? { valid: true } : fail("schema_validation_failed", path, "must be a boolean");
|
|
98
|
+
}
|
|
99
|
+
if (schema.type === "number") {
|
|
100
|
+
if (typeof value !== "number")
|
|
101
|
+
return fail("schema_validation_failed", path, "must be a number");
|
|
102
|
+
if (schema.enum !== undefined && !schema.enum.includes(value)) {
|
|
103
|
+
return fail("schema_validation_failed", path, `must be one of: ${schema.enum.join(", ")}`);
|
|
104
|
+
}
|
|
105
|
+
return { valid: true };
|
|
106
|
+
}
|
|
107
|
+
if (schema.type === "string") {
|
|
108
|
+
if (typeof value !== "string")
|
|
109
|
+
return fail("schema_validation_failed", path, "must be a string");
|
|
110
|
+
if (schema.enum !== undefined && !schema.enum.includes(value)) {
|
|
111
|
+
return fail("schema_validation_failed", path, `must be one of: ${schema.enum.join(", ")}`);
|
|
112
|
+
}
|
|
113
|
+
return { valid: true };
|
|
114
|
+
}
|
|
115
|
+
if (schema.type === "array") {
|
|
116
|
+
if (!Array.isArray(value))
|
|
117
|
+
return fail("schema_validation_failed", path, "must be an array");
|
|
118
|
+
if (schema.minItems !== undefined && value.length < schema.minItems) {
|
|
119
|
+
return fail("schema_validation_failed", path, `must contain at least ${schema.minItems} item(s)`);
|
|
120
|
+
}
|
|
121
|
+
if (schema.maxItems !== undefined && value.length > schema.maxItems) {
|
|
122
|
+
return fail("schema_validation_failed", path, `must contain at most ${schema.maxItems} item(s)`);
|
|
123
|
+
}
|
|
124
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
125
|
+
const result = validateValue(value[index], schema.items, `${path}/${index}`);
|
|
126
|
+
if (!result.valid)
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
return { valid: true };
|
|
130
|
+
}
|
|
131
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
132
|
+
return fail("schema_validation_failed", path, "must be an object");
|
|
133
|
+
}
|
|
134
|
+
for (const key of schema.required ?? []) {
|
|
135
|
+
if (!Object.prototype.hasOwnProperty.call(value, key)) {
|
|
136
|
+
return fail("schema_validation_failed", `${path}/${key}`, "is required");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
for (const [key, child] of Object.entries(value)) {
|
|
140
|
+
const childSchema = schema.properties[key];
|
|
141
|
+
if (childSchema === undefined) {
|
|
142
|
+
if (schema.additionalProperties === false) {
|
|
143
|
+
return fail("schema_validation_failed", `${path}/${key}`, "is not allowed");
|
|
144
|
+
}
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const result = validateValue(child, childSchema, `${path}/${key}`);
|
|
148
|
+
if (!result.valid)
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
return { valid: true };
|
|
152
|
+
}
|
|
153
|
+
export function validatePinnedStructuredOutput(input, registry) {
|
|
154
|
+
const output = declaredOutput(input.plan, input.node_id, input.output_name);
|
|
155
|
+
if (output === undefined) {
|
|
156
|
+
return fail("output_not_declared", "$.output_name", `output '${input.output_name}' is not declared by worker '${input.node_id}'`);
|
|
157
|
+
}
|
|
158
|
+
if (output.kind !== "structured") {
|
|
159
|
+
return fail("output_not_structured", "$.output_name", `output '${input.output_name}' is not structured`);
|
|
160
|
+
}
|
|
161
|
+
if (output.schema_ref !== input.schema_ref) {
|
|
162
|
+
return fail("schema_ref_mismatch", "$.schema_ref", `submitted schema_ref does not match pinned output contract '${String(output.schema_ref)}'`);
|
|
163
|
+
}
|
|
164
|
+
if (input.schema_ref === null)
|
|
165
|
+
return { valid: true };
|
|
166
|
+
const schema = registry.schemas[input.schema_ref];
|
|
167
|
+
if (schema === undefined) {
|
|
168
|
+
return fail("schema_ref_unsupported", "$.schema_ref", `no service-owned validator is registered for '${input.schema_ref}'`);
|
|
169
|
+
}
|
|
170
|
+
return validateValue(input.value, schema, "$.value");
|
|
171
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const TRUSTED_FULL_VERIFIER_COMMAND_SCHEMA_VERSION = "intentdna.trusted_full_verifier_command.v1";
|
|
2
|
+
export interface TrustedFullVerifierCommand {
|
|
3
|
+
readonly schema_version: typeof TRUSTED_FULL_VERIFIER_COMMAND_SCHEMA_VERSION;
|
|
4
|
+
readonly source: "compiled_plan";
|
|
5
|
+
}
|
|
6
|
+
export type VerifierCommandBindingResult = {
|
|
7
|
+
readonly bound: true;
|
|
8
|
+
readonly command: string;
|
|
9
|
+
readonly env: Readonly<Record<string, string>>;
|
|
10
|
+
readonly mode: "environment" | "trusted_full_command";
|
|
11
|
+
} | {
|
|
12
|
+
readonly bound: false;
|
|
13
|
+
readonly code: "unresolved_template" | "unsafe_template_context" | "full_command_trust_required";
|
|
14
|
+
readonly variable: string | null;
|
|
15
|
+
readonly message: string;
|
|
16
|
+
};
|
|
17
|
+
export interface BindVerifierCommandInput {
|
|
18
|
+
readonly command_template: string;
|
|
19
|
+
readonly runtime_values: Readonly<Record<string, string>>;
|
|
20
|
+
readonly full_command_trust?: TrustedFullVerifierCommand;
|
|
21
|
+
}
|
|
22
|
+
export declare function bindVerifierCommand(input: BindVerifierCommandInput): VerifierCommandBindingResult;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
export const TRUSTED_FULL_VERIFIER_COMMAND_SCHEMA_VERSION = "intentdna.trusted_full_verifier_command.v1";
|
|
2
|
+
function occurrences(template) {
|
|
3
|
+
const found = [];
|
|
4
|
+
for (const match of template.matchAll(/\{\{(\w+)\}\}/g)) {
|
|
5
|
+
found.push({ start: match.index, end: match.index + match[0].length, name: match[1] });
|
|
6
|
+
}
|
|
7
|
+
for (const match of template.matchAll(/(^|[^\\])(\$ARGUMENTS)/g)) {
|
|
8
|
+
const start = match.index + match[1].length;
|
|
9
|
+
found.push({ start, end: start + match[2].length, name: "ARGUMENTS" });
|
|
10
|
+
}
|
|
11
|
+
return found.sort((left, right) => left.start - right.start);
|
|
12
|
+
}
|
|
13
|
+
function wholeCommandVariable(template) {
|
|
14
|
+
const trimmed = template.trim();
|
|
15
|
+
const named = /^\{\{(\w+)\}\}$/.exec(trimmed);
|
|
16
|
+
if (named)
|
|
17
|
+
return named[1];
|
|
18
|
+
return trimmed === "$ARGUMENTS" ? "ARGUMENTS" : null;
|
|
19
|
+
}
|
|
20
|
+
function shellTemplateContext(template, offset) {
|
|
21
|
+
let quote = "unquoted";
|
|
22
|
+
let escaped = false;
|
|
23
|
+
const expansions = [];
|
|
24
|
+
for (let index = 0; index < offset; index += 1) {
|
|
25
|
+
const character = template[index];
|
|
26
|
+
if (escaped) {
|
|
27
|
+
escaped = false;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (character === "\\" && quote !== "single") {
|
|
31
|
+
escaped = true;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (character === "'" && quote === "unquoted") {
|
|
35
|
+
quote = "single";
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (character === "'" && quote === "single") {
|
|
39
|
+
quote = "unquoted";
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (character === "\"" && quote === "unquoted") {
|
|
43
|
+
quote = "double";
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (character === "\"" && quote === "double") {
|
|
47
|
+
quote = "unquoted";
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (character === "`" && quote !== "single") {
|
|
51
|
+
quote = quote === "backtick" ? "unquoted" : "backtick";
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (quote === "single" || quote === "backtick")
|
|
55
|
+
continue;
|
|
56
|
+
if (template.startsWith("$((", index)) {
|
|
57
|
+
expansions.push("arithmetic");
|
|
58
|
+
index += 2;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (template.startsWith("$(", index)) {
|
|
62
|
+
expansions.push("command");
|
|
63
|
+
index += 1;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (template.startsWith("${", index)) {
|
|
67
|
+
expansions.push("parameter");
|
|
68
|
+
index += 1;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (character === "}" && expansions.at(-1) === "parameter") {
|
|
72
|
+
expansions.pop();
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (character === ")" && expansions.length > 0) {
|
|
76
|
+
if (expansions.at(-1) === "arithmetic" && template[index + 1] === ")")
|
|
77
|
+
index += 1;
|
|
78
|
+
expansions.pop();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return { quote, nestedExpansion: expansions.length > 0 };
|
|
82
|
+
}
|
|
83
|
+
function shellWords(prefix) {
|
|
84
|
+
return prefix
|
|
85
|
+
.trim()
|
|
86
|
+
.split(/\s+/u)
|
|
87
|
+
.filter(Boolean);
|
|
88
|
+
}
|
|
89
|
+
function isAssignmentWord(word) {
|
|
90
|
+
return /^[A-Za-z_][A-Za-z0-9_]*=/u.test(word);
|
|
91
|
+
}
|
|
92
|
+
function isSafeDataArgument(template, occurrence) {
|
|
93
|
+
const context = shellTemplateContext(template, occurrence.start);
|
|
94
|
+
if (context.nestedExpansion || context.quote === "single" || context.quote === "backtick") {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
const before = template.slice(0, occurrence.start);
|
|
98
|
+
const after = template.slice(occurrence.end);
|
|
99
|
+
const wholeDoubleQuotedArgument = context.quote === "double"
|
|
100
|
+
&& before.endsWith("\"")
|
|
101
|
+
&& after.startsWith("\"");
|
|
102
|
+
if (!wholeDoubleQuotedArgument)
|
|
103
|
+
return false;
|
|
104
|
+
const commandPrefix = before.slice(0, -1).trimEnd();
|
|
105
|
+
if (!commandPrefix || /(?:^|[;&|()\n])\s*$/u.test(commandPrefix))
|
|
106
|
+
return false;
|
|
107
|
+
const segment = commandPrefix.split(/[;&|()\n]/u).at(-1) ?? "";
|
|
108
|
+
const words = shellWords(segment);
|
|
109
|
+
const commandIndex = words.findIndex((word) => !isAssignmentWord(word));
|
|
110
|
+
if (commandIndex < 0)
|
|
111
|
+
return false;
|
|
112
|
+
const commandWords = words.slice(commandIndex);
|
|
113
|
+
if (commandWords.length === 0)
|
|
114
|
+
return false;
|
|
115
|
+
const previous = words.at(-1);
|
|
116
|
+
const program = commandWords[0].split("/").at(-1);
|
|
117
|
+
if ([".", "command", "env", "eval", "exec", "source", "xargs"].includes(program))
|
|
118
|
+
return false;
|
|
119
|
+
if (["-c", "-e"].includes(previous)
|
|
120
|
+
&& ["bash", "dash", "ksh", "node", "perl", "python", "python3", "sh", "zsh"].includes(program))
|
|
121
|
+
return false;
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
function environmentName(name, index) {
|
|
125
|
+
const normalized = name.replace(/[^A-Za-z0-9_]/g, "_").toUpperCase();
|
|
126
|
+
return `INTENTDNA_VERIFIER_${index}_${normalized}`;
|
|
127
|
+
}
|
|
128
|
+
export function bindVerifierCommand(input) {
|
|
129
|
+
const wholeVariable = wholeCommandVariable(input.command_template);
|
|
130
|
+
if (wholeVariable !== null) {
|
|
131
|
+
const value = input.runtime_values[wholeVariable];
|
|
132
|
+
if (value === undefined) {
|
|
133
|
+
return { bound: false, code: "unresolved_template", variable: wholeVariable, message: `Verifier command variable '${wholeVariable}' is unresolved` };
|
|
134
|
+
}
|
|
135
|
+
if (input.full_command_trust?.schema_version !== TRUSTED_FULL_VERIFIER_COMMAND_SCHEMA_VERSION
|
|
136
|
+
|| input.full_command_trust.source !== "compiled_plan") {
|
|
137
|
+
return { bound: false, code: "full_command_trust_required", variable: wholeVariable, message: "Whole-command runtime templates require compiled-plan trust" };
|
|
138
|
+
}
|
|
139
|
+
return { bound: true, command: value, env: {}, mode: "trusted_full_command" };
|
|
140
|
+
}
|
|
141
|
+
const found = occurrences(input.command_template);
|
|
142
|
+
const env = {};
|
|
143
|
+
let command = "";
|
|
144
|
+
let cursor = 0;
|
|
145
|
+
for (let index = 0; index < found.length; index += 1) {
|
|
146
|
+
const occurrence = found[index];
|
|
147
|
+
const value = input.runtime_values[occurrence.name];
|
|
148
|
+
if (value === undefined) {
|
|
149
|
+
return { bound: false, code: "unresolved_template", variable: occurrence.name, message: `Verifier command variable '${occurrence.name}' is unresolved` };
|
|
150
|
+
}
|
|
151
|
+
if (!isSafeDataArgument(input.command_template, occurrence)) {
|
|
152
|
+
return { bound: false, code: "unsafe_template_context", variable: occurrence.name, message: `Verifier command variable '${occurrence.name}' is not in a proven shell data argument position` };
|
|
153
|
+
}
|
|
154
|
+
const envName = environmentName(occurrence.name, index);
|
|
155
|
+
env[envName] = value;
|
|
156
|
+
command += input.command_template.slice(cursor, occurrence.start);
|
|
157
|
+
command += `\${${envName}}`;
|
|
158
|
+
cursor = occurrence.end;
|
|
159
|
+
}
|
|
160
|
+
command += input.command_template.slice(cursor);
|
|
161
|
+
return { bound: true, command, env, mode: "environment" };
|
|
162
|
+
}
|
|
@@ -17,6 +17,51 @@ export interface VerifierCheckResult {
|
|
|
17
17
|
message?: string;
|
|
18
18
|
timedOut?: boolean;
|
|
19
19
|
}
|
|
20
|
+
export interface VerifierCommandLaunchError {
|
|
21
|
+
readonly code: string;
|
|
22
|
+
readonly messageDigest: `sha256:${string}`;
|
|
23
|
+
}
|
|
24
|
+
export type VerifierCommandProcessOutcome = {
|
|
25
|
+
readonly kind: "exited";
|
|
26
|
+
readonly exitCode: number;
|
|
27
|
+
readonly signal: null;
|
|
28
|
+
readonly timedOut: false;
|
|
29
|
+
readonly launchError: null;
|
|
30
|
+
} | {
|
|
31
|
+
readonly kind: "signaled";
|
|
32
|
+
readonly exitCode: null;
|
|
33
|
+
readonly signal: NodeJS.Signals;
|
|
34
|
+
readonly timedOut: false;
|
|
35
|
+
readonly launchError: null;
|
|
36
|
+
} | {
|
|
37
|
+
readonly kind: "timed_out";
|
|
38
|
+
readonly exitCode: number | null;
|
|
39
|
+
readonly signal: NodeJS.Signals | null;
|
|
40
|
+
readonly timedOut: true;
|
|
41
|
+
readonly launchError: null;
|
|
42
|
+
} | {
|
|
43
|
+
readonly kind: "launch_error";
|
|
44
|
+
readonly exitCode: null;
|
|
45
|
+
readonly signal: null;
|
|
46
|
+
readonly timedOut: false;
|
|
47
|
+
readonly launchError: VerifierCommandLaunchError;
|
|
48
|
+
};
|
|
49
|
+
export interface VerifierCommandExecutionResult {
|
|
50
|
+
readonly outcome: VerifierCommandProcessOutcome;
|
|
51
|
+
readonly processTreeDrained: boolean;
|
|
52
|
+
readonly evidence?: string;
|
|
53
|
+
readonly startedAt: string;
|
|
54
|
+
readonly completedAt: string;
|
|
55
|
+
readonly stdoutDigest: `sha256:${string}`;
|
|
56
|
+
readonly stdoutBytes: number;
|
|
57
|
+
readonly stderrDigest: `sha256:${string}`;
|
|
58
|
+
readonly stderrBytes: number;
|
|
59
|
+
}
|
|
60
|
+
export declare function assertVerifierCommandExecutionResult(value: unknown): asserts value is VerifierCommandExecutionResult;
|
|
61
|
+
export declare function verifierCommandExecutionPassed(execution: VerifierCommandExecutionResult): boolean;
|
|
62
|
+
export declare function verifierCommandExecutionTimedOut(execution: VerifierCommandExecutionResult): boolean;
|
|
63
|
+
export declare function verifierCommandExecutionExitCode(execution: VerifierCommandExecutionResult): number | null;
|
|
64
|
+
export declare function verifierCommandExecutionSignal(execution: VerifierCommandExecutionResult): NodeJS.Signals | null;
|
|
20
65
|
export declare function resolveVerifierTemplate(template: string, variables?: Record<string, string>): string;
|
|
21
66
|
export declare function verifierTemplateVariableNames(template: string): string[];
|
|
22
67
|
export declare function validateVerifierTemplateVariables(template: string, variables?: Record<string, string>, mode?: "command" | "path"): {
|
|
@@ -36,12 +81,7 @@ export declare function isBuiltinAssertAllowed(policy: VerifierCommandPolicy | u
|
|
|
36
81
|
export declare function verifierAssertPolicyMessage(assertName: string): string;
|
|
37
82
|
export declare function trimVerifierEvidence(raw: string | undefined): string | undefined;
|
|
38
83
|
export declare function summarizeCommandEvidence(stdout: string, stderr: string): string | undefined;
|
|
39
|
-
export declare function execVerifierCommand(projectDir: string, command: string, timeoutMs?: number, env?:
|
|
40
|
-
passed: boolean;
|
|
41
|
-
timedOut: boolean;
|
|
42
|
-
exitCode?: number;
|
|
43
|
-
evidence?: string;
|
|
44
|
-
}>;
|
|
84
|
+
export declare function execVerifierCommand(projectDir: string, command: string, timeoutMs?: number, env?: Readonly<Record<string, string>>, now?: () => Date): Promise<VerifierCommandExecutionResult>;
|
|
45
85
|
export declare function resolveVerifierTarget(projectDir: string, targetPath: string): Promise<string | null>;
|
|
46
86
|
export declare function toProjectRelativePath(projectDir: string, filePath: string): Promise<string>;
|
|
47
87
|
export declare function getCompletionCheckFields(completion: CompletionCheck): string[];
|