@deftai/directive-core 0.83.0 → 0.84.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.
Files changed (42) hide show
  1. package/dist/cache/main.js +36 -2
  2. package/dist/cache/task-cache/constants.d.ts +4 -0
  3. package/dist/cache/task-cache/constants.js +4 -0
  4. package/dist/cache/task-cache/executor.d.ts +9 -0
  5. package/dist/cache/task-cache/executor.js +51 -0
  6. package/dist/cache/task-cache/hash.d.ts +17 -0
  7. package/dist/cache/task-cache/hash.js +92 -0
  8. package/dist/cache/task-cache/index.d.ts +14 -0
  9. package/dist/cache/task-cache/index.js +15 -0
  10. package/dist/cache/task-cache/lint.d.ts +4 -0
  11. package/dist/cache/task-cache/lint.js +55 -0
  12. package/dist/cache/task-cache/registry.d.ts +7 -0
  13. package/dist/cache/task-cache/registry.js +67 -0
  14. package/dist/cache/task-cache/store.d.ts +10 -0
  15. package/dist/cache/task-cache/store.js +48 -0
  16. package/dist/cache/task-cache/types.d.ts +48 -0
  17. package/dist/cache/task-cache/types.js +3 -0
  18. package/dist/check/cached-orchestrator.d.ts +16 -0
  19. package/dist/check/cached-orchestrator.js +76 -0
  20. package/dist/check/context.d.ts +30 -0
  21. package/dist/check/context.js +28 -0
  22. package/dist/check/gate-lists.d.ts +18 -0
  23. package/dist/check/gate-lists.js +68 -0
  24. package/dist/check/index.d.ts +4 -1
  25. package/dist/check/index.js +3 -0
  26. package/dist/check/orchestrator.d.ts +3 -45
  27. package/dist/check/orchestrator.js +8 -46
  28. package/dist/check/runner-detect.d.ts +20 -0
  29. package/dist/check/runner-detect.js +131 -0
  30. package/dist/eval/readback.js +6 -1
  31. package/dist/hooks/dispatcher.d.ts +2 -0
  32. package/dist/hooks/dispatcher.js +48 -6
  33. package/dist/init-deposit/gitignore.js +1 -0
  34. package/dist/scope/decompose.js +9 -3
  35. package/dist/session/git.d.ts +2 -0
  36. package/dist/session/git.js +14 -0
  37. package/dist/session/verify-session-ritual.js +39 -5
  38. package/dist/swarm/routing-set-cli.js +16 -5
  39. package/dist/swarm/routing.d.ts +1 -1
  40. package/dist/swarm/routing.js +3 -1
  41. package/dist/value/readback.js +6 -1
  42. package/package.json +7 -3
@@ -1,6 +1,6 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { formatFrameworkCommand } from "../render/framework-commands.js";
3
- import { defaultGitRunner, gitHead, worktreePath } from "./git.js";
3
+ import { defaultGitRunner, gitHead, gitIsAncestor, worktreePath } from "./git.js";
4
4
  import { pythonJsonDump } from "./json.js";
5
5
  import { ENV_SESSION_POSTURE, readOnlyPostureMessage, resolveSessionPosture, ritualStateIsPostureAuthority, } from "./posture.js";
6
6
  import { defaultRitualRunner } from "./ritual-entrypoint.js";
@@ -59,6 +59,10 @@ function runGatedStep(projectRoot, payload, stepName, runner, now) {
59
59
  }
60
60
  return null;
61
61
  }
62
+ function headDriftRecoveryMessage() {
63
+ return (`session ritual state is stale because git HEAD changed discontinuously. ` +
64
+ `Run \`${formatFrameworkCommand(["session:start"])}\` again.`);
65
+ }
62
66
  function evaluateLoadedState(projectRoot, state, input) {
63
67
  const runGit = input.runGit ?? defaultGitRunner;
64
68
  const { head: currentHead, error: headError } = gitHead(projectRoot, runGit);
@@ -73,10 +77,22 @@ function evaluateLoadedState(projectRoot, state, input) {
73
77
  ];
74
78
  }
75
79
  if (state.gitHead !== currentHead) {
76
- return [
77
- 1,
78
- `session ritual state is stale because git HEAD changed. Run \`${formatFrameworkCommand(["session:start"])}\` again.`,
79
- ];
80
+ const forward = gitIsAncestor(projectRoot, state.gitHead, currentHead, runGit);
81
+ if (forward === null) {
82
+ return [2, "could not verify git history for session ritual"];
83
+ }
84
+ if (!forward) {
85
+ return [1, headDriftRecoveryMessage()];
86
+ }
87
+ if (input.rebindForwardHead) {
88
+ const payload = { ...state.raw, git_head: currentHead };
89
+ try {
90
+ writeRitualState(projectRoot, payload);
91
+ }
92
+ catch (exc) {
93
+ return [2, `could not rebind session ritual git HEAD: ${String(exc)}`];
94
+ }
95
+ }
80
96
  }
81
97
  const staleness = resolveSessionRitualStalenessHours(projectRoot);
82
98
  if (staleness.source === "default-on-error") {
@@ -157,6 +173,7 @@ export function inspectSessionRitual(projectRoot, options = {}) {
157
173
  tier,
158
174
  now: options.now ?? new Date(),
159
175
  runGit: options.runGit,
176
+ rebindForwardHead: false,
160
177
  });
161
178
  return {
162
179
  code,
@@ -246,6 +263,7 @@ export function verifySessionRitual(projectRoot, options = {}) {
246
263
  tier: "quick",
247
264
  now: instant,
248
265
  runGit: options.runGit,
266
+ rebindForwardHead: true,
249
267
  });
250
268
  if (precheckCode !== 0) {
251
269
  return {
@@ -259,6 +277,21 @@ export function verifySessionRitual(projectRoot, options = {}) {
259
277
  ritualStateRequired,
260
278
  };
261
279
  }
280
+ const reloadedAfterPrecheck = readRitualState(projectRoot);
281
+ state = reloadedAfterPrecheck[0];
282
+ err = reloadedAfterPrecheck[1];
283
+ if (state === null) {
284
+ return {
285
+ code: 2,
286
+ message: err ?? "ritual state invalid after precheck",
287
+ tier,
288
+ statePath,
289
+ bypassed: false,
290
+ wouldFailCode: null,
291
+ posture,
292
+ ritualStateRequired,
293
+ };
294
+ }
262
295
  const payload = { ...state.raw };
263
296
  const gated = { ...payload.gated_steps };
264
297
  payload.gated_steps = gated;
@@ -303,6 +336,7 @@ export function verifySessionRitual(projectRoot, options = {}) {
303
336
  tier,
304
337
  now: instant,
305
338
  runGit: options.runGit,
339
+ rebindForwardHead: true,
306
340
  });
307
341
  if (isBypassed) {
308
342
  return {
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import { PROJECTION_CONTAINMENT_REFUSED_EXIT_CODE, ProjectionContainmentError, } from "../fs/projection-containment.js";
4
5
  import { getPlatformCapabilities } from "../intake/platform-capabilities.js";
5
6
  import { EXIT_CONFIG_ERROR, EXIT_OK } from "./constants.js";
6
7
  import { dispatchProviderFromRuntime, HARNESS_BOUND_PROVIDERS, ROUTING_MODE_HARNESS_DEFAULT, ROUTING_MODE_PINNED, resolveRoutingPath, SWARM_WORKER_ROLES, writeModelDecision, } from "./routing.js";
@@ -66,11 +67,21 @@ export function routingSetMain(argv = process.argv.slice(2)) {
66
67
  "so only --harness-default is recordable here.\n");
67
68
  return EXIT_CONFIG_ERROR;
68
69
  }
69
- const path = resolveRoutingPath(resolve(projectRoot));
70
- writeModelDecision(path, resolvedProvider, role, {
71
- model,
72
- mode: harnessDefault ? ROUTING_MODE_HARNESS_DEFAULT : ROUTING_MODE_PINNED,
73
- });
70
+ const root = resolve(projectRoot);
71
+ const path = resolveRoutingPath(root);
72
+ try {
73
+ writeModelDecision(root, path, resolvedProvider, role, {
74
+ model,
75
+ mode: harnessDefault ? ROUTING_MODE_HARNESS_DEFAULT : ROUTING_MODE_PINNED,
76
+ });
77
+ }
78
+ catch (err) {
79
+ if (err instanceof ProjectionContainmentError) {
80
+ process.stderr.write(`ERROR: ${err.message}\n`);
81
+ return PROJECTION_CONTAINMENT_REFUSED_EXIT_CODE;
82
+ }
83
+ throw err;
84
+ }
74
85
  const modelText = model ?? "<harness default>";
75
86
  process.stdout.write(`Recorded route: provider '${resolvedProvider}', role '${role}' -> model ${modelText}.\n` +
76
87
  `Route file: ${path}\n`);
@@ -62,5 +62,5 @@ export declare function resolveDispatchProvider(environ?: NodeJS.ProcessEnv): st
62
62
  * `decidedAt` when the caller did not supply one. Used by the interactive
63
63
  * resolver path (resolver step 5) and the `swarm:routing-set` task.
64
64
  */
65
- export declare function writeModelDecision(path: string, provider: string, role: string, decision: RouteDecision): void;
65
+ export declare function writeModelDecision(projectRoot: string, path: string, provider: string, role: string, decision: RouteDecision): void;
66
66
  //# sourceMappingURL=routing.d.ts.map
@@ -16,6 +16,7 @@
16
16
  import { execFileSync } from "node:child_process";
17
17
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
18
18
  import { dirname, isAbsolute, join, resolve } from "node:path";
19
+ import { assertWriteTargetSafe } from "../fs/projection-containment.js";
19
20
  /**
20
21
  * The fixed worker-role vocabulary (reused from #1531). No separate tier
21
22
  * vocabulary to start; decisions are strictly per-role.
@@ -201,9 +202,10 @@ function assertSafeRoutingKey(kind, key) {
201
202
  * `decidedAt` when the caller did not supply one. Used by the interactive
202
203
  * resolver path (resolver step 5) and the `swarm:routing-set` task.
203
204
  */
204
- export function writeModelDecision(path, provider, role, decision) {
205
+ export function writeModelDecision(projectRoot, path, provider, role, decision) {
205
206
  assertSafeRoutingKey("provider", provider);
206
207
  assertSafeRoutingKey("role", role);
208
+ assertWriteTargetSafe(projectRoot, path);
207
209
  const { data } = loadRoutingFile(path);
208
210
  // Null-prototype write targets so a computed provider/role key can only ever
209
211
  // set an own property and can never reach `Object.prototype`, even if the
@@ -2,6 +2,7 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
2
2
  import { join, resolve } from "node:path";
3
3
  import { runningInsideDeftRepo } from "../doctor/paths.js";
4
4
  import { ALL_ATTRIBUTION_EVENT_NAMES } from "../events/attribution-constants.js";
5
+ import { assertWriteTargetSafe, ProjectionContainmentError } from "../fs/projection-containment.js";
5
6
  import { DEFAULT_EVENT_LOG, readEvents } from "../lifecycle/events.js";
6
7
  import { policyColonInvocation } from "../policy/policy-invocation.js";
7
8
  import { isValueFeedbackPathAllowed, resolveValueFeedback, } from "../policy/value-feedback.js";
@@ -229,10 +230,14 @@ function appendReadbackHistory(projectRoot, eventId, line, options = {}) {
229
230
  line,
230
231
  };
231
232
  try {
233
+ assertWriteTargetSafe(projectRoot, path);
232
234
  mkdirSync(join(path, ".."), { recursive: true });
233
235
  appendFileSync(path, `${JSON.stringify(record)}\n`, "utf8");
234
236
  }
235
- catch {
237
+ catch (err) {
238
+ if (err instanceof ProjectionContainmentError) {
239
+ throw err;
240
+ }
236
241
  // observability only
237
242
  }
238
243
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deftai/directive-core",
3
- "version": "0.83.0",
3
+ "version": "0.84.0",
4
4
  "description": "TypeScript engine core for the Directive framework.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -86,6 +86,10 @@
86
86
  "types": "./dist/cache/index.d.ts",
87
87
  "default": "./dist/cache/index.js"
88
88
  },
89
+ "./cache/task-cache": {
90
+ "types": "./dist/cache/task-cache/index.d.ts",
91
+ "default": "./dist/cache/task-cache/index.js"
92
+ },
89
93
  "./doctor": {
90
94
  "types": "./dist/doctor/index.d.ts",
91
95
  "default": "./dist/doctor/index.js"
@@ -313,8 +317,8 @@
313
317
  "provenance": true
314
318
  },
315
319
  "dependencies": {
316
- "@deftai/directive-content": "^0.83.0",
317
- "@deftai/directive-types": "^0.83.0",
320
+ "@deftai/directive-content": "^0.84.0",
321
+ "@deftai/directive-types": "^0.84.0",
318
322
  "archiver": "^8.0.0"
319
323
  },
320
324
  "scripts": {