@event4u/agent-config 9.36.0 → 10.0.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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +57 -0
- package/agents/templates/.ai-council.yml.example +31 -51
- package/dist/agent-src/commands/analyze/conformance.md +15 -4
- package/dist/agent-src/rules/session-canary.md +28 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli-delegate/{chunk-SFGARMAB.js → chunk-2H5SE7DU.js} +24 -4
- package/dist/cli-delegate/{chunk-U7CMAIOC.js → chunk-3JUTT26I.js} +1 -1
- package/dist/cli-delegate/{chunk-JP53LNVT.js → chunk-HJSKTMIF.js} +6 -14
- package/dist/cli-delegate/{chunk-QEEB4LHO.js → chunk-LRXJK6GN.js} +1 -1
- package/dist/cli-delegate/{chunk-NR7HSN3P.js → chunk-NX5AXSLD.js} +3 -3
- package/dist/cli-delegate/{chunk-AG47LAGQ.js → chunk-TVGXH7VA.js} +1 -1
- package/dist/cli-delegate/{chunk-55RI5UXK.js → chunk-XT2YTJCZ.js} +22 -18
- package/dist/cli-delegate/{chunk-JEJABGKB.js → chunk-XTC5ARB6.js} +16 -12
- package/dist/cli-delegate/cmd_analyze_session.js +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +1 -1
- package/dist/cli-delegate/cmd_conformance.js +1037 -196
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_explain.js +2 -2
- package/dist/cli-delegate/cmd_export.js +4 -4
- package/dist/cli-delegate/cmd_handoff.js +3 -3
- package/dist/cli-delegate/cmd_mcp_available.js +1 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +2 -2
- package/dist/cli-delegate/cmd_prune.js +4 -4
- package/dist/cli-delegate/cmd_route_audit.js +2 -2
- package/dist/cli-delegate/cmd_route_explain.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +1 -1
- package/dist/cli-delegate/cmd_settings_get.js +1 -1
- package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +4 -4
- package/dist/cli-delegate/cmd_uninstall.js +4 -4
- package/dist/cli-delegate/cmd_update.js +1 -1
- package/dist/cli-delegate/cmd_upgrade.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +4 -4
- package/dist/cli-delegate/cmd_versions.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +4 -4
- 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 +125 -89
- package/dist/install/install.mjs +22 -26
- package/dist/mcp/registry-manifest.json +1 -1
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1556 -613
- package/dist/scripts/_lib/agent_settings.js +39 -25
- package/dist/scripts/_lib/agent_settings.js.map +1 -1
- package/dist/scripts/condense.js +15 -2
- package/dist/scripts/condense.js.map +1 -1
- package/dist/server/routes/wizard.js +23 -3
- package/dist/server/routes/wizard.js.map +1 -1
- package/dist/server/schemas/settings.js +6 -6
- package/dist/server/schemas/settings.js.map +1 -1
- package/docs/contracts/settings-classes.md +14 -9
- package/package.json +1 -1
- package/src/config/agent-settings.template.yml +6 -36
- package/src/config/ci-local-parity.yml +44 -0
- package/src/scripts/_lib/agent_settings.ts +39 -25
- package/src/scripts/_lib/environment_detector.ts +52 -1
- package/src/scripts/ai_council/config.ts +62 -24
- package/src/scripts/before_complete_hook.ts +39 -0
- package/src/scripts/check_bridge_derivation.ts +89 -3
- package/src/scripts/check_ci_local_parity.ts +74 -0
- package/src/scripts/check_references.ts +15 -1
- package/src/scripts/condense.ts +16 -2
- package/src/scripts/conformance_scan.ts +200 -9
- package/src/scripts/council_cli.ts +95 -3
- package/src/scripts/hook_manifest.yaml +12 -6
- package/src/scripts/hooks/block_no_verify.ts +82 -9
- package/src/scripts/hooks/turn_end_gate_hook.ts +138 -105
- package/src/scripts/probe_promissory_closing.ts +180 -0
- package/src/scripts/probe_session_canary.ts +180 -0
- package/src/server/schemas/settings.ts +6 -14
package/dist/hooks/dispatch.js
CHANGED
|
@@ -11852,14 +11852,18 @@ function load_agent_settings(options = {}) {
|
|
|
11852
11852
|
_warn_removed_always_on_keys(merged);
|
|
11853
11853
|
return merged;
|
|
11854
11854
|
}
|
|
11855
|
-
var
|
|
11856
|
-
"subagents.enabled",
|
|
11857
|
-
"subagents.auto",
|
|
11858
|
-
"subagents.host_capabilities",
|
|
11859
|
-
"subagents.budget_routing",
|
|
11860
|
-
"ai_team.enabled"
|
|
11861
|
-
]
|
|
11862
|
-
|
|
11855
|
+
var REMOVED_KEYS = /* @__PURE__ */ new Map([
|
|
11856
|
+
["subagents.enabled", "always-on orchestration"],
|
|
11857
|
+
["subagents.auto", "always-on orchestration"],
|
|
11858
|
+
["subagents.host_capabilities", "always-on orchestration"],
|
|
11859
|
+
["subagents.budget_routing", "always-on orchestration"],
|
|
11860
|
+
["ai_team.enabled", "always-on orchestration"],
|
|
11861
|
+
["hooks.turn_end_gate.enabled", "the turn-end gate is always armed"],
|
|
11862
|
+
["hooks.turn_end_gate.promissory", "the turn-end gate is always armed"],
|
|
11863
|
+
["hooks.turn_end_gate.language", "the turn-end gate is always armed"],
|
|
11864
|
+
["hooks.turn_end_gate.verification", "the turn-end gate is always armed"]
|
|
11865
|
+
]);
|
|
11866
|
+
var _warnedRemovedKeys = /* @__PURE__ */ new Set();
|
|
11863
11867
|
function _readDottedSettingsPath(root, dotted) {
|
|
11864
11868
|
let node = root;
|
|
11865
11869
|
for (const part of dotted.split(".")) {
|
|
@@ -11871,15 +11875,15 @@ function _readDottedSettingsPath(root, dotted) {
|
|
|
11871
11875
|
return node;
|
|
11872
11876
|
}
|
|
11873
11877
|
function _warn_removed_always_on_keys(merged) {
|
|
11874
|
-
for (const key of
|
|
11875
|
-
if (
|
|
11878
|
+
for (const [key, reason] of REMOVED_KEYS) {
|
|
11879
|
+
if (_warnedRemovedKeys.has(key)) {
|
|
11876
11880
|
continue;
|
|
11877
11881
|
}
|
|
11878
11882
|
if (_readDottedSettingsPath(merged, key) === void 0) {
|
|
11879
11883
|
continue;
|
|
11880
11884
|
}
|
|
11881
|
-
|
|
11882
|
-
process.stderr.write(`${key} was removed (
|
|
11885
|
+
_warnedRemovedKeys.add(key);
|
|
11886
|
+
process.stderr.write(`${key} was removed (${reason}); ignored.
|
|
11883
11887
|
`);
|
|
11884
11888
|
}
|
|
11885
11889
|
}
|
|
@@ -14048,7 +14052,6 @@ var _VALID_PROVIDERS = /* @__PURE__ */ new Set([
|
|
|
14048
14052
|
"xai",
|
|
14049
14053
|
"perplexity"
|
|
14050
14054
|
]);
|
|
14051
|
-
var _VALID_MODES = /* @__PURE__ */ new Set(["api", "manual", "cli", "auto"]);
|
|
14052
14055
|
var _RAW_KEY_PREFIXES = [
|
|
14053
14056
|
"sk-",
|
|
14054
14057
|
"sk-ant-",
|
|
@@ -14301,7 +14304,11 @@ function _build_config(raw, source_path) {
|
|
|
14301
14304
|
if (!_isBool(enabled4)) {
|
|
14302
14305
|
throw new CouncilConfigError("`enabled` must be a bool.");
|
|
14303
14306
|
}
|
|
14304
|
-
const
|
|
14307
|
+
const ignored_transport_keys = [];
|
|
14308
|
+
const defaults = _build_defaults(
|
|
14309
|
+
_asDict(_getOr(raw, "defaults", {})),
|
|
14310
|
+
ignored_transport_keys
|
|
14311
|
+
);
|
|
14305
14312
|
const cost_budget = _build_cost_budget(
|
|
14306
14313
|
_asDict(_getOr(raw, "cost_budget", {}))
|
|
14307
14314
|
);
|
|
@@ -14313,7 +14320,12 @@ function _build_config(raw, source_path) {
|
|
|
14313
14320
|
for (const [name, cfg] of Object.entries(members_raw)) {
|
|
14314
14321
|
members.set(
|
|
14315
14322
|
name,
|
|
14316
|
-
_build_member(
|
|
14323
|
+
_build_member(
|
|
14324
|
+
name,
|
|
14325
|
+
_asDict(_orEmpty(cfg)),
|
|
14326
|
+
defaults.mode,
|
|
14327
|
+
ignored_transport_keys
|
|
14328
|
+
)
|
|
14317
14329
|
);
|
|
14318
14330
|
}
|
|
14319
14331
|
const advisors_raw = _getOr(raw, "advisors", {});
|
|
@@ -14412,7 +14424,8 @@ function _build_config(raw, source_path) {
|
|
|
14412
14424
|
routing,
|
|
14413
14425
|
low_impact,
|
|
14414
14426
|
lens_overrides,
|
|
14415
|
-
source_path
|
|
14427
|
+
source_path,
|
|
14428
|
+
ignored_transport_keys
|
|
14416
14429
|
};
|
|
14417
14430
|
}
|
|
14418
14431
|
function _orEmpty(value) {
|
|
@@ -14890,16 +14903,14 @@ function _build_consensus_scoring(d) {
|
|
|
14890
14903
|
};
|
|
14891
14904
|
}
|
|
14892
14905
|
var _VALID_MEMBER_MODES = /* @__PURE__ */ new Set(["cli", "api"]);
|
|
14893
|
-
function _build_defaults(d) {
|
|
14906
|
+
function _build_defaults(d, ignored = []) {
|
|
14894
14907
|
if (!_isDict(d)) {
|
|
14895
14908
|
throw new CouncilConfigError("`defaults` must be a mapping.");
|
|
14896
14909
|
}
|
|
14897
|
-
|
|
14898
|
-
|
|
14899
|
-
throw new CouncilConfigError(
|
|
14900
|
-
`defaults.mode=${_pyRepr3(mode)} not in ${_sortedListRepr(_VALID_MODES)}.`
|
|
14901
|
-
);
|
|
14910
|
+
if (_get(d, "mode", null) !== null) {
|
|
14911
|
+
ignored.push("defaults.mode");
|
|
14902
14912
|
}
|
|
14913
|
+
const mode = "auto";
|
|
14903
14914
|
const member_mode = _get(d, "member_mode", "cli");
|
|
14904
14915
|
if (!(_isStr(member_mode) && _VALID_MEMBER_MODES.has(member_mode))) {
|
|
14905
14916
|
throw new CouncilConfigError(
|
|
@@ -15056,7 +15067,7 @@ function _build_cost_budget(d) {
|
|
|
15056
15067
|
}
|
|
15057
15068
|
return cb;
|
|
15058
15069
|
}
|
|
15059
|
-
function _build_member(name, cfg, default_mode = "auto") {
|
|
15070
|
+
function _build_member(name, cfg, default_mode = "auto", ignored = []) {
|
|
15060
15071
|
if (!_VALID_PROVIDERS.has(name)) {
|
|
15061
15072
|
throw new CouncilConfigError(
|
|
15062
15073
|
`members.${name}: unknown provider; valid: ${_sortedListRepr(_VALID_PROVIDERS)}.`
|
|
@@ -15067,14 +15078,11 @@ function _build_member(name, cfg, default_mode = "auto") {
|
|
|
15067
15078
|
const model = _pyTruthy(modelVal) ? modelVal : "";
|
|
15068
15079
|
const api_key_ref_raw = _get(cfg, "api_key_ref", null);
|
|
15069
15080
|
const api_key_ref = api_key_ref_raw === void 0 ? null : api_key_ref_raw;
|
|
15070
|
-
|
|
15071
|
-
|
|
15072
|
-
if (member_mode !== null && !(_isStr(member_mode) && _VALID_MODES.has(member_mode))) {
|
|
15073
|
-
throw new CouncilConfigError(
|
|
15074
|
-
`members.${name}.mode=${_pyRepr3(member_mode)} not in ${_sortedListRepr(_VALID_MODES)}.`
|
|
15075
|
-
);
|
|
15081
|
+
if (_get(cfg, "mode", null) !== null) {
|
|
15082
|
+
ignored.push(`members.${name}.mode`);
|
|
15076
15083
|
}
|
|
15077
|
-
const
|
|
15084
|
+
const member_mode = null;
|
|
15085
|
+
const effective_mode = default_mode;
|
|
15078
15086
|
const binary_raw = _get(cfg, "binary", null);
|
|
15079
15087
|
const binary = binary_raw === void 0 ? null : binary_raw;
|
|
15080
15088
|
if (binary !== null) {
|
|
@@ -17233,6 +17241,14 @@ function _empty_state2() {
|
|
|
17233
17241
|
verified_this_turn: false,
|
|
17234
17242
|
ci_saw_pending: false,
|
|
17235
17243
|
nonevidence_this_turn: 0,
|
|
17244
|
+
// Round 7 § Phase 1 — the last CI read, SESSION-scoped on purpose. Every
|
|
17245
|
+
// other counter here is turn-scoped because a stale POSITIVE would wrongly
|
|
17246
|
+
// vouch ("I verified"). This one is a NEGATIVE ("CI was not settled"), and a
|
|
17247
|
+
// stale negative only ever refuses more often — so surviving the turn
|
|
17248
|
+
// boundary preserves the freshness invariant rather than bypassing it. It
|
|
17249
|
+
// has to survive it: the measured failure is a completion claim in a LATER
|
|
17250
|
+
// turn than the poll it rests on.
|
|
17251
|
+
ci_last: null,
|
|
17236
17252
|
checked_at: _now2()
|
|
17237
17253
|
};
|
|
17238
17254
|
}
|
|
@@ -17360,6 +17376,7 @@ function _update2(state, event, envelope) {
|
|
|
17360
17376
|
if (session_id && session_id !== state["session_id"]) {
|
|
17361
17377
|
state["session_id"] = session_id;
|
|
17362
17378
|
state["verifications_this_session"] = 0;
|
|
17379
|
+
state["ci_last"] = null;
|
|
17363
17380
|
state = _reset_turn(state, session_id);
|
|
17364
17381
|
}
|
|
17365
17382
|
let payload = envelope["payload"];
|
|
@@ -17385,6 +17402,12 @@ function _update2(state, event, envelope) {
|
|
|
17385
17402
|
} else {
|
|
17386
17403
|
counts = false;
|
|
17387
17404
|
}
|
|
17405
|
+
state["ci_last"] = {
|
|
17406
|
+
at: _now2(),
|
|
17407
|
+
command: cmd.slice(0, 512),
|
|
17408
|
+
pending,
|
|
17409
|
+
settled: pending === 0 && counts === true
|
|
17410
|
+
};
|
|
17388
17411
|
}
|
|
17389
17412
|
state["last_verification"] = {
|
|
17390
17413
|
command: cmd.slice(0, 512),
|
|
@@ -22054,28 +22077,51 @@ function _looks_like_git_invocation(cmd, depth = 0) {
|
|
|
22054
22077
|
}
|
|
22055
22078
|
return false;
|
|
22056
22079
|
}
|
|
22080
|
+
var _SHELL_CONSUMER_RE = /(?:^|[|&;]|\$\(|`)\s*(?:env\s+(?:[A-Za-z_][A-Za-z0-9_]*=\S*\s+)*)?(?:\S*\/)?(?:ba|z|k)?sh\b/;
|
|
22081
|
+
function _heredocs(cmd) {
|
|
22082
|
+
const re = /<<-?\s*(['"]?)([A-Za-z_][A-Za-z0-9_]*)\1([\s\S]*?)^\s*\2\s*$/gm;
|
|
22083
|
+
const docs = [];
|
|
22084
|
+
let stripped = "";
|
|
22085
|
+
let last = 0;
|
|
22086
|
+
let m;
|
|
22087
|
+
while ((m = re.exec(cmd)) !== null) {
|
|
22088
|
+
const lineStart = cmd.lastIndexOf("\n", m.index) + 1;
|
|
22089
|
+
const prefix = cmd.slice(lineStart, m.index);
|
|
22090
|
+
docs.push({ body: m[3] ?? "", shellConsumed: _SHELL_CONSUMER_RE.test(prefix) });
|
|
22091
|
+
stripped += cmd.slice(last, m.index) + "<<HEREDOC";
|
|
22092
|
+
last = m.index + m[0].length;
|
|
22093
|
+
}
|
|
22094
|
+
stripped += cmd.slice(last);
|
|
22095
|
+
return { stripped, docs };
|
|
22096
|
+
}
|
|
22057
22097
|
function _check_command(cmd, depth = 0) {
|
|
22098
|
+
const { stripped, docs } = depth <= 3 ? _heredocs(cmd) : { stripped: cmd, docs: [] };
|
|
22058
22099
|
if (depth <= 3) {
|
|
22059
|
-
const
|
|
22060
|
-
/<<-?\s*(['"]?)([A-Za-z_][A-Za-z0-9_]*)\1[\s\S]*?^\s*\2\s*$/gm,
|
|
22061
|
-
"<<HEREDOC"
|
|
22062
|
-
);
|
|
22063
|
-
for (const payload of substitutionPayloads(withoutHeredocs)) {
|
|
22100
|
+
for (const payload of substitutionPayloads(stripped)) {
|
|
22064
22101
|
const [blocked, reason] = _check_command(payload, depth + 1);
|
|
22065
22102
|
if (blocked) {
|
|
22066
22103
|
return [true, reason];
|
|
22067
22104
|
}
|
|
22068
22105
|
}
|
|
22106
|
+
for (const doc of docs) {
|
|
22107
|
+
if (!doc.shellConsumed) {
|
|
22108
|
+
continue;
|
|
22109
|
+
}
|
|
22110
|
+
const [blocked, reason] = _check_command(doc.body, depth + 1);
|
|
22111
|
+
if (blocked) {
|
|
22112
|
+
return [true, reason];
|
|
22113
|
+
}
|
|
22114
|
+
}
|
|
22069
22115
|
}
|
|
22070
22116
|
let tokens;
|
|
22071
22117
|
try {
|
|
22072
|
-
tokens = shlexSplit(
|
|
22118
|
+
tokens = shlexSplit(stripped);
|
|
22073
22119
|
} catch (e) {
|
|
22074
22120
|
if (e instanceof ShlexError) {
|
|
22075
|
-
if (_looks_like_git_invocation(
|
|
22121
|
+
if (_looks_like_git_invocation(stripped)) {
|
|
22076
22122
|
return [
|
|
22077
22123
|
true,
|
|
22078
|
-
"command parse failed (shlex) on a git-containing command \u2014 fail-closed (git-history-discipline). \
|
|
22124
|
+
"command parse failed (shlex) on a git-containing command \u2014 fail-closed (git-history-discipline). \nThe quoting is unbalanced OUTSIDE any terminated heredoc \u2014 an unterminated heredoc, or an odd quote. \nbash refuses this command too; fix the quoting rather than working around the guard."
|
|
22079
22125
|
];
|
|
22080
22126
|
}
|
|
22081
22127
|
return [false, ""];
|
|
@@ -25654,6 +25700,33 @@ function detectPromissory(reply) {
|
|
|
25654
25700
|
}
|
|
25655
25701
|
return null;
|
|
25656
25702
|
}
|
|
25703
|
+
function readCiSettled(workspaceRoot2) {
|
|
25704
|
+
try {
|
|
25705
|
+
const raw = fs60.readFileSync(path61.join(workspaceRoot2, STATE_FILE3), "utf-8");
|
|
25706
|
+
const decoded = JSON.parse(raw);
|
|
25707
|
+
if (typeof decoded === "object" && decoded !== null && !Array.isArray(decoded)) {
|
|
25708
|
+
const ci = decoded["ci_last"];
|
|
25709
|
+
if (typeof ci === "object" && ci !== null && !Array.isArray(ci)) {
|
|
25710
|
+
return { seen: true, settled: ci["settled"] === true };
|
|
25711
|
+
}
|
|
25712
|
+
}
|
|
25713
|
+
} catch {
|
|
25714
|
+
}
|
|
25715
|
+
return { seen: false, settled: false };
|
|
25716
|
+
}
|
|
25717
|
+
var _COMPLETION_RE = /(^|\n)\s*(?:\*\*)?(?:fertig\b|damit ist alles erledigt|aufgabe erledigt|alles erledigt\b|komplett(?:er)? (?:auftrag|abgearbeitet)|der komplette auftrag ist durch|done[.!]|all done\b|task complete)/i;
|
|
25718
|
+
function detectCompletionClaim(reply, ci) {
|
|
25719
|
+
if (!ci.seen || ci.settled) return null;
|
|
25720
|
+
const prose = visibleProse(reply);
|
|
25721
|
+
const m = _COMPLETION_RE.exec(prose);
|
|
25722
|
+
if (!m) return null;
|
|
25723
|
+
const at = m.index;
|
|
25724
|
+
return {
|
|
25725
|
+
detector: "completion",
|
|
25726
|
+
evidence: prose.slice(at, at + 120).trim(),
|
|
25727
|
+
reason: "a completion claim while the last CI read was not settled \u2014 read the verdict, then claim it (verify-before-complete)"
|
|
25728
|
+
};
|
|
25729
|
+
}
|
|
25657
25730
|
function readLanguagePin(workspaceRoot2) {
|
|
25658
25731
|
try {
|
|
25659
25732
|
const raw = fs60.readFileSync(path61.join(workspaceRoot2, STATE_FILE5), "utf-8");
|
|
@@ -25816,43 +25889,6 @@ function _messageText(content) {
|
|
|
25816
25889
|
}
|
|
25817
25890
|
return parts.length > 0 ? parts.join("\n") : null;
|
|
25818
25891
|
}
|
|
25819
|
-
function _isOn(v) {
|
|
25820
|
-
if (typeof v === "boolean") return v;
|
|
25821
|
-
if (typeof v !== "string") return null;
|
|
25822
|
-
const s = v.trim().toLowerCase();
|
|
25823
|
-
if (s === "true" || s === "yes" || s === "on") return true;
|
|
25824
|
-
if (s === "false" || s === "no" || s === "off") return false;
|
|
25825
|
-
return null;
|
|
25826
|
-
}
|
|
25827
|
-
function readGateSettings(workspaceRoot2) {
|
|
25828
|
-
const off = {
|
|
25829
|
-
enabled: false,
|
|
25830
|
-
promissory: true,
|
|
25831
|
-
language: true,
|
|
25832
|
-
verification: true
|
|
25833
|
-
};
|
|
25834
|
-
let settings;
|
|
25835
|
-
try {
|
|
25836
|
-
settings = load_agent_settings({ cwd: workspaceRoot2 });
|
|
25837
|
-
} catch {
|
|
25838
|
-
return off;
|
|
25839
|
-
}
|
|
25840
|
-
const hooks = settings["hooks"];
|
|
25841
|
-
if (typeof hooks !== "object" || hooks === null || Array.isArray(hooks)) return off;
|
|
25842
|
-
const block = hooks["turn_end_gate"];
|
|
25843
|
-
if (typeof block !== "object" || block === null || Array.isArray(block)) return off;
|
|
25844
|
-
const gate = block;
|
|
25845
|
-
const flag = (name, dflt) => {
|
|
25846
|
-
const v = _isOn(gate[name]);
|
|
25847
|
-
return v === null ? dflt : v;
|
|
25848
|
-
};
|
|
25849
|
-
return {
|
|
25850
|
-
enabled: flag("enabled", false),
|
|
25851
|
-
promissory: flag("promissory", true),
|
|
25852
|
-
language: flag("language", true),
|
|
25853
|
-
verification: flag("verification", true)
|
|
25854
|
-
};
|
|
25855
|
-
}
|
|
25856
25892
|
function str4(v) {
|
|
25857
25893
|
return typeof v === "string" ? v : "";
|
|
25858
25894
|
}
|
|
@@ -25866,8 +25902,6 @@ function main42() {
|
|
|
25866
25902
|
}
|
|
25867
25903
|
if (payload["stop_hook_active"] === true) return EXIT_ALLOW18;
|
|
25868
25904
|
const workspaceRoot2 = str4(envelope["workspace_root"]) || process.cwd();
|
|
25869
|
-
const settings = readGateSettings(workspaceRoot2);
|
|
25870
|
-
if (!settings.enabled) return EXIT_ALLOW18;
|
|
25871
25905
|
const transcriptPath = str4(
|
|
25872
25906
|
payload["transcript_path"] ?? payload["transcriptPath"]
|
|
25873
25907
|
);
|
|
@@ -25880,16 +25914,19 @@ function main42() {
|
|
|
25880
25914
|
);
|
|
25881
25915
|
if (alreadyRefusedTurn(workspaceRoot2, sessionKey, turnOrdinal)) return EXIT_ALLOW18;
|
|
25882
25916
|
const findings = [];
|
|
25883
|
-
|
|
25884
|
-
|
|
25885
|
-
|
|
25886
|
-
|
|
25887
|
-
|
|
25888
|
-
|
|
25889
|
-
|
|
25890
|
-
|
|
25891
|
-
|
|
25892
|
-
|
|
25917
|
+
for (const f of [
|
|
25918
|
+
detectPromissory(lastAssistant),
|
|
25919
|
+
detectLanguage(lastAssistant, readLanguagePin(workspaceRoot2)),
|
|
25920
|
+
detectUnverifiedEdit(toolCalls),
|
|
25921
|
+
// Round 7 § Phase 1 — detector D, in the same unconditional list as the
|
|
25922
|
+
// other three. It shipped with its own settings flag one commit earlier
|
|
25923
|
+
// and that flag is gone: `hooks.turn_end_gate.*` was deleted on
|
|
25924
|
+
// 2026-08-12 because a default-off safety gate is an absent one. Its
|
|
25925
|
+
// gating is where the comment above says gating belongs — inside the
|
|
25926
|
+
// detector: no CI observed, or a settled read, or no completion claim
|
|
25927
|
+
// ⇒ no finding.
|
|
25928
|
+
detectCompletionClaim(lastAssistant, readCiSettled(workspaceRoot2))
|
|
25929
|
+
]) {
|
|
25893
25930
|
if (f) findings.push(f);
|
|
25894
25931
|
}
|
|
25895
25932
|
if (findings.length === 0) return EXIT_ALLOW18;
|
|
@@ -25901,9 +25938,8 @@ function main42() {
|
|
|
25901
25938
|
process.stderr.write(
|
|
25902
25939
|
`turn-end-gate: REFUSED \u2014 this turn is not finished.
|
|
25903
25940
|
${lines.join("\n")}
|
|
25904
|
-
Do the promised work now, correct the language,
|
|
25941
|
+
Do the promised work now, correct the language, run the verification the edit needs, or read the CI verdict before claiming it \u2014 then end the turn.
|
|
25905
25942
|
This turn will not be refused a second time.
|
|
25906
|
-
Disable: hooks.turn_end_gate.enabled: false in .agent-settings.yml
|
|
25907
25943
|
`
|
|
25908
25944
|
);
|
|
25909
25945
|
return EXIT_BLOCK4;
|
package/dist/install/install.mjs
CHANGED
|
@@ -10210,14 +10210,18 @@ function load_agent_settings(options = {}) {
|
|
|
10210
10210
|
_warn_removed_always_on_keys(merged);
|
|
10211
10211
|
return merged;
|
|
10212
10212
|
}
|
|
10213
|
-
var
|
|
10214
|
-
"subagents.enabled",
|
|
10215
|
-
"subagents.auto",
|
|
10216
|
-
"subagents.host_capabilities",
|
|
10217
|
-
"subagents.budget_routing",
|
|
10218
|
-
"ai_team.enabled"
|
|
10219
|
-
]
|
|
10220
|
-
|
|
10213
|
+
var REMOVED_KEYS = /* @__PURE__ */ new Map([
|
|
10214
|
+
["subagents.enabled", "always-on orchestration"],
|
|
10215
|
+
["subagents.auto", "always-on orchestration"],
|
|
10216
|
+
["subagents.host_capabilities", "always-on orchestration"],
|
|
10217
|
+
["subagents.budget_routing", "always-on orchestration"],
|
|
10218
|
+
["ai_team.enabled", "always-on orchestration"],
|
|
10219
|
+
["hooks.turn_end_gate.enabled", "the turn-end gate is always armed"],
|
|
10220
|
+
["hooks.turn_end_gate.promissory", "the turn-end gate is always armed"],
|
|
10221
|
+
["hooks.turn_end_gate.language", "the turn-end gate is always armed"],
|
|
10222
|
+
["hooks.turn_end_gate.verification", "the turn-end gate is always armed"]
|
|
10223
|
+
]);
|
|
10224
|
+
var _warnedRemovedKeys = /* @__PURE__ */ new Set();
|
|
10221
10225
|
function _readDottedSettingsPath(root, dotted) {
|
|
10222
10226
|
let node = root;
|
|
10223
10227
|
for (const part of dotted.split(".")) {
|
|
@@ -10229,15 +10233,15 @@ function _readDottedSettingsPath(root, dotted) {
|
|
|
10229
10233
|
return node;
|
|
10230
10234
|
}
|
|
10231
10235
|
function _warn_removed_always_on_keys(merged) {
|
|
10232
|
-
for (const key of
|
|
10233
|
-
if (
|
|
10236
|
+
for (const [key, reason] of REMOVED_KEYS) {
|
|
10237
|
+
if (_warnedRemovedKeys.has(key)) {
|
|
10234
10238
|
continue;
|
|
10235
10239
|
}
|
|
10236
10240
|
if (_readDottedSettingsPath(merged, key) === void 0) {
|
|
10237
10241
|
continue;
|
|
10238
10242
|
}
|
|
10239
|
-
|
|
10240
|
-
process.stderr.write(`${key} was removed (
|
|
10243
|
+
_warnedRemovedKeys.add(key);
|
|
10244
|
+
process.stderr.write(`${key} was removed (${reason}); ignored.
|
|
10241
10245
|
`);
|
|
10242
10246
|
}
|
|
10243
10247
|
}
|
|
@@ -15075,21 +15079,13 @@ var settingsSchema = external_exports.object({
|
|
|
15075
15079
|
enabled: external_exports.boolean().default(false).describe(
|
|
15076
15080
|
"PreToolUse code-graph nudge (ADR-124 Phase 4). Default off. When on AND a native code-graph cache or a consumer-shipped graph.json/SCIP index is present, warns once per session (never blocks) as the agent is about to Grep/Glob or Read a source file \u2014 query the graph first for who-calls/where-used/impact questions (or rebuild if stale, build if absent). Source G\u2019s strict block-first-read mode is deliberately un-ported."
|
|
15077
15081
|
)
|
|
15078
|
-
}).default({}),
|
|
15079
|
-
turn_end_gate: external_exports.object({
|
|
15080
|
-
enabled: external_exports.boolean().default(false).describe(
|
|
15081
|
-
"Stop-slot turn-end gate (road-to-conformance-round5 Phase 3). Default off, and it is the only concern that can REFUSE a turn-end rather than a tool call \u2014 so it soaks behind an explicit opt-in before it binds. Round 5 measured the reason: both blocking carriers reached zero violations, neither advisory carrier did, and 19 wrong-language replies survived a pin that had fired 26-35 seconds earlier. A refused turn continues in the SAME turn and is never refused twice."
|
|
15082
|
-
),
|
|
15083
|
-
promissory: external_exports.boolean().default(true).describe(
|
|
15084
|
-
"Detector A of the turn-end gate: refuse a turn whose closing paragraph promises work not yet performed while asking the user nothing (20 measured occurrences). No fire rate is quoted here on purpose: the measured rate moved twice while the instrument itself was being corrected, so a number in a shipped description would be a moving target. Re-derive it with `measure-turn-end-gate --store <transcript dir>` against your own corpus before arming the gate. Inert while hooks.turn_end_gate.enabled is false."
|
|
15085
|
-
),
|
|
15086
|
-
language: external_exports.boolean().default(true).describe(
|
|
15087
|
-
"Detector B of the turn-end gate: refuse a turn whose reply is not in the pinned language (19 measured occurrences). Fenced code, inline code, quoted tool output, tables, URLs and paths are stripped first, and the remainder is then scored LEAD-FIRST by the same classifier that sets the pin \u2014 so the verdict usually comes from the reply opening, not from all of it. Sharing that classifier is deliberate: it stops the pin and the detector disagreeing about a turn. Accepted opt-in spellings for the switches below include YAML true/false and yes/no/on/off. Inert while hooks.turn_end_gate.enabled is false."
|
|
15088
|
-
),
|
|
15089
|
-
verification: external_exports.boolean().default(true).describe(
|
|
15090
|
-
"Detector C of the turn-end gate: refuse a turn that changed a file and then ran nothing that could have checked it \u2014 no test, type-check, lint or build call follows the LAST edit. Ordering is the point: a run before the final edit did not exercise it, which is the freshness clause verify-before-complete states. Read off the turn tool calls, not the prose, because a reply cannot say whether an edit was verified. Deliberately narrow on what counts as verification \u2014 a missed detection costs one unguarded turn, a false refusal teaches you to switch the gate off. No fire rate is quoted: none has been measured yet. Inert while hooks.turn_end_gate.enabled is false."
|
|
15091
|
-
)
|
|
15092
15082
|
}).default({})
|
|
15083
|
+
// `turn_end_gate` is deliberately ABSENT. The stop-slot turn-end gate is
|
|
15084
|
+
// always armed (2026-08-12) and has no settings surface: whether it
|
|
15085
|
+
// fires is decided by each detector's own trigger conditions, not by a
|
|
15086
|
+
// flag. A leftover `hooks.turn_end_gate.*` block from an older install
|
|
15087
|
+
// warns once on stderr and is ignored — see REMOVED_KEYS in
|
|
15088
|
+
// `src/scripts/_lib/agent_settings.ts`.
|
|
15093
15089
|
}),
|
|
15094
15090
|
decision_engine: external_exports.object({
|
|
15095
15091
|
surface_traces: external_exports.boolean().default(false).describe(
|
package/dist/mcp/server.json
CHANGED
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
"transport": {
|
|
10
10
|
"type": "stdio"
|
|
11
11
|
},
|
|
12
|
-
"version": "
|
|
12
|
+
"version": "10.0.0"
|
|
13
13
|
}
|
|
14
14
|
],
|
|
15
15
|
"repository": {
|
|
16
16
|
"source": "github",
|
|
17
17
|
"url": "https://github.com/event4u-app/agent-config"
|
|
18
18
|
},
|
|
19
|
-
"version": "
|
|
19
|
+
"version": "10.0.0"
|
|
20
20
|
}
|