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,187 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { canonicalizeJson } from "../runtime/canonical-json.js";
|
|
3
|
+
import { isCurrentClaudeSubagentStopInput } from "./schema.js";
|
|
4
|
+
export const CLAUDE_HOST_EVENT_SCHEMA_VERSION = "intentdna.claude_host_event.v1";
|
|
5
|
+
export const CLAUDE_HOST_EVENT_STDERR_PREFIX = "INTENTDNA_CLAUDE_HOST_EVENT_V1 ";
|
|
6
|
+
export class ClaudeHostEventError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
constructor(code, message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "ClaudeHostEventError";
|
|
11
|
+
this.code = code;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function nonEmpty(value) {
|
|
15
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
16
|
+
}
|
|
17
|
+
function digest(value) {
|
|
18
|
+
return `sha256:${createHash("sha256").update(value, "utf8").digest("hex")}`;
|
|
19
|
+
}
|
|
20
|
+
function hostSessionId(input) {
|
|
21
|
+
if (nonEmpty(input.session_id))
|
|
22
|
+
return input.session_id;
|
|
23
|
+
if (nonEmpty(input.sessionId))
|
|
24
|
+
return input.sessionId;
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
export function buildClaudeSubagentStopHostEvent(input, state) {
|
|
28
|
+
if (!state?.active
|
|
29
|
+
|| !nonEmpty(state.run_id)
|
|
30
|
+
|| !nonEmpty(state.step_id)
|
|
31
|
+
|| !nonEmpty(state.attempt_id)
|
|
32
|
+
|| !nonEmpty(state.worker_session_id)
|
|
33
|
+
|| !isCurrentClaudeSubagentStopInput(input)
|
|
34
|
+
|| input.agent_id !== state.worker_session_id) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
schema_version: CLAUDE_HOST_EVENT_SCHEMA_VERSION,
|
|
39
|
+
hook_event_name: "SubagentStop",
|
|
40
|
+
session_id: hostSessionId(input),
|
|
41
|
+
transcript_path: input.transcript_path,
|
|
42
|
+
agent_id: input.agent_id,
|
|
43
|
+
agent_type: input.agent_type,
|
|
44
|
+
agent_transcript_path: input.agent_transcript_path,
|
|
45
|
+
last_assistant_message_digest: digest(input.last_assistant_message),
|
|
46
|
+
run_id: state.run_id,
|
|
47
|
+
step_id: state.step_id,
|
|
48
|
+
attempt_id: state.attempt_id,
|
|
49
|
+
worker_session_id: state.worker_session_id,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function encodeClaudeHostEventForStderr(event) {
|
|
53
|
+
return `${CLAUDE_HOST_EVENT_STDERR_PREFIX}${Buffer.from(canonicalizeJson(event), "utf8").toString("base64url")}\n`;
|
|
54
|
+
}
|
|
55
|
+
function parseEnvelope(value) {
|
|
56
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
57
|
+
throw new ClaudeHostEventError("malformed_envelope", "Claude host event envelope must be an object");
|
|
58
|
+
}
|
|
59
|
+
const event = value;
|
|
60
|
+
const expectedKeys = [
|
|
61
|
+
"agent_id",
|
|
62
|
+
"agent_transcript_path",
|
|
63
|
+
"agent_type",
|
|
64
|
+
"attempt_id",
|
|
65
|
+
"hook_event_name",
|
|
66
|
+
"last_assistant_message_digest",
|
|
67
|
+
"run_id",
|
|
68
|
+
"schema_version",
|
|
69
|
+
"session_id",
|
|
70
|
+
"step_id",
|
|
71
|
+
"transcript_path",
|
|
72
|
+
"worker_session_id",
|
|
73
|
+
];
|
|
74
|
+
if (Object.keys(event).sort().join("\0") !== expectedKeys.join("\0")) {
|
|
75
|
+
throw new ClaudeHostEventError("malformed_envelope", "Claude host event envelope fields are not the fixed v1 projection");
|
|
76
|
+
}
|
|
77
|
+
if (event.schema_version !== CLAUDE_HOST_EVENT_SCHEMA_VERSION
|
|
78
|
+
|| event.hook_event_name !== "SubagentStop"
|
|
79
|
+
|| !nonEmpty(event.session_id)
|
|
80
|
+
|| !nonEmpty(event.transcript_path)
|
|
81
|
+
|| !nonEmpty(event.agent_id)
|
|
82
|
+
|| !nonEmpty(event.agent_type)
|
|
83
|
+
|| !nonEmpty(event.agent_transcript_path)
|
|
84
|
+
|| !nonEmpty(event.last_assistant_message_digest)
|
|
85
|
+
|| !/^sha256:[0-9a-f]{64}$/.test(event.last_assistant_message_digest)
|
|
86
|
+
|| !nonEmpty(event.run_id)
|
|
87
|
+
|| !nonEmpty(event.step_id)
|
|
88
|
+
|| !nonEmpty(event.attempt_id)
|
|
89
|
+
|| !nonEmpty(event.worker_session_id)) {
|
|
90
|
+
throw new ClaudeHostEventError("malformed_envelope", "Claude host event envelope is invalid");
|
|
91
|
+
}
|
|
92
|
+
if (event.agent_id !== event.worker_session_id) {
|
|
93
|
+
throw new ClaudeHostEventError("identity_mismatch", "Claude agent_id does not match the Controller worker_session_id");
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
schema_version: CLAUDE_HOST_EVENT_SCHEMA_VERSION,
|
|
97
|
+
hook_event_name: "SubagentStop",
|
|
98
|
+
session_id: event.session_id,
|
|
99
|
+
transcript_path: event.transcript_path,
|
|
100
|
+
agent_id: event.agent_id,
|
|
101
|
+
agent_type: event.agent_type,
|
|
102
|
+
agent_transcript_path: event.agent_transcript_path,
|
|
103
|
+
last_assistant_message_digest: event.last_assistant_message_digest,
|
|
104
|
+
run_id: event.run_id,
|
|
105
|
+
step_id: event.step_id,
|
|
106
|
+
attempt_id: event.attempt_id,
|
|
107
|
+
worker_session_id: event.worker_session_id,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function assertExpectedContext(value, event, expected) {
|
|
111
|
+
const mismatches = [
|
|
112
|
+
["hook_id", value.hook_id, expected.hook_id],
|
|
113
|
+
["session_id", event.session_id, expected.session_id],
|
|
114
|
+
["run_id", event.run_id, expected.run_id],
|
|
115
|
+
["step_id", event.step_id, expected.step_id],
|
|
116
|
+
["attempt_id", event.attempt_id, expected.attempt_id],
|
|
117
|
+
["worker_session_id", event.worker_session_id, expected.worker_session_id],
|
|
118
|
+
].filter(([, actual, wanted]) => actual !== wanted);
|
|
119
|
+
if (mismatches.length > 0) {
|
|
120
|
+
throw new ClaudeHostEventError("context_mismatch", `Claude hook response does not match the Controller expectation: ${mismatches
|
|
121
|
+
.map(([field]) => field)
|
|
122
|
+
.join(", ")}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
export async function parseClaudeSubagentStopHookResponse(frame, expected, consumptionStore) {
|
|
126
|
+
if (frame === null || typeof frame !== "object" || Array.isArray(frame)) {
|
|
127
|
+
throw new ClaudeHostEventError("invalid_frame", "Claude hook response frame must be an object");
|
|
128
|
+
}
|
|
129
|
+
const value = frame;
|
|
130
|
+
if (value.type !== "system"
|
|
131
|
+
|| value.subtype !== "hook_response"
|
|
132
|
+
|| value.hook_event !== "SubagentStop"
|
|
133
|
+
|| value.outcome !== "success"
|
|
134
|
+
|| value.exit_code !== 0
|
|
135
|
+
|| !nonEmpty(value.hook_id)
|
|
136
|
+
|| !nonEmpty(value.session_id)
|
|
137
|
+
|| !nonEmpty(value.uuid)
|
|
138
|
+
|| typeof value.stderr !== "string") {
|
|
139
|
+
throw new ClaudeHostEventError("invalid_frame", "Claude hook response frame is not an accepted SubagentStop success");
|
|
140
|
+
}
|
|
141
|
+
const encoded = value.stderr
|
|
142
|
+
.split(/\r?\n/u)
|
|
143
|
+
.filter((line) => line.startsWith(CLAUDE_HOST_EVENT_STDERR_PREFIX))
|
|
144
|
+
.map((line) => line.slice(CLAUDE_HOST_EVENT_STDERR_PREFIX.length));
|
|
145
|
+
if (encoded.length === 0) {
|
|
146
|
+
throw new ClaudeHostEventError("missing_envelope", "Claude hook response has no IntentDNA host event envelope");
|
|
147
|
+
}
|
|
148
|
+
if (encoded.length !== 1) {
|
|
149
|
+
throw new ClaudeHostEventError("duplicate_envelope", "Claude hook response has multiple IntentDNA host event envelopes");
|
|
150
|
+
}
|
|
151
|
+
const payload = encoded[0];
|
|
152
|
+
if (!/^[A-Za-z0-9_-]+$/u.test(payload)) {
|
|
153
|
+
throw new ClaudeHostEventError("malformed_envelope", "Claude host event envelope encoding is invalid");
|
|
154
|
+
}
|
|
155
|
+
let parsed;
|
|
156
|
+
try {
|
|
157
|
+
const decoded = Buffer.from(payload, "base64url").toString("utf8");
|
|
158
|
+
if (Buffer.from(decoded, "utf8").toString("base64url") !== payload) {
|
|
159
|
+
throw new Error("non-canonical base64url");
|
|
160
|
+
}
|
|
161
|
+
parsed = JSON.parse(decoded);
|
|
162
|
+
if (canonicalizeJson(parsed) !== decoded) {
|
|
163
|
+
throw new Error("non-canonical JSON");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
throw new ClaudeHostEventError("malformed_envelope", `Claude host event envelope cannot be decoded: ${error instanceof Error ? error.message : String(error)}`);
|
|
168
|
+
}
|
|
169
|
+
const event = parseEnvelope(parsed);
|
|
170
|
+
if (event.session_id !== value.session_id) {
|
|
171
|
+
throw new ClaudeHostEventError("identity_mismatch", "Claude hook frame session_id does not match its host event envelope");
|
|
172
|
+
}
|
|
173
|
+
assertExpectedContext(value, event, expected);
|
|
174
|
+
const consumptionKey = {
|
|
175
|
+
session_id: event.session_id,
|
|
176
|
+
frame_uuid: value.uuid,
|
|
177
|
+
hook_id: value.hook_id,
|
|
178
|
+
};
|
|
179
|
+
if (!await consumptionStore.consume(consumptionKey)) {
|
|
180
|
+
throw new ClaudeHostEventError("replayed_frame", "Claude hook response frame was already consumed");
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
hook_id: value.hook_id,
|
|
184
|
+
frame_uuid: value.uuid,
|
|
185
|
+
event,
|
|
186
|
+
};
|
|
187
|
+
}
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export { type HookEvent, type HookInput, type HookOutput, type HookInputBase, type PreToolUseInput, type PostToolUseInput, type UserPromptSubmitInput, type SubagentStopInput, type PreCompactInput, type NotificationInput, type StopInput, type SessionStartInput as SessionStartHookInput, type StdinReadResult, readStdin, readStdinResult, writeOutput, allowOutput, blockOutput, escalateOutput, silentOutput, } from "./protocol.js";
|
|
8
8
|
export { type HookFailureDisposition, type HookFailureKind, hookFailureDisposition, hookFailureOutput, } from "./enforcement-boundary.js";
|
|
9
|
+
export { type ClaudeSubagentStopHostEvent, type ClaudeSubagentStopHostEventExpectation, type ClaudeHookResponseFrame, type ClaudeHostEventConsumptionKey, type ClaudeHostEventConsumptionStore, type VerifiedClaudeSubagentStopHostEvent, type ClaudeHostEventErrorCode, ClaudeHostEventError, CLAUDE_HOST_EVENT_SCHEMA_VERSION, CLAUDE_HOST_EVENT_STDERR_PREFIX, buildClaudeSubagentStopHostEvent, encodeClaudeHostEventForStderr, parseClaudeSubagentStopHookResponse, } from "./host-event.js";
|
|
9
10
|
export { type EnforceState, type SessionStartInput, type StopEnforceInput, type StopWorkflowContext, enforcePreToolUse, enforcePostToolUse, enforceUserPromptSubmit, enforceSubagentStop, enforcePreCompact, enforceNotification, enforceSessionStart, enforceStop, checkWriteAllowed, globToPrefix, evaluateGateCondition, resolveToolTarget, } from "./enforce.js";
|
|
10
11
|
export { type DNAWorkflowState, type AuditEntry, resolveStateDir, readWorkflowState, writeWorkflowState, clearWorkflowState, cleanupWorkerHookState, appendAudit, } from "./state.js";
|
|
11
12
|
export { type SessionSummary, computeSummary, formatSummary, } from "./cli.js";
|
package/dist/hooks/index.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// Protocol types and I/O
|
|
8
8
|
export { readStdin, readStdinResult, writeOutput, allowOutput, blockOutput, escalateOutput, silentOutput, } from "./protocol.js";
|
|
9
9
|
export { hookFailureDisposition, hookFailureOutput, } from "./enforcement-boundary.js";
|
|
10
|
+
export { ClaudeHostEventError, CLAUDE_HOST_EVENT_SCHEMA_VERSION, CLAUDE_HOST_EVENT_STDERR_PREFIX, buildClaudeSubagentStopHostEvent, encodeClaudeHostEventForStderr, parseClaudeSubagentStopHookResponse, } from "./host-event.js";
|
|
10
11
|
// Enforcement engine
|
|
11
12
|
export { enforcePreToolUse, enforcePostToolUse, enforceUserPromptSubmit, enforceSubagentStop, enforcePreCompact, enforceNotification, enforceSessionStart, enforceStop, checkWriteAllowed, globToPrefix, evaluateGateCondition, resolveToolTarget, } from "./enforce.js";
|
|
12
13
|
// State management
|
package/dist/hooks/protocol.d.ts
CHANGED
|
@@ -11,6 +11,10 @@ export type { HookEvent } from "./event-registry.js";
|
|
|
11
11
|
export interface HookInputBase {
|
|
12
12
|
cwd?: string;
|
|
13
13
|
sessionId?: string;
|
|
14
|
+
transcript_path?: string;
|
|
15
|
+
permission_mode?: string;
|
|
16
|
+
hook_event_name?: string;
|
|
17
|
+
agent_id?: string;
|
|
14
18
|
worker_session_id?: string;
|
|
15
19
|
run_id?: string;
|
|
16
20
|
step_id?: string;
|
|
@@ -33,6 +37,9 @@ export interface UserPromptSubmitInput extends HookInputBase {
|
|
|
33
37
|
export interface SubagentStopInput extends HookInputBase {
|
|
34
38
|
agent_name?: string;
|
|
35
39
|
agent_type?: string;
|
|
40
|
+
agent_transcript_path?: string;
|
|
41
|
+
last_assistant_message?: string;
|
|
42
|
+
stop_hook_active?: boolean;
|
|
36
43
|
}
|
|
37
44
|
export interface PreCompactInput extends HookInputBase {
|
|
38
45
|
}
|
package/dist/hooks/schema.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export interface ValidationResult {
|
|
|
8
8
|
errors: string[];
|
|
9
9
|
normalized?: Record<string, unknown>;
|
|
10
10
|
}
|
|
11
|
+
export declare function isCurrentClaudeSubagentStopInput(input: Record<string, unknown>): boolean;
|
|
11
12
|
/**
|
|
12
13
|
* Validate hook input against expected schema for each event type.
|
|
13
14
|
* Returns normalized input (camelCase) on success.
|
package/dist/hooks/schema.js
CHANGED
|
@@ -4,6 +4,30 @@
|
|
|
4
4
|
* Classification of invalid input belongs to the event boundary.
|
|
5
5
|
*/
|
|
6
6
|
import { isHookEventSupportedBySurface } from "./event-registry.js";
|
|
7
|
+
function nonEmptyString(value) {
|
|
8
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
9
|
+
}
|
|
10
|
+
export function isCurrentClaudeSubagentStopInput(input) {
|
|
11
|
+
const parentSessionId = nonEmptyString(input.session_id)
|
|
12
|
+
? input.session_id
|
|
13
|
+
: nonEmptyString(input.sessionId)
|
|
14
|
+
? input.sessionId
|
|
15
|
+
: undefined;
|
|
16
|
+
return parentSessionId !== undefined
|
|
17
|
+
&& !(nonEmptyString(input.session_id)
|
|
18
|
+
&& nonEmptyString(input.sessionId)
|
|
19
|
+
&& input.session_id !== input.sessionId)
|
|
20
|
+
&& input.hook_event_name === "SubagentStop"
|
|
21
|
+
&& nonEmptyString(input.transcript_path)
|
|
22
|
+
&& nonEmptyString(input.permission_mode)
|
|
23
|
+
&& typeof input.stop_hook_active === "boolean"
|
|
24
|
+
&& nonEmptyString(input.agent_id)
|
|
25
|
+
&& nonEmptyString(input.agent_type)
|
|
26
|
+
&& nonEmptyString(input.agent_transcript_path)
|
|
27
|
+
&& typeof input.last_assistant_message === "string"
|
|
28
|
+
&& (input.worker_session_id === undefined
|
|
29
|
+
|| input.worker_session_id === input.agent_id);
|
|
30
|
+
}
|
|
7
31
|
/**
|
|
8
32
|
* Validate hook input against expected schema for each event type.
|
|
9
33
|
* Returns normalized input (camelCase) on success.
|
|
@@ -15,6 +39,26 @@ export function validateHookInput(event, raw) {
|
|
|
15
39
|
const input = raw;
|
|
16
40
|
const errors = [];
|
|
17
41
|
const normalized = { ...input };
|
|
42
|
+
for (const field of [
|
|
43
|
+
"session_id",
|
|
44
|
+
"sessionId",
|
|
45
|
+
"worker_session_id",
|
|
46
|
+
"transcript_path",
|
|
47
|
+
"permission_mode",
|
|
48
|
+
"hook_event_name",
|
|
49
|
+
]) {
|
|
50
|
+
if (input[field] !== undefined && typeof input[field] !== "string") {
|
|
51
|
+
errors.push(`${field} must be a string if present`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (typeof input.session_id === "string"
|
|
55
|
+
&& typeof input.sessionId === "string"
|
|
56
|
+
&& input.session_id !== input.sessionId) {
|
|
57
|
+
errors.push("session_id and sessionId must match when both are present");
|
|
58
|
+
}
|
|
59
|
+
if (typeof input.hook_event_name === "string" && input.hook_event_name !== event) {
|
|
60
|
+
errors.push(`hook_event_name must match ${event}`);
|
|
61
|
+
}
|
|
18
62
|
// The Controller's disposable worker session is the Hook context key.
|
|
19
63
|
const sessionId = normalizeSessionId(input);
|
|
20
64
|
if (sessionId !== undefined) {
|
|
@@ -46,7 +90,51 @@ export function validateHookInput(event, raw) {
|
|
|
46
90
|
// session_id is optional but should be normalized if present
|
|
47
91
|
break;
|
|
48
92
|
case "SubagentStop":
|
|
49
|
-
|
|
93
|
+
for (const field of [
|
|
94
|
+
"agent_id",
|
|
95
|
+
"agent_name",
|
|
96
|
+
"agent_type",
|
|
97
|
+
"agent_transcript_path",
|
|
98
|
+
"last_assistant_message",
|
|
99
|
+
]) {
|
|
100
|
+
if (input[field] !== undefined && typeof input[field] !== "string") {
|
|
101
|
+
errors.push(`SubagentStop: ${field} must be a string if present`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (typeof input.agent_id === "string" && input.agent_id.trim().length === 0) {
|
|
105
|
+
errors.push("SubagentStop: agent_id must be non-empty if present");
|
|
106
|
+
}
|
|
107
|
+
if (typeof input.agent_id === "string" && input.agent_id.trim().length > 0) {
|
|
108
|
+
for (const field of [
|
|
109
|
+
"transcript_path",
|
|
110
|
+
"permission_mode",
|
|
111
|
+
"agent_type",
|
|
112
|
+
"agent_transcript_path",
|
|
113
|
+
]) {
|
|
114
|
+
if (typeof input[field] !== "string" || input[field].trim().length === 0) {
|
|
115
|
+
errors.push(`SubagentStop: ${field} must be a non-empty string when agent_id is present`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!nonEmptyString(input.session_id) && !nonEmptyString(input.sessionId)) {
|
|
119
|
+
errors.push("SubagentStop: a non-empty parent session_id is required when agent_id is present");
|
|
120
|
+
}
|
|
121
|
+
if (input.hook_event_name !== "SubagentStop") {
|
|
122
|
+
errors.push("SubagentStop: hook_event_name must be SubagentStop when agent_id is present");
|
|
123
|
+
}
|
|
124
|
+
if (typeof input.last_assistant_message !== "string") {
|
|
125
|
+
errors.push("SubagentStop: last_assistant_message must be a string when agent_id is present");
|
|
126
|
+
}
|
|
127
|
+
if (typeof input.stop_hook_active !== "boolean") {
|
|
128
|
+
errors.push("SubagentStop: stop_hook_active must be a boolean when agent_id is present");
|
|
129
|
+
}
|
|
130
|
+
if (typeof input.worker_session_id === "string"
|
|
131
|
+
&& input.worker_session_id !== input.agent_id) {
|
|
132
|
+
errors.push("SubagentStop: worker_session_id must match agent_id when both are present");
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (input.stop_hook_active !== undefined && typeof input.stop_hook_active !== "boolean") {
|
|
136
|
+
errors.push("SubagentStop: stop_hook_active must be a boolean if present");
|
|
137
|
+
}
|
|
50
138
|
break;
|
|
51
139
|
case "Notification":
|
|
52
140
|
if (typeof input.title !== "string" && input.title !== undefined) {
|
package/dist/hooks/state.d.ts
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* All write operations use atomic temp+rename pattern.
|
|
11
11
|
*/
|
|
12
12
|
import type { ArtifactFact, ArtifactKey, ArtifactManifest, CompletedArtifactEntry, HandoffArtifact, HandoffType, VerifierKind, VerifierSeverity, VerifierWhen } from "../schema/types.js";
|
|
13
|
+
import type { CompiledIRFile } from "../runtime/plugin-adapter.js";
|
|
14
|
+
import type { ClaimToken } from "../runtime/run-contracts.js";
|
|
13
15
|
import { type RuntimeDecisionEvent } from "../governance/index.js";
|
|
14
16
|
export declare const EVIDENCE_CAPTURE_SCHEMA_VERSION = "intentdna.evidence_capture.v1";
|
|
15
17
|
/** Workflow state written by runtime and read by hooks */
|
|
@@ -108,6 +110,25 @@ export declare function safePathComponent(value: string, label: string): string;
|
|
|
108
110
|
* Without: `.dna/state/`
|
|
109
111
|
*/
|
|
110
112
|
export declare function resolveStateDir(projectDir: string, sessionId?: string): string;
|
|
113
|
+
interface CanonicalHookRuntimeProjectionBase {
|
|
114
|
+
readonly run_id: string;
|
|
115
|
+
readonly step_id: string;
|
|
116
|
+
readonly attempt_id: string;
|
|
117
|
+
readonly worker_session_id: string;
|
|
118
|
+
readonly compiled_ir_hash: string;
|
|
119
|
+
readonly compiled_ir: CompiledIRFile;
|
|
120
|
+
}
|
|
121
|
+
export interface CanonicalHookRuntimeProjectionV1 extends CanonicalHookRuntimeProjectionBase {
|
|
122
|
+
readonly schema_version: "intentdna.canonical_hook_projection.v1";
|
|
123
|
+
}
|
|
124
|
+
export interface CanonicalHookRuntimeProjectionV2 extends CanonicalHookRuntimeProjectionBase {
|
|
125
|
+
readonly schema_version: "intentdna.canonical_hook_projection.v2";
|
|
126
|
+
readonly lease_token: ClaimToken;
|
|
127
|
+
readonly owner_id: string;
|
|
128
|
+
}
|
|
129
|
+
export type CanonicalHookRuntimeProjection = CanonicalHookRuntimeProjectionV1 | CanonicalHookRuntimeProjectionV2;
|
|
130
|
+
export declare function resolveSessionCompiledIRPath(projectDir: string, workerSessionId: string): string;
|
|
131
|
+
export declare function writeCanonicalHookRuntimeProjection(projectDir: string, projection: CanonicalHookRuntimeProjection): Promise<void>;
|
|
111
132
|
export declare const ARTIFACT_RESOLVER_VERSION = 1;
|
|
112
133
|
export declare function isRequiredArtifact(artifact: HandoffArtifact): boolean;
|
|
113
134
|
export declare function artifactIdentity(artifact: HandoffArtifact, resolvedPath?: string): string | null;
|
|
@@ -237,7 +258,7 @@ export interface TraceEntry {
|
|
|
237
258
|
* Fail-open: never throws.
|
|
238
259
|
*/
|
|
239
260
|
export declare function appendTrace(projectDir: string, entry: TraceEntry, sessionId?: string): Promise<void>;
|
|
240
|
-
export declare function appendEvidenceCaptureEvent(projectDir: string, event: EvidenceCaptureEvent): Promise<
|
|
261
|
+
export declare function appendEvidenceCaptureEvent(projectDir: string, event: EvidenceCaptureEvent): Promise<boolean>;
|
|
241
262
|
export declare function appendRuntimeDecisionEvent(projectDir: string, event: RuntimeDecisionEvent, sessionId?: string): Promise<void>;
|
|
242
263
|
export declare function readRuntimeDecisionEvents(projectDir: string, days?: number, sessionId?: string): Promise<RuntimeDecisionEvent[]>;
|
|
243
264
|
export declare function readTraces(projectDir: string, days?: number, sessionId?: string): Promise<TraceEntry[]>;
|
|
@@ -265,3 +286,4 @@ export declare function cleanStaleState(projectDir: string): Promise<number>;
|
|
|
265
286
|
* or removed by this operation.
|
|
266
287
|
*/
|
|
267
288
|
export declare function cleanupWorkerHookState(projectDir: string, workerSessionId: string): Promise<void>;
|
|
289
|
+
export {};
|
package/dist/hooks/state.js
CHANGED
|
@@ -17,6 +17,7 @@ import { CASCADE_LAYER_NAMES, RUNTIME_DECISION_EVENT_SCHEMA_VERSION } from "../g
|
|
|
17
17
|
export const EVIDENCE_CAPTURE_SCHEMA_VERSION = "intentdna.evidence_capture.v1";
|
|
18
18
|
// ── Constants ──────────────────────────────────────────────
|
|
19
19
|
const WORKFLOW_FILE = "workflow.json";
|
|
20
|
+
const SESSION_COMPILED_IR_FILE = "compiled-ir.json";
|
|
20
21
|
const DEFAULT_STALENESS_MS = 2 * 60 * 60 * 1000; // 2 hours
|
|
21
22
|
const SAFE_PATH_COMPONENT = /^[A-Za-z0-9._-]{1,128}$/;
|
|
22
23
|
const USER_ARTIFACT_PATH_ARGUMENTS = new Set(["ARGUMENTS", "arguments", "task_id"]);
|
|
@@ -47,6 +48,12 @@ export function resolveStateDir(projectDir, sessionId) {
|
|
|
47
48
|
}
|
|
48
49
|
return base;
|
|
49
50
|
}
|
|
51
|
+
export function resolveSessionCompiledIRPath(projectDir, workerSessionId) {
|
|
52
|
+
return join(resolveStateDir(projectDir, safePathComponent(workerSessionId, "worker_session_id")), SESSION_COMPILED_IR_FILE);
|
|
53
|
+
}
|
|
54
|
+
export async function writeCanonicalHookRuntimeProjection(projectDir, projection) {
|
|
55
|
+
await atomicWrite(resolveSessionCompiledIRPath(projectDir, projection.worker_session_id), `${JSON.stringify(projection, null, 2)}\n`);
|
|
56
|
+
}
|
|
50
57
|
export const ARTIFACT_RESOLVER_VERSION = 1;
|
|
51
58
|
export function isRequiredArtifact(artifact) {
|
|
52
59
|
return artifact.required !== false;
|
|
@@ -847,9 +854,11 @@ export async function appendEvidenceCaptureEvent(projectDir, event) {
|
|
|
847
854
|
const captureDir = join(projectDir, ".dna", EVIDENCE_CAPTURE_DIR);
|
|
848
855
|
await mkdir(captureDir, { recursive: true });
|
|
849
856
|
await atomicAppend(join(captureDir, evidenceCaptureFileName(date)), line);
|
|
857
|
+
return true;
|
|
850
858
|
}
|
|
851
859
|
catch {
|
|
852
860
|
// Fail-open: evidence capture write failure never affects hook execution
|
|
861
|
+
return false;
|
|
853
862
|
}
|
|
854
863
|
}
|
|
855
864
|
export async function appendRuntimeDecisionEvent(projectDir, event, sessionId) {
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/mcp/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import { createEnforceTools } from "./tools-enforce.js";
|
|
|
17
17
|
import { createObservabilityTools } from "./tools-observability.js";
|
|
18
18
|
import { createContextTools } from "./tools-context.js";
|
|
19
19
|
import { createArtifactTools } from "./tools-artifacts.js";
|
|
20
|
+
import { createRunTools } from "./tools-run.js";
|
|
20
21
|
// Parse args
|
|
21
22
|
const args = process.argv.slice(2);
|
|
22
23
|
let projectDir = process.cwd();
|
|
@@ -42,5 +43,6 @@ const tools = [
|
|
|
42
43
|
...createArtifactTools(projectDir),
|
|
43
44
|
...createEnforceTools(projectDir),
|
|
44
45
|
...createObservabilityTools(projectDir),
|
|
46
|
+
...createRunTools(projectDir),
|
|
45
47
|
];
|
|
46
48
|
createMCPServer({ name: "intentdna", version }, tools);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { HarnessPullAdapter } from "../runtime/harness-pull-adapter.js";
|
|
2
|
+
import type { ToolDef } from "./server.js";
|
|
3
|
+
export declare function createMCPHarnessAdapter(projectDirectory: string): HarnessPullAdapter;
|
|
4
|
+
export declare function createRunTools(projectDir: string): ToolDef[];
|