@event4u/agent-config 14.19.0 → 14.20.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 (101) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +108 -156
  5. package/dist/agent-src/commands/agent-handoff.md +42 -41
  6. package/dist/agent-src/contexts/execution/host-capability-manifest.md +7 -1
  7. package/dist/agent-src/rules/untrusted-input-defense.md +0 -2
  8. package/dist/agent-src/skills/evaluate-llm-feature/SKILL.md +1 -1
  9. package/dist/agent-src/skills/markitdown/SKILL.md +1 -1
  10. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +28 -6
  11. package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -3
  12. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  13. package/dist/agent-src/templates/roadmaps.md +14 -1
  14. package/dist/agent-src/templates/scripts/telemetry/mcp_lite.ts +213 -0
  15. package/dist/agent-src/templates/scripts/telemetry/report_renderer.ts +16 -0
  16. package/dist/agent-src/templates/scripts/telemetry_report.ts +22 -2
  17. package/dist/cli/mcp/dispatch.js +10 -0
  18. package/dist/cli/mcp/stdio.js +33 -2
  19. package/dist/cli/mcp/telemetry.js +227 -0
  20. package/dist/cli-delegate/{chunk-GZTMRYCN.js → chunk-C6IPZRDA.js} +79 -12
  21. package/dist/cli-delegate/{chunk-CYH5SP6M.js → chunk-I6RHFQEG.js} +151 -16
  22. package/dist/cli-delegate/{chunk-IDHUQIXP.js → chunk-NUMQHLS5.js} +21 -4
  23. package/dist/cli-delegate/{chunk-PMEKGDED.js → chunk-PI2GU3DP.js} +163 -1
  24. package/dist/cli-delegate/{chunk-7PRE6ZV2.js → chunk-YS6XTLEK.js} +199 -4
  25. package/dist/cli-delegate/chunk-ZVBOHSVF.js +1109 -0
  26. package/dist/cli-delegate/cmd_conformance.js +73 -109
  27. package/dist/cli-delegate/cmd_doctor.js +2 -2
  28. package/dist/cli-delegate/cmd_export.js +2 -2
  29. package/dist/cli-delegate/cmd_handoff.js +41 -11
  30. package/dist/cli-delegate/cmd_memory_get.js +1 -1
  31. package/dist/cli-delegate/cmd_prune.js +2 -2
  32. package/dist/cli-delegate/cmd_route_audit.js +1 -1
  33. package/dist/cli-delegate/cmd_session_recycle.js +59 -902
  34. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  35. package/dist/cli-delegate/cmd_sync.js +2 -2
  36. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  37. package/dist/cli-delegate/cmd_validate.js +2 -2
  38. package/dist/discovery/deprecation-report.md +1 -1
  39. package/dist/discovery/discovery-manifest.json +9 -9
  40. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  41. package/dist/discovery/discovery-manifest.summary.md +1 -1
  42. package/dist/discovery/dormancy-report.md +1 -1
  43. package/dist/discovery/orphan-report.md +1 -1
  44. package/dist/discovery/packs.json +2 -2
  45. package/dist/discovery/trust-report.md +1 -1
  46. package/dist/discovery/workspaces.json +2 -2
  47. package/dist/hooks/dispatch.js +7257 -6497
  48. package/dist/install/install.mjs +393 -168
  49. package/dist/mcp/registry-manifest.json +2 -2
  50. package/dist/mcp/server.json +2 -2
  51. package/dist/mcp/server.mjs +497 -118
  52. package/dist/scripts/mcp_server/telemetry.js +148 -0
  53. package/docs/guidelines/agent-infra/naming.md +1 -1
  54. package/docs/guidelines/agent-infra/security-lint-containment.md +46 -6
  55. package/docs/guidelines/agent-infra/self-improvement-pipeline.md +4 -4
  56. package/docs/guidelines/agent-infra/untrusted-input-spotlighting.md +1 -1
  57. package/docs/guidelines/redundancy-taxonomy.md +28 -0
  58. package/package.json +1 -1
  59. package/src/agent-src/templates/scripts/telemetry/mcp_lite.ts +213 -0
  60. package/src/agent-src/templates/scripts/telemetry/report_renderer.ts +16 -0
  61. package/src/agent-src/templates/scripts/telemetry_report.ts +22 -2
  62. package/src/config/assurance-capability-registry.json +9 -0
  63. package/src/config/gate-coverage.yml +55 -0
  64. package/src/config/gate-violation-baselines.json +15 -3
  65. package/src/scripts/_cli/cmd_session_recycle.ts +51 -3
  66. package/src/scripts/_lib/audit_field_provenance.ts +44 -3
  67. package/src/scripts/_lib/host_capability.ts +38 -0
  68. package/src/scripts/_lib/install_reach_checks.ts +33 -1
  69. package/src/scripts/_lib/mcp_bridge.ts +222 -10
  70. package/src/scripts/_lib/mcp_consent_residual.ts +173 -0
  71. package/src/scripts/_lib/orchestration_record.ts +23 -1
  72. package/src/scripts/_lib/recycle_envelope_paths.ts +190 -0
  73. package/src/scripts/_lib/release_position.ts +96 -0
  74. package/src/scripts/_lib/review_skipped_record.ts +17 -1
  75. package/src/scripts/_lib/security_lint.ts +179 -7
  76. package/src/scripts/_lib/self_repair.ts +141 -6
  77. package/src/scripts/_lib/self_repair_store.ts +273 -7
  78. package/src/scripts/_lib/subagent_capsule.ts +155 -3
  79. package/src/scripts/build_rule_trigger_matrix.ts +5 -0
  80. package/src/scripts/check_mcp_doc_drift.ts +217 -0
  81. package/src/scripts/check_pack_size.ts +321 -3
  82. package/src/scripts/handoff_context_hook.ts +54 -7
  83. package/src/scripts/hooks/session_eol_hook.ts +18 -7
  84. package/src/scripts/install.ts +4 -4
  85. package/src/scripts/lint_agent_security.ts +316 -25
  86. package/src/scripts/lint_confusables.ts +42 -6
  87. package/src/scripts/lint_handoffs.ts +0 -132
  88. package/src/scripts/lint_hidden_unicode.ts +49 -7
  89. package/src/scripts/lint_instruction_smuggling.ts +42 -6
  90. package/src/scripts/lint_mcp_config_security.ts +42 -6
  91. package/src/scripts/lint_roadmap_later_disposition.ts +217 -27
  92. package/src/scripts/lint_skill_frontmatter_safety.ts +43 -7
  93. package/src/scripts/mcp_server/resources.ts +41 -0
  94. package/src/scripts/mcp_server/telemetry.ts +19 -4
  95. package/src/scripts/orchestration_record.ts +12 -1
  96. package/src/scripts/release.ts +24 -24
  97. package/src/scripts/release_publication.ts +6 -2
  98. package/src/scripts/self_repair_cli.ts +31 -1
  99. package/src/scripts/skill_scout.ts +69 -0
  100. package/dist/cli-delegate/chunk-2GNWGMAL.js +0 -172
  101. package/dist/cli-delegate/chunk-LPPWO5CE.js +0 -161
@@ -34,11 +34,13 @@ import {
34
34
  DEFAULT_DEPLOY_ROOTS,
35
35
  MANAGED_SIGNATURE,
36
36
  SCHEMA_VERSIONS_SUPPORTED,
37
+ TOOL_TO_HOST,
37
38
  build_claude_hook_matrix,
38
39
  global_deploy_inventory_exports,
39
40
  manifest_path,
40
- read_manifest
41
- } from "./chunk-PMEKGDED.js";
41
+ read_manifest,
42
+ residualReport
43
+ } from "./chunk-PI2GU3DP.js";
42
44
  import {
43
45
  read_lockfile
44
46
  } from "./chunk-T7L2QK3K.js";
@@ -2779,12 +2781,27 @@ function checkOverrideSet(projectRoot) {
2779
2781
  remedy: "route it through the upstream-contribution path, never by changing a shipped default on one install"
2780
2782
  };
2781
2783
  }
2782
- var REACH_CHECK_IDS = ["instruction-path-reach", "version-axis", "override-set"];
2784
+ function checkMcpConsentResidual() {
2785
+ const id = "mcp-consent-residual";
2786
+ const lines = residualReport(Object.keys(TOOL_TO_HOST));
2787
+ if (lines.length === 0) {
2788
+ return { id, status: "ok", message: "no MCP consent residual recorded for any known host", remedy: "" };
2789
+ }
2790
+ const actionable = lines.filter((l) => l.state === "residual");
2791
+ return {
2792
+ id,
2793
+ status: "warn",
2794
+ message: lines.map((l) => l.text).join(" \xB7 "),
2795
+ remedy: actionable.length > 0 ? "complete the step named above on each host you use" : "record an observation for the unchecked hosts, or ignore if you do not use them"
2796
+ };
2797
+ }
2798
+ var REACH_CHECK_IDS = ["instruction-path-reach", "version-axis", "override-set", "mcp-consent-residual"];
2783
2799
  function reachRunners(opts) {
2784
2800
  return {
2785
2801
  "instruction-path-reach": () => checkInstructionPaths(opts.projectRoot),
2786
2802
  "version-axis": () => checkVersionAxis(opts.projectRoot, opts.resolvableVersion),
2787
- "override-set": () => checkOverrideSet(opts.projectRoot)
2803
+ "override-set": () => checkOverrideSet(opts.projectRoot),
2804
+ "mcp-consent-residual": () => checkMcpConsentResidual()
2788
2805
  };
2789
2806
  }
2790
2807
 
@@ -968,6 +968,164 @@ function splitlines2(text) {
968
968
  return parts;
969
969
  }
970
970
 
971
+ // src/scripts/_lib/host_capability.ts
972
+ import process2 from "node:process";
973
+ var SAFE_DEFAULT = {
974
+ schema_version: 1,
975
+ subagent_spawn: false,
976
+ parallel_spawn: false,
977
+ status_polling: false,
978
+ separate_quota_pool: false,
979
+ agent_teams: false,
980
+ worker_respawn: false,
981
+ reads_project_mcp_config: false,
982
+ // `true` because the safe answer here is "there is still a step", not
983
+ // "there is nothing to do" — see the field's own note.
984
+ mcp_needs_manual_activation: true
985
+ };
986
+ function asBool(value) {
987
+ return value === true;
988
+ }
989
+ function normalizeHostManifest(input) {
990
+ if (input === null || typeof input !== "object") {
991
+ return { ...SAFE_DEFAULT };
992
+ }
993
+ const src = input;
994
+ return {
995
+ schema_version: 1,
996
+ subagent_spawn: asBool(src.subagent_spawn),
997
+ parallel_spawn: asBool(src.parallel_spawn),
998
+ status_polling: asBool(src.status_polling),
999
+ separate_quota_pool: asBool(src.separate_quota_pool),
1000
+ agent_teams: asBool(src.agent_teams),
1001
+ worker_respawn: asBool(src.worker_respawn),
1002
+ reads_project_mcp_config: asBool(src.reads_project_mcp_config),
1003
+ // Inverted coercion, matching the inverted default: only an explicit
1004
+ // `false` clears the residual, so an absent or malformed value keeps
1005
+ // the manual step visible instead of silently dropping it.
1006
+ mcp_needs_manual_activation: src.mcp_needs_manual_activation !== false
1007
+ };
1008
+ }
1009
+ var HOST_CAPABILITY_REGISTRY = {
1010
+ claude: { subagent_spawn: true, parallel_spawn: true }
1011
+ };
1012
+ function resolveHostCapabilities(hostId, override) {
1013
+ if (override !== void 0 && override !== null && typeof override === "object" && !Array.isArray(override)) {
1014
+ return normalizeHostManifest(override);
1015
+ }
1016
+ const row = hostId !== null && hostId !== void 0 ? HOST_CAPABILITY_REGISTRY[hostId] : void 0;
1017
+ if (row === void 0) {
1018
+ return { ...SAFE_DEFAULT };
1019
+ }
1020
+ return { ...SAFE_DEFAULT, ...row, schema_version: 1 };
1021
+ }
1022
+ function probeHostCapabilities(hostId) {
1023
+ const base = resolveHostCapabilities(hostId);
1024
+ const flag = process2.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
1025
+ const probedAgentTeams = typeof flag === "string" && flag !== "";
1026
+ return { ...base, agent_teams: base.agent_teams || probedAgentTeams };
1027
+ }
1028
+ var CAPABILITY_FIELDS = [
1029
+ "subagent_spawn",
1030
+ "parallel_spawn",
1031
+ "status_polling",
1032
+ "separate_quota_pool",
1033
+ "agent_teams",
1034
+ "worker_respawn",
1035
+ "reads_project_mcp_config",
1036
+ "mcp_needs_manual_activation"
1037
+ ];
1038
+ function describeHostCapabilities(hostId) {
1039
+ const manifest = probeHostCapabilities(hostId);
1040
+ const row = hostId !== null && hostId !== void 0 ? HOST_CAPABILITY_REGISTRY[hostId] : void 0;
1041
+ const flag = process2.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
1042
+ const probedAgentTeams = typeof flag === "string" && flag !== "";
1043
+ const sources = {};
1044
+ for (const field of CAPABILITY_FIELDS) {
1045
+ if (row !== void 0 && typeof row[field] === "boolean") {
1046
+ sources[field] = "registry";
1047
+ } else if (field === "agent_teams" && probedAgentTeams) {
1048
+ sources[field] = "live-probe";
1049
+ } else {
1050
+ sources[field] = "default";
1051
+ }
1052
+ }
1053
+ return { manifest, sources };
1054
+ }
1055
+
1056
+ // src/scripts/_lib/mcp_consent_residual.ts
1057
+ var SEEDED = {
1058
+ claude: {
1059
+ hostId: "claude",
1060
+ residual: "Approve the project-scoped `agent-config` server the first time the host prompts \u2014 the installer writes `.mcp.json` but does not pre-approve it.",
1061
+ source: "vendor-doc",
1062
+ cite: "Host documentation: a project-scope MCP server requires explicit approval, suppressible only through the user-global `enabledMcpjsonServers` key, which this package deliberately does not write (blocker `mcp-user-scope-approval-consent`)."
1063
+ }
1064
+ };
1065
+ var TOOL_TO_HOST = {
1066
+ "claude-code": "claude",
1067
+ cursor: "cursor",
1068
+ "gemini-cli": "gemini",
1069
+ windsurf: "windsurf",
1070
+ cline: "cline",
1071
+ copilot: "copilot",
1072
+ augment: "augment"
1073
+ };
1074
+ function residualFor(hostId) {
1075
+ const { manifest, sources } = describeHostCapabilities(hostId);
1076
+ if (sources.mcp_needs_manual_activation === "registry" && !manifest.mcp_needs_manual_activation) {
1077
+ return null;
1078
+ }
1079
+ return SEEDED[hostId] ?? null;
1080
+ }
1081
+ function isRecorded(hostId) {
1082
+ const { sources } = describeHostCapabilities(hostId);
1083
+ return hostId in SEEDED || sources.mcp_needs_manual_activation === "registry";
1084
+ }
1085
+ function residualLine(hostId) {
1086
+ const r = residualFor(hostId);
1087
+ if (r !== null) {
1088
+ return {
1089
+ hostId,
1090
+ state: "residual",
1091
+ text: `${hostId}: ${r.residual} (source: ${r.source} \u2014 not observed here)`
1092
+ };
1093
+ }
1094
+ if (isRecorded(hostId)) {
1095
+ return {
1096
+ hostId,
1097
+ state: "cleared",
1098
+ text: `${hostId}: nothing left to do (source: observed)`
1099
+ };
1100
+ }
1101
+ return {
1102
+ hostId,
1103
+ state: "unrecorded",
1104
+ text: `${hostId}: no residual recorded \u2014 nobody checked this host, which is not the same as nothing to do`
1105
+ };
1106
+ }
1107
+ function residualReport(toolIds) {
1108
+ const seen = /* @__PURE__ */ new Set();
1109
+ const out = [];
1110
+ for (const toolId of toolIds) {
1111
+ const hostId = TOOL_TO_HOST[toolId];
1112
+ if (hostId === void 0 || seen.has(hostId)) continue;
1113
+ seen.add(hostId);
1114
+ const line = residualLine(hostId);
1115
+ if (line.state !== "cleared") out.push(line);
1116
+ }
1117
+ return out;
1118
+ }
1119
+ function printResiduals(toolIds, quiet, write = (s) => process.stdout.write(s)) {
1120
+ if (quiet) return;
1121
+ const lines = residualReport(toolIds);
1122
+ if (lines.length === 0) return;
1123
+ write(" Still needs you (MCP):\n");
1124
+ for (const line of lines) write(` \u2022 ${line.text}
1125
+ `);
1126
+ write("\n");
1127
+ }
1128
+
971
1129
  export {
972
1130
  SCHEMA_VERSIONS_SUPPORTED,
973
1131
  DEFAULT_DEPLOY_ROOTS,
@@ -977,6 +1135,7 @@ export {
977
1135
  ScopeMismatchError,
978
1136
  upsert_tool,
979
1137
  atomicAppendLine,
1138
+ probeHostCapabilities,
980
1139
  MANAGED_SIGNATURE,
981
1140
  build_claude_hook_matrix,
982
1141
  ensure_managed_hooks,
@@ -988,5 +1147,8 @@ export {
988
1147
  reap_tagged_orphans,
989
1148
  record_deploy,
990
1149
  is_ancestor,
991
- global_deploy_inventory_exports
1150
+ global_deploy_inventory_exports,
1151
+ TOOL_TO_HOST,
1152
+ residualReport,
1153
+ printResiduals
992
1154
  };
@@ -9,6 +9,9 @@ import {
9
9
  session_state_file,
10
10
  update_json_under_lock
11
11
  } from "./chunk-E4XIZDIB.js";
12
+ import {
13
+ read_lockfile
14
+ } from "./chunk-T7L2QK3K.js";
12
15
 
13
16
  // src/scripts/_lib/secret_detector.ts
14
17
  var ENTROPY_MIN_TOKEN_LENGTH = 20;
@@ -219,7 +222,8 @@ var EPISTEMIC = new Set(EPISTEMIC_STATES);
219
222
  var MAX_REF_CHARS = 200;
220
223
  var MAX_LINE_CHARS = 240;
221
224
  var MAX_ENTRIES = 40;
222
- var CAPSULE_SCHEMA_VERSION = 3;
225
+ var CAPSULE_SCHEMA_VERSION = 4;
226
+ var ACCEPTED_CAPSULE_VERSIONS = [3, 4];
223
227
  function isShortLine(value, max) {
224
228
  return typeof value === "string" && value.length > 0 && value.length <= max && !value.includes("\n");
225
229
  }
@@ -378,6 +382,10 @@ var RECYCLE_ENVELOPE_KEYS = /* @__PURE__ */ new Set([
378
382
  "next_task",
379
383
  "suggested_skills",
380
384
  "failed_approaches",
385
+ "successful_approaches",
386
+ "open_questions",
387
+ "predecessor",
388
+ "session_id",
381
389
  "repo_identity",
382
390
  "branch",
383
391
  "head",
@@ -398,9 +406,11 @@ function validateRecycleEnvelope(input) {
398
406
  );
399
407
  }
400
408
  }
401
- if (e["capsule_version"] !== CAPSULE_SCHEMA_VERSION) {
402
- errors.push(`capsule_version must be ${CAPSULE_SCHEMA_VERSION}`);
409
+ const version = e["capsule_version"];
410
+ if (typeof version !== "number" || !ACCEPTED_CAPSULE_VERSIONS.includes(version)) {
411
+ errors.push(`capsule_version must be one of ${ACCEPTED_CAPSULE_VERSIONS.join(" | ")}`);
403
412
  }
413
+ const atV4 = version === 4;
404
414
  if (e["variant"] !== "main_session") {
405
415
  errors.push("variant must be 'main_session'");
406
416
  }
@@ -439,6 +449,26 @@ function validateRecycleEnvelope(input) {
439
449
  'failed_approaches must carry at least one entry \u2014 write "none" explicitly, so silence and "nothing was abandoned" stay distinguishable'
440
450
  );
441
451
  }
452
+ checkList(errors, "successful_approaches", e["successful_approaches"], MAX_LINE_CHARS, atV4);
453
+ if (Array.isArray(e["successful_approaches"]) && e["successful_approaches"].length === 0) {
454
+ errors.push(
455
+ 'successful_approaches must carry at least one entry \u2014 write "none" explicitly, so silence and "nothing worked" stay distinguishable'
456
+ );
457
+ }
458
+ checkList(errors, "open_questions", e["open_questions"], MAX_LINE_CHARS, false);
459
+ const predecessor = e["predecessor"];
460
+ if (predecessor === void 0) {
461
+ if (atV4) {
462
+ errors.push(
463
+ 'predecessor is required \u2014 name the session this one continues, or write "none" explicitly for the first session in a workspace'
464
+ );
465
+ }
466
+ } else if (!isShortLine(predecessor, MAX_REF_CHARS)) {
467
+ errors.push(`predecessor must be a single line of 1\u2013${MAX_REF_CHARS} chars ("none" when there is no predecessor)`);
468
+ }
469
+ if (e["session_id"] !== void 0 && !isShortLine(e["session_id"], MAX_REF_CHARS)) {
470
+ errors.push(`session_id must be a single line of 1\u2013${MAX_REF_CHARS} chars`);
471
+ }
442
472
  for (const field of ["repo_identity", "branch", "head", "status_summary", "last_verify"]) {
443
473
  if (e[field] !== void 0 && !isShortLine(e[field], MAX_REF_CHARS)) {
444
474
  errors.push(`${field} must be a single line of 1\u2013${MAX_REF_CHARS} chars`);
@@ -803,6 +833,164 @@ if (isCliEntry) {
803
833
  process.exit(main(process.argv.slice(2)));
804
834
  }
805
835
 
836
+ // src/scripts/_lib/turn_end_refusals.ts
837
+ import * as crypto from "node:crypto";
838
+ import * as fs2 from "node:fs";
839
+ import * as path2 from "node:path";
840
+ var DETECTOR_IDS = [
841
+ "promissory",
842
+ "language",
843
+ "verification",
844
+ "completion"
845
+ ];
846
+ var REFUSAL_STATE_REL = path2.join("agents", "runtime", "state", "turn-end-gate");
847
+ function refusalStateDir(workspaceRoot) {
848
+ return path2.join(workspaceRoot, REFUSAL_STATE_REL);
849
+ }
850
+ function deriveSessionKey(sessionId) {
851
+ return crypto.createHash("sha256").update(sessionId).digest("hex").slice(0, 32);
852
+ }
853
+ function sessionRefusalFile(workspaceRoot, sessionKey) {
854
+ return path2.join(refusalStateDir(workspaceRoot), `${sessionKey}.json`);
855
+ }
856
+ function readSessionCounts(workspaceRoot, sessionId) {
857
+ try {
858
+ const raw = fs2.readFileSync(
859
+ sessionRefusalFile(workspaceRoot, deriveSessionKey(sessionId)),
860
+ "utf-8"
861
+ );
862
+ const rec = parseRecord(raw);
863
+ return rec === null ? null : countsOf(rec);
864
+ } catch {
865
+ return null;
866
+ }
867
+ }
868
+ function emptyCounts() {
869
+ return { promissory: 0, language: 0, verification: 0, completion: 0 };
870
+ }
871
+ function isDetector(v) {
872
+ return typeof v === "string" && DETECTOR_IDS.includes(v);
873
+ }
874
+ function parseRecord(raw) {
875
+ let decoded;
876
+ try {
877
+ decoded = JSON.parse(raw);
878
+ } catch {
879
+ return null;
880
+ }
881
+ if (typeof decoded !== "object" || decoded === null || Array.isArray(decoded)) return null;
882
+ const o = decoded;
883
+ if (typeof o["refused_at"] !== "string") return null;
884
+ if (typeof o["refused_turn"] !== "number") return null;
885
+ if (!isDetector(o["detector"])) return null;
886
+ const rec = {
887
+ refused_at: o["refused_at"],
888
+ refused_turn: o["refused_turn"],
889
+ detector: o["detector"]
890
+ };
891
+ if (typeof o["first_refused_at"] === "string") rec.first_refused_at = o["first_refused_at"];
892
+ if (typeof o["agent_config_version"] === "string") {
893
+ rec.agent_config_version = o["agent_config_version"];
894
+ }
895
+ const counts = o["counts"];
896
+ if (typeof counts === "object" && counts !== null && !Array.isArray(counts)) {
897
+ const parsed = {};
898
+ for (const id of DETECTOR_IDS) {
899
+ const n = counts[id];
900
+ if (typeof n === "number" && Number.isFinite(n) && n >= 0) parsed[id] = n;
901
+ }
902
+ rec.counts = parsed;
903
+ }
904
+ return rec;
905
+ }
906
+ function countsOf(rec) {
907
+ const out = emptyCounts();
908
+ if (rec.counts === void 0) {
909
+ out[rec.detector] = 1;
910
+ return out;
911
+ }
912
+ let any = false;
913
+ for (const id of DETECTOR_IDS) {
914
+ const n = rec.counts[id];
915
+ if (typeof n === "number" && n > 0) {
916
+ out[id] = n;
917
+ any = true;
918
+ }
919
+ }
920
+ if (!any) out[rec.detector] = 1;
921
+ return out;
922
+ }
923
+ function foldRefusal(prev, input) {
924
+ const counts = prev === null ? emptyCounts() : countsOf(prev);
925
+ for (const d of input.detectors) counts[d] += 1;
926
+ const primary = input.detectors[0] ?? prev?.detector ?? "verification";
927
+ const rec = {
928
+ refused_at: input.at,
929
+ refused_turn: input.turnOrdinal,
930
+ detector: primary,
931
+ first_refused_at: prev?.first_refused_at ?? prev?.refused_at ?? input.at,
932
+ counts
933
+ };
934
+ if (input.version !== void 0) rec.agent_config_version = input.version;
935
+ else if (prev?.agent_config_version !== void 0) {
936
+ rec.agent_config_version = prev.agent_config_version;
937
+ }
938
+ return rec;
939
+ }
940
+ function readInstallBoundary() {
941
+ try {
942
+ const lock = read_lockfile();
943
+ if (lock === null) return { version: null, installed_at: null };
944
+ return {
945
+ version: lock.agent_config_version ?? null,
946
+ installed_at: lock.installed_at ?? null
947
+ };
948
+ } catch {
949
+ return { version: null, installed_at: null };
950
+ }
951
+ }
952
+ var REFUSAL_STATE_MAX_AGE_DAYS = 90;
953
+ function pruneAgedRefusalState(workspaceRoot, opts = {}) {
954
+ const maxAgeDays = opts.maxAgeDays ?? REFUSAL_STATE_MAX_AGE_DAYS;
955
+ const now = opts.now ?? /* @__PURE__ */ new Date();
956
+ const dir = refusalStateDir(workspaceRoot);
957
+ const result = { scanned: 0, pruned: 0, kept: 0 };
958
+ let entries;
959
+ try {
960
+ entries = fs2.readdirSync(dir);
961
+ } catch {
962
+ return result;
963
+ }
964
+ const cutoffMs = now.getTime() - maxAgeDays * 24 * 60 * 60 * 1e3;
965
+ for (const name of entries) {
966
+ if (!name.endsWith(".json")) continue;
967
+ result.scanned += 1;
968
+ const file = path2.join(dir, name);
969
+ let rec = null;
970
+ try {
971
+ rec = parseRecord(fs2.readFileSync(file, "utf-8"));
972
+ } catch {
973
+ rec = null;
974
+ }
975
+ if (rec === null) {
976
+ result.kept += 1;
977
+ continue;
978
+ }
979
+ const at = Date.parse(rec.refused_at);
980
+ if (!Number.isFinite(at) || at >= cutoffMs) {
981
+ result.kept += 1;
982
+ continue;
983
+ }
984
+ try {
985
+ fs2.unlinkSync(file);
986
+ result.pruned += 1;
987
+ } catch {
988
+ result.kept += 1;
989
+ }
990
+ }
991
+ return result;
992
+ }
993
+
806
994
  export {
807
995
  statePathFor,
808
996
  isVacuousOutput,
@@ -813,5 +1001,12 @@ export {
813
1001
  wrapAsPriorSessionData,
814
1002
  hasBoundaryMarker,
815
1003
  validateRecycleEnvelope,
816
- validateAssumption
1004
+ validateAssumption,
1005
+ deriveSessionKey,
1006
+ sessionRefusalFile,
1007
+ readSessionCounts,
1008
+ parseRecord,
1009
+ foldRefusal,
1010
+ readInstallBoundary,
1011
+ pruneAgedRefusalState
817
1012
  };