@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.
- package/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +108 -156
- package/dist/agent-src/commands/agent-handoff.md +42 -41
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +7 -1
- package/dist/agent-src/rules/untrusted-input-defense.md +0 -2
- package/dist/agent-src/skills/evaluate-llm-feature/SKILL.md +1 -1
- package/dist/agent-src/skills/markitdown/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +28 -6
- package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -3
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +14 -1
- package/dist/agent-src/templates/scripts/telemetry/mcp_lite.ts +213 -0
- package/dist/agent-src/templates/scripts/telemetry/report_renderer.ts +16 -0
- package/dist/agent-src/templates/scripts/telemetry_report.ts +22 -2
- package/dist/cli/mcp/dispatch.js +10 -0
- package/dist/cli/mcp/stdio.js +33 -2
- package/dist/cli/mcp/telemetry.js +227 -0
- package/dist/cli-delegate/{chunk-GZTMRYCN.js → chunk-C6IPZRDA.js} +79 -12
- package/dist/cli-delegate/{chunk-CYH5SP6M.js → chunk-I6RHFQEG.js} +151 -16
- package/dist/cli-delegate/{chunk-IDHUQIXP.js → chunk-NUMQHLS5.js} +21 -4
- package/dist/cli-delegate/{chunk-PMEKGDED.js → chunk-PI2GU3DP.js} +163 -1
- package/dist/cli-delegate/{chunk-7PRE6ZV2.js → chunk-YS6XTLEK.js} +199 -4
- package/dist/cli-delegate/chunk-ZVBOHSVF.js +1109 -0
- package/dist/cli-delegate/cmd_conformance.js +73 -109
- package/dist/cli-delegate/cmd_doctor.js +2 -2
- package/dist/cli-delegate/cmd_export.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +41 -11
- package/dist/cli-delegate/cmd_memory_get.js +1 -1
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_route_audit.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +59 -902
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- package/dist/cli-delegate/cmd_sync.js +2 -2
- package/dist/cli-delegate/cmd_uninstall.js +2 -2
- package/dist/cli-delegate/cmd_validate.js +2 -2
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +9 -9
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +7257 -6497
- package/dist/install/install.mjs +393 -168
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +497 -118
- package/dist/scripts/mcp_server/telemetry.js +148 -0
- package/docs/guidelines/agent-infra/naming.md +1 -1
- package/docs/guidelines/agent-infra/security-lint-containment.md +46 -6
- package/docs/guidelines/agent-infra/self-improvement-pipeline.md +4 -4
- package/docs/guidelines/agent-infra/untrusted-input-spotlighting.md +1 -1
- package/docs/guidelines/redundancy-taxonomy.md +28 -0
- package/package.json +1 -1
- package/src/agent-src/templates/scripts/telemetry/mcp_lite.ts +213 -0
- package/src/agent-src/templates/scripts/telemetry/report_renderer.ts +16 -0
- package/src/agent-src/templates/scripts/telemetry_report.ts +22 -2
- package/src/config/assurance-capability-registry.json +9 -0
- package/src/config/gate-coverage.yml +55 -0
- package/src/config/gate-violation-baselines.json +15 -3
- package/src/scripts/_cli/cmd_session_recycle.ts +51 -3
- package/src/scripts/_lib/audit_field_provenance.ts +44 -3
- package/src/scripts/_lib/host_capability.ts +38 -0
- package/src/scripts/_lib/install_reach_checks.ts +33 -1
- package/src/scripts/_lib/mcp_bridge.ts +222 -10
- package/src/scripts/_lib/mcp_consent_residual.ts +173 -0
- package/src/scripts/_lib/orchestration_record.ts +23 -1
- package/src/scripts/_lib/recycle_envelope_paths.ts +190 -0
- package/src/scripts/_lib/release_position.ts +96 -0
- package/src/scripts/_lib/review_skipped_record.ts +17 -1
- package/src/scripts/_lib/security_lint.ts +179 -7
- package/src/scripts/_lib/self_repair.ts +141 -6
- package/src/scripts/_lib/self_repair_store.ts +273 -7
- package/src/scripts/_lib/subagent_capsule.ts +155 -3
- package/src/scripts/build_rule_trigger_matrix.ts +5 -0
- package/src/scripts/check_mcp_doc_drift.ts +217 -0
- package/src/scripts/check_pack_size.ts +321 -3
- package/src/scripts/handoff_context_hook.ts +54 -7
- package/src/scripts/hooks/session_eol_hook.ts +18 -7
- package/src/scripts/install.ts +4 -4
- package/src/scripts/lint_agent_security.ts +316 -25
- package/src/scripts/lint_confusables.ts +42 -6
- package/src/scripts/lint_handoffs.ts +0 -132
- package/src/scripts/lint_hidden_unicode.ts +49 -7
- package/src/scripts/lint_instruction_smuggling.ts +42 -6
- package/src/scripts/lint_mcp_config_security.ts +42 -6
- package/src/scripts/lint_roadmap_later_disposition.ts +217 -27
- package/src/scripts/lint_skill_frontmatter_safety.ts +43 -7
- package/src/scripts/mcp_server/resources.ts +41 -0
- package/src/scripts/mcp_server/telemetry.ts +19 -4
- package/src/scripts/orchestration_record.ts +12 -1
- package/src/scripts/release.ts +24 -24
- package/src/scripts/release_publication.ts +6 -2
- package/src/scripts/self_repair_cli.ts +31 -1
- package/src/scripts/skill_scout.ts +69 -0
- package/dist/cli-delegate/chunk-2GNWGMAL.js +0 -172
- 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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
402
|
-
|
|
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
|
};
|