intentdna 1.8.6 → 1.8.7
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 +1 -0
- package/dist/cli/commands/run-lifecycle.d.ts +73 -0
- package/dist/cli/commands/run-lifecycle.js +240 -0
- package/dist/cli/commands/run.d.ts +22 -40
- package/dist/cli/commands/run.js +674 -392
- package/dist/cli/index.js +87 -2
- package/dist/compiler/workflow.js +3 -2
- package/dist/hooks/cli.d.ts +1 -2
- package/dist/hooks/cli.js +119 -80
- package/dist/hooks/enforce.d.ts +2 -0
- package/dist/hooks/enforce.js +56 -27
- package/dist/hooks/enforcement-boundary.d.ts +13 -0
- package/dist/hooks/enforcement-boundary.js +33 -0
- package/dist/hooks/index.d.ts +3 -2
- package/dist/hooks/index.js +3 -2
- package/dist/hooks/protocol.d.ts +12 -4
- package/dist/hooks/protocol.js +20 -14
- package/dist/hooks/schema.d.ts +2 -1
- package/dist/hooks/schema.js +6 -2
- package/dist/hooks/state-manager.d.ts +5 -5
- package/dist/hooks/state-manager.js +26 -24
- package/dist/hooks/state.d.ts +19 -3
- package/dist/hooks/state.js +327 -80
- package/dist/mcp/index.js +0 -0
- package/dist/runtime/diagnosis-contract-verifier.d.ts +11 -0
- package/dist/runtime/diagnosis-contract-verifier.js +417 -0
- package/dist/runtime/execution-provider.d.ts +40 -0
- package/dist/runtime/execution-provider.js +138 -0
- package/dist/runtime/handoff-resolver.d.ts +61 -0
- package/dist/runtime/handoff-resolver.js +167 -0
- package/dist/runtime/index.d.ts +24 -0
- package/dist/runtime/index.js +13 -0
- package/dist/runtime/process-tree.d.ts +47 -0
- package/dist/runtime/process-tree.js +402 -0
- package/dist/runtime/providers/claude.d.ts +9 -0
- package/dist/runtime/providers/claude.js +64 -0
- package/dist/runtime/providers/codex.d.ts +8 -0
- package/dist/runtime/providers/codex.js +72 -0
- package/dist/runtime/result-store.d.ts +32 -0
- package/dist/runtime/result-store.js +130 -0
- package/dist/runtime/run-contracts.d.ts +290 -0
- package/dist/runtime/run-contracts.js +58 -0
- package/dist/runtime/run-controller.d.ts +149 -0
- package/dist/runtime/run-controller.js +1108 -0
- package/dist/runtime/run-store.d.ts +96 -0
- package/dist/runtime/run-store.js +725 -0
- package/dist/runtime/worker-executor.d.ts +19 -0
- package/dist/runtime/worker-executor.js +194 -0
- package/dist/runtime/workflow-plan-adapter.d.ts +26 -0
- package/dist/runtime/workflow-plan-adapter.js +416 -0
- package/dist/runtime/workflow-runner.d.ts +15 -3
- package/dist/runtime/workflow-runner.js +13 -1
- package/dist/runtime/workspace-isolation.d.ts +103 -0
- package/dist/runtime/workspace-isolation.js +373 -0
- package/dist/schema/types.d.ts +1 -0
- package/dist/schema/validate.js +64 -6
- package/dist/schema/yaml-parser.js +7 -2
- package/package.json +1 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { decodeDeclaredOutputs, MalformedProviderResultError, } from "../execution-provider.js";
|
|
2
|
+
function isRecord(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
export function createClaudeExecutionProvider(options = {}) {
|
|
6
|
+
return {
|
|
7
|
+
name: "claude",
|
|
8
|
+
createLaunch(packet) {
|
|
9
|
+
return {
|
|
10
|
+
command: options.executable ?? "claude",
|
|
11
|
+
args: [
|
|
12
|
+
"-p",
|
|
13
|
+
"--agent",
|
|
14
|
+
options.agentName?.(packet) ?? packet.role,
|
|
15
|
+
"--output-format",
|
|
16
|
+
"json",
|
|
17
|
+
"--session-id",
|
|
18
|
+
packet.worker_session_id,
|
|
19
|
+
"--no-session-persistence",
|
|
20
|
+
...(options.extraArgs ?? []),
|
|
21
|
+
],
|
|
22
|
+
stdin: packet.standalone_prompt,
|
|
23
|
+
cwd: packet.workspace.working_directory,
|
|
24
|
+
...(options.env ? { env: options.env } : {}),
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
parseResult(context) {
|
|
28
|
+
let value;
|
|
29
|
+
try {
|
|
30
|
+
value = JSON.parse(context.stdout);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
throw new MalformedProviderResultError("Claude output is not valid JSON");
|
|
34
|
+
}
|
|
35
|
+
if (!isRecord(value)) {
|
|
36
|
+
throw new MalformedProviderResultError("Claude output must be a JSON object");
|
|
37
|
+
}
|
|
38
|
+
if (value.is_error === true) {
|
|
39
|
+
throw new MalformedProviderResultError(typeof value.result === "string"
|
|
40
|
+
? value.result
|
|
41
|
+
: "Claude reported an error result");
|
|
42
|
+
}
|
|
43
|
+
if (value.result === undefined
|
|
44
|
+
&& context.packet.output_contract.length === 0) {
|
|
45
|
+
return {
|
|
46
|
+
provider_session_id: typeof value.session_id === "string"
|
|
47
|
+
? value.session_id
|
|
48
|
+
: null,
|
|
49
|
+
outputs: [],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (typeof value.result !== "string") {
|
|
53
|
+
throw new MalformedProviderResultError("Claude output is missing its result text");
|
|
54
|
+
}
|
|
55
|
+
const providerSessionId = typeof value.session_id === "string"
|
|
56
|
+
? value.session_id
|
|
57
|
+
: null;
|
|
58
|
+
return {
|
|
59
|
+
provider_session_id: providerSessionId,
|
|
60
|
+
outputs: decodeDeclaredOutputs(value.result, context.packet.output_contract),
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ExecutionProvider } from "../execution-provider.js";
|
|
2
|
+
export interface CodexExecutionProviderOptions {
|
|
3
|
+
readonly executable?: string;
|
|
4
|
+
readonly sandbox?: "read-only" | "workspace-write" | "danger-full-access";
|
|
5
|
+
readonly extraArgs?: readonly string[];
|
|
6
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
7
|
+
}
|
|
8
|
+
export declare function createCodexExecutionProvider(options?: CodexExecutionProviderOptions): ExecutionProvider;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { decodeDeclaredOutputs, MalformedProviderResultError, } from "../execution-provider.js";
|
|
2
|
+
function isRecord(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
export function createCodexExecutionProvider(options = {}) {
|
|
6
|
+
return {
|
|
7
|
+
name: "codex",
|
|
8
|
+
createLaunch(packet) {
|
|
9
|
+
return {
|
|
10
|
+
command: options.executable ?? "codex",
|
|
11
|
+
args: [
|
|
12
|
+
"exec",
|
|
13
|
+
"--ephemeral",
|
|
14
|
+
"--json",
|
|
15
|
+
"--skip-git-repo-check",
|
|
16
|
+
"-C",
|
|
17
|
+
packet.workspace.working_directory,
|
|
18
|
+
"--sandbox",
|
|
19
|
+
options.sandbox ?? "workspace-write",
|
|
20
|
+
...(options.extraArgs ?? []),
|
|
21
|
+
packet.standalone_prompt,
|
|
22
|
+
],
|
|
23
|
+
stdin: null,
|
|
24
|
+
cwd: packet.workspace.working_directory,
|
|
25
|
+
...(options.env ? { env: options.env } : {}),
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
parseResult(context) {
|
|
29
|
+
let providerSessionId = null;
|
|
30
|
+
let finalText = null;
|
|
31
|
+
const lines = context.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
32
|
+
if (lines.length === 0) {
|
|
33
|
+
throw new MalformedProviderResultError("Codex returned no JSON events");
|
|
34
|
+
}
|
|
35
|
+
for (const line of lines) {
|
|
36
|
+
let event;
|
|
37
|
+
try {
|
|
38
|
+
event = JSON.parse(line);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
throw new MalformedProviderResultError("Codex output contains a malformed JSON event");
|
|
42
|
+
}
|
|
43
|
+
if (!isRecord(event) || typeof event.type !== "string") {
|
|
44
|
+
throw new MalformedProviderResultError("Codex output contains an invalid event");
|
|
45
|
+
}
|
|
46
|
+
if (event.type === "thread.started"
|
|
47
|
+
&& typeof event.thread_id === "string") {
|
|
48
|
+
providerSessionId = event.thread_id;
|
|
49
|
+
}
|
|
50
|
+
if (event.type === "item.completed" && isRecord(event.item)) {
|
|
51
|
+
if (event.item.type === "agent_message"
|
|
52
|
+
&& typeof event.item.text === "string") {
|
|
53
|
+
finalText = event.item.text;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (event.type === "error") {
|
|
57
|
+
const message = isRecord(event.error) && typeof event.error.message === "string"
|
|
58
|
+
? event.error.message
|
|
59
|
+
: "Codex reported an error event";
|
|
60
|
+
throw new MalformedProviderResultError(message);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (finalText === null) {
|
|
64
|
+
throw new MalformedProviderResultError("Codex output has no completed agent message");
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
provider_session_id: providerSessionId,
|
|
68
|
+
outputs: decodeDeclaredOutputs(finalText, context.packet.output_contract),
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { AttemptId, CommittedStepResult, ResultId, RunId } from "./run-contracts.js";
|
|
2
|
+
import { DurableRunStore, type RunStoreSnapshot } from "./run-store.js";
|
|
3
|
+
export type ResultStoreErrorCode = "invalid_result" | "result_not_found";
|
|
4
|
+
export declare class ResultStoreError extends Error {
|
|
5
|
+
readonly code: ResultStoreErrorCode;
|
|
6
|
+
constructor(code: ResultStoreErrorCode, message: string);
|
|
7
|
+
}
|
|
8
|
+
export interface CommitStepResultRequest {
|
|
9
|
+
readonly result: CommittedStepResult;
|
|
10
|
+
readonly expected_record_version: number;
|
|
11
|
+
}
|
|
12
|
+
export interface CommitStepResultResponse {
|
|
13
|
+
readonly result: CommittedStepResult;
|
|
14
|
+
readonly snapshot: RunStoreSnapshot;
|
|
15
|
+
/** False only when the exact immutable result was already committed. */
|
|
16
|
+
readonly committed: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Immutable result access over the durable run ledger.
|
|
20
|
+
*
|
|
21
|
+
* Mutation authority remains in DurableRunStore so result commits share the
|
|
22
|
+
* run's atomic/versioned transaction boundary.
|
|
23
|
+
*/
|
|
24
|
+
export declare class ImmutableResultStore {
|
|
25
|
+
private readonly runStore;
|
|
26
|
+
constructor(runStore: DurableRunStore);
|
|
27
|
+
commit(request: CommitStepResultRequest): Promise<CommitStepResultResponse>;
|
|
28
|
+
read(runId: RunId, resultId: ResultId): Promise<CommittedStepResult | null>;
|
|
29
|
+
require(runId: RunId, resultId: ResultId): Promise<CommittedStepResult>;
|
|
30
|
+
readByAttempt(runId: RunId, attemptId: AttemptId): Promise<CommittedStepResult | null>;
|
|
31
|
+
list(runId: RunId): Promise<readonly CommittedStepResult[]>;
|
|
32
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
export class ResultStoreError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
constructor(code, message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "ResultStoreError";
|
|
6
|
+
this.code = code;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function assertNonEmptyString(value, label) {
|
|
10
|
+
if (!value) {
|
|
11
|
+
throw new ResultStoreError("invalid_result", `${label} must be a non-empty string`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function assertJsonValue(value, path) {
|
|
15
|
+
if (value === null ||
|
|
16
|
+
typeof value === "string" ||
|
|
17
|
+
typeof value === "boolean") {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (typeof value === "number") {
|
|
21
|
+
if (!Number.isFinite(value)) {
|
|
22
|
+
throw new ResultStoreError("invalid_result", `${path} must contain only finite JSON numbers`);
|
|
23
|
+
}
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(value)) {
|
|
27
|
+
value.forEach((entry, index) => assertJsonValue(entry, `${path}[${index}]`));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (typeof value === "object") {
|
|
31
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
32
|
+
if (entry === undefined) {
|
|
33
|
+
throw new ResultStoreError("invalid_result", `${path}.${key} must not be undefined`);
|
|
34
|
+
}
|
|
35
|
+
assertJsonValue(entry, `${path}.${key}`);
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
throw new ResultStoreError("invalid_result", `${path} must be a JSON value`);
|
|
40
|
+
}
|
|
41
|
+
function assertOutput(output, index) {
|
|
42
|
+
const label = `result.outputs[${index}]`;
|
|
43
|
+
assertNonEmptyString(output.name, `${label}.name`);
|
|
44
|
+
if (output.kind === "reference") {
|
|
45
|
+
assertNonEmptyString(output.reference.value, `${label}.reference.value`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (output.kind === "text") {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
assertJsonValue(output.value, `${label}.value`);
|
|
52
|
+
}
|
|
53
|
+
function assertCommittedResult(result) {
|
|
54
|
+
assertNonEmptyString(result.result_id, "result.result_id");
|
|
55
|
+
assertNonEmptyString(result.run_id, "result.run_id");
|
|
56
|
+
assertNonEmptyString(result.step_id, "result.step_id");
|
|
57
|
+
assertNonEmptyString(result.attempt_id, "result.attempt_id");
|
|
58
|
+
assertNonEmptyString(result.worker_session_id, "result.worker_session_id");
|
|
59
|
+
assertNonEmptyString(result.provider, "result.provider");
|
|
60
|
+
assertNonEmptyString(result.started_at, "result.started_at");
|
|
61
|
+
assertNonEmptyString(result.completed_at, "result.completed_at");
|
|
62
|
+
assertNonEmptyString(result.committed_at, "result.committed_at");
|
|
63
|
+
if (result.commit_status !== "committed") {
|
|
64
|
+
throw new ResultStoreError("invalid_result", `result ${result.result_id} is not committed`);
|
|
65
|
+
}
|
|
66
|
+
const outputNames = new Set();
|
|
67
|
+
result.outputs.forEach((output, index) => {
|
|
68
|
+
assertOutput(output, index);
|
|
69
|
+
if (outputNames.has(output.name)) {
|
|
70
|
+
throw new ResultStoreError("invalid_result", `result ${result.result_id} has duplicate output ${output.name}`);
|
|
71
|
+
}
|
|
72
|
+
outputNames.add(output.name);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Immutable result access over the durable run ledger.
|
|
77
|
+
*
|
|
78
|
+
* Mutation authority remains in DurableRunStore so result commits share the
|
|
79
|
+
* run's atomic/versioned transaction boundary.
|
|
80
|
+
*/
|
|
81
|
+
export class ImmutableResultStore {
|
|
82
|
+
runStore;
|
|
83
|
+
constructor(runStore) {
|
|
84
|
+
this.runStore = runStore;
|
|
85
|
+
}
|
|
86
|
+
async commit(request) {
|
|
87
|
+
assertCommittedResult(request.result);
|
|
88
|
+
const update = await this.runStore.updateRun({
|
|
89
|
+
run_id: request.result.run_id,
|
|
90
|
+
expected_record_version: request.expected_record_version,
|
|
91
|
+
results: [request.result],
|
|
92
|
+
});
|
|
93
|
+
const result = update.snapshot.results.find((candidate) => candidate.result_id === request.result.result_id);
|
|
94
|
+
if (!result) {
|
|
95
|
+
throw new ResultStoreError("result_not_found", `committed result ${request.result.result_id} was not retained`);
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
result,
|
|
99
|
+
snapshot: update.snapshot,
|
|
100
|
+
committed: update.applied,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
async read(runId, resultId) {
|
|
104
|
+
const snapshot = await this.runStore.loadRun(runId);
|
|
105
|
+
const result = snapshot?.results.find((candidate) => candidate.result_id === resultId) ?? null;
|
|
106
|
+
if (result)
|
|
107
|
+
assertCommittedResult(result);
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
async require(runId, resultId) {
|
|
111
|
+
const result = await this.read(runId, resultId);
|
|
112
|
+
if (!result) {
|
|
113
|
+
throw new ResultStoreError("result_not_found", `committed result ${resultId} was not found in run ${runId}`);
|
|
114
|
+
}
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
async readByAttempt(runId, attemptId) {
|
|
118
|
+
const snapshot = await this.runStore.loadRun(runId);
|
|
119
|
+
const result = snapshot?.results.find((candidate) => candidate.attempt_id === attemptId) ?? null;
|
|
120
|
+
if (result)
|
|
121
|
+
assertCommittedResult(result);
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
async list(runId) {
|
|
125
|
+
const snapshot = await this.runStore.loadRun(runId);
|
|
126
|
+
const results = snapshot?.results ?? [];
|
|
127
|
+
results.forEach(assertCommittedResult);
|
|
128
|
+
return results;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Foundation Runtime contracts.
|
|
3
|
+
*
|
|
4
|
+
* These types describe durable run truth and the boundary to one disposable
|
|
5
|
+
* worker session. They intentionally contain no storage, scheduling, provider,
|
|
6
|
+
* CLI, or Hook behavior.
|
|
7
|
+
*/
|
|
8
|
+
declare const runtimeIdBrand: unique symbol;
|
|
9
|
+
export type RuntimeId<Kind extends string> = string & {
|
|
10
|
+
readonly [runtimeIdBrand]: Kind;
|
|
11
|
+
};
|
|
12
|
+
export type RunId = RuntimeId<"run_id">;
|
|
13
|
+
export type StepId = RuntimeId<"step_id">;
|
|
14
|
+
export type AttemptId = RuntimeId<"attempt_id">;
|
|
15
|
+
export type WorkerSessionId = RuntimeId<"worker_session_id">;
|
|
16
|
+
export type ResultId = RuntimeId<"result_id">;
|
|
17
|
+
export type EventId = RuntimeId<"event_id">;
|
|
18
|
+
export type ClaimToken = RuntimeId<"claim_token">;
|
|
19
|
+
export type JsonValue = null | boolean | number | string | readonly JsonValue[] | {
|
|
20
|
+
readonly [key: string]: JsonValue;
|
|
21
|
+
};
|
|
22
|
+
export interface TaskKey {
|
|
23
|
+
readonly run_id: RunId;
|
|
24
|
+
readonly step_id: StepId;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A worker_session_id is allocated once for one attempt and is carried through
|
|
28
|
+
* its packet, provider events, provider result, and committed result.
|
|
29
|
+
*/
|
|
30
|
+
export interface AttemptIdentity extends TaskKey {
|
|
31
|
+
readonly attempt_id: AttemptId;
|
|
32
|
+
readonly worker_session_id: WorkerSessionId;
|
|
33
|
+
}
|
|
34
|
+
export declare const TASK_STATE_VALUES: readonly ["dependency_blocked", "ready", "claimed", "running", "succeeded", "failed", "skipped", "cancelled"];
|
|
35
|
+
export type TaskState = typeof TASK_STATE_VALUES[number];
|
|
36
|
+
export type TerminalTaskState = Extract<TaskState, "succeeded" | "failed" | "skipped" | "cancelled">;
|
|
37
|
+
/**
|
|
38
|
+
* Failed attempts may return a non-terminal task to ready for a new attempt.
|
|
39
|
+
* Terminal task states are immutable and therefore have no outgoing edges.
|
|
40
|
+
*/
|
|
41
|
+
export declare const TASK_STATE_TRANSITIONS: {
|
|
42
|
+
readonly dependency_blocked: readonly ["ready", "skipped", "cancelled"];
|
|
43
|
+
readonly ready: readonly ["claimed", "skipped", "cancelled"];
|
|
44
|
+
readonly claimed: readonly ["ready", "running", "cancelled"];
|
|
45
|
+
readonly running: readonly ["ready", "succeeded", "failed", "cancelled"];
|
|
46
|
+
readonly succeeded: readonly [];
|
|
47
|
+
readonly failed: readonly [];
|
|
48
|
+
readonly skipped: readonly [];
|
|
49
|
+
readonly cancelled: readonly [];
|
|
50
|
+
};
|
|
51
|
+
export interface TaskRecord extends TaskKey {
|
|
52
|
+
readonly state: TaskState;
|
|
53
|
+
readonly attempt_count: number;
|
|
54
|
+
readonly current_attempt_id: AttemptId | null;
|
|
55
|
+
readonly committed_result_id: ResultId | null;
|
|
56
|
+
readonly created_at: string;
|
|
57
|
+
readonly updated_at: string;
|
|
58
|
+
}
|
|
59
|
+
export interface TaskLease {
|
|
60
|
+
readonly acquired_at: string;
|
|
61
|
+
readonly expires_at: string;
|
|
62
|
+
readonly heartbeat_at: string;
|
|
63
|
+
}
|
|
64
|
+
export interface TaskClaim extends AttemptIdentity {
|
|
65
|
+
readonly claim_token: ClaimToken;
|
|
66
|
+
readonly owner_id: string;
|
|
67
|
+
readonly claimed_record_version: number;
|
|
68
|
+
readonly lease: TaskLease;
|
|
69
|
+
}
|
|
70
|
+
export declare const RETRY_BACKOFF_VALUES: readonly ["none", "linear", "exponential"];
|
|
71
|
+
export type RetryBackoff = typeof RETRY_BACKOFF_VALUES[number];
|
|
72
|
+
export interface RetryPolicy {
|
|
73
|
+
/** Includes the initial attempt. */
|
|
74
|
+
readonly max_attempts: number;
|
|
75
|
+
readonly backoff: RetryBackoff;
|
|
76
|
+
readonly initial_delay_ms: number;
|
|
77
|
+
readonly max_delay_ms: number | null;
|
|
78
|
+
readonly retryable_outcomes: readonly TerminalOutcomeKind[];
|
|
79
|
+
}
|
|
80
|
+
export type RetryDecision = {
|
|
81
|
+
readonly action: "retry";
|
|
82
|
+
readonly prior_attempt_id: AttemptId;
|
|
83
|
+
readonly next_attempt_number: number;
|
|
84
|
+
readonly delay_ms: number;
|
|
85
|
+
readonly requires_new_attempt_id: true;
|
|
86
|
+
readonly requires_new_worker_session_id: true;
|
|
87
|
+
} | {
|
|
88
|
+
readonly action: "stop";
|
|
89
|
+
readonly prior_attempt_id: AttemptId;
|
|
90
|
+
readonly reason: "attempts_exhausted" | "outcome_not_retryable" | "task_cancelled" | "run_terminal";
|
|
91
|
+
};
|
|
92
|
+
export type AttemptPhase = "claimed" | "running" | "terminal";
|
|
93
|
+
export interface AttemptRecord extends AttemptIdentity {
|
|
94
|
+
readonly attempt_number: number;
|
|
95
|
+
readonly phase: AttemptPhase;
|
|
96
|
+
readonly claim_token: ClaimToken;
|
|
97
|
+
/** Direct provider process owned by this attempt, once launch is observed. */
|
|
98
|
+
readonly process_id: number | null;
|
|
99
|
+
readonly started_at: string | null;
|
|
100
|
+
readonly completed_at: string | null;
|
|
101
|
+
readonly terminal_outcome: TerminalOutcome | null;
|
|
102
|
+
}
|
|
103
|
+
export type HandoffSource = {
|
|
104
|
+
readonly result_id: ResultId;
|
|
105
|
+
readonly step_id: StepId;
|
|
106
|
+
readonly output_name: string;
|
|
107
|
+
};
|
|
108
|
+
interface HandoffBindingBase {
|
|
109
|
+
readonly binding_id: string;
|
|
110
|
+
readonly input_name: string;
|
|
111
|
+
readonly required: boolean;
|
|
112
|
+
readonly description: string;
|
|
113
|
+
}
|
|
114
|
+
export interface ReferenceHandoffBinding extends HandoffBindingBase {
|
|
115
|
+
readonly mode: "reference";
|
|
116
|
+
readonly source: HandoffSource;
|
|
117
|
+
readonly reference: {
|
|
118
|
+
readonly kind: "file" | "directory" | "artifact" | "uri";
|
|
119
|
+
readonly value: string;
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export interface QuoteHandoffBinding extends HandoffBindingBase {
|
|
123
|
+
readonly mode: "quote";
|
|
124
|
+
readonly source: HandoffSource;
|
|
125
|
+
/** Exact upstream text. A resolver must not summarize or paraphrase it. */
|
|
126
|
+
readonly exact_text: string;
|
|
127
|
+
readonly selection: {
|
|
128
|
+
readonly start: number | null;
|
|
129
|
+
readonly end: number | null;
|
|
130
|
+
readonly selector: string | null;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
export interface StructuredHandoffBinding extends HandoffBindingBase {
|
|
134
|
+
readonly mode: "structured";
|
|
135
|
+
readonly source: HandoffSource;
|
|
136
|
+
readonly value: JsonValue;
|
|
137
|
+
readonly schema_ref: string | null;
|
|
138
|
+
}
|
|
139
|
+
export interface NoHandoffBinding extends HandoffBindingBase {
|
|
140
|
+
readonly mode: "none";
|
|
141
|
+
readonly required: false;
|
|
142
|
+
readonly reason: string | null;
|
|
143
|
+
}
|
|
144
|
+
export type HandoffBinding = ReferenceHandoffBinding | QuoteHandoffBinding | StructuredHandoffBinding | NoHandoffBinding;
|
|
145
|
+
export type StepOutputKind = "reference" | "text" | "structured";
|
|
146
|
+
export interface DeclaredStepOutput {
|
|
147
|
+
readonly name: string;
|
|
148
|
+
readonly kind: StepOutputKind;
|
|
149
|
+
readonly required: boolean;
|
|
150
|
+
readonly description: string;
|
|
151
|
+
readonly schema_ref: string | null;
|
|
152
|
+
}
|
|
153
|
+
export type StepOutput = {
|
|
154
|
+
readonly name: string;
|
|
155
|
+
readonly kind: "reference";
|
|
156
|
+
readonly reference: {
|
|
157
|
+
readonly kind: "file" | "directory" | "artifact" | "uri";
|
|
158
|
+
readonly value: string;
|
|
159
|
+
};
|
|
160
|
+
} | {
|
|
161
|
+
readonly name: string;
|
|
162
|
+
readonly kind: "text";
|
|
163
|
+
readonly text: string;
|
|
164
|
+
} | {
|
|
165
|
+
readonly name: string;
|
|
166
|
+
readonly kind: "structured";
|
|
167
|
+
readonly value: JsonValue;
|
|
168
|
+
readonly schema_ref: string | null;
|
|
169
|
+
};
|
|
170
|
+
export interface StepPacket extends AttemptIdentity {
|
|
171
|
+
readonly packet_version: 1;
|
|
172
|
+
readonly attempt_number: number;
|
|
173
|
+
readonly role: string;
|
|
174
|
+
readonly constraints: readonly string[];
|
|
175
|
+
readonly standalone_prompt: string;
|
|
176
|
+
readonly input_bindings: readonly HandoffBinding[];
|
|
177
|
+
readonly output_contract: readonly DeclaredStepOutput[];
|
|
178
|
+
readonly workspace: {
|
|
179
|
+
readonly isolation: "none" | "worktree";
|
|
180
|
+
readonly working_directory: string;
|
|
181
|
+
};
|
|
182
|
+
readonly execution: {
|
|
183
|
+
readonly timeout_ms: number | null;
|
|
184
|
+
readonly cancellation_grace_ms: number;
|
|
185
|
+
readonly retry: RetryPolicy;
|
|
186
|
+
};
|
|
187
|
+
readonly created_at: string;
|
|
188
|
+
}
|
|
189
|
+
export declare const TERMINAL_OUTCOME_KIND_VALUES: readonly ["success", "non_zero_exit", "malformed_result", "timeout", "cancelled", "signal", "launch_error"];
|
|
190
|
+
export type TerminalOutcomeKind = typeof TERMINAL_OUTCOME_KIND_VALUES[number];
|
|
191
|
+
export type TerminalOutcome = {
|
|
192
|
+
readonly kind: "success";
|
|
193
|
+
readonly exit_code: 0;
|
|
194
|
+
readonly signal: null;
|
|
195
|
+
} | {
|
|
196
|
+
readonly kind: "non_zero_exit";
|
|
197
|
+
readonly exit_code: number;
|
|
198
|
+
readonly signal: null;
|
|
199
|
+
} | {
|
|
200
|
+
readonly kind: "malformed_result";
|
|
201
|
+
readonly exit_code: number | null;
|
|
202
|
+
readonly signal: null;
|
|
203
|
+
readonly error: string;
|
|
204
|
+
} | {
|
|
205
|
+
readonly kind: "timeout";
|
|
206
|
+
readonly exit_code: number | null;
|
|
207
|
+
readonly signal: string | null;
|
|
208
|
+
readonly timeout_ms: number;
|
|
209
|
+
} | {
|
|
210
|
+
readonly kind: "cancelled";
|
|
211
|
+
readonly exit_code: number | null;
|
|
212
|
+
readonly signal: string | null;
|
|
213
|
+
readonly reason: string | null;
|
|
214
|
+
} | {
|
|
215
|
+
readonly kind: "signal";
|
|
216
|
+
readonly exit_code: null;
|
|
217
|
+
readonly signal: string;
|
|
218
|
+
} | {
|
|
219
|
+
readonly kind: "launch_error";
|
|
220
|
+
readonly exit_code: null;
|
|
221
|
+
readonly signal: null;
|
|
222
|
+
readonly error: string;
|
|
223
|
+
};
|
|
224
|
+
interface ProviderEventBase extends AttemptIdentity {
|
|
225
|
+
readonly event_id: EventId;
|
|
226
|
+
readonly sequence: number;
|
|
227
|
+
readonly occurred_at: string;
|
|
228
|
+
}
|
|
229
|
+
export type ProviderEvent = (ProviderEventBase & {
|
|
230
|
+
readonly type: "launch_started";
|
|
231
|
+
readonly provider: string;
|
|
232
|
+
}) | (ProviderEventBase & {
|
|
233
|
+
readonly type: "session_started";
|
|
234
|
+
readonly provider_session_id: string | null;
|
|
235
|
+
}) | (ProviderEventBase & {
|
|
236
|
+
readonly type: "process_started";
|
|
237
|
+
readonly process_id: number;
|
|
238
|
+
}) | (ProviderEventBase & {
|
|
239
|
+
readonly type: "stdout";
|
|
240
|
+
readonly text: string;
|
|
241
|
+
}) | (ProviderEventBase & {
|
|
242
|
+
readonly type: "stderr";
|
|
243
|
+
readonly text: string;
|
|
244
|
+
}) | (ProviderEventBase & {
|
|
245
|
+
readonly type: "termination_requested";
|
|
246
|
+
readonly reason: "timeout" | "cancellation" | "process_limit";
|
|
247
|
+
}) | (ProviderEventBase & {
|
|
248
|
+
readonly type: "process_exited";
|
|
249
|
+
readonly exit_code: number | null;
|
|
250
|
+
readonly signal: string | null;
|
|
251
|
+
});
|
|
252
|
+
/**
|
|
253
|
+
* Provider-neutral output from one worker launch. Policy such as retry, skip,
|
|
254
|
+
* or run failure is deliberately not represented here.
|
|
255
|
+
*/
|
|
256
|
+
export interface ProviderExecutionResult extends AttemptIdentity {
|
|
257
|
+
readonly provider: string;
|
|
258
|
+
readonly provider_session_id: string | null;
|
|
259
|
+
readonly started_at: string;
|
|
260
|
+
readonly completed_at: string;
|
|
261
|
+
readonly outcome: TerminalOutcome;
|
|
262
|
+
readonly outputs: readonly StepOutput[];
|
|
263
|
+
readonly stdout_reference: string | null;
|
|
264
|
+
readonly stderr_reference: string | null;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* The immutable, committed result used by downstream handoffs. A retry commits
|
|
268
|
+
* a different result_id for a different attempt_id and worker_session_id.
|
|
269
|
+
*/
|
|
270
|
+
export interface CommittedStepResult extends ProviderExecutionResult {
|
|
271
|
+
readonly result_id: ResultId;
|
|
272
|
+
readonly commit_status: "committed";
|
|
273
|
+
readonly committed_at: string;
|
|
274
|
+
}
|
|
275
|
+
export declare const RUNTIME_EVENT_TYPE_VALUES: readonly ["run_created", "run_cancel_requested", "run_terminal", "task_state_changed", "task_claimed", "task_lease_renewed", "task_claim_released", "attempt_started", "attempt_terminal", "handoff_resolved", "result_committed"];
|
|
276
|
+
export type RuntimeEventType = typeof RUNTIME_EVENT_TYPE_VALUES[number];
|
|
277
|
+
export interface RuntimeEvent {
|
|
278
|
+
readonly event_id: EventId;
|
|
279
|
+
readonly run_id: RunId;
|
|
280
|
+
readonly step_id: StepId | null;
|
|
281
|
+
readonly attempt_id: AttemptId | null;
|
|
282
|
+
readonly worker_session_id: WorkerSessionId | null;
|
|
283
|
+
readonly result_id: ResultId | null;
|
|
284
|
+
readonly sequence: number;
|
|
285
|
+
readonly idempotency_key: string;
|
|
286
|
+
readonly type: RuntimeEventType;
|
|
287
|
+
readonly occurred_at: string;
|
|
288
|
+
readonly payload: JsonValue;
|
|
289
|
+
}
|
|
290
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Foundation Runtime contracts.
|
|
3
|
+
*
|
|
4
|
+
* These types describe durable run truth and the boundary to one disposable
|
|
5
|
+
* worker session. They intentionally contain no storage, scheduling, provider,
|
|
6
|
+
* CLI, or Hook behavior.
|
|
7
|
+
*/
|
|
8
|
+
export const TASK_STATE_VALUES = [
|
|
9
|
+
"dependency_blocked",
|
|
10
|
+
"ready",
|
|
11
|
+
"claimed",
|
|
12
|
+
"running",
|
|
13
|
+
"succeeded",
|
|
14
|
+
"failed",
|
|
15
|
+
"skipped",
|
|
16
|
+
"cancelled",
|
|
17
|
+
];
|
|
18
|
+
/**
|
|
19
|
+
* Failed attempts may return a non-terminal task to ready for a new attempt.
|
|
20
|
+
* Terminal task states are immutable and therefore have no outgoing edges.
|
|
21
|
+
*/
|
|
22
|
+
export const TASK_STATE_TRANSITIONS = {
|
|
23
|
+
dependency_blocked: ["ready", "skipped", "cancelled"],
|
|
24
|
+
ready: ["claimed", "skipped", "cancelled"],
|
|
25
|
+
claimed: ["ready", "running", "cancelled"],
|
|
26
|
+
running: ["ready", "succeeded", "failed", "cancelled"],
|
|
27
|
+
succeeded: [],
|
|
28
|
+
failed: [],
|
|
29
|
+
skipped: [],
|
|
30
|
+
cancelled: [],
|
|
31
|
+
};
|
|
32
|
+
export const RETRY_BACKOFF_VALUES = [
|
|
33
|
+
"none",
|
|
34
|
+
"linear",
|
|
35
|
+
"exponential",
|
|
36
|
+
];
|
|
37
|
+
export const TERMINAL_OUTCOME_KIND_VALUES = [
|
|
38
|
+
"success",
|
|
39
|
+
"non_zero_exit",
|
|
40
|
+
"malformed_result",
|
|
41
|
+
"timeout",
|
|
42
|
+
"cancelled",
|
|
43
|
+
"signal",
|
|
44
|
+
"launch_error",
|
|
45
|
+
];
|
|
46
|
+
export const RUNTIME_EVENT_TYPE_VALUES = [
|
|
47
|
+
"run_created",
|
|
48
|
+
"run_cancel_requested",
|
|
49
|
+
"run_terminal",
|
|
50
|
+
"task_state_changed",
|
|
51
|
+
"task_claimed",
|
|
52
|
+
"task_lease_renewed",
|
|
53
|
+
"task_claim_released",
|
|
54
|
+
"attempt_started",
|
|
55
|
+
"attempt_terminal",
|
|
56
|
+
"handoff_resolved",
|
|
57
|
+
"result_committed",
|
|
58
|
+
];
|