@event4u/agent-config 9.34.0 → 9.35.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 +91 -0
- package/CONTRIBUTING.md +23 -14
- package/dist/agent-src/commands/roadmap/next.md +37 -5
- package/dist/agent-src/scripts/roadmap_gates.ts +119 -4
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +25 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/scripts/memory_lookup.ts +1 -2
- package/dist/agent-src/templates/scripts/memory_signal.ts +2 -4
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_gate.ts +35 -5
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/staged_confirmation.ts +252 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/staged_confirmation_store.ts +307 -0
- package/dist/cli/registry.js +2 -2
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/{chunk-REMEZSVP.js → chunk-2TGMGLBL.js} +2 -2
- package/dist/cli-delegate/{chunk-YPAWXXXR.js → chunk-55RI5UXK.js} +17 -0
- package/dist/cli-delegate/{chunk-MA4GFWDY.js → chunk-H6F2CQFI.js} +1 -1
- package/dist/cli-delegate/{chunk-UVKNIJH2.js → chunk-ZAXH3ZWU.js} +3 -0
- package/dist/cli-delegate/cmd_conformance.js +2 -2
- 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 +1 -1
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- 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 +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 +259 -78
- package/dist/install/install.mjs +3 -0
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +74 -2
- package/dist/server/schemas/settings.js +1 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/docs/contracts/settings-classes.md +3 -2
- package/package.json +1 -1
- package/src/agent-src/scripts/roadmap_gates.ts +119 -4
- package/src/agent-src/templates/scripts/memory_lookup.ts +1 -2
- package/src/agent-src/templates/scripts/memory_signal.ts +2 -4
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/decision_gate.ts +35 -5
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/staged_confirmation.ts +252 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/staged_confirmation_store.ts +307 -0
- package/src/config/agent-settings.template.yml +11 -2
- package/src/config/quorum-attendance-budget.json +10 -2
- package/src/scripts/_dispatch.bash +6 -2
- package/src/scripts/_lib/session_register.ts +67 -1
- package/src/scripts/ai_council/config.ts +60 -0
- package/src/scripts/ai_council/events_log.ts +103 -4
- package/src/scripts/ai_council/quorum.ts +83 -6
- package/src/scripts/council_cli.ts +36 -1
- package/src/scripts/hooks/turn_end_gate_hook.ts +172 -11
- package/src/scripts/lint_framework_leakage.ts +195 -28
- package/src/scripts/lint_framework_leakage_allowlist.json +94 -86
- package/src/scripts/memory_lookup.ts +1 -2
- package/src/scripts/schemas/command.schema.json +4 -0
- package/src/scripts/schemas/skill.schema.json +4 -0
- package/src/scripts/session_register_hook.ts +238 -18
- package/src/scripts/sessions_cli.ts +377 -12
- package/src/server/schemas/settings.ts +3 -0
package/dist/hooks/dispatch.js
CHANGED
|
@@ -3996,10 +3996,10 @@ var require_resolve_block_map = __commonJS({
|
|
|
3996
3996
|
let offset = bm.offset;
|
|
3997
3997
|
let commentEnd = null;
|
|
3998
3998
|
for (const collItem of bm.items) {
|
|
3999
|
-
const { start, key, sep:
|
|
3999
|
+
const { start, key, sep: sep7, value } = collItem;
|
|
4000
4000
|
const keyProps = resolveProps.resolveProps(start, {
|
|
4001
4001
|
indicator: "explicit-key-ind",
|
|
4002
|
-
next: key ??
|
|
4002
|
+
next: key ?? sep7?.[0],
|
|
4003
4003
|
offset,
|
|
4004
4004
|
onError,
|
|
4005
4005
|
parentIndent: bm.indent,
|
|
@@ -4013,7 +4013,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
4013
4013
|
else if ("indent" in key && key.indent !== bm.indent)
|
|
4014
4014
|
onError(offset, "BAD_INDENT", startColMsg);
|
|
4015
4015
|
}
|
|
4016
|
-
if (!keyProps.anchor && !keyProps.tag && !
|
|
4016
|
+
if (!keyProps.anchor && !keyProps.tag && !sep7) {
|
|
4017
4017
|
commentEnd = keyProps.end;
|
|
4018
4018
|
if (keyProps.comment) {
|
|
4019
4019
|
if (map.comment)
|
|
@@ -4037,7 +4037,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
4037
4037
|
ctx.atKey = false;
|
|
4038
4038
|
if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
|
|
4039
4039
|
onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
|
|
4040
|
-
const valueProps = resolveProps.resolveProps(
|
|
4040
|
+
const valueProps = resolveProps.resolveProps(sep7 ?? [], {
|
|
4041
4041
|
indicator: "map-value-ind",
|
|
4042
4042
|
next: value,
|
|
4043
4043
|
offset: keyNode.range[2],
|
|
@@ -4053,7 +4053,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
4053
4053
|
if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
|
|
4054
4054
|
onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
|
|
4055
4055
|
}
|
|
4056
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset,
|
|
4056
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep7, null, valueProps, onError);
|
|
4057
4057
|
if (ctx.schema.compat)
|
|
4058
4058
|
utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
|
|
4059
4059
|
offset = valueNode.range[2];
|
|
@@ -4144,7 +4144,7 @@ var require_resolve_end = __commonJS({
|
|
|
4144
4144
|
let comment = "";
|
|
4145
4145
|
if (end) {
|
|
4146
4146
|
let hasSpace = false;
|
|
4147
|
-
let
|
|
4147
|
+
let sep7 = "";
|
|
4148
4148
|
for (const token of end) {
|
|
4149
4149
|
const { source, type } = token;
|
|
4150
4150
|
switch (type) {
|
|
@@ -4158,13 +4158,13 @@ var require_resolve_end = __commonJS({
|
|
|
4158
4158
|
if (!comment)
|
|
4159
4159
|
comment = cb;
|
|
4160
4160
|
else
|
|
4161
|
-
comment +=
|
|
4162
|
-
|
|
4161
|
+
comment += sep7 + cb;
|
|
4162
|
+
sep7 = "";
|
|
4163
4163
|
break;
|
|
4164
4164
|
}
|
|
4165
4165
|
case "newline":
|
|
4166
4166
|
if (comment)
|
|
4167
|
-
|
|
4167
|
+
sep7 += source;
|
|
4168
4168
|
hasSpace = true;
|
|
4169
4169
|
break;
|
|
4170
4170
|
default:
|
|
@@ -4207,18 +4207,18 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4207
4207
|
let offset = fc.offset + fc.start.source.length;
|
|
4208
4208
|
for (let i = 0; i < fc.items.length; ++i) {
|
|
4209
4209
|
const collItem = fc.items[i];
|
|
4210
|
-
const { start, key, sep:
|
|
4210
|
+
const { start, key, sep: sep7, value } = collItem;
|
|
4211
4211
|
const props = resolveProps.resolveProps(start, {
|
|
4212
4212
|
flow: fcName,
|
|
4213
4213
|
indicator: "explicit-key-ind",
|
|
4214
|
-
next: key ??
|
|
4214
|
+
next: key ?? sep7?.[0],
|
|
4215
4215
|
offset,
|
|
4216
4216
|
onError,
|
|
4217
4217
|
parentIndent: fc.indent,
|
|
4218
4218
|
startOnNewline: false
|
|
4219
4219
|
});
|
|
4220
4220
|
if (!props.found) {
|
|
4221
|
-
if (!props.anchor && !props.tag && !
|
|
4221
|
+
if (!props.anchor && !props.tag && !sep7 && !value) {
|
|
4222
4222
|
if (i === 0 && props.comma)
|
|
4223
4223
|
onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
|
|
4224
4224
|
else if (i < fc.items.length - 1)
|
|
@@ -4272,8 +4272,8 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4272
4272
|
}
|
|
4273
4273
|
}
|
|
4274
4274
|
}
|
|
4275
|
-
if (!isMap && !
|
|
4276
|
-
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end,
|
|
4275
|
+
if (!isMap && !sep7 && !props.found) {
|
|
4276
|
+
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep7, null, props, onError);
|
|
4277
4277
|
coll.items.push(valueNode);
|
|
4278
4278
|
offset = valueNode.range[2];
|
|
4279
4279
|
if (isBlock(value))
|
|
@@ -4285,7 +4285,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4285
4285
|
if (isBlock(key))
|
|
4286
4286
|
onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
4287
4287
|
ctx.atKey = false;
|
|
4288
|
-
const valueProps = resolveProps.resolveProps(
|
|
4288
|
+
const valueProps = resolveProps.resolveProps(sep7 ?? [], {
|
|
4289
4289
|
flow: fcName,
|
|
4290
4290
|
indicator: "map-value-ind",
|
|
4291
4291
|
next: value,
|
|
@@ -4296,8 +4296,8 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4296
4296
|
});
|
|
4297
4297
|
if (valueProps.found) {
|
|
4298
4298
|
if (!isMap && !props.found && ctx.options.strict) {
|
|
4299
|
-
if (
|
|
4300
|
-
for (const st of
|
|
4299
|
+
if (sep7)
|
|
4300
|
+
for (const st of sep7) {
|
|
4301
4301
|
if (st === valueProps.found)
|
|
4302
4302
|
break;
|
|
4303
4303
|
if (st.type === "newline") {
|
|
@@ -4314,7 +4314,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4314
4314
|
else
|
|
4315
4315
|
onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
|
|
4316
4316
|
}
|
|
4317
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end,
|
|
4317
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep7, null, valueProps, onError) : null;
|
|
4318
4318
|
if (valueNode) {
|
|
4319
4319
|
if (isBlock(value))
|
|
4320
4320
|
onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
@@ -4494,7 +4494,7 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
4494
4494
|
chompStart = i + 1;
|
|
4495
4495
|
}
|
|
4496
4496
|
let value = "";
|
|
4497
|
-
let
|
|
4497
|
+
let sep7 = "";
|
|
4498
4498
|
let prevMoreIndented = false;
|
|
4499
4499
|
for (let i = 0; i < contentStart; ++i)
|
|
4500
4500
|
value += lines[i][0].slice(trimIndent) + "\n";
|
|
@@ -4511,24 +4511,24 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
4511
4511
|
indent = "";
|
|
4512
4512
|
}
|
|
4513
4513
|
if (type === Scalar.Scalar.BLOCK_LITERAL) {
|
|
4514
|
-
value +=
|
|
4515
|
-
|
|
4514
|
+
value += sep7 + indent.slice(trimIndent) + content;
|
|
4515
|
+
sep7 = "\n";
|
|
4516
4516
|
} else if (indent.length > trimIndent || content[0] === " ") {
|
|
4517
|
-
if (
|
|
4518
|
-
|
|
4519
|
-
else if (!prevMoreIndented &&
|
|
4520
|
-
|
|
4521
|
-
value +=
|
|
4522
|
-
|
|
4517
|
+
if (sep7 === " ")
|
|
4518
|
+
sep7 = "\n";
|
|
4519
|
+
else if (!prevMoreIndented && sep7 === "\n")
|
|
4520
|
+
sep7 = "\n\n";
|
|
4521
|
+
value += sep7 + indent.slice(trimIndent) + content;
|
|
4522
|
+
sep7 = "\n";
|
|
4523
4523
|
prevMoreIndented = true;
|
|
4524
4524
|
} else if (content === "") {
|
|
4525
|
-
if (
|
|
4525
|
+
if (sep7 === "\n")
|
|
4526
4526
|
value += "\n";
|
|
4527
4527
|
else
|
|
4528
|
-
|
|
4528
|
+
sep7 = "\n";
|
|
4529
4529
|
} else {
|
|
4530
|
-
value +=
|
|
4531
|
-
|
|
4530
|
+
value += sep7 + content;
|
|
4531
|
+
sep7 = " ";
|
|
4532
4532
|
prevMoreIndented = false;
|
|
4533
4533
|
}
|
|
4534
4534
|
}
|
|
@@ -4710,25 +4710,25 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
4710
4710
|
if (!match)
|
|
4711
4711
|
return source;
|
|
4712
4712
|
let res = match[1];
|
|
4713
|
-
let
|
|
4713
|
+
let sep7 = " ";
|
|
4714
4714
|
let pos = first.lastIndex;
|
|
4715
4715
|
line.lastIndex = pos;
|
|
4716
4716
|
while (match = line.exec(source)) {
|
|
4717
4717
|
if (match[1] === "") {
|
|
4718
|
-
if (
|
|
4719
|
-
res +=
|
|
4718
|
+
if (sep7 === "\n")
|
|
4719
|
+
res += sep7;
|
|
4720
4720
|
else
|
|
4721
|
-
|
|
4721
|
+
sep7 = "\n";
|
|
4722
4722
|
} else {
|
|
4723
|
-
res +=
|
|
4724
|
-
|
|
4723
|
+
res += sep7 + match[1];
|
|
4724
|
+
sep7 = " ";
|
|
4725
4725
|
}
|
|
4726
4726
|
pos = line.lastIndex;
|
|
4727
4727
|
}
|
|
4728
4728
|
const last = /[ \t]*(.*)/sy;
|
|
4729
4729
|
last.lastIndex = pos;
|
|
4730
4730
|
match = last.exec(source);
|
|
4731
|
-
return res +
|
|
4731
|
+
return res + sep7 + (match?.[1] ?? "");
|
|
4732
4732
|
}
|
|
4733
4733
|
function doubleQuotedValue(source, onError) {
|
|
4734
4734
|
let res = "";
|
|
@@ -5538,14 +5538,14 @@ var require_cst_stringify = __commonJS({
|
|
|
5538
5538
|
}
|
|
5539
5539
|
}
|
|
5540
5540
|
}
|
|
5541
|
-
function stringifyItem({ start, key, sep:
|
|
5541
|
+
function stringifyItem({ start, key, sep: sep7, value }) {
|
|
5542
5542
|
let res = "";
|
|
5543
5543
|
for (const st of start)
|
|
5544
5544
|
res += st.source;
|
|
5545
5545
|
if (key)
|
|
5546
5546
|
res += stringifyToken(key);
|
|
5547
|
-
if (
|
|
5548
|
-
for (const st of
|
|
5547
|
+
if (sep7)
|
|
5548
|
+
for (const st of sep7)
|
|
5549
5549
|
res += st.source;
|
|
5550
5550
|
if (value)
|
|
5551
5551
|
res += stringifyToken(value);
|
|
@@ -6712,18 +6712,18 @@ var require_parser = __commonJS({
|
|
|
6712
6712
|
if (this.type === "map-value-ind") {
|
|
6713
6713
|
const prev = getPrevProps(this.peek(2));
|
|
6714
6714
|
const start = getFirstKeyStartProps(prev);
|
|
6715
|
-
let
|
|
6715
|
+
let sep7;
|
|
6716
6716
|
if (scalar.end) {
|
|
6717
|
-
|
|
6718
|
-
|
|
6717
|
+
sep7 = scalar.end;
|
|
6718
|
+
sep7.push(this.sourceToken);
|
|
6719
6719
|
delete scalar.end;
|
|
6720
6720
|
} else
|
|
6721
|
-
|
|
6721
|
+
sep7 = [this.sourceToken];
|
|
6722
6722
|
const map = {
|
|
6723
6723
|
type: "block-map",
|
|
6724
6724
|
offset: scalar.offset,
|
|
6725
6725
|
indent: scalar.indent,
|
|
6726
|
-
items: [{ start, key: scalar, sep:
|
|
6726
|
+
items: [{ start, key: scalar, sep: sep7 }]
|
|
6727
6727
|
};
|
|
6728
6728
|
this.onKeyLine = true;
|
|
6729
6729
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -6876,15 +6876,15 @@ var require_parser = __commonJS({
|
|
|
6876
6876
|
} else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
|
|
6877
6877
|
const start2 = getFirstKeyStartProps(it.start);
|
|
6878
6878
|
const key = it.key;
|
|
6879
|
-
const
|
|
6880
|
-
|
|
6879
|
+
const sep7 = it.sep;
|
|
6880
|
+
sep7.push(this.sourceToken);
|
|
6881
6881
|
delete it.key;
|
|
6882
6882
|
delete it.sep;
|
|
6883
6883
|
this.stack.push({
|
|
6884
6884
|
type: "block-map",
|
|
6885
6885
|
offset: this.offset,
|
|
6886
6886
|
indent: this.indent,
|
|
6887
|
-
items: [{ start: start2, key, sep:
|
|
6887
|
+
items: [{ start: start2, key, sep: sep7 }]
|
|
6888
6888
|
});
|
|
6889
6889
|
} else if (start.length > 0) {
|
|
6890
6890
|
it.sep = it.sep.concat(start, this.sourceToken);
|
|
@@ -7078,13 +7078,13 @@ var require_parser = __commonJS({
|
|
|
7078
7078
|
const prev = getPrevProps(parent);
|
|
7079
7079
|
const start = getFirstKeyStartProps(prev);
|
|
7080
7080
|
fixFlowSeqItems(fc);
|
|
7081
|
-
const
|
|
7082
|
-
|
|
7081
|
+
const sep7 = fc.end.splice(1, fc.end.length);
|
|
7082
|
+
sep7.push(this.sourceToken);
|
|
7083
7083
|
const map = {
|
|
7084
7084
|
type: "block-map",
|
|
7085
7085
|
offset: fc.offset,
|
|
7086
7086
|
indent: fc.indent,
|
|
7087
|
-
items: [{ start, key: fc, sep:
|
|
7087
|
+
items: [{ start, key: fc, sep: sep7 }]
|
|
7088
7088
|
};
|
|
7089
7089
|
this.onKeyLine = true;
|
|
7090
7090
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -14036,6 +14036,11 @@ var import_yaml3 = __toESM(require_dist(), 1);
|
|
|
14036
14036
|
import * as fs14 from "node:fs";
|
|
14037
14037
|
import * as os2 from "node:os";
|
|
14038
14038
|
import * as path13 from "node:path";
|
|
14039
|
+
|
|
14040
|
+
// src/scripts/ai_council/quorum.ts
|
|
14041
|
+
var SOLO_FLOOR_MIN_PRESENT = 2;
|
|
14042
|
+
|
|
14043
|
+
// src/scripts/ai_council/config.ts
|
|
14039
14044
|
var _VALID_PROVIDERS = /* @__PURE__ */ new Set([
|
|
14040
14045
|
"anthropic",
|
|
14041
14046
|
"openai",
|
|
@@ -14342,6 +14347,9 @@ function _build_config(raw, source_path) {
|
|
|
14342
14347
|
_asDict(_getOr(raw, "cli_call_budget", {}))
|
|
14343
14348
|
);
|
|
14344
14349
|
const quorum = _build_quorum(_get(raw, "quorum", "majority"));
|
|
14350
|
+
const quorum_min_present = _build_quorum_min_present(
|
|
14351
|
+
_get(raw, "quorum_min_present", SOLO_FLOOR_MIN_PRESENT)
|
|
14352
|
+
);
|
|
14345
14353
|
const necessity_classifier = _build_necessity_classifier(
|
|
14346
14354
|
_asDict(_getOr(raw, "necessity_classifier", {}))
|
|
14347
14355
|
);
|
|
@@ -14390,6 +14398,7 @@ function _build_config(raw, source_path) {
|
|
|
14390
14398
|
consensus_scoring: consensus,
|
|
14391
14399
|
cli_call_budget,
|
|
14392
14400
|
quorum,
|
|
14401
|
+
quorum_min_present,
|
|
14393
14402
|
necessity_classifier,
|
|
14394
14403
|
model_downgrade,
|
|
14395
14404
|
debate,
|
|
@@ -15217,6 +15226,14 @@ function _build_quorum(raw) {
|
|
|
15217
15226
|
`\`quorum\`=${_pyRepr3(raw)} must be 'majority' or an integer >= 1 (got ${_pyTypeName(raw)}).`
|
|
15218
15227
|
);
|
|
15219
15228
|
}
|
|
15229
|
+
function _build_quorum_min_present(raw) {
|
|
15230
|
+
if (_isInt(raw) && raw >= 1) {
|
|
15231
|
+
return raw;
|
|
15232
|
+
}
|
|
15233
|
+
throw new CouncilConfigError(
|
|
15234
|
+
`\`quorum_min_present\`=${_pyRepr3(raw)} must be an integer >= 1 (got ${_pyTypeName(raw)}).`
|
|
15235
|
+
);
|
|
15236
|
+
}
|
|
15220
15237
|
function _build_advisor(name, cfg) {
|
|
15221
15238
|
if (!_isDict(cfg)) {
|
|
15222
15239
|
throw new CouncilConfigError(`advisors.${name}: must be a mapping.`);
|
|
@@ -20494,35 +20511,109 @@ function foreign_live_records(dir, session_id, options = {}) {
|
|
|
20494
20511
|
const own = safe_stem(session_id);
|
|
20495
20512
|
return read_live_records(dir, options).filter((r) => safe_stem(r.session_id) !== own);
|
|
20496
20513
|
}
|
|
20514
|
+
function classify_collisions(others, here) {
|
|
20515
|
+
const out = [];
|
|
20516
|
+
for (const r of others) {
|
|
20517
|
+
if (here.roadmap_slug !== null && r.roadmap_slug !== null && r.roadmap_slug === here.roadmap_slug) {
|
|
20518
|
+
out.push({ kind: "roadmap", record: r });
|
|
20519
|
+
}
|
|
20520
|
+
}
|
|
20521
|
+
for (const r of others) {
|
|
20522
|
+
if (here.branch !== null && r.branch === here.branch) {
|
|
20523
|
+
out.push({ kind: "branch", record: r });
|
|
20524
|
+
}
|
|
20525
|
+
}
|
|
20526
|
+
return out;
|
|
20527
|
+
}
|
|
20497
20528
|
|
|
20498
20529
|
// src/scripts/session_register_hook.ts
|
|
20499
20530
|
var REPLAY_ENV_VAR6 = "AGENT_CONFIG_REPLAY";
|
|
20500
|
-
var
|
|
20531
|
+
var _STATE_REL = path42.join("agents", "runtime", "state");
|
|
20532
|
+
var ROADMAP_CLAIM_REL = path42.join(_STATE_REL, "roadmap-claim.json");
|
|
20533
|
+
function roadmap_claim_rel(session_id) {
|
|
20534
|
+
const id = String(session_id ?? "").trim();
|
|
20535
|
+
if (id === "") {
|
|
20536
|
+
return ROADMAP_CLAIM_REL;
|
|
20537
|
+
}
|
|
20538
|
+
const rel = path42.join(_STATE_REL, `roadmap-claim-${safe_stem(id)}.json`);
|
|
20539
|
+
const norm = path42.normalize(rel);
|
|
20540
|
+
if (!norm.startsWith(`${_STATE_REL}${path42.sep}`) || norm.includes("..")) {
|
|
20541
|
+
return ROADMAP_CLAIM_REL;
|
|
20542
|
+
}
|
|
20543
|
+
return norm;
|
|
20544
|
+
}
|
|
20501
20545
|
function _is_replay_mode4() {
|
|
20502
20546
|
return String(process9.env[REPLAY_ENV_VAR6] ?? "").trim() !== "";
|
|
20503
20547
|
}
|
|
20504
|
-
function
|
|
20548
|
+
function _read_claim_file(file) {
|
|
20505
20549
|
try {
|
|
20506
|
-
const
|
|
20507
|
-
|
|
20508
|
-
|
|
20509
|
-
const slug = parsed["slug"];
|
|
20510
|
-
if (typeof slug === "string" && slug.trim().length > 0) {
|
|
20511
|
-
return slug.trim();
|
|
20512
|
-
}
|
|
20550
|
+
const parsed = JSON.parse(fs41.readFileSync(file, "utf-8"));
|
|
20551
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
20552
|
+
return null;
|
|
20513
20553
|
}
|
|
20514
|
-
|
|
20554
|
+
const rec = parsed;
|
|
20555
|
+
const slug = rec["slug"];
|
|
20556
|
+
if (typeof slug !== "string" || slug.trim().length === 0) {
|
|
20557
|
+
return null;
|
|
20558
|
+
}
|
|
20559
|
+
const sid = rec["session_id"];
|
|
20560
|
+
return {
|
|
20561
|
+
slug: slug.trim(),
|
|
20562
|
+
...typeof sid === "string" && sid.trim() !== "" ? { session_id: sid.trim() } : {}
|
|
20563
|
+
};
|
|
20515
20564
|
} catch {
|
|
20516
20565
|
return null;
|
|
20517
20566
|
}
|
|
20518
20567
|
}
|
|
20568
|
+
function read_claimed_slug(workspace_root, session_id) {
|
|
20569
|
+
const own = roadmap_claim_rel(session_id);
|
|
20570
|
+
if (own !== ROADMAP_CLAIM_REL) {
|
|
20571
|
+
const mine = _read_claim_file(path42.join(workspace_root, own));
|
|
20572
|
+
if (mine !== null) {
|
|
20573
|
+
return mine.slug;
|
|
20574
|
+
}
|
|
20575
|
+
}
|
|
20576
|
+
const legacy = _read_claim_file(path42.join(workspace_root, ROADMAP_CLAIM_REL));
|
|
20577
|
+
if (legacy === null) {
|
|
20578
|
+
return null;
|
|
20579
|
+
}
|
|
20580
|
+
const id = String(session_id ?? "").trim();
|
|
20581
|
+
if (id !== "") {
|
|
20582
|
+
return null;
|
|
20583
|
+
}
|
|
20584
|
+
return legacy.slug;
|
|
20585
|
+
}
|
|
20586
|
+
function claim_is_stale(workspace_root, slug, peer_worktree) {
|
|
20587
|
+
if (slug === null || slug.trim() === "") {
|
|
20588
|
+
return false;
|
|
20589
|
+
}
|
|
20590
|
+
const base = slug.trim().replace(/\.md$/, "");
|
|
20591
|
+
const rel = path42.normalize(path42.join("agents", "roadmaps", `${base}.md`));
|
|
20592
|
+
const inside = rel.startsWith(`agents${path42.sep}roadmaps${path42.sep}`) && !rel.includes("..");
|
|
20593
|
+
if (!inside || path42.isAbsolute(base)) {
|
|
20594
|
+
return true;
|
|
20595
|
+
}
|
|
20596
|
+
const roots = [peer_worktree, workspace_root].filter(
|
|
20597
|
+
(r) => typeof r === "string" && r.trim() !== ""
|
|
20598
|
+
);
|
|
20599
|
+
for (const root of roots) {
|
|
20600
|
+
try {
|
|
20601
|
+
if (fs41.existsSync(path42.join(root, rel))) {
|
|
20602
|
+
return false;
|
|
20603
|
+
}
|
|
20604
|
+
} catch {
|
|
20605
|
+
return false;
|
|
20606
|
+
}
|
|
20607
|
+
}
|
|
20608
|
+
return true;
|
|
20609
|
+
}
|
|
20519
20610
|
function build_record(workspace_root, session_id, platform, started_at, now = /* @__PURE__ */ new Date()) {
|
|
20520
20611
|
return {
|
|
20521
20612
|
session_id,
|
|
20522
20613
|
platform,
|
|
20523
20614
|
worktree: workspace_root,
|
|
20524
20615
|
branch: current_branch(workspace_root),
|
|
20525
|
-
roadmap_slug: read_claimed_slug(workspace_root),
|
|
20616
|
+
roadmap_slug: read_claimed_slug(workspace_root, session_id),
|
|
20526
20617
|
started_at,
|
|
20527
20618
|
last_seen: iso_now(now)
|
|
20528
20619
|
};
|
|
@@ -20540,20 +20631,26 @@ function foreign_sessions_block(workspace_root, session_id, now = /* @__PURE__ *
|
|
|
20540
20631
|
const others = foreign_live_records(dir, session_id, { prune: true, now });
|
|
20541
20632
|
if (others.length === 0) return null;
|
|
20542
20633
|
const here = current_branch(workspace_root);
|
|
20634
|
+
const my_slug = read_claimed_slug(workspace_root, session_id);
|
|
20543
20635
|
const lines = [];
|
|
20544
|
-
|
|
20636
|
+
const live_others = [];
|
|
20545
20637
|
for (const r of others) {
|
|
20546
20638
|
const age_min = Math.max(0, Math.round((now.getTime() - Date.parse(r.last_seen)) / 6e4));
|
|
20547
|
-
const
|
|
20639
|
+
const stale = claim_is_stale(workspace_root, r.roadmap_slug, r.worktree);
|
|
20640
|
+
const slug = r.roadmap_slug === null ? "no roadmap claimed" : stale ? `roadmap: ${r.roadmap_slug} (STALE \u2014 no such open roadmap; treat as no claim)` : `roadmap: ${r.roadmap_slug}`;
|
|
20548
20641
|
const ttl_note = ttl_is_measured(r.platform) ? "" : ` \xB7 unmeasured host, TTL falls back to ${Math.round(ttl_seconds_for(r.platform) / 3600)}h`;
|
|
20549
20642
|
lines.push(
|
|
20550
20643
|
`- ${r.platform} \xB7 branch \`${r.branch ?? "detached"}\` \xB7 ${slug} \xB7 last seen ${age_min} min ago${ttl_note}
|
|
20551
20644
|
worktree: ${r.worktree}`
|
|
20552
20645
|
);
|
|
20553
|
-
|
|
20554
|
-
collision = r;
|
|
20555
|
-
}
|
|
20646
|
+
live_others.push(stale ? { ...r, roadmap_slug: null } : r);
|
|
20556
20647
|
}
|
|
20648
|
+
const collisions = classify_collisions(live_others, {
|
|
20649
|
+
branch: here,
|
|
20650
|
+
roadmap_slug: claim_is_stale(workspace_root, my_slug) ? null : my_slug
|
|
20651
|
+
});
|
|
20652
|
+
const roadmap_hits = collisions.filter((c) => c.kind === "roadmap");
|
|
20653
|
+
const branch_hit = collisions.find((c) => c.kind === "branch") ?? null;
|
|
20557
20654
|
const parts = [
|
|
20558
20655
|
"<session-register>",
|
|
20559
20656
|
"The following OTHER agent sessions are live on this repository right now.",
|
|
@@ -20561,7 +20658,23 @@ function foreign_sessions_block(workspace_root, session_id, now = /* @__PURE__ *
|
|
|
20561
20658
|
"",
|
|
20562
20659
|
...lines
|
|
20563
20660
|
];
|
|
20564
|
-
if (
|
|
20661
|
+
if (roadmap_hits.length > 0) {
|
|
20662
|
+
parts.push(
|
|
20663
|
+
"",
|
|
20664
|
+
`DUPLICATE WORK: ${roadmap_hits.length} other live session(s) claim the SAME roadmap \`${my_slug}\`.`,
|
|
20665
|
+
...roadmap_hits.map(
|
|
20666
|
+
(h) => ` \xB7 branch \`${h.record.branch ?? "detached"}\` in ${h.record.worktree}`
|
|
20667
|
+
),
|
|
20668
|
+
"A different branch name does NOT make this a different task \u2014 measured twice,",
|
|
20669
|
+
"two sessions built one roadmap phase under two branch names and one PR was wasted.",
|
|
20670
|
+
"STOP before writing code. Ask the user ONCE, as numbered options:",
|
|
20671
|
+
" 1. Take a different roadmap (`agent-config sessions:list` shows what is held)",
|
|
20672
|
+
" 2. Split the roadmap explicitly \u2014 name which phases are yours",
|
|
20673
|
+
" 3. Continue anyway, accepting that one of the two results is discarded",
|
|
20674
|
+
"Never decide silently, in either direction."
|
|
20675
|
+
);
|
|
20676
|
+
}
|
|
20677
|
+
if (branch_hit !== null) {
|
|
20565
20678
|
parts.push(
|
|
20566
20679
|
"",
|
|
20567
20680
|
`COLLISION: another live session already holds branch \`${here}\`.`,
|
|
@@ -20602,6 +20715,10 @@ function main25() {
|
|
|
20602
20715
|
if (event === "session_end" || event === "stop" && stop_means_session_end(platform)) {
|
|
20603
20716
|
const dir = register_dir(root);
|
|
20604
20717
|
if (dir !== null) delete_record(dir, session_id);
|
|
20718
|
+
try {
|
|
20719
|
+
fs41.unlinkSync(path42.join(root, roadmap_claim_rel(session_id)));
|
|
20720
|
+
} catch {
|
|
20721
|
+
}
|
|
20605
20722
|
return 0;
|
|
20606
20723
|
}
|
|
20607
20724
|
if (event === "session_start" || event === "user_prompt_submit" || event === "stop") {
|
|
@@ -22528,7 +22645,7 @@ import { fileURLToPath as fileURLToPath36, pathToFileURL as pathToFileURL29 } fr
|
|
|
22528
22645
|
|
|
22529
22646
|
// src/install/rtkDetection.ts
|
|
22530
22647
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
22531
|
-
import { existsSync as
|
|
22648
|
+
import { existsSync as existsSync9, statSync as statSync9, readFileSync as readFileSync19, writeFileSync as writeFileSync8, mkdirSync as mkdirSync9 } from "node:fs";
|
|
22532
22649
|
import { homedir as homedir4 } from "node:os";
|
|
22533
22650
|
import { delimiter, join as join19, dirname as dirname9 } from "node:path";
|
|
22534
22651
|
var TOKEN_KILLER_GAIN_SIGNATURE = /RTK Token Savings/;
|
|
@@ -22541,7 +22658,7 @@ function resolveBinaryOnPath(name, pathEnv) {
|
|
|
22541
22658
|
if (dir.length === 0) continue;
|
|
22542
22659
|
for (const suffix of suffixes) {
|
|
22543
22660
|
const candidate = join19(dir, `${name}${suffix}`);
|
|
22544
|
-
if (
|
|
22661
|
+
if (existsSync9(candidate)) return candidate;
|
|
22545
22662
|
}
|
|
22546
22663
|
}
|
|
22547
22664
|
return null;
|
|
@@ -25355,6 +25472,32 @@ function detectLanguage(reply, pinned) {
|
|
|
25355
25472
|
reason: `the pinned reply language is "${pinned}" but the reply classifies as "${verdict.language}" (${verdict.de_markers} de / ${verdict.en_markers} en markers; code, quotes, tables, URLs and paths excluded, then scored lead-first by the same classifier that sets the pin)`
|
|
25356
25473
|
};
|
|
25357
25474
|
}
|
|
25475
|
+
var _EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
|
|
25476
|
+
var _VERIFY_RE = /\b(test|tests|vitest|jest|pytest|phpunit|pest|tsc|eslint|ruff|mypy|pyright|clippy|typecheck|lint|build|ci|preflight|smoke[-_:a-z]*|check[-_:a-z]*|validate[-_:a-z]*)\b|\b(task|npm|pnpm|yarn|composer|cargo|go|make|php artisan|bun)\s+(run\s+)?\S*(test|check|lint|build|ci|typecheck)/i;
|
|
25477
|
+
function isVerificationCommand(command) {
|
|
25478
|
+
return _VERIFY_RE.test(command);
|
|
25479
|
+
}
|
|
25480
|
+
function detectUnverifiedEdit(toolCalls) {
|
|
25481
|
+
let lastEdit = -1;
|
|
25482
|
+
for (let i = 0; i < toolCalls.length; i += 1) {
|
|
25483
|
+
if (_EDIT_TOOLS.has(toolCalls[i].name)) lastEdit = i;
|
|
25484
|
+
}
|
|
25485
|
+
if (lastEdit === -1) return null;
|
|
25486
|
+
for (let i = lastEdit + 1; i < toolCalls.length; i += 1) {
|
|
25487
|
+
const c = toolCalls[i];
|
|
25488
|
+
if (c.name === "Bash" && c.command !== void 0 && isVerificationCommand(c.command)) {
|
|
25489
|
+
return null;
|
|
25490
|
+
}
|
|
25491
|
+
}
|
|
25492
|
+
const edited = toolCalls[lastEdit].path;
|
|
25493
|
+
return {
|
|
25494
|
+
detector: "verification",
|
|
25495
|
+
// The path, never the diff: the evidence span is quoted into a refusal,
|
|
25496
|
+
// and `ToolCall` is shaped so a file body cannot reach it.
|
|
25497
|
+
evidence: edited ?? toolCalls[lastEdit].name,
|
|
25498
|
+
reason: "this turn changed a file and then ran no verification command \u2014 no test, type-check, lint or build call follows the last edit (verify-before-complete: a claim without a fresh run is unverified)"
|
|
25499
|
+
};
|
|
25500
|
+
}
|
|
25358
25501
|
function stateDir(workspaceRoot2) {
|
|
25359
25502
|
return path59.join(workspaceRoot2, "agents", "runtime", "state", "turn-end-gate");
|
|
25360
25503
|
}
|
|
@@ -25387,7 +25530,7 @@ function markRefusedTurn(workspaceRoot2, sessionKey, turnOrdinal, detector) {
|
|
|
25387
25530
|
}
|
|
25388
25531
|
}
|
|
25389
25532
|
function readTranscriptTail(transcriptPath, opts = {}) {
|
|
25390
|
-
const empty = { lastAssistant: "", turnOrdinal: 0 };
|
|
25533
|
+
const empty = { lastAssistant: "", turnOrdinal: 0, toolCalls: [] };
|
|
25391
25534
|
if (!transcriptPath || !isSafeTranscriptPath(transcriptPath, opts)) return empty;
|
|
25392
25535
|
let lines;
|
|
25393
25536
|
try {
|
|
@@ -25400,6 +25543,7 @@ function readTranscriptTail(transcriptPath, opts = {}) {
|
|
|
25400
25543
|
}
|
|
25401
25544
|
let lastAssistant = "";
|
|
25402
25545
|
let turnOrdinal = 0;
|
|
25546
|
+
let toolCalls = [];
|
|
25403
25547
|
for (const rawLine of lines) {
|
|
25404
25548
|
const line = rawLine.trim();
|
|
25405
25549
|
if (!line) continue;
|
|
@@ -25416,15 +25560,42 @@ function readTranscriptTail(transcriptPath, opts = {}) {
|
|
|
25416
25560
|
if (role !== "assistant" && role !== "user") continue;
|
|
25417
25561
|
const msg = entry["message"];
|
|
25418
25562
|
if (typeof msg !== "object" || msg === null || Array.isArray(msg)) continue;
|
|
25419
|
-
const
|
|
25563
|
+
const content = msg["content"];
|
|
25564
|
+
if (role === "assistant") {
|
|
25565
|
+
toolCalls.push(..._toolCalls(content));
|
|
25566
|
+
}
|
|
25567
|
+
const text = _messageText(content);
|
|
25420
25568
|
if (text === null) continue;
|
|
25421
25569
|
if (role === "assistant") {
|
|
25422
25570
|
lastAssistant = text;
|
|
25423
25571
|
} else if (!isSyntheticPrompt(text)) {
|
|
25424
25572
|
turnOrdinal += 1;
|
|
25573
|
+
toolCalls = [];
|
|
25425
25574
|
}
|
|
25426
25575
|
}
|
|
25427
|
-
return { lastAssistant: lastAssistant.trim(), turnOrdinal };
|
|
25576
|
+
return { lastAssistant: lastAssistant.trim(), turnOrdinal, toolCalls };
|
|
25577
|
+
}
|
|
25578
|
+
function _toolCalls(content) {
|
|
25579
|
+
if (!Array.isArray(content)) return [];
|
|
25580
|
+
const out = [];
|
|
25581
|
+
for (const blk of content) {
|
|
25582
|
+
if (typeof blk !== "object" || blk === null || Array.isArray(blk)) continue;
|
|
25583
|
+
const b = blk;
|
|
25584
|
+
if (b["type"] !== "tool_use") continue;
|
|
25585
|
+
const name = b["name"];
|
|
25586
|
+
if (typeof name !== "string") continue;
|
|
25587
|
+
const input = b["input"];
|
|
25588
|
+
const call = { name };
|
|
25589
|
+
if (typeof input === "object" && input !== null && !Array.isArray(input)) {
|
|
25590
|
+
const inp = input;
|
|
25591
|
+
const cmd = inp["command"];
|
|
25592
|
+
if (typeof cmd === "string") call.command = cmd;
|
|
25593
|
+
const p = inp["file_path"] ?? inp["path"] ?? inp["notebook_path"];
|
|
25594
|
+
if (typeof p === "string") call.path = p;
|
|
25595
|
+
}
|
|
25596
|
+
out.push(call);
|
|
25597
|
+
}
|
|
25598
|
+
return out;
|
|
25428
25599
|
}
|
|
25429
25600
|
function _messageText(content) {
|
|
25430
25601
|
if (typeof content === "string") return content;
|
|
@@ -25448,7 +25619,12 @@ function _isOn(v) {
|
|
|
25448
25619
|
return null;
|
|
25449
25620
|
}
|
|
25450
25621
|
function readGateSettings(workspaceRoot2) {
|
|
25451
|
-
const off = {
|
|
25622
|
+
const off = {
|
|
25623
|
+
enabled: false,
|
|
25624
|
+
promissory: true,
|
|
25625
|
+
language: true,
|
|
25626
|
+
verification: true
|
|
25627
|
+
};
|
|
25452
25628
|
let settings;
|
|
25453
25629
|
try {
|
|
25454
25630
|
settings = load_agent_settings({ cwd: workspaceRoot2 });
|
|
@@ -25467,7 +25643,8 @@ function readGateSettings(workspaceRoot2) {
|
|
|
25467
25643
|
return {
|
|
25468
25644
|
enabled: flag("enabled", false),
|
|
25469
25645
|
promissory: flag("promissory", true),
|
|
25470
|
-
language: flag("language", true)
|
|
25646
|
+
language: flag("language", true),
|
|
25647
|
+
verification: flag("verification", true)
|
|
25471
25648
|
};
|
|
25472
25649
|
}
|
|
25473
25650
|
function str4(v) {
|
|
@@ -25488,7 +25665,7 @@ function main41() {
|
|
|
25488
25665
|
const transcriptPath = str4(
|
|
25489
25666
|
payload["transcript_path"] ?? payload["transcriptPath"]
|
|
25490
25667
|
);
|
|
25491
|
-
const { lastAssistant, turnOrdinal } = readTranscriptTail(transcriptPath, {
|
|
25668
|
+
const { lastAssistant, turnOrdinal, toolCalls } = readTranscriptTail(transcriptPath, {
|
|
25492
25669
|
maxBytes: TRANSCRIPT_READ_MAX_BYTES
|
|
25493
25670
|
});
|
|
25494
25671
|
if (!lastAssistant) return EXIT_ALLOW17;
|
|
@@ -25505,6 +25682,10 @@ function main41() {
|
|
|
25505
25682
|
const f = detectLanguage(lastAssistant, readLanguagePin(workspaceRoot2));
|
|
25506
25683
|
if (f) findings.push(f);
|
|
25507
25684
|
}
|
|
25685
|
+
if (settings.verification) {
|
|
25686
|
+
const f = detectUnverifiedEdit(toolCalls);
|
|
25687
|
+
if (f) findings.push(f);
|
|
25688
|
+
}
|
|
25508
25689
|
if (findings.length === 0) return EXIT_ALLOW17;
|
|
25509
25690
|
markRefusedTurn(workspaceRoot2, sessionKey, turnOrdinal, findings[0].detector);
|
|
25510
25691
|
const lines = findings.map(
|
|
@@ -25514,7 +25695,7 @@ function main41() {
|
|
|
25514
25695
|
process.stderr.write(
|
|
25515
25696
|
`turn-end-gate: REFUSED \u2014 this turn is not finished.
|
|
25516
25697
|
${lines.join("\n")}
|
|
25517
|
-
Do the promised work now,
|
|
25698
|
+
Do the promised work now, correct the language, or run the verification the edit needs, then end the turn.
|
|
25518
25699
|
This turn will not be refused a second time.
|
|
25519
25700
|
Disable: hooks.turn_end_gate.enabled: false in .agent-settings.yml
|
|
25520
25701
|
`
|