@event4u/agent-config 9.31.0 → 9.32.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 +51 -0
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +32 -1
- package/dist/agent-src/rules/delegation-policy.md +11 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/scripts/work_engine/delivery_state.ts +6 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +332 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/refine.ts +106 -1
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +50 -15
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +49 -15
- package/dist/agent-src/templates/scripts/work_engine/state.ts +74 -0
- package/dist/agent-src/templates/scripts/work_engine/state_io.ts +6 -0
- package/dist/cli-delegate/chunk-2WELHO3P.js +582 -0
- package/dist/cli-delegate/{chunk-2WCLVDWC.js → chunk-542DZR4P.js} +117 -147
- package/dist/cli-delegate/chunk-PZWRPRWP.js +38 -0
- package/dist/cli-delegate/cmd_conformance.js +81 -30
- package/dist/cli-delegate/cmd_doctor.js +2 -1
- package/dist/cli-delegate/cmd_handoff.js +166 -57
- package/dist/cli-delegate/cmd_session_recycle.js +17 -2
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +3 -3
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.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 +1118 -690
- package/dist/mcp/registry-manifest.json +1 -1
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +79 -29
- package/package.json +1 -1
- package/src/agent-src/templates/scripts/work_engine/delivery_state.ts +6 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +332 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/refine.ts +106 -1
- package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +50 -15
- package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +49 -15
- package/src/agent-src/templates/scripts/work_engine/state.ts +74 -0
- package/src/agent-src/templates/scripts/work_engine/state_io.ts +6 -0
- package/src/scripts/_cli/cmd_session_recycle.ts +25 -0
- package/src/scripts/_cli/handoff_generate.ts +36 -1
- package/src/scripts/_cli/handoff_sessions.ts +140 -9
- package/src/scripts/_lib/carrier_divergence.ts +98 -27
- package/src/scripts/_lib/envelope_grounding.ts +206 -0
- package/src/scripts/_lib/host_capability.ts +79 -0
- package/src/scripts/_lib/session_eol.ts +85 -0
- package/src/scripts/_lib/subagent_capsule.ts +256 -1
- package/src/scripts/check_secret_leak.test.ts +43 -0
- package/src/scripts/check_secret_leak.ts +12 -0
- package/src/scripts/handoff_context_hook.ts +66 -18
- package/src/scripts/hooks/session_eol_hook.ts +12 -12
- package/src/scripts/report_carrier_divergence.ts +94 -29
- package/src/scripts/report_conformance_funnel.ts +11 -4
- package/src/scripts/routing_doctor.ts +25 -2
- package/src/scripts/schemas/dod.schema.json +45 -0
- package/dist/cli-delegate/chunk-RVGBH4XK.js +0 -142
|
@@ -17,10 +17,12 @@
|
|
|
17
17
|
* - Must be a dict.
|
|
18
18
|
* - Must carry a `verdict` key — one of `success`, `failed`,
|
|
19
19
|
* or `mixed` (targeted vs full-suite split). Anything else blocks.
|
|
20
|
-
* - `failed` or `mixed` verdicts
|
|
21
|
-
* the
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* - `failed` or `mixed` verdicts enter the bounded self-fix loop
|
|
21
|
+
* (`./_self_fix.ts`): the first attempts are delegated by directive, and an
|
|
22
|
+
* exhausted or non-progressing loop exits `PARTIAL` with the verdict still
|
|
23
|
+
* on the surface so the user decides whether to continue or stop. This
|
|
24
|
+
* follows the `verify-before-complete` rule: a bad test outcome must never
|
|
25
|
+
* be silently skipped, and the loop cannot report one as success.
|
|
24
26
|
* - Optional keys (`targeted`, `full`, `duration_ms`,
|
|
25
27
|
* `followups`) feed the delivery report.
|
|
26
28
|
*/
|
|
@@ -28,6 +30,7 @@
|
|
|
28
30
|
import type { DeliveryState} from '../../delivery_state.js';
|
|
29
31
|
import { type Any, Outcome, StepResult, agent_directive } from '../../delivery_state.js';
|
|
30
32
|
import { resolve_policy } from '../../persona_policy.js';
|
|
33
|
+
import { decide, partial_exit, record_attempt, retry_halt, unmet_dod, verdict_signature } from './_self_fix.js';
|
|
31
34
|
|
|
32
35
|
const _ALLOWED_VERDICTS = ['success', 'failed', 'mixed'] as const;
|
|
33
36
|
|
|
@@ -53,7 +56,18 @@ export const AMBIGUITIES: ReadonlyArray<Record<string, string>> = [
|
|
|
53
56
|
{
|
|
54
57
|
code: 'bad_test_verdict',
|
|
55
58
|
trigger: '`state.tests[\'verdict\']` is `failed` or `mixed`',
|
|
56
|
-
resolution:
|
|
59
|
+
resolution:
|
|
60
|
+
'bounded self-fix loop: agent directive `fix-failing-checks` ' +
|
|
61
|
+
'while attempts remain and the verdict signature keeps changing',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
code: 'self_fix_exhausted',
|
|
65
|
+
trigger:
|
|
66
|
+
'the self-fix budget for `test` is spent, or two consecutive ' +
|
|
67
|
+
'attempts produced an identical verdict signature',
|
|
68
|
+
resolution:
|
|
69
|
+
'PARTIAL honest exit — the red verdict stays on the surface, ' +
|
|
70
|
+
'the user decides whether to take over, override, or abort',
|
|
57
71
|
},
|
|
58
72
|
];
|
|
59
73
|
|
|
@@ -156,18 +170,39 @@ function _blocked_on_shape(state: DeliveryState, issues: string[]): StepResult {
|
|
|
156
170
|
});
|
|
157
171
|
}
|
|
158
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Route a red test verdict through the bounded self-fix loop.
|
|
175
|
+
*
|
|
176
|
+
* Before this loop existed the function below returned a directive-free
|
|
177
|
+
* BLOCKED — a user question block — so every red cost a user round-trip even
|
|
178
|
+
* when the failure was one the agent could fix unaided. Now the first attempts
|
|
179
|
+
* are delegated and only an exhausted or non-progressing loop reaches the
|
|
180
|
+
* user, as a PARTIAL that still carries the red verdict.
|
|
181
|
+
*/
|
|
159
182
|
function _blocked_on_bad_verdict(state: DeliveryState, verdict: Any): StepResult {
|
|
160
183
|
const ticketId = _ticketId(state);
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
184
|
+
const signature = verdict_signature('test', state.tests);
|
|
185
|
+
const decision = decide(state, 'test', signature);
|
|
186
|
+
|
|
187
|
+
if (decision.kind === 'retry') {
|
|
188
|
+
record_attempt(state, 'test', signature);
|
|
189
|
+
return retry_halt({
|
|
190
|
+
lane: 'test',
|
|
191
|
+
ticket_id: ticketId,
|
|
192
|
+
verdict,
|
|
193
|
+
decision,
|
|
194
|
+
fix_hint: 'read the failing assertions, fix the cause, re-run the same filter.',
|
|
195
|
+
rerun_directive: 'run-tests',
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return partial_exit({
|
|
200
|
+
lane: 'test',
|
|
201
|
+
ticket_id: ticketId,
|
|
202
|
+
verdict,
|
|
203
|
+
decision,
|
|
204
|
+
unmet: unmet_dod(state),
|
|
205
|
+
rerun_directive: 'run-tests',
|
|
171
206
|
});
|
|
172
207
|
}
|
|
173
208
|
|
|
@@ -18,9 +18,12 @@
|
|
|
18
18
|
* `partial`. Matches the `Outcome` vocabulary used everywhere
|
|
19
19
|
* else in the flow.
|
|
20
20
|
* - A `success` verdict advances the flow to `report`.
|
|
21
|
-
* - A `blocked` or `partial` verdict
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* - A `blocked` or `partial` verdict enters the bounded self-fix loop
|
|
22
|
+
* (`./_self_fix.ts`): while attempts remain and the verdict signature
|
|
23
|
+
* keeps changing, the fix is delegated by directive. An exhausted or
|
|
24
|
+
* non-progressing loop exits `PARTIAL` with numbered options so the user
|
|
25
|
+
* decides whether to take over, override (rarely appropriate), or abort.
|
|
26
|
+
* The loop never turns a non-success verdict into `SUCCESS`.
|
|
24
27
|
* - Optional keys (`confidence`, `findings`, `followups`) feed
|
|
25
28
|
* the delivery report.
|
|
26
29
|
*/
|
|
@@ -28,6 +31,7 @@
|
|
|
28
31
|
import type { DeliveryState} from '../../delivery_state.js';
|
|
29
32
|
import { type Any, Outcome, StepResult, agent_directive } from '../../delivery_state.js';
|
|
30
33
|
import { resolve_policy } from '../../persona_policy.js';
|
|
34
|
+
import { decide, partial_exit, record_attempt, retry_halt, unmet_dod, verdict_signature } from './_self_fix.js';
|
|
31
35
|
|
|
32
36
|
const _ALLOWED_VERDICTS = ['success', 'blocked', 'partial'] as const;
|
|
33
37
|
|
|
@@ -52,7 +56,17 @@ export const AMBIGUITIES: ReadonlyArray<Record<string, string>> = [
|
|
|
52
56
|
code: 'bad_verify_verdict',
|
|
53
57
|
trigger: '`state.verify[\'verdict\']` is `blocked` or `partial`',
|
|
54
58
|
resolution:
|
|
55
|
-
'
|
|
59
|
+
'bounded self-fix loop: agent directive `fix-failing-checks` ' +
|
|
60
|
+
'while attempts remain — never bypass ' + '(see `verify-before-complete`)',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
code: 'self_fix_exhausted',
|
|
64
|
+
trigger:
|
|
65
|
+
'the self-fix budget for `verify` is spent, or two consecutive ' +
|
|
66
|
+
'attempts produced an identical verdict signature',
|
|
67
|
+
resolution:
|
|
68
|
+
'PARTIAL honest exit — the non-success verdict stays on the ' +
|
|
69
|
+
'surface; the user takes over, overrides, or aborts',
|
|
56
70
|
},
|
|
57
71
|
];
|
|
58
72
|
|
|
@@ -145,19 +159,39 @@ function _blocked_on_shape(state: DeliveryState, issues: string[]): StepResult {
|
|
|
145
159
|
});
|
|
146
160
|
}
|
|
147
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Route a red review verdict through the bounded self-fix loop.
|
|
164
|
+
*
|
|
165
|
+
* Same shape as the `test` lane, separate counter: `autonomous-execution`
|
|
166
|
+
* resets its N=3 budget on a different validation target, and a review finding
|
|
167
|
+
* is not a failing test. `verify-before-complete` is unaffected — the loop
|
|
168
|
+
* cannot report completion, and its exhausted exit is PARTIAL with the
|
|
169
|
+
* non-success verdict still on the surface.
|
|
170
|
+
*/
|
|
148
171
|
function _blocked_on_bad_verdict(state: DeliveryState, verdict: Any): StepResult {
|
|
149
172
|
const ticketId = _ticketId(state);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
173
|
+
const signature = verdict_signature('verify', state.verify);
|
|
174
|
+
const decision = decide(state, 'verify', signature);
|
|
175
|
+
|
|
176
|
+
if (decision.kind === 'retry') {
|
|
177
|
+
record_attempt(state, 'verify', signature);
|
|
178
|
+
return retry_halt({
|
|
179
|
+
lane: 'verify',
|
|
180
|
+
ticket_id: ticketId,
|
|
181
|
+
verdict,
|
|
182
|
+
decision,
|
|
183
|
+
fix_hint: 'address the recorded findings, then re-run the four-judge review.',
|
|
184
|
+
rerun_directive: 'review-changes',
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return partial_exit({
|
|
189
|
+
lane: 'verify',
|
|
190
|
+
ticket_id: ticketId,
|
|
191
|
+
verdict,
|
|
192
|
+
decision,
|
|
193
|
+
unmet: unmet_dod(state),
|
|
194
|
+
rerun_directive: 'review-changes',
|
|
161
195
|
});
|
|
162
196
|
}
|
|
163
197
|
|
|
@@ -220,6 +220,7 @@ export class WorkState {
|
|
|
220
220
|
ui_polish: Dict | null;
|
|
221
221
|
contract: Dict | null;
|
|
222
222
|
stitch: Dict | null;
|
|
223
|
+
self_fix: Dict | null;
|
|
223
224
|
halts: Dict[];
|
|
224
225
|
version: number;
|
|
225
226
|
persona: string;
|
|
@@ -245,6 +246,7 @@ export class WorkState {
|
|
|
245
246
|
ui_polish?: Dict | null;
|
|
246
247
|
contract?: Dict | null;
|
|
247
248
|
stitch?: Dict | null;
|
|
249
|
+
self_fix?: Dict | null;
|
|
248
250
|
halts?: Dict[];
|
|
249
251
|
version?: number;
|
|
250
252
|
persona?: string;
|
|
@@ -269,6 +271,7 @@ export class WorkState {
|
|
|
269
271
|
this.ui_polish = opts.ui_polish ?? null;
|
|
270
272
|
this.contract = opts.contract ?? null;
|
|
271
273
|
this.stitch = opts.stitch ?? null;
|
|
274
|
+
this.self_fix = opts.self_fix ?? null;
|
|
272
275
|
this.halts = opts.halts ?? [];
|
|
273
276
|
this.version = opts.version ?? SCHEMA_VERSION;
|
|
274
277
|
this.persona = opts.persona ?? 'senior-engineer';
|
|
@@ -309,6 +312,7 @@ export function to_dict(state: WorkState): Dict {
|
|
|
309
312
|
_validate_ui_polish(state.ui_polish);
|
|
310
313
|
_validate_contract(state.contract);
|
|
311
314
|
_validate_stitch(state.stitch);
|
|
315
|
+
_validate_self_fix(state.self_fix);
|
|
312
316
|
_validate_halts(state.halts);
|
|
313
317
|
return {
|
|
314
318
|
version: state.version,
|
|
@@ -324,6 +328,7 @@ export function to_dict(state: WorkState): Dict {
|
|
|
324
328
|
ui_polish: state.ui_polish,
|
|
325
329
|
contract: state.contract,
|
|
326
330
|
stitch: state.stitch,
|
|
331
|
+
self_fix: state.self_fix,
|
|
327
332
|
halts: [...state.halts],
|
|
328
333
|
persona: state.persona,
|
|
329
334
|
memory: state.memory,
|
|
@@ -406,6 +411,9 @@ export function from_dict(payload: JsonValue): WorkState {
|
|
|
406
411
|
const stitch = payload['stitch'] ?? null;
|
|
407
412
|
_validate_stitch(stitch);
|
|
408
413
|
|
|
414
|
+
const self_fix = payload['self_fix'] ?? null;
|
|
415
|
+
_validate_self_fix(self_fix);
|
|
416
|
+
|
|
409
417
|
const halts = 'halts' in payload ? payload['halts'] : [];
|
|
410
418
|
_validate_halts(halts);
|
|
411
419
|
|
|
@@ -422,6 +430,7 @@ export function from_dict(payload: JsonValue): WorkState {
|
|
|
422
430
|
ui_polish: _isDict(ui_polish) ? { ...ui_polish } : null,
|
|
423
431
|
contract: _isDict(contract) ? { ...contract } : null,
|
|
424
432
|
stitch: _isDict(stitch) ? { ...stitch } : null,
|
|
433
|
+
self_fix: _isDict(self_fix) ? { ...self_fix } : null,
|
|
425
434
|
halts: Array.isArray(halts) ? ([...halts] as Dict[]) : [],
|
|
426
435
|
version: version as number,
|
|
427
436
|
persona: _get(payload, 'persona', 'senior-engineer') as string,
|
|
@@ -866,6 +875,71 @@ function _validate_ui_review(ui_review: JsonValue): void {
|
|
|
866
875
|
}
|
|
867
876
|
}
|
|
868
877
|
|
|
878
|
+
/**
|
|
879
|
+
* Reject malformed `self_fix` envelopes; tolerate `null` and `{}`.
|
|
880
|
+
*
|
|
881
|
+
* `null` means no red check has been seen. Once populated, each key is a lane
|
|
882
|
+
* name (`test`, `verify`) whose record may carry `attempts` (a non-negative
|
|
883
|
+
* int) and `signatures` (a list of strings). As with `ui_polish`, the schema
|
|
884
|
+
* enforces only shape: the ceiling and the no-progress floor are the handler's
|
|
885
|
+
* business (`directives/backend/_self_fix.ts`), so a state file cannot decide
|
|
886
|
+
* how many attempts are allowed.
|
|
887
|
+
*
|
|
888
|
+
* `attempts` is validated but NOT bounded here on purpose. A file carrying
|
|
889
|
+
* `attempts: 99` is well-formed and simply exhausted — rejecting it would turn
|
|
890
|
+
* a spent budget into a crash, and the loop's whole point is a clean exit.
|
|
891
|
+
*/
|
|
892
|
+
function _validate_self_fix(self_fix: JsonValue): void {
|
|
893
|
+
if (self_fix === null) {
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
if (!_isDict(self_fix)) {
|
|
897
|
+
throw new SchemaError(
|
|
898
|
+
`state.self_fix must be a JSON object or null; ` +
|
|
899
|
+
`got ${pyTypeName(self_fix)}`,
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
for (const [lane, record] of Object.entries(self_fix)) {
|
|
903
|
+
if (!_isDict(record)) {
|
|
904
|
+
throw new SchemaError(
|
|
905
|
+
`state.self_fix.${lane} must be a JSON object; ` +
|
|
906
|
+
`got ${pyTypeName(record)}`,
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
if ('attempts' in record) {
|
|
910
|
+
const attempts = record['attempts'];
|
|
911
|
+
if (
|
|
912
|
+
typeof attempts !== 'number' ||
|
|
913
|
+
typeof attempts === 'boolean' ||
|
|
914
|
+
!Number.isInteger(attempts) ||
|
|
915
|
+
attempts < 0
|
|
916
|
+
) {
|
|
917
|
+
throw new SchemaError(
|
|
918
|
+
`state.self_fix.${lane}.attempts must be a non-negative ` +
|
|
919
|
+
`integer when present; got ${pyRepr(attempts)}`,
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
if ('signatures' in record) {
|
|
924
|
+
const signatures = record['signatures'];
|
|
925
|
+
if (!Array.isArray(signatures)) {
|
|
926
|
+
throw new SchemaError(
|
|
927
|
+
`state.self_fix.${lane}.signatures must be a list when ` +
|
|
928
|
+
`present; got ${pyTypeName(signatures)}`,
|
|
929
|
+
);
|
|
930
|
+
}
|
|
931
|
+
for (const entry of signatures) {
|
|
932
|
+
if (typeof entry !== 'string') {
|
|
933
|
+
throw new SchemaError(
|
|
934
|
+
`state.self_fix.${lane}.signatures entries must be ` +
|
|
935
|
+
`strings; got ${pyTypeName(entry)}`,
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
869
943
|
/**
|
|
870
944
|
* Reject malformed `ui_polish` envelopes; tolerate `null` and `{}`.
|
|
871
945
|
*
|
|
@@ -150,6 +150,7 @@ export function _to_delivery(work: WorkState): DeliveryState {
|
|
|
150
150
|
contract: work.contract as Record<string, DeliveryAny> | null,
|
|
151
151
|
stitch: work.stitch as Record<string, DeliveryAny> | null,
|
|
152
152
|
stack: work.stack as Record<string, DeliveryAny> | null,
|
|
153
|
+
self_fix: work.self_fix as Record<string, DeliveryAny> | null,
|
|
153
154
|
});
|
|
154
155
|
}
|
|
155
156
|
|
|
@@ -182,6 +183,11 @@ export function _sync_back(work: WorkState, delivery: DeliveryState): void {
|
|
|
182
183
|
work.contract = delivery.contract as Dict | null;
|
|
183
184
|
work.stitch = delivery.stitch as Dict | null;
|
|
184
185
|
work.stack = delivery.stack as Dict | null;
|
|
186
|
+
// Reassigned (not mutated) by `record_attempt`, so the defensive mirror
|
|
187
|
+
// above is load-bearing here rather than a no-op: without this line the
|
|
188
|
+
// attempt counter would reset on every dispatch and the loop would never
|
|
189
|
+
// reach either floor.
|
|
190
|
+
work.self_fix = delivery.self_fix as Dict | null;
|
|
185
191
|
}
|
|
186
192
|
|
|
187
193
|
/**
|