intentdna 1.2.2 → 1.3.0
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/hooks/hooks.json +12 -0
- package/.claude-plugin/plugin.json +5 -0
- package/dist/audit/index.d.ts +0 -1
- package/dist/audit/index.js +0 -1
- package/dist/cli/commands/compile.d.ts +0 -1
- package/dist/cli/commands/compile.js +0 -1
- package/dist/cli/commands/epigenetic.d.ts +0 -1
- package/dist/cli/commands/epigenetic.js +0 -1
- package/dist/cli/commands/evolve.d.ts +0 -1
- package/dist/cli/commands/evolve.js +0 -1
- package/dist/cli/commands/guard.d.ts +0 -1
- package/dist/cli/commands/guard.js +0 -3
- package/dist/cli/commands/import.d.ts +0 -1
- package/dist/cli/commands/import.js +0 -1
- package/dist/cli/commands/init.d.ts +0 -1
- package/dist/cli/commands/init.js +0 -1
- package/dist/cli/commands/run.d.ts +0 -1
- package/dist/cli/commands/run.js +4 -27
- package/dist/cli/commands/setup.d.ts +9 -0
- package/dist/cli/commands/setup.js +42 -0
- package/dist/cli/commands/show.d.ts +0 -1
- package/dist/cli/commands/show.js +0 -1
- package/dist/cli/commands/sync.d.ts +13 -1
- package/dist/cli/commands/sync.js +158 -33
- package/dist/cli/commands/validate.d.ts +0 -1
- package/dist/cli/commands/validate.js +0 -1
- package/dist/cli/commands/verify.d.ts +0 -1
- package/dist/cli/commands/verify.js +0 -1
- package/dist/cli/index.d.ts +0 -1
- package/dist/cli/index.js +9 -1
- package/dist/cli/util/format.d.ts +0 -1
- package/dist/cli/util/format.js +0 -1
- package/dist/cli/util/prompt.d.ts +0 -1
- package/dist/cli/util/prompt.js +0 -1
- package/dist/compiler/activate.d.ts +0 -1
- package/dist/compiler/activate.js +0 -1
- package/dist/compiler/cascade.d.ts +1 -3
- package/dist/compiler/cascade.js +1 -10
- package/dist/compiler/compile.d.ts +0 -1
- package/dist/compiler/compile.js +33 -17
- package/dist/compiler/index.d.ts +0 -1
- package/dist/compiler/index.js +0 -1
- package/dist/compiler/workflow.d.ts +0 -1
- package/dist/compiler/workflow.js +0 -2
- package/dist/evolution/collector.d.ts +0 -1
- package/dist/evolution/collector.js +0 -1
- package/dist/evolution/fitness.d.ts +0 -1
- package/dist/evolution/fitness.js +0 -1
- package/dist/evolution/index.d.ts +0 -1
- package/dist/evolution/index.js +0 -1
- package/dist/evolution/marker-gen.d.ts +0 -1
- package/dist/evolution/marker-gen.js +0 -1
- package/dist/evolution/store.d.ts +0 -1
- package/dist/evolution/store.js +0 -1
- package/dist/evolution/types.d.ts +0 -1
- package/dist/evolution/types.js +0 -1
- package/dist/hooks/cli.d.ts +17 -0
- package/dist/hooks/cli.js +156 -0
- package/dist/hooks/enforce.d.ts +106 -0
- package/dist/hooks/enforce.js +431 -0
- package/dist/hooks/index.d.ts +9 -0
- package/dist/hooks/index.js +12 -0
- package/dist/hooks/protocol.d.ts +73 -0
- package/dist/hooks/protocol.js +91 -0
- package/dist/hooks/state.d.ts +53 -0
- package/dist/hooks/state.js +93 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/log/logger.d.ts +0 -1
- package/dist/log/logger.js +0 -1
- package/dist/runtime/agent-md.d.ts +0 -1
- package/dist/runtime/agent-md.js +95 -105
- package/dist/runtime/claude-sdk.d.ts +0 -1
- package/dist/runtime/claude-sdk.js +0 -1
- package/dist/runtime/index.d.ts +3 -3
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/markdown.d.ts +0 -1
- package/dist/runtime/markdown.js +0 -1
- package/dist/runtime/plugin-adapter.d.ts +42 -0
- package/dist/runtime/plugin-adapter.js +77 -0
- package/dist/runtime/settings-adapter.d.ts +8 -9
- package/dist/runtime/settings-adapter.js +57 -46
- package/dist/runtime/skill-adapter.d.ts +0 -1
- package/dist/runtime/skill-adapter.js +94 -71
- package/dist/runtime/workflow-runner.d.ts +0 -1
- package/dist/runtime/workflow-runner.js +0 -14
- package/dist/schema/index.d.ts +0 -1
- package/dist/schema/index.js +0 -1
- package/dist/schema/types.d.ts +33 -20
- package/dist/schema/types.js +0 -1
- package/dist/schema/validate.d.ts +0 -1
- package/dist/schema/validate.js +32 -19
- package/dist/schema/yaml-parser.d.ts +0 -1
- package/dist/schema/yaml-parser.js +0 -1
- package/dist/templates/code-review-pipeline.dna.yaml +0 -11
- package/dist/templates/flutter-behavior-lock.dna.yaml +0 -16
- package/dist/templates/flutter-refactoring-rescue.dna.yaml +0 -12
- package/dist/templates/flutter-rewrite.dna.yaml +0 -27
- package/dist/templates/full-pipeline.dna.yaml +0 -25
- package/dist/templates/multi-perspective-review.dna.yaml +0 -12
- package/dist/templates/subagent-parallel.dna.yaml +0 -16
- package/package.json +4 -2
- package/dist/audit/index.d.ts.map +0 -1
- package/dist/audit/index.js.map +0 -1
- package/dist/cli/commands/compile.d.ts.map +0 -1
- package/dist/cli/commands/compile.js.map +0 -1
- package/dist/cli/commands/epigenetic.d.ts.map +0 -1
- package/dist/cli/commands/epigenetic.js.map +0 -1
- package/dist/cli/commands/evolve.d.ts.map +0 -1
- package/dist/cli/commands/evolve.js.map +0 -1
- package/dist/cli/commands/guard.d.ts.map +0 -1
- package/dist/cli/commands/guard.js.map +0 -1
- package/dist/cli/commands/import.d.ts.map +0 -1
- package/dist/cli/commands/import.js.map +0 -1
- package/dist/cli/commands/init.d.ts.map +0 -1
- package/dist/cli/commands/init.js.map +0 -1
- package/dist/cli/commands/run.d.ts.map +0 -1
- package/dist/cli/commands/run.js.map +0 -1
- package/dist/cli/commands/show.d.ts.map +0 -1
- package/dist/cli/commands/show.js.map +0 -1
- package/dist/cli/commands/sync.d.ts.map +0 -1
- package/dist/cli/commands/sync.js.map +0 -1
- package/dist/cli/commands/validate.d.ts.map +0 -1
- package/dist/cli/commands/validate.js.map +0 -1
- package/dist/cli/commands/verify.d.ts.map +0 -1
- package/dist/cli/commands/verify.js.map +0 -1
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/util/format.d.ts.map +0 -1
- package/dist/cli/util/format.js.map +0 -1
- package/dist/cli/util/prompt.d.ts.map +0 -1
- package/dist/cli/util/prompt.js.map +0 -1
- package/dist/compiler/activate.d.ts.map +0 -1
- package/dist/compiler/activate.js.map +0 -1
- package/dist/compiler/cascade.d.ts.map +0 -1
- package/dist/compiler/cascade.js.map +0 -1
- package/dist/compiler/compile.d.ts.map +0 -1
- package/dist/compiler/compile.js.map +0 -1
- package/dist/compiler/index.d.ts.map +0 -1
- package/dist/compiler/index.js.map +0 -1
- package/dist/compiler/workflow.d.ts.map +0 -1
- package/dist/compiler/workflow.js.map +0 -1
- package/dist/evolution/collector.d.ts.map +0 -1
- package/dist/evolution/collector.js.map +0 -1
- package/dist/evolution/fitness.d.ts.map +0 -1
- package/dist/evolution/fitness.js.map +0 -1
- package/dist/evolution/index.d.ts.map +0 -1
- package/dist/evolution/index.js.map +0 -1
- package/dist/evolution/marker-gen.d.ts.map +0 -1
- package/dist/evolution/marker-gen.js.map +0 -1
- package/dist/evolution/store.d.ts.map +0 -1
- package/dist/evolution/store.js.map +0 -1
- package/dist/evolution/types.d.ts.map +0 -1
- package/dist/evolution/types.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/log/logger.d.ts.map +0 -1
- package/dist/log/logger.js.map +0 -1
- package/dist/runtime/agent-md.d.ts.map +0 -1
- package/dist/runtime/agent-md.js.map +0 -1
- package/dist/runtime/claude-sdk.d.ts.map +0 -1
- package/dist/runtime/claude-sdk.js.map +0 -1
- package/dist/runtime/hooks.d.ts +0 -67
- package/dist/runtime/hooks.d.ts.map +0 -1
- package/dist/runtime/hooks.js +0 -785
- package/dist/runtime/hooks.js.map +0 -1
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/index.js.map +0 -1
- package/dist/runtime/markdown.d.ts.map +0 -1
- package/dist/runtime/markdown.js.map +0 -1
- package/dist/runtime/settings-adapter.d.ts.map +0 -1
- package/dist/runtime/settings-adapter.js.map +0 -1
- package/dist/runtime/skill-adapter.d.ts.map +0 -1
- package/dist/runtime/skill-adapter.js.map +0 -1
- package/dist/runtime/workflow-runner.d.ts.map +0 -1
- package/dist/runtime/workflow-runner.js.map +0 -1
- package/dist/schema/index.d.ts.map +0 -1
- package/dist/schema/index.js.map +0 -1
- package/dist/schema/types.d.ts.map +0 -1
- package/dist/schema/types.js.map +0 -1
- package/dist/schema/validate.d.ts.map +0 -1
- package/dist/schema/validate.js.map +0 -1
- package/dist/schema/yaml-parser.d.ts.map +0 -1
- package/dist/schema/yaml-parser.js.map +0 -1
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intent DNA — Enforcement Engine
|
|
3
|
+
*
|
|
4
|
+
* Pure functions that evaluate ConstraintIR against hook inputs.
|
|
5
|
+
* No I/O — reads no files, runs no commands, writes no state.
|
|
6
|
+
* The hook CLI handles stdin/stdout, IR loading, state, and checkpoint execution.
|
|
7
|
+
*
|
|
8
|
+
* Enforcement order for PreToolUse:
|
|
9
|
+
* 1. Role scope (agent_type → write glob patterns)
|
|
10
|
+
* 2. Tool filters (block/warn/deprioritize by tool name)
|
|
11
|
+
* 3. Pre-execution gates (condition-based block/escalate)
|
|
12
|
+
* 4. Output schema (required sections for Write tool)
|
|
13
|
+
*
|
|
14
|
+
* Step checkpoints are handled by the CLI, not here.
|
|
15
|
+
*/
|
|
16
|
+
import { allowOutput, blockOutput, escalateOutput, silentOutput } from "./protocol.js";
|
|
17
|
+
// ── PreToolUse Enforcement ─────────────────────────────────
|
|
18
|
+
/**
|
|
19
|
+
* Enforce PreToolUse constraints.
|
|
20
|
+
* Returns the first blocking/escalating result, or allow if all pass.
|
|
21
|
+
*
|
|
22
|
+
* Optional `roles` parameter provides output_schema enforcement.
|
|
23
|
+
*/
|
|
24
|
+
export function enforcePreToolUse(ir, input, state, roles) {
|
|
25
|
+
// Layer 2: Role scope
|
|
26
|
+
if (input.agent_type && ir.roles_scope_map && ir.roles_scope_map.length > 0) {
|
|
27
|
+
const result = enforceRoleScope(ir.roles_scope_map, input);
|
|
28
|
+
if (result)
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
// Layer 3: Tool filters
|
|
32
|
+
if (ir.tool_filters.length > 0) {
|
|
33
|
+
const result = enforceToolFilters(ir.tool_filters, input);
|
|
34
|
+
if (result)
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
// Layer 4: Pre-execution gates
|
|
38
|
+
if (ir.pre_execution_gates.length > 0) {
|
|
39
|
+
const result = enforceGates(ir.pre_execution_gates, input);
|
|
40
|
+
if (result)
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
// Layer 5: Output schema
|
|
44
|
+
if (roles && input.tool_name === "Write") {
|
|
45
|
+
const result = enforceOutputSchema(roles, input);
|
|
46
|
+
if (result)
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
return silentOutput();
|
|
50
|
+
}
|
|
51
|
+
// ── PostToolUse Enforcement ────────────────────────────────
|
|
52
|
+
/**
|
|
53
|
+
* Enforce PostToolUse validators.
|
|
54
|
+
* Currently logs validators for audit. Returns allow with context.
|
|
55
|
+
*/
|
|
56
|
+
export function enforcePostToolUse(ir, input) {
|
|
57
|
+
const validators = ir.post_execution_validators;
|
|
58
|
+
if (validators.length === 0)
|
|
59
|
+
return silentOutput();
|
|
60
|
+
const lines = [];
|
|
61
|
+
for (const v of validators) {
|
|
62
|
+
lines.push(`[Intent DNA] PostToolUse validator: ${v.check} (gene: ${v.source_gene})`);
|
|
63
|
+
}
|
|
64
|
+
return allowOutput(lines.join("\n"));
|
|
65
|
+
}
|
|
66
|
+
// ── UserPromptSubmit Enforcement ───────────────────────────
|
|
67
|
+
/**
|
|
68
|
+
* Inject DNA policy reminders on each user prompt.
|
|
69
|
+
* Only injects when there are high-priority directives.
|
|
70
|
+
*/
|
|
71
|
+
export function enforceUserPromptSubmit(ir, _input) {
|
|
72
|
+
const highPriority = ir.prompt_directives.filter(d => d.priority === "high");
|
|
73
|
+
if (highPriority.length === 0)
|
|
74
|
+
return silentOutput();
|
|
75
|
+
const lines = [
|
|
76
|
+
"<system-reminder>[Intent DNA] Active high-priority policies:",
|
|
77
|
+
];
|
|
78
|
+
for (const d of highPriority) {
|
|
79
|
+
lines.push(` - [${d.source_gene}] ${d.text}`);
|
|
80
|
+
}
|
|
81
|
+
lines.push("</system-reminder>");
|
|
82
|
+
return allowOutput(lines.join("\n"));
|
|
83
|
+
}
|
|
84
|
+
// ── SubagentStop Enforcement ───────────────────────────────
|
|
85
|
+
/**
|
|
86
|
+
* Audit role scope compliance when a subagent finishes.
|
|
87
|
+
*/
|
|
88
|
+
export function enforceSubagentStop(ir, input) {
|
|
89
|
+
const rolesScopeMap = ir.roles_scope_map;
|
|
90
|
+
if (!rolesScopeMap || rolesScopeMap.length === 0)
|
|
91
|
+
return silentOutput();
|
|
92
|
+
if (!input.agent_type)
|
|
93
|
+
return silentOutput();
|
|
94
|
+
for (const entry of rolesScopeMap) {
|
|
95
|
+
const agentTypeName = `dna-${toKebabCase(entry.role_name)}`;
|
|
96
|
+
if (input.agent_type === agentTypeName) {
|
|
97
|
+
const writeGlobs = entry.scope.write ?? [];
|
|
98
|
+
const scopeDesc = writeGlobs.length > 0
|
|
99
|
+
? `write:[${writeGlobs.join(",")}]`
|
|
100
|
+
: "write:[]";
|
|
101
|
+
return allowOutput(`[Intent DNA] SubagentStop audit: role=${entry.role_name} agent=${input.agent_name ?? "unknown"} scope=${scopeDesc}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return allowOutput(`[Intent DNA] SubagentStop: unknown role for agent_type=${input.agent_type}`);
|
|
105
|
+
}
|
|
106
|
+
// ── PreCompact Enforcement ─────────────────────────────────
|
|
107
|
+
/**
|
|
108
|
+
* Preserve DNA directive summary before context compaction.
|
|
109
|
+
*/
|
|
110
|
+
export function enforcePreCompact(ir) {
|
|
111
|
+
if (ir.prompt_directives.length === 0)
|
|
112
|
+
return silentOutput();
|
|
113
|
+
const critical = ir.prompt_directives.filter(d => d.priority === "high");
|
|
114
|
+
const standard = ir.prompt_directives.filter(d => d.priority === "medium");
|
|
115
|
+
const lines = [
|
|
116
|
+
"[Intent DNA] PreCompact: Preserving directive summary for compact",
|
|
117
|
+
];
|
|
118
|
+
if (critical.length > 0) {
|
|
119
|
+
lines.push(" Critical directives:");
|
|
120
|
+
for (const d of critical) {
|
|
121
|
+
lines.push(` - [${d.source_gene}] ${d.text}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (standard.length > 0) {
|
|
125
|
+
lines.push(` Standard directives (${standard.length}):`);
|
|
126
|
+
for (const d of standard) {
|
|
127
|
+
lines.push(` - [${d.source_gene}] ${d.text}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return allowOutput(lines.join("\n"));
|
|
131
|
+
}
|
|
132
|
+
// ── Notification Enforcement ───────────────────────────────
|
|
133
|
+
/**
|
|
134
|
+
* Check if a notification is a DNA violation and return audit info.
|
|
135
|
+
* The CLI handles actual audit file writes.
|
|
136
|
+
*/
|
|
137
|
+
export function enforceNotification(ir, input) {
|
|
138
|
+
const blockGates = ir.pre_execution_gates.filter(g => g.action === "block" || g.action === "escalate");
|
|
139
|
+
const blockFilters = ir.tool_filters.filter(f => f.action === "remove");
|
|
140
|
+
if (blockGates.length === 0 && blockFilters.length === 0)
|
|
141
|
+
return silentOutput();
|
|
142
|
+
const message = input.message ?? "";
|
|
143
|
+
if (message.includes("Intent DNA")) {
|
|
144
|
+
return allowOutput(`[Intent DNA] Notification: violation detected — ${input.title ?? "untitled"}: ${message}`);
|
|
145
|
+
}
|
|
146
|
+
return silentOutput();
|
|
147
|
+
}
|
|
148
|
+
// ── SessionStart Enforcement ───────────────────────────────
|
|
149
|
+
/**
|
|
150
|
+
* Initialize DNA state and inject policy summary on session start.
|
|
151
|
+
* Returns silent if no DNA policies are active.
|
|
152
|
+
*/
|
|
153
|
+
export function enforceSessionStart(ir, input) {
|
|
154
|
+
// Check if there's anything meaningful to report
|
|
155
|
+
const directiveCount = ir.prompt_directives.length;
|
|
156
|
+
const gateCount = ir.pre_execution_gates.length;
|
|
157
|
+
const filterCount = ir.tool_filters.length;
|
|
158
|
+
const roleCount = ir.roles_scope_map?.length ?? 0;
|
|
159
|
+
const workflowCount = ir.workflows_ir?.length ?? 0;
|
|
160
|
+
if (directiveCount === 0 && gateCount === 0 && filterCount === 0 && roleCount === 0) {
|
|
161
|
+
return silentOutput();
|
|
162
|
+
}
|
|
163
|
+
// Build policy summary
|
|
164
|
+
const lines = [
|
|
165
|
+
"[Intent DNA] Session initialized — active governance:",
|
|
166
|
+
];
|
|
167
|
+
if (ir.source_dna_ids.length > 0) {
|
|
168
|
+
lines.push(` Templates: ${ir.source_dna_ids.join(", ")}`);
|
|
169
|
+
}
|
|
170
|
+
const stats = [];
|
|
171
|
+
if (directiveCount > 0)
|
|
172
|
+
stats.push(`${directiveCount} directives`);
|
|
173
|
+
if (gateCount > 0)
|
|
174
|
+
stats.push(`${gateCount} gates`);
|
|
175
|
+
if (filterCount > 0)
|
|
176
|
+
stats.push(`${filterCount} filters`);
|
|
177
|
+
if (roleCount > 0)
|
|
178
|
+
stats.push(`${roleCount} roles`);
|
|
179
|
+
if (workflowCount > 0)
|
|
180
|
+
stats.push(`${workflowCount} workflows`);
|
|
181
|
+
if (stats.length > 0) {
|
|
182
|
+
lines.push(` Enforcement: ${stats.join(", ")}`);
|
|
183
|
+
}
|
|
184
|
+
return allowOutput(lines.join("\n"));
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Enforce Stop hook — verify workflow checkpoint completion.
|
|
188
|
+
*
|
|
189
|
+
* Safety valves (NEVER block):
|
|
190
|
+
* - context_limit / context_window stops
|
|
191
|
+
* - Stale workflow state (>2h)
|
|
192
|
+
* - No active workflow
|
|
193
|
+
* - No checkpoints defined
|
|
194
|
+
*
|
|
195
|
+
* Block when:
|
|
196
|
+
* - Workflow active + unmet checkpoints for current step
|
|
197
|
+
*/
|
|
198
|
+
export function enforceStop(ir, input, workflowState) {
|
|
199
|
+
// Safety valve 1: no workflow state → silent
|
|
200
|
+
if (!workflowState || !workflowState.active) {
|
|
201
|
+
return silentOutput();
|
|
202
|
+
}
|
|
203
|
+
// Safety valve 2: context_limit stops → NEVER block
|
|
204
|
+
const reason = input.stop_reason ?? "";
|
|
205
|
+
if (reason.includes("context_limit") || reason.includes("context_window")) {
|
|
206
|
+
return silentOutput();
|
|
207
|
+
}
|
|
208
|
+
// Safety valve 3: user abort → don't block
|
|
209
|
+
if (reason === "user_abort" || reason === "sigint") {
|
|
210
|
+
return silentOutput();
|
|
211
|
+
}
|
|
212
|
+
// Safety valve 4: stale state (>2h)
|
|
213
|
+
const STALE_MS = 2 * 60 * 60 * 1000;
|
|
214
|
+
if (workflowState.started_at) {
|
|
215
|
+
const age = Date.now() - new Date(workflowState.started_at).getTime();
|
|
216
|
+
if (age > STALE_MS) {
|
|
217
|
+
return silentOutput();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Check for unmet checkpoints in current step
|
|
221
|
+
const checkpoints = ir.step_checkpoints ?? [];
|
|
222
|
+
const currentStepCheckpoints = checkpoints.filter(cp => cp.step_id === workflowState.current_step);
|
|
223
|
+
// Also check workflows_ir for the active workflow's checkpoints
|
|
224
|
+
let workflowCheckpoints = currentStepCheckpoints;
|
|
225
|
+
if (ir.workflows_ir && ir.workflows_ir.length > 0) {
|
|
226
|
+
const activeWf = ir.workflows_ir.find(w => w.workflow_name === workflowState.workflow);
|
|
227
|
+
if (activeWf) {
|
|
228
|
+
const wfStepCps = activeWf.step_checkpoints.filter(cp => cp.step_id === workflowState.current_step);
|
|
229
|
+
if (wfStepCps.length > 0) {
|
|
230
|
+
workflowCheckpoints = wfStepCps;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (workflowCheckpoints.length === 0) {
|
|
235
|
+
return silentOutput(); // No checkpoints for current step
|
|
236
|
+
}
|
|
237
|
+
// Build list of blocking checkpoints
|
|
238
|
+
const blocking = workflowCheckpoints.flatMap(cp => cp.checkpoints.filter(c => (c.action ?? "block") === "block"));
|
|
239
|
+
if (blocking.length === 0) {
|
|
240
|
+
return silentOutput();
|
|
241
|
+
}
|
|
242
|
+
// Block: workflow active with unmet checkpoints
|
|
243
|
+
const messages = blocking.map(c => c.message);
|
|
244
|
+
return blockOutput(`[Intent DNA] Workflow '${workflowState.workflow}' has unmet checkpoints at step '${workflowState.current_step}':\n` +
|
|
245
|
+
messages.map(m => ` - ${m}`).join("\n"));
|
|
246
|
+
}
|
|
247
|
+
// ── Internal: Layer Enforcement ────────────────────────────
|
|
248
|
+
function enforceRoleScope(rolesScopeMap, input) {
|
|
249
|
+
if (!input.agent_type)
|
|
250
|
+
return null;
|
|
251
|
+
if (!isWriteTool(input.tool_name))
|
|
252
|
+
return null;
|
|
253
|
+
const filePath = extractFilePath(input);
|
|
254
|
+
if (!filePath)
|
|
255
|
+
return null;
|
|
256
|
+
for (const entry of rolesScopeMap) {
|
|
257
|
+
const agentTypeName = `dna-${toKebabCase(entry.role_name)}`;
|
|
258
|
+
if (input.agent_type !== agentTypeName)
|
|
259
|
+
continue;
|
|
260
|
+
const writeGlobs = entry.scope.write ?? [];
|
|
261
|
+
if (writeGlobs.length === 0) {
|
|
262
|
+
return blockOutput(`[Intent DNA]: Role '${entry.role_name}' has no write permission (path: ${filePath})`);
|
|
263
|
+
}
|
|
264
|
+
if (!checkWriteAllowed(filePath, writeGlobs)) {
|
|
265
|
+
return blockOutput(`[Intent DNA]: Role '${entry.role_name}' cannot write to '${filePath}' (allowed: ${writeGlobs.join(", ")})`);
|
|
266
|
+
}
|
|
267
|
+
return null; // Role matched, write allowed
|
|
268
|
+
}
|
|
269
|
+
return null; // No matching role — allow
|
|
270
|
+
}
|
|
271
|
+
function enforceToolFilters(filters, input) {
|
|
272
|
+
for (const filter of filters) {
|
|
273
|
+
const matchedTools = resolveToolTarget(filter.target);
|
|
274
|
+
if (!matchedTools)
|
|
275
|
+
continue;
|
|
276
|
+
if (!matchedTools.includes(input.tool_name))
|
|
277
|
+
continue;
|
|
278
|
+
if (filter.action === "remove") {
|
|
279
|
+
return blockOutput(`[Intent DNA]: ${filter.reason} (gene: ${filter.source_gene})`);
|
|
280
|
+
}
|
|
281
|
+
if (filter.action === "warn") {
|
|
282
|
+
// Warn but don't block — inject context
|
|
283
|
+
return allowOutput(`WARN [Intent DNA]: ${filter.reason} (gene: ${filter.source_gene})`);
|
|
284
|
+
}
|
|
285
|
+
// deprioritize — log only
|
|
286
|
+
// Don't return, continue checking other filters
|
|
287
|
+
}
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
function enforceGates(gates, input) {
|
|
291
|
+
for (const gate of gates) {
|
|
292
|
+
const matches = evaluateGateCondition(gate.condition, input);
|
|
293
|
+
if (matches === null)
|
|
294
|
+
continue; // Condition too complex — skip
|
|
295
|
+
if (!matches)
|
|
296
|
+
continue; // Condition not triggered
|
|
297
|
+
if (gate.action === "block") {
|
|
298
|
+
return blockOutput(`[Intent DNA]: ${gate.message}`);
|
|
299
|
+
}
|
|
300
|
+
if (gate.action === "escalate") {
|
|
301
|
+
return escalateOutput(`[Intent DNA] ${gate.message}`);
|
|
302
|
+
}
|
|
303
|
+
// warn
|
|
304
|
+
return allowOutput(`WARN [Intent DNA]: ${gate.message}`);
|
|
305
|
+
}
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
function enforceOutputSchema(roles, input) {
|
|
309
|
+
if (!input.agent_type)
|
|
310
|
+
return null;
|
|
311
|
+
const content = input.tool_input.content;
|
|
312
|
+
if (typeof content !== "string" || !content)
|
|
313
|
+
return null;
|
|
314
|
+
for (const [roleName, role] of Object.entries(roles)) {
|
|
315
|
+
if (!role.output_schema || typeof role.output_schema === "string")
|
|
316
|
+
continue;
|
|
317
|
+
const schema = role.output_schema;
|
|
318
|
+
if (!schema.required_sections || schema.required_sections.length === 0)
|
|
319
|
+
continue;
|
|
320
|
+
const agentName = `dna-${toKebabCase(roleName)}`;
|
|
321
|
+
if (input.agent_type !== agentName)
|
|
322
|
+
continue;
|
|
323
|
+
for (const section of schema.required_sections) {
|
|
324
|
+
if (!content.includes(`## ${section}`)) {
|
|
325
|
+
return blockOutput(`[Intent DNA]: Output missing required section '${section}' (role: ${roleName})`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
// ── Public Helpers ──────────────────────────────────────────
|
|
332
|
+
/** Check if a file path is allowed by a list of write globs (prefix matching). */
|
|
333
|
+
export function checkWriteAllowed(filePath, allowedGlobs) {
|
|
334
|
+
for (const glob of allowedGlobs) {
|
|
335
|
+
const prefix = globToPrefix(glob);
|
|
336
|
+
if (prefix.length === 0)
|
|
337
|
+
return true; // Root glob like "*.md" — allow all
|
|
338
|
+
if (filePath.startsWith(prefix))
|
|
339
|
+
return true;
|
|
340
|
+
// Also check exact match for non-glob patterns
|
|
341
|
+
if (!glob.includes("*") && filePath === glob)
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Extract directory prefix from a glob pattern.
|
|
348
|
+
* "src/**\/*.ts" → "src/"
|
|
349
|
+
* "test/**" → "test/"
|
|
350
|
+
* "*.md" → "" (root)
|
|
351
|
+
* "package.json" → "package.json" (exact path)
|
|
352
|
+
*/
|
|
353
|
+
export function globToPrefix(glob) {
|
|
354
|
+
const starIdx = glob.indexOf("*");
|
|
355
|
+
if (starIdx === -1)
|
|
356
|
+
return glob; // exact path
|
|
357
|
+
return glob.substring(0, starIdx);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Evaluate a gate condition against a PreToolUse input.
|
|
361
|
+
* Returns true if the condition triggers, false if not, null if too complex.
|
|
362
|
+
*/
|
|
363
|
+
export function evaluateGateCondition(condition, input) {
|
|
364
|
+
// Pattern: "personal_data_exposure == false"
|
|
365
|
+
// → check if tool input contains sensitive patterns
|
|
366
|
+
const boolFalseMatch = condition.match(/^(\w+)\s*==\s*false$/);
|
|
367
|
+
if (boolFalseMatch) {
|
|
368
|
+
const metric = boolFalseMatch[1];
|
|
369
|
+
if (metric === "personal_data_exposure") {
|
|
370
|
+
const inputStr = JSON.stringify(input.tool_input).toLowerCase();
|
|
371
|
+
const sensitivePatterns = ["password", "secret", "token", "api_key", "ssn", "credit_card"];
|
|
372
|
+
return sensitivePatterns.some(p => inputStr.includes(p));
|
|
373
|
+
}
|
|
374
|
+
return null; // Unknown boolean metric
|
|
375
|
+
}
|
|
376
|
+
// Pattern: "tool_name in ['X', 'Y']"
|
|
377
|
+
const toolInMatch = condition.match(/^tool_name\s+in\s+\[([^\]]+)\]/);
|
|
378
|
+
if (toolInMatch) {
|
|
379
|
+
const tools = toolInMatch[1]
|
|
380
|
+
.split(",")
|
|
381
|
+
.map(t => t.trim().replace(/['"]/g, ""));
|
|
382
|
+
return tools.includes(input.tool_name);
|
|
383
|
+
}
|
|
384
|
+
return null; // Too complex
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Resolve a tool filter target to concrete Claude Code tool names.
|
|
388
|
+
*/
|
|
389
|
+
export function resolveToolTarget(target) {
|
|
390
|
+
if (!target)
|
|
391
|
+
return null;
|
|
392
|
+
const toolMappings = {
|
|
393
|
+
file_modification: ["Edit", "Write", "NotebookEdit"],
|
|
394
|
+
file_write: ["Write"],
|
|
395
|
+
file_edit: ["Edit"],
|
|
396
|
+
code_execution: ["Bash"],
|
|
397
|
+
shell: ["Bash"],
|
|
398
|
+
harmful_actions: ["Bash"],
|
|
399
|
+
unsafe_operations: ["Bash"],
|
|
400
|
+
data_sharing_external: ["WebFetch", "Bash"],
|
|
401
|
+
};
|
|
402
|
+
const tools = toolMappings[target];
|
|
403
|
+
if (tools)
|
|
404
|
+
return tools;
|
|
405
|
+
// PascalCase target → direct tool name
|
|
406
|
+
if (/^[A-Z][a-zA-Z]+$/.test(target))
|
|
407
|
+
return [target];
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
// ── Internal Helpers ───────────────────────────────────────
|
|
411
|
+
const WRITE_TOOLS = new Set(["Edit", "Write", "NotebookEdit"]);
|
|
412
|
+
function isWriteTool(toolName) {
|
|
413
|
+
return WRITE_TOOLS.has(toolName);
|
|
414
|
+
}
|
|
415
|
+
function extractFilePath(input) {
|
|
416
|
+
const fp = input.tool_input.file_path;
|
|
417
|
+
if (typeof fp === "string" && fp)
|
|
418
|
+
return fp;
|
|
419
|
+
// Bash tool — try to extract from command
|
|
420
|
+
const cmd = input.tool_input.command;
|
|
421
|
+
if (typeof cmd === "string")
|
|
422
|
+
return null; // Can't reliably extract
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
/** Convert string to kebab-case (matches agent-md.ts toKebabCase). */
|
|
426
|
+
function toKebabCase(s) {
|
|
427
|
+
return s
|
|
428
|
+
.replace(/([a-z])([A-Z])/g, "$1-$2")
|
|
429
|
+
.replace(/[_\s]+/g, "-")
|
|
430
|
+
.toLowerCase();
|
|
431
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intent DNA — Node.js Hook Runtime
|
|
3
|
+
*
|
|
4
|
+
* Public exports for the hook enforcement engine.
|
|
5
|
+
* Used by the `dna-hook` CLI and importable for programmatic use.
|
|
6
|
+
*/
|
|
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, readStdin, writeOutput, allowOutput, blockOutput, escalateOutput, silentOutput, } from "./protocol.js";
|
|
8
|
+
export { type EnforceState, type SessionStartInput, type StopEnforceInput, type StopWorkflowContext, enforcePreToolUse, enforcePostToolUse, enforceUserPromptSubmit, enforceSubagentStop, enforcePreCompact, enforceNotification, enforceSessionStart, enforceStop, checkWriteAllowed, globToPrefix, evaluateGateCondition, resolveToolTarget, } from "./enforce.js";
|
|
9
|
+
export { type DNAWorkflowState, type AuditEntry, resolveStateDir, readWorkflowState, writeWorkflowState, clearWorkflowState, appendAudit, } from "./state.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intent DNA — Node.js Hook Runtime
|
|
3
|
+
*
|
|
4
|
+
* Public exports for the hook enforcement engine.
|
|
5
|
+
* Used by the `dna-hook` CLI and importable for programmatic use.
|
|
6
|
+
*/
|
|
7
|
+
// Protocol types and I/O
|
|
8
|
+
export { readStdin, writeOutput, allowOutput, blockOutput, escalateOutput, silentOutput, } from "./protocol.js";
|
|
9
|
+
// Enforcement engine
|
|
10
|
+
export { enforcePreToolUse, enforcePostToolUse, enforceUserPromptSubmit, enforceSubagentStop, enforcePreCompact, enforceNotification, enforceSessionStart, enforceStop, checkWriteAllowed, globToPrefix, evaluateGateCondition, resolveToolTarget, } from "./enforce.js";
|
|
11
|
+
// State management
|
|
12
|
+
export { resolveStateDir, readWorkflowState, writeWorkflowState, clearWorkflowState, appendAudit, } from "./state.js";
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intent DNA — Hook Protocol Types
|
|
3
|
+
*
|
|
4
|
+
* TypeScript types for Claude Code hook I/O and stdin/stdout utilities.
|
|
5
|
+
* All hooks read JSON from stdin and write JSON to stdout.
|
|
6
|
+
*
|
|
7
|
+
* Protocol reference: docs/references/cc-source-analysis.md §2
|
|
8
|
+
*/
|
|
9
|
+
export type HookEvent = "PreToolUse" | "PostToolUse" | "UserPromptSubmit" | "SubagentStop" | "PreCompact" | "Notification" | "Stop" | "SessionStart";
|
|
10
|
+
/** Base fields present in all hook inputs */
|
|
11
|
+
export interface HookInputBase {
|
|
12
|
+
cwd?: string;
|
|
13
|
+
sessionId?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface PreToolUseInput extends HookInputBase {
|
|
16
|
+
tool_name: string;
|
|
17
|
+
tool_input: Record<string, unknown>;
|
|
18
|
+
agent_type?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface PostToolUseInput extends HookInputBase {
|
|
21
|
+
tool_name: string;
|
|
22
|
+
tool_input: Record<string, unknown>;
|
|
23
|
+
tool_output?: string;
|
|
24
|
+
agent_type?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface UserPromptSubmitInput extends HookInputBase {
|
|
27
|
+
prompt?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface SubagentStopInput extends HookInputBase {
|
|
30
|
+
agent_name?: string;
|
|
31
|
+
agent_type?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface PreCompactInput extends HookInputBase {
|
|
34
|
+
}
|
|
35
|
+
export interface NotificationInput extends HookInputBase {
|
|
36
|
+
title?: string;
|
|
37
|
+
message?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface StopInput extends HookInputBase {
|
|
40
|
+
stop_reason?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface SessionStartInput extends HookInputBase {
|
|
43
|
+
trigger?: string;
|
|
44
|
+
}
|
|
45
|
+
export type HookInput = PreToolUseInput | PostToolUseInput | UserPromptSubmitInput | SubagentStopInput | PreCompactInput | NotificationInput | StopInput | SessionStartInput;
|
|
46
|
+
export interface HookOutput {
|
|
47
|
+
continue: boolean;
|
|
48
|
+
decision?: "block" | "allow";
|
|
49
|
+
reason?: string;
|
|
50
|
+
suppressOutput?: boolean;
|
|
51
|
+
hookSpecificOutput?: {
|
|
52
|
+
hookEventName?: string;
|
|
53
|
+
additionalContext?: string;
|
|
54
|
+
permissionDecision?: "ask" | "allow" | "deny";
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** Create an allow output, optionally injecting text into the conversation. */
|
|
58
|
+
export declare function allowOutput(additionalContext?: string): HookOutput;
|
|
59
|
+
/** Create a block output with reason. */
|
|
60
|
+
export declare function blockOutput(reason: string): HookOutput;
|
|
61
|
+
/** Create an escalate output (ask user for permission). */
|
|
62
|
+
export declare function escalateOutput(reason: string): HookOutput;
|
|
63
|
+
/** Create a silent allow output (no visible output to user). */
|
|
64
|
+
export declare function silentOutput(): HookOutput;
|
|
65
|
+
/**
|
|
66
|
+
* Read JSON from stdin with timeout protection.
|
|
67
|
+
* Returns parsed object on success, empty object on failure (fail-open).
|
|
68
|
+
*/
|
|
69
|
+
export declare function readStdin(timeoutMs?: number): Promise<Record<string, unknown>>;
|
|
70
|
+
/**
|
|
71
|
+
* Write hook output to stdout as JSON.
|
|
72
|
+
*/
|
|
73
|
+
export declare function writeOutput(output: HookOutput): void;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intent DNA — Hook Protocol Types
|
|
3
|
+
*
|
|
4
|
+
* TypeScript types for Claude Code hook I/O and stdin/stdout utilities.
|
|
5
|
+
* All hooks read JSON from stdin and write JSON to stdout.
|
|
6
|
+
*
|
|
7
|
+
* Protocol reference: docs/references/cc-source-analysis.md §2
|
|
8
|
+
*/
|
|
9
|
+
// ── Output Helpers ─────────────────────────────────────────
|
|
10
|
+
/** Create an allow output, optionally injecting text into the conversation. */
|
|
11
|
+
export function allowOutput(additionalContext) {
|
|
12
|
+
if (additionalContext) {
|
|
13
|
+
return {
|
|
14
|
+
continue: true,
|
|
15
|
+
hookSpecificOutput: { additionalContext },
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
return { continue: true };
|
|
19
|
+
}
|
|
20
|
+
/** Create a block output with reason. */
|
|
21
|
+
export function blockOutput(reason) {
|
|
22
|
+
return {
|
|
23
|
+
continue: false,
|
|
24
|
+
decision: "block",
|
|
25
|
+
reason,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** Create an escalate output (ask user for permission). */
|
|
29
|
+
export function escalateOutput(reason) {
|
|
30
|
+
return {
|
|
31
|
+
continue: true,
|
|
32
|
+
hookSpecificOutput: {
|
|
33
|
+
permissionDecision: "ask",
|
|
34
|
+
additionalContext: reason,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Create a silent allow output (no visible output to user). */
|
|
39
|
+
export function silentOutput() {
|
|
40
|
+
return { continue: true, suppressOutput: true };
|
|
41
|
+
}
|
|
42
|
+
// ── Stdin/Stdout I/O ───────────────────────────────────────
|
|
43
|
+
/**
|
|
44
|
+
* Read JSON from stdin with timeout protection.
|
|
45
|
+
* Returns parsed object on success, empty object on failure (fail-open).
|
|
46
|
+
*/
|
|
47
|
+
export function readStdin(timeoutMs = 5000) {
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
const chunks = [];
|
|
50
|
+
let settled = false;
|
|
51
|
+
const finish = (data) => {
|
|
52
|
+
if (settled)
|
|
53
|
+
return;
|
|
54
|
+
settled = true;
|
|
55
|
+
clearTimeout(timer);
|
|
56
|
+
process.stdin.removeAllListeners("data");
|
|
57
|
+
process.stdin.removeAllListeners("end");
|
|
58
|
+
process.stdin.removeAllListeners("error");
|
|
59
|
+
resolve(data);
|
|
60
|
+
};
|
|
61
|
+
const timer = setTimeout(() => finish({}), timeoutMs);
|
|
62
|
+
process.stdin.on("data", (chunk) => {
|
|
63
|
+
chunks.push(chunk);
|
|
64
|
+
});
|
|
65
|
+
process.stdin.on("end", () => {
|
|
66
|
+
try {
|
|
67
|
+
const raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
68
|
+
if (!raw) {
|
|
69
|
+
finish({});
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const parsed = JSON.parse(raw);
|
|
73
|
+
finish(typeof parsed === "object" && parsed !== null ? parsed : {});
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
finish({});
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
process.stdin.on("error", () => finish({}));
|
|
80
|
+
// If stdin is already ended (piped and closed)
|
|
81
|
+
if (process.stdin.readableEnded) {
|
|
82
|
+
finish({});
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Write hook output to stdout as JSON.
|
|
88
|
+
*/
|
|
89
|
+
export function writeOutput(output) {
|
|
90
|
+
process.stdout.write(JSON.stringify(output) + "\n");
|
|
91
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intent DNA — Hook State System
|
|
3
|
+
*
|
|
4
|
+
* Manages `.dna/state/` directory for runtime state tracking:
|
|
5
|
+
* - Workflow state (current workflow, step, role — read by hooks)
|
|
6
|
+
* - Audit log (violation records in JSON Lines format)
|
|
7
|
+
* - Session isolation (per-session state directories)
|
|
8
|
+
*
|
|
9
|
+
* All read operations are fail-safe (return null on error, never throw).
|
|
10
|
+
* All write operations use atomic temp+rename pattern.
|
|
11
|
+
*/
|
|
12
|
+
/** Workflow state written by runtime and read by hooks */
|
|
13
|
+
export interface DNAWorkflowState {
|
|
14
|
+
active: boolean;
|
|
15
|
+
workflow: string;
|
|
16
|
+
current_step: string;
|
|
17
|
+
current_role: string;
|
|
18
|
+
iteration: number;
|
|
19
|
+
session_id: string;
|
|
20
|
+
started_at: string;
|
|
21
|
+
}
|
|
22
|
+
/** Audit log entry (one per line in JSON Lines format) */
|
|
23
|
+
export interface AuditEntry {
|
|
24
|
+
timestamp: string;
|
|
25
|
+
event: string;
|
|
26
|
+
tool_name?: string;
|
|
27
|
+
agent_type?: string;
|
|
28
|
+
message: string;
|
|
29
|
+
session_id?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolve state directory path.
|
|
33
|
+
* With session isolation: `.dna/state/sessions/<sessionId>/`
|
|
34
|
+
* Without: `.dna/state/`
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveStateDir(projectDir: string, sessionId?: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Read current workflow state. Returns null if not found or stale.
|
|
39
|
+
*/
|
|
40
|
+
export declare function readWorkflowState(projectDir: string, sessionId?: string, stalenessMs?: number): Promise<DNAWorkflowState | null>;
|
|
41
|
+
/**
|
|
42
|
+
* Write workflow state atomically.
|
|
43
|
+
*/
|
|
44
|
+
export declare function writeWorkflowState(projectDir: string, state: DNAWorkflowState, sessionId?: string): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Clear workflow state (workflow complete).
|
|
47
|
+
*/
|
|
48
|
+
export declare function clearWorkflowState(projectDir: string, sessionId?: string): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Append an entry to the audit log.
|
|
51
|
+
* Log file: `.dna/audit/violations-YYYY-MM-DD.log` (JSON Lines format)
|
|
52
|
+
*/
|
|
53
|
+
export declare function appendAudit(projectDir: string, entry: AuditEntry): Promise<void>;
|