@devflow-tools/sdk 0.17.1 → 0.17.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/dist/action-lease.d.ts +65 -0
- package/dist/action-lease.d.ts.map +1 -0
- package/dist/action-lease.js +153 -0
- package/dist/action-lease.js.map +1 -0
- package/dist/action-lease.test.d.ts +2 -0
- package/dist/action-lease.test.d.ts.map +1 -0
- package/dist/action-lease.test.js +67 -0
- package/dist/action-lease.test.js.map +1 -0
- package/dist/canonical-action-binding.d.ts +15 -0
- package/dist/canonical-action-binding.d.ts.map +1 -0
- package/dist/canonical-action-binding.js +98 -0
- package/dist/canonical-action-binding.js.map +1 -0
- package/dist/canonical-action-binding.test.d.ts +2 -0
- package/dist/canonical-action-binding.test.d.ts.map +1 -0
- package/dist/canonical-action-binding.test.js +42 -0
- package/dist/canonical-action-binding.test.js.map +1 -0
- package/dist/capability-rollout.d.ts +56 -0
- package/dist/capability-rollout.d.ts.map +1 -1
- package/dist/capability-rollout.js +178 -1
- package/dist/capability-rollout.js.map +1 -1
- package/dist/host-adapters/claude-code-runtime.d.ts.map +1 -1
- package/dist/host-adapters/claude-code-runtime.js +20 -7
- package/dist/host-adapters/claude-code-runtime.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/task-runtime-reducer.d.ts +3 -0
- package/dist/task-runtime-reducer.d.ts.map +1 -0
- package/dist/task-runtime-reducer.js +219 -0
- package/dist/task-runtime-reducer.js.map +1 -0
- package/dist/task-runtime-store.d.ts +34 -0
- package/dist/task-runtime-store.d.ts.map +1 -0
- package/dist/task-runtime-store.js +126 -0
- package/dist/task-runtime-store.js.map +1 -0
- package/dist/task-runtime.d.ts +114 -0
- package/dist/task-runtime.d.ts.map +1 -0
- package/dist/task-runtime.js +157 -0
- package/dist/task-runtime.js.map +1 -0
- package/dist/task-runtime.test.d.ts +2 -0
- package/dist/task-runtime.test.d.ts.map +1 -0
- package/dist/task-runtime.test.js +60 -0
- package/dist/task-runtime.test.js.map +1 -0
- package/dist/types/task-context-envelope.d.ts +18 -0
- package/dist/types/task-context-envelope.d.ts.map +1 -1
- package/dist/types/task-semantic-control.d.ts +5 -1
- package/dist/types/task-semantic-control.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { CanonicalAction } from './types/task-context-envelope.js';
|
|
2
|
+
export type ToolFieldClass = 'locked' | 'constrained' | 'advisory' | 'transport';
|
|
3
|
+
export interface ToolFieldPolicyV1 {
|
|
4
|
+
class: ToolFieldClass;
|
|
5
|
+
type?: 'string' | 'number' | 'boolean' | 'array' | 'object';
|
|
6
|
+
enum?: Array<string | number | boolean>;
|
|
7
|
+
minimum?: number;
|
|
8
|
+
maximum?: number;
|
|
9
|
+
pattern?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ToolFieldManifestV1 {
|
|
12
|
+
schemaVersion: 'tool-field-manifest.v1';
|
|
13
|
+
tool: string;
|
|
14
|
+
version: string;
|
|
15
|
+
fields: Record<string, ToolFieldPolicyV1>;
|
|
16
|
+
}
|
|
17
|
+
export interface ActionLeaseV1 {
|
|
18
|
+
schemaVersion: 'action-lease.v1';
|
|
19
|
+
leaseId: string;
|
|
20
|
+
taskSpecHash: string;
|
|
21
|
+
manifestVersion: string;
|
|
22
|
+
tool: string;
|
|
23
|
+
projectRealpathHash: string;
|
|
24
|
+
operationMode: string;
|
|
25
|
+
lockedInputHash: string;
|
|
26
|
+
targets: Array<{
|
|
27
|
+
identityId: string;
|
|
28
|
+
path: string;
|
|
29
|
+
expectedState: 'existing' | 'missing' | 'unknown';
|
|
30
|
+
contentHash?: string;
|
|
31
|
+
}>;
|
|
32
|
+
verificationPlanHash: string;
|
|
33
|
+
issuedAt: number;
|
|
34
|
+
expiresAt: number;
|
|
35
|
+
leaseHash: string;
|
|
36
|
+
}
|
|
37
|
+
export declare function canonicalToolFieldManifest(action: CanonicalAction): ToolFieldManifestV1;
|
|
38
|
+
export declare function issueActionLease(input: {
|
|
39
|
+
taskSpecHash: string;
|
|
40
|
+
action: CanonicalAction;
|
|
41
|
+
manifest: ToolFieldManifestV1;
|
|
42
|
+
projectRoot: string;
|
|
43
|
+
targets: ActionLeaseV1['targets'];
|
|
44
|
+
issuedAt?: number;
|
|
45
|
+
ttlMs?: number;
|
|
46
|
+
}): ActionLeaseV1;
|
|
47
|
+
export declare function validateActionLease(input: {
|
|
48
|
+
lease: ActionLeaseV1;
|
|
49
|
+
manifest: ToolFieldManifestV1;
|
|
50
|
+
authenticated: boolean;
|
|
51
|
+
activeTaskSpecHash: string;
|
|
52
|
+
projectRoot: string;
|
|
53
|
+
toolName: string;
|
|
54
|
+
operationMode: string;
|
|
55
|
+
args: Record<string, unknown>;
|
|
56
|
+
currentTargets: ActionLeaseV1['targets'];
|
|
57
|
+
now?: number;
|
|
58
|
+
}): {
|
|
59
|
+
valid: true;
|
|
60
|
+
} | {
|
|
61
|
+
valid: false;
|
|
62
|
+
reason: string;
|
|
63
|
+
};
|
|
64
|
+
export declare function projectRealpathHash(projectRoot: string): string;
|
|
65
|
+
//# sourceMappingURL=action-lease.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-lease.d.ts","sourceRoot":"","sources":["../src/action-lease.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAGxE,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,aAAa,GAAG,UAAU,GAAG,WAAW,CAAC;AAEjF,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,cAAc,CAAC;IACtB,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC5D,IAAI,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,wBAAwB,CAAC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,iBAAiB,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,KAAK,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,aAAa,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;QAClD,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IACH,oBAAoB,EAAE,MAAM,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,eAAe,GAAG,mBAAmB,CASvF;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,eAAe,CAAC;IACxB,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,aAAa,CAkBhB;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IACzC,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,aAAa,EAAE,OAAO,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,cAAc,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IACzC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GAAG;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAyBrD;AAED,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAI/D"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, realpathSync } from 'node:fs';
|
|
3
|
+
import { dirname, isAbsolute, relative, resolve } from 'node:path';
|
|
4
|
+
import { stableRuntimeHash } from './task-runtime.js';
|
|
5
|
+
export function canonicalToolFieldManifest(action) {
|
|
6
|
+
const fields = Object.fromEntries(Object.keys(action.input).map(field => [field, { class: 'locked' }]));
|
|
7
|
+
for (const [field, policy] of Object.entries(action.binding.safeInputVariations ?? {})) {
|
|
8
|
+
fields[field] = { class: 'constrained', ...policy };
|
|
9
|
+
}
|
|
10
|
+
fields.evidenceRefs = { class: 'advisory', type: 'array' };
|
|
11
|
+
return { schemaVersion: 'tool-field-manifest.v1', tool: action.tool, version: '1.0.0', fields };
|
|
12
|
+
}
|
|
13
|
+
export function issueActionLease(input) {
|
|
14
|
+
if (input.manifest.tool !== input.action.tool)
|
|
15
|
+
throw new Error('ACTION_LEASE_MANIFEST_TOOL_MISMATCH');
|
|
16
|
+
const issuedAt = input.issuedAt ?? Date.now();
|
|
17
|
+
const body = {
|
|
18
|
+
schemaVersion: 'action-lease.v1',
|
|
19
|
+
leaseId: `lease:${stableRuntimeHash({ taskSpecHash: input.taskSpecHash, tool: input.action.tool, issuedAt }).slice(0, 32)}`,
|
|
20
|
+
taskSpecHash: input.taskSpecHash,
|
|
21
|
+
manifestVersion: input.manifest.version,
|
|
22
|
+
tool: input.action.tool,
|
|
23
|
+
projectRealpathHash: projectRealpathHash(input.projectRoot),
|
|
24
|
+
operationMode: input.action.binding.operationMode ?? 'unknown',
|
|
25
|
+
lockedInputHash: stableRuntimeHash(lockedInput(input.action.input, input.manifest)),
|
|
26
|
+
targets: input.targets.map(target => ({ ...target })),
|
|
27
|
+
verificationPlanHash: stableRuntimeHash(input.action.verificationPlan),
|
|
28
|
+
issuedAt,
|
|
29
|
+
expiresAt: issuedAt + (input.ttlMs ?? 10 * 60_000),
|
|
30
|
+
};
|
|
31
|
+
return { ...body, leaseHash: stableRuntimeHash(body) };
|
|
32
|
+
}
|
|
33
|
+
export function validateActionLease(input) {
|
|
34
|
+
if (!input.authenticated)
|
|
35
|
+
return invalid('action_lease_unauthenticated');
|
|
36
|
+
if (input.lease.schemaVersion !== 'action-lease.v1')
|
|
37
|
+
return invalid('action_lease_schema_unsupported');
|
|
38
|
+
const { leaseHash: _hash, ...body } = input.lease;
|
|
39
|
+
if (stableRuntimeHash(body) !== input.lease.leaseHash)
|
|
40
|
+
return invalid('action_lease_hash_mismatch');
|
|
41
|
+
if ((input.now ?? Date.now()) > input.lease.expiresAt)
|
|
42
|
+
return invalid('action_lease_expired');
|
|
43
|
+
if (input.lease.taskSpecHash !== input.activeTaskSpecHash)
|
|
44
|
+
return invalid('action_lease_task_mismatch');
|
|
45
|
+
if (input.manifest.tool !== input.lease.tool || input.manifest.version !== input.lease.manifestVersion) {
|
|
46
|
+
return invalid('action_lease_manifest_mismatch');
|
|
47
|
+
}
|
|
48
|
+
if (projectRealpathHash(input.projectRoot) !== input.lease.projectRealpathHash)
|
|
49
|
+
return invalid('action_lease_project_mismatch');
|
|
50
|
+
if (!pathsContained(input.projectRoot, input.args))
|
|
51
|
+
return invalid('action_lease_path_escape');
|
|
52
|
+
if (input.toolName !== input.lease.tool)
|
|
53
|
+
return invalid('action_lease_tool_mismatch');
|
|
54
|
+
if (input.operationMode !== input.lease.operationMode)
|
|
55
|
+
return invalid('action_lease_mode_mismatch');
|
|
56
|
+
if (!targetsEqual(input.lease.targets, input.currentTargets))
|
|
57
|
+
return invalid('action_lease_target_stale');
|
|
58
|
+
if (stableRuntimeHash(lockedInput(input.args, input.manifest)) !== input.lease.lockedInputHash) {
|
|
59
|
+
return invalid('action_lease_locked_field_mismatch');
|
|
60
|
+
}
|
|
61
|
+
for (const [field, value] of Object.entries(input.args)) {
|
|
62
|
+
if (field.startsWith('_devflow_'))
|
|
63
|
+
continue;
|
|
64
|
+
const policy = input.manifest.fields[field];
|
|
65
|
+
if (!policy)
|
|
66
|
+
return invalid(`action_lease_unknown_field:${field}`);
|
|
67
|
+
if (policy.class === 'constrained' && !matches(value, policy))
|
|
68
|
+
return invalid(`action_lease_constraint:${field}`);
|
|
69
|
+
}
|
|
70
|
+
return { valid: true };
|
|
71
|
+
}
|
|
72
|
+
export function projectRealpathHash(projectRoot) {
|
|
73
|
+
let path = resolve(projectRoot);
|
|
74
|
+
try {
|
|
75
|
+
path = realpathSync(path);
|
|
76
|
+
}
|
|
77
|
+
catch { /* Creation targets may not exist yet. */ }
|
|
78
|
+
return createHash('sha256').update(path).digest('hex');
|
|
79
|
+
}
|
|
80
|
+
function lockedInput(args, manifest) {
|
|
81
|
+
return Object.fromEntries(Object.entries(args)
|
|
82
|
+
.filter(([field]) => manifest.fields[field]?.class === 'locked')
|
|
83
|
+
.sort(([a], [b]) => a.localeCompare(b)));
|
|
84
|
+
}
|
|
85
|
+
function pathsContained(projectRoot, args) {
|
|
86
|
+
let root = resolve(projectRoot);
|
|
87
|
+
try {
|
|
88
|
+
root = realpathSync(root);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
for (const field of ['filePath', 'targetPath', 'path']) {
|
|
94
|
+
const value = args[field];
|
|
95
|
+
if (typeof value !== 'string')
|
|
96
|
+
continue;
|
|
97
|
+
let target = isAbsolute(value) ? resolve(value) : resolve(root, value);
|
|
98
|
+
try {
|
|
99
|
+
target = realpathSync(target);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
const parent = dirname(target);
|
|
103
|
+
if (existsSync(parent))
|
|
104
|
+
target = resolve(realpathSync(parent), target.split('/').at(-1));
|
|
105
|
+
}
|
|
106
|
+
const rel = relative(root, target);
|
|
107
|
+
if (rel === '..' || rel.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`) || isAbsolute(rel))
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
function targetsEqual(left, right) {
|
|
113
|
+
if (left.length !== right.length)
|
|
114
|
+
return false;
|
|
115
|
+
const current = new Map(right.map(target => [target.identityId, normalizeTarget(target)]));
|
|
116
|
+
return left.every(rawExpected => {
|
|
117
|
+
const expected = normalizeTarget(rawExpected);
|
|
118
|
+
const actual = current.get(expected.identityId);
|
|
119
|
+
if (!actual || actual.path !== expected.path)
|
|
120
|
+
return false;
|
|
121
|
+
if (expected.expectedState !== 'unknown' && actual.expectedState !== expected.expectedState)
|
|
122
|
+
return false;
|
|
123
|
+
return expected.contentHash === undefined || expected.contentHash === actual.contentHash;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function normalizeTarget(target) {
|
|
127
|
+
return { ...target, path: target.path.normalize('NFKC').replaceAll('\\', '/') };
|
|
128
|
+
}
|
|
129
|
+
function matches(value, policy) {
|
|
130
|
+
if (policy.type && runtimeType(value) !== policy.type)
|
|
131
|
+
return false;
|
|
132
|
+
if (policy.enum && !policy.enum.includes(value))
|
|
133
|
+
return false;
|
|
134
|
+
if (typeof value === 'number' && (!Number.isFinite(value)
|
|
135
|
+
|| policy.minimum !== undefined && value < policy.minimum
|
|
136
|
+
|| policy.maximum !== undefined && value > policy.maximum))
|
|
137
|
+
return false;
|
|
138
|
+
if (typeof value === 'string' && policy.pattern && !new RegExp(policy.pattern, 'u').test(value))
|
|
139
|
+
return false;
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
function runtimeType(value) {
|
|
143
|
+
if (Array.isArray(value))
|
|
144
|
+
return 'array';
|
|
145
|
+
if (value && typeof value === 'object')
|
|
146
|
+
return 'object';
|
|
147
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
148
|
+
return typeof value;
|
|
149
|
+
}
|
|
150
|
+
return 'other';
|
|
151
|
+
}
|
|
152
|
+
function invalid(reason) { return { valid: false, reason }; }
|
|
153
|
+
//# sourceMappingURL=action-lease.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-lease.js","sourceRoot":"","sources":["../src/action-lease.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-lease.test.d.ts","sourceRoot":"","sources":["../src/action-lease.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { canonicalToolFieldManifest, issueActionLease, validateActionLease } from './action-lease.js';
|
|
6
|
+
const roots = [];
|
|
7
|
+
afterEach(() => { for (const root of roots.splice(0))
|
|
8
|
+
rmSync(root, { recursive: true, force: true }); });
|
|
9
|
+
function fixture() {
|
|
10
|
+
const root = mkdtempSync(join(tmpdir(), 'devflow-action-lease-'));
|
|
11
|
+
roots.push(root);
|
|
12
|
+
mkdirSync(join(root, 'src'));
|
|
13
|
+
writeFileSync(join(root, 'src', 'FlightMonitorPage.tsx'), 'export const FlightMonitorPage = 1;');
|
|
14
|
+
const action = {
|
|
15
|
+
tool: 'react_audit_performance', status: 'required', required: true,
|
|
16
|
+
input: { projectRoot: root, filePath: 'src/FlightMonitorPage.tsx', scope: 'related' },
|
|
17
|
+
binding: {
|
|
18
|
+
projectRoot: root, sessionId: 'session', requestId: 'request', targetAnchors: [],
|
|
19
|
+
requiredContextReceipt: 'context', requiredPolicyFacets: [], operationMode: 'analyze',
|
|
20
|
+
safeInputVariations: {
|
|
21
|
+
componentName: { type: 'string', enum: ['FlightMonitorPage'] },
|
|
22
|
+
maxFindings: { type: 'number', minimum: 1, maximum: 50 },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
verificationPlan: [],
|
|
26
|
+
};
|
|
27
|
+
const manifest = canonicalToolFieldManifest(action);
|
|
28
|
+
const targets = [{
|
|
29
|
+
identityId: 'artifact:flight', path: 'src/FlightMonitorPage.tsx',
|
|
30
|
+
expectedState: 'existing', contentHash: 'content:1',
|
|
31
|
+
}];
|
|
32
|
+
const lease = issueActionLease({
|
|
33
|
+
taskSpecHash: 'spec:1', action, manifest, projectRoot: root, targets, issuedAt: 100, ttlMs: 100,
|
|
34
|
+
});
|
|
35
|
+
return { root, action, manifest, targets, lease };
|
|
36
|
+
}
|
|
37
|
+
describe('ActionLease.v1', () => {
|
|
38
|
+
it('allows bounded safe refinements and rejects unknown or changed semantic fields', () => {
|
|
39
|
+
const x = fixture();
|
|
40
|
+
const base = {
|
|
41
|
+
lease: x.lease, manifest: x.manifest, authenticated: true, activeTaskSpecHash: 'spec:1',
|
|
42
|
+
projectRoot: x.root, toolName: x.action.tool, operationMode: 'analyze', currentTargets: x.targets, now: 150,
|
|
43
|
+
};
|
|
44
|
+
expect(validateActionLease({ ...base, args: { ...x.action.input, componentName: 'FlightMonitorPage', maxFindings: 8 } })).toEqual({ valid: true });
|
|
45
|
+
expect(validateActionLease({ ...base, args: { ...x.action.input, componentName: 'OtherPage' } })).toMatchObject({ valid: false });
|
|
46
|
+
expect(validateActionLease({ ...base, args: { ...x.action.input, surprise: true } })).toMatchObject({ valid: false });
|
|
47
|
+
});
|
|
48
|
+
it('fails closed for expiry, cross-project, stale content, mode drift, and symlink escape', () => {
|
|
49
|
+
const x = fixture();
|
|
50
|
+
const base = {
|
|
51
|
+
lease: x.lease, manifest: x.manifest, authenticated: true, activeTaskSpecHash: 'spec:1',
|
|
52
|
+
projectRoot: x.root, toolName: x.action.tool, operationMode: 'analyze',
|
|
53
|
+
args: x.action.input, currentTargets: x.targets, now: 150,
|
|
54
|
+
};
|
|
55
|
+
expect(validateActionLease({ ...base, now: 201 })).toMatchObject({ reason: 'action_lease_expired' });
|
|
56
|
+
expect(validateActionLease({ ...base, projectRoot: mkdtempSync(join(tmpdir(), 'other-project-')) })).toMatchObject({ reason: 'action_lease_project_mismatch' });
|
|
57
|
+
expect(validateActionLease({ ...base, currentTargets: [{ ...x.targets[0], contentHash: 'changed' }] })).toMatchObject({ reason: 'action_lease_target_stale' });
|
|
58
|
+
expect(validateActionLease({ ...base, operationMode: 'modify' })).toMatchObject({ reason: 'action_lease_mode_mismatch' });
|
|
59
|
+
const outside = mkdtempSync(join(tmpdir(), 'outside-target-'));
|
|
60
|
+
roots.push(outside);
|
|
61
|
+
writeFileSync(join(outside, 'escape.tsx'), 'escape');
|
|
62
|
+
symlinkSync(join(outside, 'escape.tsx'), join(x.root, 'src', 'escape.tsx'));
|
|
63
|
+
expect(validateActionLease({ ...base, args: { ...x.action.input, filePath: 'src/escape.tsx' } }))
|
|
64
|
+
.toMatchObject({ reason: 'action_lease_path_escape' });
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=action-lease.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-lease.test.js","sourceRoot":"","sources":["../src/action-lease.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CanonicalAction } from './types/task-context-envelope.js';
|
|
2
|
+
export declare function canonicalActionInputHash(input: Record<string, unknown>): string;
|
|
3
|
+
export declare function validateCanonicalActionBinding(input: {
|
|
4
|
+
action: CanonicalAction;
|
|
5
|
+
toolName: string;
|
|
6
|
+
args: Record<string, unknown>;
|
|
7
|
+
}): {
|
|
8
|
+
valid: true;
|
|
9
|
+
actualInputHash: string;
|
|
10
|
+
} | {
|
|
11
|
+
valid: false;
|
|
12
|
+
reason: string;
|
|
13
|
+
actualInputHash?: string;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=canonical-action-binding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical-action-binding.d.ts","sourceRoot":"","sources":["../src/canonical-action-binding.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAgBxE,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAE/E;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE;IACpD,MAAM,EAAE,eAAe,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B,GAAG;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,CA2BxG"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
const TRANSPORT_FIELDS = new Set([
|
|
3
|
+
'_devflow_token',
|
|
4
|
+
'_devflow_session_id',
|
|
5
|
+
'_devflow_execution_id',
|
|
6
|
+
'_devflow_turn_id',
|
|
7
|
+
'_devflow_request_id',
|
|
8
|
+
'_devflow_intent_artifact_hash',
|
|
9
|
+
'_devflow_active_skill',
|
|
10
|
+
'_devflow_tool_use_id',
|
|
11
|
+
'_devflow_worker_id',
|
|
12
|
+
'_devflow_worker_context_receipt',
|
|
13
|
+
]);
|
|
14
|
+
const STRUCTURAL_EVIDENCE_FIELDS = new Set(['evidenceRefs']);
|
|
15
|
+
export function canonicalActionInputHash(input) {
|
|
16
|
+
return createHash('sha256').update(stableJson(normalizeActionInput(input))).digest('hex');
|
|
17
|
+
}
|
|
18
|
+
export function validateCanonicalActionBinding(input) {
|
|
19
|
+
if (input.action.tool !== input.toolName) {
|
|
20
|
+
return { valid: false, reason: `canonical_tool_mismatch:${input.action.tool}` };
|
|
21
|
+
}
|
|
22
|
+
const expectedHash = input.action.binding.normalizedInputHash
|
|
23
|
+
?? canonicalActionInputHash(input.action.input);
|
|
24
|
+
const actualInputHash = canonicalActionInputHash(input.args);
|
|
25
|
+
const safeVariations = input.action.binding.safeInputVariations ?? {};
|
|
26
|
+
const variedFields = Object.keys(safeVariations)
|
|
27
|
+
.filter(field => input.action.input[field] === undefined && input.args[field] !== undefined);
|
|
28
|
+
for (const field of variedFields) {
|
|
29
|
+
if (!matchesSafeVariation(input.args[field], safeVariations[field])) {
|
|
30
|
+
return { valid: false, reason: `canonical_safe_input_invalid:${field}`, actualInputHash };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const lockedActualInput = Object.fromEntries(Object.entries(input.args)
|
|
34
|
+
.filter(([field]) => !variedFields.includes(field) && !STRUCTURAL_EVIDENCE_FIELDS.has(field)));
|
|
35
|
+
if (expectedHash !== canonicalActionInputHash(lockedActualInput)) {
|
|
36
|
+
return { valid: false, reason: 'canonical_input_hash_mismatch', actualInputHash };
|
|
37
|
+
}
|
|
38
|
+
for (const target of input.action.binding.operationTargets ?? []) {
|
|
39
|
+
const actualPath = firstString(input.args, ['filePath', 'targetPath', 'path']);
|
|
40
|
+
if (actualPath && normalizePath(actualPath) !== normalizePath(target.path)) {
|
|
41
|
+
return { valid: false, reason: `canonical_target_mismatch:${target.path}`, actualInputHash };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { valid: true, actualInputHash };
|
|
45
|
+
}
|
|
46
|
+
function matchesSafeVariation(value, policy) {
|
|
47
|
+
if (typeof value !== policy.type)
|
|
48
|
+
return false;
|
|
49
|
+
if (policy.enum && !policy.enum.some(candidate => candidate === value))
|
|
50
|
+
return false;
|
|
51
|
+
if (typeof value === 'number') {
|
|
52
|
+
if (!Number.isFinite(value))
|
|
53
|
+
return false;
|
|
54
|
+
if (policy.minimum !== undefined && value < policy.minimum)
|
|
55
|
+
return false;
|
|
56
|
+
if (policy.maximum !== undefined && value > policy.maximum)
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
if (typeof value === 'string' && policy.pattern && !new RegExp(policy.pattern, 'u').test(value))
|
|
60
|
+
return false;
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
function normalizeActionInput(input) {
|
|
64
|
+
return Object.fromEntries(Object.entries(input)
|
|
65
|
+
.filter(([key, value]) => !TRANSPORT_FIELDS.has(key) && !key.startsWith('_devflow_') && value !== undefined)
|
|
66
|
+
.map(([key, value]) => [key, normalizeValue(value)]));
|
|
67
|
+
}
|
|
68
|
+
function normalizeValue(value) {
|
|
69
|
+
if (Array.isArray(value))
|
|
70
|
+
return value.map(normalizeValue);
|
|
71
|
+
if (!value || typeof value !== 'object')
|
|
72
|
+
return typeof value === 'string' ? value.normalize('NFKC').trim() : value;
|
|
73
|
+
return Object.fromEntries(Object.entries(value)
|
|
74
|
+
.filter(([, entry]) => entry !== undefined)
|
|
75
|
+
.map(([key, entry]) => [key, normalizeValue(entry)]));
|
|
76
|
+
}
|
|
77
|
+
function stableJson(value) {
|
|
78
|
+
if (Array.isArray(value))
|
|
79
|
+
return `[${value.map(stableJson).join(',')}]`;
|
|
80
|
+
if (!value || typeof value !== 'object')
|
|
81
|
+
return JSON.stringify(value);
|
|
82
|
+
return `{${Object.entries(value)
|
|
83
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
84
|
+
.map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`)
|
|
85
|
+
.join(',')}}`;
|
|
86
|
+
}
|
|
87
|
+
function firstString(input, keys) {
|
|
88
|
+
for (const key of keys) {
|
|
89
|
+
const value = input[key];
|
|
90
|
+
if (typeof value === 'string' && value.trim())
|
|
91
|
+
return value.trim();
|
|
92
|
+
}
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
function normalizePath(value) {
|
|
96
|
+
return value.normalize('NFKC').trim().replaceAll('\\', '/').replace(/^\.\//u, '');
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=canonical-action-binding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical-action-binding.js","sourceRoot":"","sources":["../src/canonical-action-binding.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical-action-binding.test.d.ts","sourceRoot":"","sources":["../src/canonical-action-binding.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { canonicalActionInputHash, validateCanonicalActionBinding } from './canonical-action-binding.js';
|
|
3
|
+
function action() {
|
|
4
|
+
const input = { projectRoot: '/project', filePath: 'src/FlightMonitorPage.tsx', scope: 'related' };
|
|
5
|
+
return {
|
|
6
|
+
tool: 'react_audit_performance', input, status: 'recommended', required: false,
|
|
7
|
+
binding: {
|
|
8
|
+
projectRoot: '/project', sessionId: 'session', requestId: 'request',
|
|
9
|
+
targetAnchors: ['src/FlightMonitorPage.tsx'], requiredContextReceipt: 'context',
|
|
10
|
+
requiredPolicyFacets: [], normalizedInputHash: canonicalActionInputHash(input),
|
|
11
|
+
operationTargets: [{ path: 'src/FlightMonitorPage.tsx', expectedState: 'existing' }],
|
|
12
|
+
safeInputVariations: {
|
|
13
|
+
componentName: { type: 'string', enum: ['FlightMonitorPage'] },
|
|
14
|
+
maxFindings: { type: 'number', minimum: 1, maximum: 50 },
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
verificationPlan: [],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
describe('canonical action safe input variations', () => {
|
|
21
|
+
it('allows declared bounded read-only refinements', () => {
|
|
22
|
+
expect(validateCanonicalActionBinding({
|
|
23
|
+
action: action(), toolName: 'react_audit_performance',
|
|
24
|
+
args: {
|
|
25
|
+
projectRoot: '/project', filePath: 'src/FlightMonitorPage.tsx', scope: 'related',
|
|
26
|
+
componentName: 'FlightMonitorPage', maxFindings: 8,
|
|
27
|
+
},
|
|
28
|
+
}).valid).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
it('rejects unknown, out-of-range, and target-changing input', () => {
|
|
31
|
+
for (const args of [
|
|
32
|
+
{ projectRoot: '/project', filePath: 'src/FlightMonitorPage.tsx', scope: 'related', maxFindings: 500 },
|
|
33
|
+
{ projectRoot: '/project', filePath: 'src/FlightMonitorPage.tsx', scope: 'related', componentName: 'OtherPage' },
|
|
34
|
+
{ projectRoot: '/project', filePath: 'src/Other.tsx', scope: 'related', maxFindings: 8 },
|
|
35
|
+
{ projectRoot: '/project', filePath: 'src/FlightMonitorPage.tsx', scope: 'related', surprise: true },
|
|
36
|
+
]) {
|
|
37
|
+
expect(validateCanonicalActionBinding({ action: action(), toolName: 'react_audit_performance', args }).valid)
|
|
38
|
+
.toBe(false);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
//# sourceMappingURL=canonical-action-binding.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical-action-binding.test.js","sourceRoot":"","sources":["../src/canonical-action-binding.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,wBAAwB,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAEzG,SAAS,MAAM;IACb,MAAM,KAAK,GAAG,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,2BAA2B,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IACnG,OAAO;QACL,IAAI,EAAE,yBAAyB,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK;QAC9E,OAAO,EAAE;YACP,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS;YACnE,aAAa,EAAE,CAAC,2BAA2B,CAAC,EAAE,sBAAsB,EAAE,SAAS;YAC/E,oBAAoB,EAAE,EAAE,EAAE,mBAAmB,EAAE,wBAAwB,CAAC,KAAK,CAAC;YAC9E,gBAAgB,EAAE,CAAC,EAAE,IAAI,EAAE,2BAA2B,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;YACpF,mBAAmB,EAAE;gBACnB,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,mBAAmB,CAAC,EAAE;gBAC9D,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;aACzD;SACF;QACD,gBAAgB,EAAE,EAAE;KACrB,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,wCAAwC,EAAE,GAAG,EAAE;IACtD,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,8BAA8B,CAAC;YACpC,MAAM,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,yBAAyB;YACrD,IAAI,EAAE;gBACJ,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,2BAA2B,EAAE,KAAK,EAAE,SAAS;gBAChF,aAAa,EAAE,mBAAmB,EAAE,WAAW,EAAE,CAAC;aACnD;SACF,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,KAAK,MAAM,IAAI,IAAI;YACjB,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,2BAA2B,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,EAAE;YACtG,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,2BAA2B,EAAE,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE;YAChH,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,EAAE;YACxF,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,2BAA2B,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE;SACrG,EAAE,CAAC;YACF,MAAM,CAAC,8BAA8B,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,yBAAyB,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC;iBAC1G,IAAI,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -27,4 +27,60 @@ export interface CapabilityRolloutResolution {
|
|
|
27
27
|
}
|
|
28
28
|
export declare const DEFAULT_CAPABILITY_ROLLOUT: Readonly<CapabilityRolloutConfig>;
|
|
29
29
|
export declare function resolveCapabilityRollout(projectRoot: string, env?: NodeJS.ProcessEnv): CapabilityRolloutResolution;
|
|
30
|
+
export type EvidenceRuntimeRolloutStage = 'off' | 'shadow' | 'project' | 'percentage';
|
|
31
|
+
export type EvidenceRuntimeRolloutSetting = {
|
|
32
|
+
stage: 'off' | 'shadow';
|
|
33
|
+
} | {
|
|
34
|
+
stage: 'project';
|
|
35
|
+
projects: string[];
|
|
36
|
+
} | {
|
|
37
|
+
stage: 'percentage';
|
|
38
|
+
percentage: number;
|
|
39
|
+
salt: string;
|
|
40
|
+
};
|
|
41
|
+
export interface EvidenceRuntimeRolloutConfig {
|
|
42
|
+
artifactIdentity: EvidenceRuntimeRolloutSetting;
|
|
43
|
+
channelOutcomes: EvidenceRuntimeRolloutSetting;
|
|
44
|
+
actionLease: EvidenceRuntimeRolloutSetting;
|
|
45
|
+
onlineTerminalProjection: EvidenceRuntimeRolloutSetting;
|
|
46
|
+
memoryAutoSkip: EvidenceRuntimeRolloutSetting;
|
|
47
|
+
knowledgePublish: EvidenceRuntimeRolloutSetting;
|
|
48
|
+
}
|
|
49
|
+
export type EvidenceRuntimeRolloutKey = keyof EvidenceRuntimeRolloutConfig;
|
|
50
|
+
export interface EvidenceRuntimeRolloutDiagnostic {
|
|
51
|
+
code: 'invalid_evidence_rollout' | 'unknown_evidence_rollout_key';
|
|
52
|
+
source: 'project' | 'environment';
|
|
53
|
+
message: string;
|
|
54
|
+
key?: EvidenceRuntimeRolloutKey;
|
|
55
|
+
value?: unknown;
|
|
56
|
+
environmentVariable?: string;
|
|
57
|
+
}
|
|
58
|
+
export interface EvidenceRuntimeRolloutResolution {
|
|
59
|
+
config: EvidenceRuntimeRolloutConfig;
|
|
60
|
+
projectId: string;
|
|
61
|
+
diagnostics: EvidenceRuntimeRolloutDiagnostic[];
|
|
62
|
+
}
|
|
63
|
+
export interface EvidenceRuntimeRolloutDecision {
|
|
64
|
+
stage: EvidenceRuntimeRolloutStage;
|
|
65
|
+
writeEvents: boolean;
|
|
66
|
+
shadowCompare: boolean;
|
|
67
|
+
readCandidate: boolean;
|
|
68
|
+
projectTerminalOutcome: boolean;
|
|
69
|
+
preserveEvents: true;
|
|
70
|
+
rewriteOutcomes: false;
|
|
71
|
+
}
|
|
72
|
+
export declare const DEFAULT_EVIDENCE_RUNTIME_ROLLOUT: Readonly<EvidenceRuntimeRolloutConfig>;
|
|
73
|
+
/** Resolve the independently reversible evidence-runtime rollout controls. */
|
|
74
|
+
export declare function resolveEvidenceRuntimeRollout(projectRoot: string, env?: NodeJS.ProcessEnv): EvidenceRuntimeRolloutResolution;
|
|
75
|
+
export declare function decideEvidenceRuntimeRollout(input: {
|
|
76
|
+
setting: EvidenceRuntimeRolloutSetting;
|
|
77
|
+
projectId: string;
|
|
78
|
+
bucketKey: string;
|
|
79
|
+
}): EvidenceRuntimeRolloutDecision;
|
|
80
|
+
/**
|
|
81
|
+
* Rollback retains append-only evidence by returning every active capability to
|
|
82
|
+
* shadow, while disabling all candidate reads and terminal projections.
|
|
83
|
+
*/
|
|
84
|
+
export declare function rollbackEvidenceRuntimeRollout(config: EvidenceRuntimeRolloutConfig): EvidenceRuntimeRolloutConfig;
|
|
85
|
+
export declare function evidenceRuntimeProjectId(projectRoot: string): string;
|
|
30
86
|
//# sourceMappingURL=capability-rollout.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capability-rollout.d.ts","sourceRoot":"","sources":["../src/capability-rollout.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"capability-rollout.d.ts","sourceRoot":"","sources":["../src/capability-rollout.ts"],"names":[],"mappings":"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"}
|