@deftai/directive-core 0.94.0 → 0.96.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 (99) hide show
  1. package/dist/authz/classify.js +485 -0
  2. package/dist/cache/archive.d.ts +134 -0
  3. package/dist/cache/archive.js +630 -0
  4. package/dist/cache/index.d.ts +1 -0
  5. package/dist/cache/index.js +1 -0
  6. package/dist/cache/main.js +298 -1
  7. package/dist/content-contracts/skills/helpers.d.ts +1 -1
  8. package/dist/content-contracts/skills/helpers.js +1 -0
  9. package/dist/doctor/main.js +41 -9
  10. package/dist/doctor/types.d.ts +2 -2
  11. package/dist/freshness/bind.d.ts +67 -0
  12. package/dist/freshness/bind.js +201 -0
  13. package/dist/freshness/cli.d.ts +30 -0
  14. package/dist/freshness/cli.js +180 -0
  15. package/dist/freshness/compare.d.ts +14 -0
  16. package/dist/freshness/compare.js +134 -0
  17. package/dist/freshness/generation.d.ts +44 -0
  18. package/dist/freshness/generation.js +172 -0
  19. package/dist/freshness/index.d.ts +13 -0
  20. package/dist/freshness/index.js +13 -0
  21. package/dist/freshness/report.d.ts +43 -0
  22. package/dist/freshness/report.js +139 -0
  23. package/dist/freshness/types.d.ts +70 -0
  24. package/dist/freshness/types.js +30 -0
  25. package/dist/handoff-evidence/index.d.ts +8 -0
  26. package/dist/handoff-evidence/index.js +7 -0
  27. package/dist/handoff-evidence/validate.d.ts +105 -0
  28. package/dist/handoff-evidence/validate.js +423 -0
  29. package/dist/hooks/dispatcher.d.ts +3 -0
  30. package/dist/hooks/dispatcher.js +12 -3
  31. package/dist/index.d.ts +3 -0
  32. package/dist/index.js +3 -0
  33. package/dist/init-deposit/agent-hooks.d.ts +1 -1
  34. package/dist/init-deposit/agent-hooks.js +5 -2
  35. package/dist/init-deposit/hygiene.d.ts +1 -1
  36. package/dist/init-deposit/hygiene.js +16 -4
  37. package/dist/init-deposit/init-deposit.d.ts +7 -1
  38. package/dist/init-deposit/init-deposit.js +24 -4
  39. package/dist/init-deposit/refresh.d.ts +9 -1
  40. package/dist/init-deposit/refresh.js +48 -5
  41. package/dist/init-deposit/scaffold.js +6 -5
  42. package/dist/parent-turn-shape/evaluate.d.ts +84 -0
  43. package/dist/parent-turn-shape/evaluate.js +353 -0
  44. package/dist/parent-turn-shape/index.d.ts +8 -0
  45. package/dist/parent-turn-shape/index.js +8 -0
  46. package/dist/policy/index.d.ts +3 -0
  47. package/dist/policy/index.js +35 -0
  48. package/dist/release-e2e/greenfield-python-free-smoke.js +7 -3
  49. package/dist/review-monitor/constants.js +3 -2
  50. package/dist/review-monitor/tier-detection.d.ts +7 -3
  51. package/dist/review-monitor/tier-detection.js +18 -1
  52. package/dist/review-monitor/verify.js +18 -0
  53. package/dist/scope/index.d.ts +2 -0
  54. package/dist/scope/index.js +2 -0
  55. package/dist/scope/main.d.ts +10 -0
  56. package/dist/scope/main.js +109 -24
  57. package/dist/scope/promote-from-issue.d.ts +49 -0
  58. package/dist/scope/promote-from-issue.js +367 -0
  59. package/dist/scope/promote-path.d.ts +39 -0
  60. package/dist/scope/promote-path.js +105 -0
  61. package/dist/session/ritual-entrypoint.d.ts +6 -2
  62. package/dist/session/ritual-entrypoint.js +15 -2
  63. package/dist/session/session-ready.js +31 -14
  64. package/dist/session/session-start.d.ts +2 -1
  65. package/dist/session/session-start.js +66 -3
  66. package/dist/session/verify-session-ritual.d.ts +4 -2
  67. package/dist/session/verify-session-ritual.js +11 -11
  68. package/dist/slash/product-set.js +4 -1
  69. package/dist/swarm/routing.d.ts +4 -2
  70. package/dist/swarm/routing.js +26 -4
  71. package/dist/swarm/verify-review-clean.d.ts +4 -3
  72. package/dist/swarm/verify-review-clean.js +28 -73
  73. package/dist/triage/actions/index.js +62 -2
  74. package/dist/triage/actions/types.d.ts +8 -1
  75. package/dist/triage/author-filter.d.ts +51 -0
  76. package/dist/triage/author-filter.js +152 -0
  77. package/dist/triage/classify/index.d.ts +9 -0
  78. package/dist/triage/classify/index.js +34 -2
  79. package/dist/triage/classify/label-mirror.d.ts +206 -0
  80. package/dist/triage/classify/label-mirror.js +914 -0
  81. package/dist/triage/help/registry-data.d.ts +49 -38
  82. package/dist/triage/help/registry-data.js +134 -39
  83. package/dist/triage/index.d.ts +1 -0
  84. package/dist/triage/index.js +1 -0
  85. package/dist/triage/queue/index.d.ts +1 -0
  86. package/dist/triage/queue/index.js +1 -0
  87. package/dist/triage/queue/render.d.ts +2 -0
  88. package/dist/triage/queue/render.js +6 -0
  89. package/dist/triage/queue/show.d.ts +1 -1
  90. package/dist/triage/queue/show.js +4 -2
  91. package/dist/vbrief-validate/project-definition.js +1 -1
  92. package/dist/verify-env/agent-hook-readiness.d.ts +42 -0
  93. package/dist/verify-env/agent-hook-readiness.js +150 -0
  94. package/dist/verify-env/agent-hooks-live-probe.d.ts +16 -4
  95. package/dist/verify-env/agent-hooks-live-probe.js +120 -107
  96. package/dist/verify-env/agent-hooks.js +11 -3
  97. package/dist/verify-env/index.d.ts +1 -0
  98. package/dist/verify-env/index.js +1 -0
  99. package/package.json +11 -3
@@ -1,5 +1,8 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
+ import { latestDecisions, readAuditLog } from "../triage/actions/candidates-log.js";
4
+ import { resolveCandidatesLogPath } from "../triage/cache-path.js";
5
+ import { archiveClosedEntries, DEFAULT_ARCHIVE_OLDER_THAN_DAYS, listArchivedEntries, restoreFromArchive, } from "./archive.js";
3
6
  import { ALLOWED_SOURCES, DEFAULT_BATCH_SIZE, DEFAULT_DELAY_MS, DEFAULT_PRUNE_OLDER_THAN_DAYS, } from "./constants.js";
4
7
  import { CacheCapBreachedError, CacheError, CacheFetchError, CacheNotFoundError, CacheValidationError, } from "./errors.js";
5
8
  import { cacheFetchAll, cacheRefreshClosed } from "./fetch.js";
@@ -8,7 +11,7 @@ import { cacheGet, cacheInvalidate, cachePrune, cachePruneToCap, cachePut } from
8
11
  import { resolveCaps } from "./quota.js";
9
12
  import { clearTaskCache } from "./task-cache/store.js";
10
13
  function usage() {
11
- process.stderr.write("usage: cache [-h] {put,get,invalidate,fetch-all,prune,clear} ...\n");
14
+ process.stderr.write("usage: cache [-h] {put,get,invalidate,fetch-all,prune,clear,archive-closed,archive-list,restore-from-archive} ...\n");
12
15
  }
13
16
  function normaliseLabelFilter(raw) {
14
17
  if (!raw || raw.length === 0)
@@ -309,6 +312,293 @@ function cmdPrune(args) {
309
312
  process.stdout.write(`${pythonJsonPretty(payload)}\n`);
310
313
  return 0;
311
314
  }
315
+ /**
316
+ * Reversible closed-entry archive (#1137). Operator-only; not TTL prune.
317
+ * Distinct from `cache prune` (hard-delete by expires_at).
318
+ */
319
+ function cmdArchiveClosed(args) {
320
+ let olderThanDays = DEFAULT_ARCHIVE_OLDER_THAN_DAYS;
321
+ let source = "github-issue";
322
+ let repo;
323
+ let dryRun = false;
324
+ let json = false;
325
+ let terminalDecisionOnly = false;
326
+ let projectRoot;
327
+ let cacheRoot;
328
+ let reason;
329
+ for (let i = 0; i < args.length; i += 1) {
330
+ const arg = args[i];
331
+ if (arg === "--older-than-days") {
332
+ olderThanDays = Number.parseInt(args[i + 1] ?? "", 10);
333
+ i += 1;
334
+ }
335
+ else if (arg === "--source") {
336
+ source = args[i + 1] ?? "github-issue";
337
+ i += 1;
338
+ }
339
+ else if (arg === "--repo") {
340
+ repo = args[i + 1];
341
+ i += 1;
342
+ }
343
+ else if (arg === "--dry-run") {
344
+ dryRun = true;
345
+ }
346
+ else if (arg === "--json") {
347
+ json = true;
348
+ }
349
+ else if (arg === "--terminal-decision-only") {
350
+ terminalDecisionOnly = true;
351
+ }
352
+ else if (arg === "--project-root") {
353
+ projectRoot = args[i + 1];
354
+ i += 1;
355
+ }
356
+ else if (arg === "--cache-root") {
357
+ cacheRoot = args[i + 1];
358
+ i += 1;
359
+ }
360
+ else if (arg === "--reason") {
361
+ reason = args[i + 1];
362
+ i += 1;
363
+ }
364
+ else if (arg === "-h" || arg === "--help") {
365
+ process.stdout.write("usage: cache archive-closed [--dry-run] [--older-than-days 30] [--repo OWNER/NAME]\n" +
366
+ " [--source github-issue] [--json] [--terminal-decision-only]\n" +
367
+ " [--project-root PATH] [--cache-root PATH] [--reason TEXT]\n" +
368
+ "\n" +
369
+ "Reversible archive of closed github-issue cache entries (not TTL cache:prune).\n" +
370
+ "Skips issues still referenced by xbrief/{proposed,pending,active}.\n" +
371
+ "Also available as: task triage:cache-archive\n");
372
+ return 0;
373
+ }
374
+ else {
375
+ throw new CacheError(`unexpected argument: ${arg}`);
376
+ }
377
+ }
378
+ const resolvedProjectRoot = projectRoot ?? process.cwd();
379
+ const resolvedCacheRoot = cacheRoot ?? (projectRoot !== undefined ? resolve(projectRoot, ".deft-cache") : undefined);
380
+ let latestDecisionMap = null;
381
+ if (terminalDecisionOnly) {
382
+ try {
383
+ const logPath = resolveCandidatesLogPath(resolvedProjectRoot);
384
+ latestDecisionMap = latestDecisions(readAuditLog(logPath, repo ?? null));
385
+ }
386
+ catch {
387
+ latestDecisionMap = new Map();
388
+ }
389
+ }
390
+ const result = archiveClosedEntries({
391
+ olderThanDays,
392
+ source,
393
+ repo: repo ?? null,
394
+ dryRun,
395
+ terminalDecisionOnly,
396
+ latestDecisions: latestDecisionMap,
397
+ projectRoot: resolvedProjectRoot,
398
+ cacheRoot: resolvedCacheRoot,
399
+ reason,
400
+ });
401
+ const payload = {
402
+ mode: "archive-closed",
403
+ note: "Reversible closed-entry archive; distinct from cache:prune (TTL hard-delete).",
404
+ dry_run: result.dryRun,
405
+ older_than_days: result.olderThanDays,
406
+ source: result.source,
407
+ repo: result.repo,
408
+ terminal_decision_only: terminalDecisionOnly,
409
+ archived_count: result.archivedCount,
410
+ skipped_count: result.skippedCount,
411
+ archived: result.archived.map((c) => ({
412
+ key: c.key,
413
+ live_dir: c.liveDir,
414
+ archive_dir: c.archiveDir,
415
+ age_basis: c.ageBasis,
416
+ closed_at: c.closedAt,
417
+ pre_archive_decision: c.preArchiveDecision,
418
+ })),
419
+ skipped: result.skipped.map((s) => ({
420
+ key: s.key,
421
+ reason: s.reason,
422
+ detail: s.detail ?? null,
423
+ })),
424
+ };
425
+ if (json) {
426
+ process.stdout.write(`${pythonJsonPretty(payload)}\n`);
427
+ }
428
+ else {
429
+ process.stdout.write(`cache:archive-closed dry_run=${pythonBool(result.dryRun)} archived=${result.archivedCount} skipped=${result.skippedCount} older_than_days=${result.olderThanDays}\n`);
430
+ for (const c of result.archived) {
431
+ process.stdout.write(` archived ${c.key} -> ${c.archiveDir}\n`);
432
+ }
433
+ for (const s of result.skipped) {
434
+ process.stdout.write(` skipped ${s.key} (${s.reason}${s.detail ? `: ${s.detail}` : ""})\n`);
435
+ }
436
+ }
437
+ return 0;
438
+ }
439
+ function cmdArchiveList(args) {
440
+ let source = "github-issue";
441
+ let repo;
442
+ let since;
443
+ let limit;
444
+ let format = "text";
445
+ let cacheRoot;
446
+ let projectRoot;
447
+ for (let i = 0; i < args.length; i += 1) {
448
+ const arg = args[i];
449
+ if (arg === "--source") {
450
+ source = args[i + 1] ?? "github-issue";
451
+ i += 1;
452
+ }
453
+ else if (arg === "--repo") {
454
+ repo = args[i + 1];
455
+ i += 1;
456
+ }
457
+ else if (arg === "--since") {
458
+ since = args[i + 1];
459
+ i += 1;
460
+ }
461
+ else if (arg === "--limit") {
462
+ limit = Number.parseInt(args[i + 1] ?? "", 10);
463
+ i += 1;
464
+ }
465
+ else if (arg === "--format") {
466
+ format = args[i + 1] ?? "text";
467
+ i += 1;
468
+ }
469
+ else if (arg === "--format=json" || arg === "--json") {
470
+ format = "json";
471
+ }
472
+ else if (arg?.startsWith("--format=")) {
473
+ format = arg.slice("--format=".length);
474
+ }
475
+ else if (arg === "--cache-root") {
476
+ cacheRoot = args[i + 1];
477
+ i += 1;
478
+ }
479
+ else if (arg === "--project-root") {
480
+ projectRoot = args[i + 1];
481
+ i += 1;
482
+ }
483
+ else if (arg === "-h" || arg === "--help") {
484
+ process.stdout.write("usage: cache archive-list [--repo OWNER/NAME] [--since ISO] [--limit N]\n" +
485
+ " [--format=json|text] [--source github-issue] [--cache-root PATH]\n" +
486
+ "Also available as: task triage:archive-list\n");
487
+ return 0;
488
+ }
489
+ else {
490
+ throw new CacheError(`unexpected argument: ${arg}`);
491
+ }
492
+ }
493
+ const resolvedCacheRoot = cacheRoot ?? (projectRoot !== undefined ? resolve(projectRoot, ".deft-cache") : undefined);
494
+ const result = listArchivedEntries({
495
+ source,
496
+ repo: repo ?? null,
497
+ since: since ?? null,
498
+ limit: limit ?? null,
499
+ cacheRoot: resolvedCacheRoot,
500
+ });
501
+ if (format === "json") {
502
+ process.stdout.write(`${pythonJsonPretty({
503
+ source: result.source,
504
+ repo: result.repo,
505
+ count: result.count,
506
+ entries: result.entries.map((e) => ({
507
+ key: e.key,
508
+ archived_at: e.archivedAt,
509
+ archive_dir: e.archiveDir,
510
+ meta: e.meta,
511
+ })),
512
+ })}\n`);
513
+ }
514
+ else {
515
+ process.stdout.write(`cache:archive-list count=${result.count}\n`);
516
+ for (const e of result.entries) {
517
+ process.stdout.write(` ${e.key} archived_at=${e.archivedAt}\n`);
518
+ }
519
+ }
520
+ return 0;
521
+ }
522
+ function cmdRestoreFromArchive(args) {
523
+ let source = "github-issue";
524
+ let key;
525
+ let issue;
526
+ let repo;
527
+ let force = false;
528
+ let cacheRoot;
529
+ let projectRoot;
530
+ let json = false;
531
+ for (let i = 0; i < args.length; i += 1) {
532
+ const arg = args[i];
533
+ if (arg === "--source") {
534
+ source = args[i + 1] ?? "github-issue";
535
+ i += 1;
536
+ }
537
+ else if (arg === "--key") {
538
+ key = args[i + 1];
539
+ i += 1;
540
+ }
541
+ else if (arg === "--issue") {
542
+ const rawIssue = args[i + 1] ?? "";
543
+ if (!/^[1-9]\d*$/.test(rawIssue)) {
544
+ throw new CacheError(`--issue must be a positive integer (got ${JSON.stringify(rawIssue)})`);
545
+ }
546
+ issue = Number.parseInt(rawIssue, 10);
547
+ i += 1;
548
+ }
549
+ else if (arg === "--repo") {
550
+ repo = args[i + 1];
551
+ i += 1;
552
+ }
553
+ else if (arg === "--force") {
554
+ force = true;
555
+ }
556
+ else if (arg === "--json") {
557
+ json = true;
558
+ }
559
+ else if (arg === "--cache-root") {
560
+ cacheRoot = args[i + 1];
561
+ i += 1;
562
+ }
563
+ else if (arg === "--project-root") {
564
+ projectRoot = args[i + 1];
565
+ i += 1;
566
+ }
567
+ else if (arg === "-h" || arg === "--help") {
568
+ process.stdout.write("usage: cache restore-from-archive -- --issue N --repo OWNER/NAME [--force]\n" +
569
+ " [--key owner/repo/N] [--source github-issue] [--json]\n" +
570
+ "Also available as: task triage:restore-from-archive\n");
571
+ return 0;
572
+ }
573
+ else if (!key && arg && !arg.startsWith("-") && arg.includes("/")) {
574
+ key = arg;
575
+ }
576
+ else {
577
+ throw new CacheError(`unexpected argument: ${arg}`);
578
+ }
579
+ }
580
+ const resolvedCacheRoot = cacheRoot ?? (projectRoot !== undefined ? resolve(projectRoot, ".deft-cache") : undefined);
581
+ const result = restoreFromArchive({
582
+ source,
583
+ key,
584
+ issue,
585
+ repo: repo ?? null,
586
+ force,
587
+ cacheRoot: resolvedCacheRoot,
588
+ });
589
+ if (json) {
590
+ process.stdout.write(`${pythonJsonPretty(result)}\n`);
591
+ }
592
+ else {
593
+ process.stdout.write(`cache:restore-from-archive status=${result.status} key=${result.key} live=${result.liveDir}\n`);
594
+ if (result.detail) {
595
+ process.stdout.write(` detail: ${result.detail}\n`);
596
+ }
597
+ }
598
+ if (result.status === "missing" || result.status === "conflict")
599
+ return 1;
600
+ return 0;
601
+ }
312
602
  /** CLI entry point (mirrors `scripts/cache.py::main`). */
313
603
  export function main(argv) {
314
604
  if (argv.length === 0) {
@@ -332,6 +622,13 @@ export function main(argv) {
332
622
  return cmdPrune(rest);
333
623
  case "clear":
334
624
  return cmdClear(rest);
625
+ case "archive-closed":
626
+ case "cache-archive":
627
+ return cmdArchiveClosed(rest);
628
+ case "archive-list":
629
+ return cmdArchiveList(rest);
630
+ case "restore-from-archive":
631
+ return cmdRestoreFromArchive(rest);
335
632
  default:
336
633
  usage();
337
634
  process.stderr.write(`cache: error: argument cmd: invalid choice: '${cmd}'\n`);
@@ -21,7 +21,7 @@ export declare function readSkill(relPath: string): string;
21
21
  */
22
22
  export declare const SWARM_SKILL_REL = "skills/deft-directive-swarm/SKILL.md";
23
23
  /** Stable load order: core phases, then host launch adapters, then ops. */
24
- export declare const SWARM_REFERENCE_ORDER: readonly ["core-phase-0.md", "core-phase-1-2.md", "core-phase-3.md", "host-warp.md", "host-generic.md", "host-grok-build.md", "host-cursor.md", "host-openclaw.md", "core-phase-4.md", "core-phase-5-6.md", "core-ops.md"];
24
+ export declare const SWARM_REFERENCE_ORDER: readonly ["core-phase-0.md", "core-phase-1-2.md", "core-phase-3.md", "host-warp.md", "host-generic.md", "host-grok-build.md", "host-cursor.md", "host-claude-code.md", "host-openclaw.md", "core-phase-4.md", "core-phase-5-6.md", "core-ops.md"];
25
25
  export declare function readSwarmSkillSurface(): string;
26
26
  export declare function readAgentsMd(): string;
27
27
  /** Slice the first `## Returning Sessions` section body out of AGENTS.md. */
@@ -48,6 +48,7 @@ export const SWARM_REFERENCE_ORDER = [
48
48
  "host-generic.md",
49
49
  "host-grok-build.md",
50
50
  "host-cursor.md",
51
+ "host-claude-code.md",
51
52
  "host-openclaw.md",
52
53
  "core-phase-4.md",
53
54
  "core-phase-5-6.md",
@@ -517,8 +517,11 @@ export function runAgentHooksHealthCheck(projectRoot, consumerContext, sink, add
517
517
  });
518
518
  return false;
519
519
  }
520
- const message = `${checkName}: registered and structurally valid; ` +
521
- "Codex runtime trust is user-controlled and must be reviewed with `/hooks`";
520
+ const codexEnabled = result.registrations.some((entry) => entry.host === "codex" && entry.status !== "disabled");
521
+ const message = `${checkName}: registered and structurally valid` +
522
+ (codexEnabled
523
+ ? "; Codex trust is manual-review-required — open `/hooks` to review the project commands"
524
+ : "");
522
525
  sink.success(message);
523
526
  addFinding({
524
527
  severity: "skip",
@@ -526,8 +529,11 @@ export function runAgentHooksHealthCheck(projectRoot, consumerContext, sink, add
526
529
  check: checkName,
527
530
  status: "registered",
528
531
  registrations: result.registrations,
529
- trust_status: "not-verifiable",
530
- trust_review: "Open `/hooks` in Codex and review the project hook commands.",
532
+ trust_status: codexEnabled ? "manual-review-required" : "not-applicable",
533
+ trust_review: codexEnabled
534
+ ? "Open `/hooks` in Codex and review the exact project hook commands."
535
+ : null,
536
+ interception_status: "not-directly-verified",
531
537
  });
532
538
  return true;
533
539
  }
@@ -543,7 +549,25 @@ export function runAgentHooksLiveProbeCheck(projectRoot, sink, addFinding, seams
543
549
  const liveCheckName = "agent-hooks-live-probe";
544
550
  try {
545
551
  const result = (seams.evaluateAgentHooks ?? evaluateAgentHooks)(projectRoot);
546
- const liveResult = (seams.probeAgentHooksLive ?? probeAgentHooksLive)(projectRoot);
552
+ if (result.code !== 0) {
553
+ const message = `${checkName}: ${result.message.replace(/\s+/g, " ").trim()}`;
554
+ sink.warn(message);
555
+ addFinding({
556
+ severity: "warning",
557
+ message,
558
+ check: checkName,
559
+ status: result.code === 2 ? "unavailable" : "incomplete",
560
+ registrations: result.registrations,
561
+ suggestion: "deft update",
562
+ });
563
+ return;
564
+ }
565
+ const enabledHosts = result.registrations
566
+ .filter((entry) => entry.status !== "disabled")
567
+ .map((entry) => entry.host);
568
+ const liveResult = (seams.probeAgentHooksLive ?? probeAgentHooksLive)(projectRoot, {
569
+ hosts: enabledHosts,
570
+ });
547
571
  if (liveResult.code !== 0) {
548
572
  const message = `${liveCheckName}: ${liveResult.message.replace(/\s+/g, " ").trim()}`;
549
573
  sink.warn(message);
@@ -557,8 +581,12 @@ export function runAgentHooksLiveProbeCheck(projectRoot, sink, addFinding, seams
557
581
  });
558
582
  return;
559
583
  }
560
- const message = `${checkName}: registered, structurally valid, and live probe passed; ` +
561
- "Codex runtime trust is user-controlled and must be reviewed with `/hooks`";
584
+ const codexEnabled = result.registrations.some((entry) => entry.host === "codex" && entry.status !== "disabled");
585
+ const message = `${checkName}: registered, structurally valid, and live probe passed` +
586
+ (codexEnabled
587
+ ? "; Codex trust is manual-review-required — open `/hooks` to review the project commands"
588
+ : "") +
589
+ "; direct shim invocation does not verify host interception";
562
590
  sink.success(message);
563
591
  addFinding({
564
592
  severity: "skip",
@@ -566,9 +594,13 @@ export function runAgentHooksLiveProbeCheck(projectRoot, sink, addFinding, seams
566
594
  check: liveCheckName,
567
595
  status: "registered-and-functional",
568
596
  registrations: result.registrations,
569
- trust_status: "not-verifiable",
570
- trust_review: "Open `/hooks` in Codex and review the project hook commands.",
597
+ trust_status: codexEnabled ? "manual-review-required" : "not-applicable",
598
+ trust_review: codexEnabled
599
+ ? "Open `/hooks` in Codex and review the exact project hook commands."
600
+ : null,
601
+ interception_status: "not-directly-verified",
571
602
  live_probe: "passed",
603
+ live_probe_duration_ms: liveResult.durationMs,
572
604
  });
573
605
  }
574
606
  catch (cause) {
@@ -4,7 +4,7 @@ import type { EngineProbeResult } from "../resolution/classify.js";
4
4
  import type { ResolutionMode } from "../resolution/index.js";
5
5
  import type { ResolveUserMdResult } from "../user-config/resolve-user-md.js";
6
6
  import type { AgentHookHealthResult } from "../verify-env/agent-hooks.js";
7
- import type { AgentHookLiveProbeResult } from "../verify-env/agent-hooks-live-probe.js";
7
+ import type { AgentHookLiveProbeResult, AgentHookLiveProbeSeams } from "../verify-env/agent-hooks-live-probe.js";
8
8
  export declare const EXIT_CLEAN = 0;
9
9
  export declare const EXIT_DRIFT = 1;
10
10
  export declare const EXIT_CONFIG_ERROR = 2;
@@ -153,7 +153,7 @@ export interface DoctorSeams {
153
153
  /** Read-only agent-host hook registration probe (#2438). */
154
154
  readonly evaluateAgentHooks?: (projectRoot: string) => AgentHookHealthResult;
155
155
  /** Live hook spawn probe for doctor --full (#2852). */
156
- readonly probeAgentHooksLive?: (projectRoot: string) => AgentHookLiveProbeResult;
156
+ readonly probeAgentHooksLive?: (projectRoot: string, seams?: AgentHookLiveProbeSeams) => AgentHookLiveProbeResult;
157
157
  /**
158
158
  * xBRIEF project-envelope staleness probe (#2971). Injected so doctor can
159
159
  * fail closed on 0.6 project JSON under an xbrief/ layout without re-deriving
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Session bind of live deposit generation (#3117).
3
+ *
4
+ * Host-agnostic storage:
5
+ * - Default (no sessionId): `.deft/session-bind.json` — single-operator convenience.
6
+ * - With sessionId: `.deft/session-binds/<safeId>.json` — isolated multi-session binds.
7
+ *
8
+ * Multi-agent hosts MUST pass a stable host session identity when binding and
9
+ * reporting so one session cannot certify another as current.
10
+ */
11
+ import { type BoundGeneration, type LiveGeneration } from "./types.js";
12
+ /** Relative path for the default (no-sessionId) bind record. */
13
+ export declare const SESSION_BIND_REL: string;
14
+ /** Directory for per-session bind records. */
15
+ export declare const SESSION_BINDS_DIR_REL: string;
16
+ export declare function sessionBindPath(projectRoot: string, sessionId?: string | null): string;
17
+ /**
18
+ * Stable filesystem-safe file name for a host session id.
19
+ * Keeps a short prefix for debug, hashes the rest to avoid path injection.
20
+ * Character filter is O(n) (no regex) — CodeQL poly-redos on uncontrolled ids.
21
+ */
22
+ export declare function safeSessionFileName(sessionId: string): string;
23
+ export interface BindSessionOptions {
24
+ readonly sessionId?: string | null;
25
+ readonly nowIso?: string;
26
+ /**
27
+ * When live generation is missing (legacy deposit), stamp generation 1 from
28
+ * contentVersion before binding. Default true.
29
+ */
30
+ readonly ensureLive?: boolean;
31
+ /** Used only when ensuring a missing live token. */
32
+ readonly contentVersion?: string;
33
+ readonly stampedBy?: string;
34
+ /**
35
+ * When binding with a sessionId, also write the default bind path.
36
+ * Default **false** — multi-agent isolation requires hosts to report with
37
+ * `--session-id`. Enabling this reopens cross-session false-current (Greptile).
38
+ */
39
+ readonly alsoWriteDefault?: boolean;
40
+ /**
41
+ * Host attests that payload surfaces for the live generation were reloaded
42
+ * into the session. Required for trusted readiness. Default false.
43
+ */
44
+ readonly payloadLoaded?: boolean;
45
+ }
46
+ export interface ReadBoundOptions {
47
+ /** When set, read only that session's bind (never the default). */
48
+ readonly sessionId?: string | null;
49
+ }
50
+ /** Parse a bound generation record (null if invalid). */
51
+ export declare function parseBoundGeneration(raw: unknown): BoundGeneration | null;
52
+ /** Read the session bind record (null when absent/unreadable). */
53
+ export declare function readBoundGeneration(projectRoot: string, options?: ReadBoundOptions): BoundGeneration | null;
54
+ /**
55
+ * Bind the current live generation into session context.
56
+ *
57
+ * Does not require restarting a shared host runtime — callers re-load payload
58
+ * surfaces into the session and call this (or `freshness:bind`) to rebind.
59
+ *
60
+ * Multi-agent hosts MUST supply `sessionId` so binds do not overwrite each other.
61
+ */
62
+ export declare function bindSessionGeneration(projectRoot: string, options?: BindSessionOptions): {
63
+ bound: BoundGeneration;
64
+ live: LiveGeneration;
65
+ path: string;
66
+ };
67
+ //# sourceMappingURL=bind.d.ts.map