@kylecheng3146/agent-ops 0.10.2 → 0.11.1
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/README.md +13 -0
- package/dist/packages/cli/src/args.js +18 -44
- package/dist/packages/cli/src/bin.js +37 -29
- package/dist/packages/cli/src/cli.js +16 -14
- package/dist/packages/cli/src/codex-loop-process.js +0 -6
- package/dist/packages/cli/src/commands/review.js +5 -10
- package/dist/packages/cli/src/commands/run.js +3 -1
- package/dist/packages/cli/src/commands/task.js +27 -54
- package/dist/packages/cli/src/commands/verify.js +7 -3
- package/dist/packages/cli/src/criteria-review.js +63 -0
- package/dist/packages/cli/src/default-base.js +26 -0
- package/dist/packages/cli/src/hook-process.js +19 -83
- package/dist/packages/cli/src/owned-run-step.js +1 -1
- package/dist/packages/cli/src/run-entry.js +203 -115
- package/dist/runtime/src/adapters/agy/config.js +2 -3
- package/dist/runtime/src/adapters/claude/config.js +2 -2
- package/dist/runtime/src/adapters/codex/config.js +2 -5
- package/dist/runtime/src/hooks/completion-gate.js +0 -15
- package/dist/runtime/src/install/harness.js +4 -4
- package/dist/runtime/src/install/plan.js +3 -3
- package/dist/runtime/src/install/profiles.js +0 -2
- package/dist/runtime/src/logging/local-log.js +12 -0
- package/dist/runtime/src/office/snapshot.js +4 -1
- package/dist/runtime/src/parallel/manage.js +0 -3
- package/dist/runtime/src/review/criteria.js +252 -0
- package/dist/runtime/src/review/execute.js +68 -4
- package/dist/runtime/src/review/invocation.js +3 -3
- package/dist/runtime/src/run/verification.js +2 -2
- package/dist/runtime/src/schema/validate.js +10 -5
- package/dist/runtime/src/task/completion.js +4 -42
- package/dist/runtime/src/task/contract.js +11 -0
- package/dist/runtime/src/task/service.js +34 -57
- package/dist/runtime/src/task/store.js +3 -7
- package/dist/runtime/src/verify/acceptance-coverage.js +2 -3
- package/dist/runtime/src/verify/acceptance-protocol.js +4 -3
- package/dist/runtime/src/verify/acceptance-replay.js +4 -7
- package/dist/runtime/src/verify/service.js +6 -23
- package/dist/runtime/src/verify/source-fingerprint.js +17 -4
- package/dist/runtime/src/verify/spawn.js +3 -2
- package/docs/en/guides/task-debug.md +39 -133
- package/docs/en/spec/acceptance-and-evidence.md +31 -23
- package/docs/en/spec/harness-adapters.md +9 -11
- package/docs/en/spec/loop-engineering.md +11 -5
- package/docs/en/spec/review.md +16 -0
- package/docs/zh-TW/guides/task-debug.md +27 -73
- package/docs/zh-TW/spec/acceptance-and-evidence.md +18 -18
- package/docs/zh-TW/spec/harness-adapters.md +7 -8
- package/docs/zh-TW/spec/loop-engineering.md +9 -4
- package/docs/zh-TW/spec/review.md +17 -1
- package/package.json +1 -1
- package/schemas/config.schema.json +1 -21
- package/dist/packages/cli/src/debug-intake.js +0 -245
- package/dist/runtime/src/task/debug-intake.js +0 -15
- package/dist/runtime/src/task/debug-state.js +0 -135
- package/dist/runtime/src/task/debug-user-event.js +0 -160
- package/dist/runtime/src/task/debug.js +0 -632
package/README.md
CHANGED
|
@@ -12,6 +12,19 @@ pre-1.0 interface.
|
|
|
12
12
|
The CLI is published as `@kylecheng3146/agent-ops` as a pre-1.0 interface;
|
|
13
13
|
command behavior may change before 1.0.
|
|
14
14
|
|
|
15
|
+
## Criteria advisory before implementation
|
|
16
|
+
|
|
17
|
+
Every new task, including DEBUG and native run parent/child tasks, waits for one
|
|
18
|
+
independent advisory review of its goal, criteria and assigned surface before
|
|
19
|
+
implementation. Read the suggestions, decide which to adopt, and record reasons
|
|
20
|
+
through `task revise`. Revisions do not automatically re-review.
|
|
21
|
+
|
|
22
|
+
The advisory has no time/token budget; explicit cancellation stops work. Genuine
|
|
23
|
+
execution errors report NOT_RUN and allow work. Detailed reports remain private;
|
|
24
|
+
`task status` shows their snapshot and whether they are historical. Explicitly
|
|
25
|
+
rerun with `agent-ops review --criteria --task <id> --rerun --yes`.
|
|
26
|
+
Final verification, two-round review and finish remain required.
|
|
27
|
+
|
|
15
28
|
## Executable acceptance and native goal runs (0.6.0)
|
|
16
29
|
|
|
17
30
|
Criteria can use `behavioral`, `invariant` or `review-only` acceptance contracts.
|
|
@@ -66,6 +66,10 @@ function parseHookTarget(option, value) {
|
|
|
66
66
|
export function parseArgs(argv) {
|
|
67
67
|
if (argv[0] === "run")
|
|
68
68
|
return parseRunArgs(argv.slice(1));
|
|
69
|
+
if (argv[0] === "task" && argv[1] === "debug")
|
|
70
|
+
throw new CliArgumentError("TASK_DEBUG_REMOVED", "task debug was removed in 0.11.0. A DEBUG task is task create --debug with a behavioral " +
|
|
71
|
+
"criterion whose redCheckIds fail at the baseline and pass on the candidate; then verify, review and finish as usual. " +
|
|
72
|
+
"Declare an escape origin, if known, with task revise --escape-of.");
|
|
69
73
|
let command;
|
|
70
74
|
let action;
|
|
71
75
|
let scope;
|
|
@@ -78,10 +82,6 @@ export function parseArgs(argv) {
|
|
|
78
82
|
let intent;
|
|
79
83
|
let taskGoal;
|
|
80
84
|
let debug = false;
|
|
81
|
-
let debugAction;
|
|
82
|
-
let payloadFile;
|
|
83
|
-
let commandId;
|
|
84
|
-
let eventId;
|
|
85
85
|
let sessionId;
|
|
86
86
|
let base;
|
|
87
87
|
let parentBase;
|
|
@@ -113,6 +113,7 @@ export function parseArgs(argv) {
|
|
|
113
113
|
let json = false;
|
|
114
114
|
let yes = false;
|
|
115
115
|
let rerun = false;
|
|
116
|
+
let criteriaReview = false;
|
|
116
117
|
let tree = false;
|
|
117
118
|
let helpSeen = false;
|
|
118
119
|
let versionSeen = false;
|
|
@@ -233,30 +234,6 @@ export function parseArgs(argv) {
|
|
|
233
234
|
duplicate(token);
|
|
234
235
|
debug = true;
|
|
235
236
|
break;
|
|
236
|
-
case "--debug-action": {
|
|
237
|
-
if (debugAction !== undefined)
|
|
238
|
-
duplicate(token);
|
|
239
|
-
const value = readOptionValue(argv, index++, token);
|
|
240
|
-
if (!["status", "hypothesis", "strategy", "investigate", "checkpoint", "resume", "extend", "origin"].includes(value))
|
|
241
|
-
invalidValue(token, value);
|
|
242
|
-
debugAction = value;
|
|
243
|
-
break;
|
|
244
|
-
}
|
|
245
|
-
case "--payload-file":
|
|
246
|
-
if (payloadFile !== undefined)
|
|
247
|
-
duplicate(token);
|
|
248
|
-
payloadFile = readOptionValue(argv, index++, token);
|
|
249
|
-
break;
|
|
250
|
-
case "--command-id":
|
|
251
|
-
if (commandId !== undefined)
|
|
252
|
-
duplicate(token);
|
|
253
|
-
commandId = readOptionValue(argv, index++, token);
|
|
254
|
-
break;
|
|
255
|
-
case "--event-id":
|
|
256
|
-
if (eventId !== undefined)
|
|
257
|
-
duplicate(token);
|
|
258
|
-
eventId = readOptionValue(argv, index++, token);
|
|
259
|
-
break;
|
|
260
237
|
case "--tree": {
|
|
261
238
|
if (tree)
|
|
262
239
|
duplicate(token);
|
|
@@ -485,6 +462,11 @@ export function parseArgs(argv) {
|
|
|
485
462
|
index += 1;
|
|
486
463
|
break;
|
|
487
464
|
}
|
|
465
|
+
case "--criteria":
|
|
466
|
+
if (criteriaReview)
|
|
467
|
+
throw new CliArgumentError("CLI_DUPLICATE_OPTION", "--criteria may appear only once.");
|
|
468
|
+
criteriaReview = true;
|
|
469
|
+
break;
|
|
488
470
|
case "--rerun":
|
|
489
471
|
if (rerun) {
|
|
490
472
|
duplicate(token);
|
|
@@ -530,7 +512,7 @@ export function parseArgs(argv) {
|
|
|
530
512
|
"complete",
|
|
531
513
|
"create",
|
|
532
514
|
"export",
|
|
533
|
-
"status", "revise", "replan", "pin-finding"
|
|
515
|
+
"status", "revise", "replan", "pin-finding"
|
|
534
516
|
].includes(token)) {
|
|
535
517
|
action = token;
|
|
536
518
|
break;
|
|
@@ -674,8 +656,8 @@ export function parseArgs(argv) {
|
|
|
674
656
|
throw new CliArgumentError("CLI_OPTION_NOT_ALLOWED", "--target-version may be used only with update.");
|
|
675
657
|
}
|
|
676
658
|
if (base !== undefined && command !== "verify" && command !== "review" &&
|
|
677
|
-
command !== "batch" && !(command === "task" &&
|
|
678
|
-
throw new CliArgumentError("CLI_OPTION_NOT_ALLOWED", "--base may be used only with verify, review, batch
|
|
659
|
+
command !== "batch" && !(command === "task" && action === "complete")) {
|
|
660
|
+
throw new CliArgumentError("CLI_OPTION_NOT_ALLOWED", "--base may be used only with verify, review, batch or task complete.");
|
|
679
661
|
}
|
|
680
662
|
if ((parentBase !== undefined || width !== undefined) && command !== "batch") {
|
|
681
663
|
throw new CliArgumentError("CLI_OPTION_NOT_ALLOWED", `${parentBase !== undefined ? "--parent-base" : "--width"} may be used only with batch.`);
|
|
@@ -711,16 +693,11 @@ export function parseArgs(argv) {
|
|
|
711
693
|
if (worktree !== undefined && command !== "init" && command !== "update") {
|
|
712
694
|
throw new CliArgumentError("CLI_OPTION_NOT_ALLOWED", "--worktree may be used only with init or update.");
|
|
713
695
|
}
|
|
696
|
+
if (criteriaReview && (command !== "review" || action !== undefined || tree || base !== undefined || output !== undefined))
|
|
697
|
+
throw new CliArgumentError("CLI_OPTION_NOT_ALLOWED", "--criteria requires review --task <id> --yes; omit show, tree, base and output.");
|
|
714
698
|
const revisionAction = command === "task" && ["revise", "replan", "pin-finding"].includes(action ?? "");
|
|
715
699
|
if ((taskGoal !== undefined || debug) && !(command === "task" && action === "create"))
|
|
716
700
|
throw new CliArgumentError("CLI_OPTION_NOT_ALLOWED", "--goal and --debug apply only to task create.");
|
|
717
|
-
if ((debugAction !== undefined || commandId !== undefined || eventId !== undefined || payloadFile !== undefined) && !(command === "task" && action === "debug"))
|
|
718
|
-
throw new CliArgumentError("CLI_OPTION_NOT_ALLOWED", "Debug operation options apply only to task debug.");
|
|
719
|
-
if (command === "task" && action === "debug" && (taskId === undefined || debugAction === undefined || title !== undefined || intent !== undefined ||
|
|
720
|
-
parentTaskId !== undefined || criteria.length > 0 || evidence.length > 0 ||
|
|
721
|
-
((debugAction === "hypothesis" || debugAction === "strategy" || debugAction === "origin") !== (payloadFile !== undefined)) ||
|
|
722
|
-
((debugAction === "investigate") !== (commandId !== undefined)) || ((debugAction === "extend") !== (eventId !== undefined))))
|
|
723
|
-
throw new CliArgumentError("CLI_MISSING_VALUE", "Task debug requires --task, --debug-action and the operation's exact payload/command/event options.");
|
|
724
701
|
if ((criterionFiles.length > 0 && !(command === "task" && (action === "create" || action === "pin-finding"))) ||
|
|
725
702
|
(criteriaFile !== undefined && !(command === "task" && action === "revise")) ||
|
|
726
703
|
(surfaceFile !== undefined && !(command === "task" && (action === "create" || action === "revise"))) ||
|
|
@@ -739,7 +716,7 @@ export function parseArgs(argv) {
|
|
|
739
716
|
if (command === "escapes" && argv.some(token => token !== "escapes" && token !== "--json"))
|
|
740
717
|
throw new CliArgumentError("CLI_OPTION_NOT_ALLOWED", "escapes accepts only --json; it is a read-only report.");
|
|
741
718
|
if (revisionAction && (taskId === undefined || evidence.length > 0 || title !== undefined || intent !== undefined || parentTaskId !== undefined || sessionId !== undefined || criteria.length > 0 || base !== undefined ||
|
|
742
|
-
(action === "revise" && (expectedContract === undefined || (criteriaFile === undefined && surfaceFile === undefined && escapeOf === undefined) || reason === undefined)) ||
|
|
719
|
+
(action === "revise" && (expectedContract === undefined || (criteriaFile === undefined && surfaceFile === undefined && escapeOf === undefined && baseline === undefined) || reason === undefined)) ||
|
|
743
720
|
(action === "replan" && (expectedTreeContract === undefined || planFile === undefined || reason === undefined)) ||
|
|
744
721
|
(action === "pin-finding" && (expectedContract === undefined || findingReference === undefined || criterionFiles.length !== 1))))
|
|
745
722
|
throw new CliArgumentError("CLI_MISSING_VALUE", "Acceptance revision requires task, expected contract, payload and reason.");
|
|
@@ -817,7 +794,7 @@ export function parseArgs(argv) {
|
|
|
817
794
|
throw new CliArgumentError("CLI_OPTION_NOT_ALLOWED", "--intent may be used only with task create.");
|
|
818
795
|
}
|
|
819
796
|
if (command === "review" && action !== "show" && !yes) {
|
|
820
|
-
throw new CliArgumentError("CLI_OPTION_NOT_ALLOWED", "Review requires --yes to authorize both reviewer sessions.");
|
|
797
|
+
throw new CliArgumentError("CLI_OPTION_NOT_ALLOWED", criteriaReview ? "Criteria advisory requires --yes to authorize one reviewer session." : "Review requires --yes to authorize both reviewer sessions.");
|
|
821
798
|
}
|
|
822
799
|
if (command === "review" && harness !== undefined) {
|
|
823
800
|
throw new CliArgumentError("CLI_OPTION_NOT_ALLOWED", "Complete review uses the configured reviewer pair; omit --harness.");
|
|
@@ -902,11 +879,8 @@ export function parseArgs(argv) {
|
|
|
902
879
|
...(intent === undefined ? {} : { intent }),
|
|
903
880
|
...(taskGoal === undefined ? {} : { goal: taskGoal }),
|
|
904
881
|
...(debug ? { debug: true } : {}),
|
|
905
|
-
...(debugAction === undefined ? {} : { debugAction }),
|
|
906
|
-
...(payloadFile === undefined ? {} : { payloadFile }),
|
|
907
|
-
...(commandId === undefined ? {} : { commandId }),
|
|
908
|
-
...(eventId === undefined ? {} : { eventId }),
|
|
909
882
|
...(reviewTargets.length === 0 ? {} : { reviewTargets }),
|
|
883
|
+
...(criteriaReview ? { criteriaReview: true } : {}),
|
|
910
884
|
...(completionGate === undefined ? {} : { completionGate }),
|
|
911
885
|
...(office === undefined ? {} : { office }),
|
|
912
886
|
...(autoRun === undefined ? {} : { autoRun }),
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { reviewCreatedCriteria, criteriaReviewStatus } from "./criteria-review.js";
|
|
3
|
+
import { renderCriteriaReview } from "../../../runtime/src/review/criteria.js";
|
|
2
4
|
import { advancePhaseObserver, productionRunContext } from "./run-deps.js";
|
|
3
5
|
import { AgentOpsError } from "../../../runtime/src/fs/paths.js";
|
|
4
6
|
import { recordRunVerification } from "../../../runtime/src/run/verification.js";
|
|
@@ -47,9 +49,11 @@ import { memoizePreflight } from "../../../runtime/src/review/batch.js";
|
|
|
47
49
|
import { createReviewExecutor, ReviewInterruptedError } from "../../../runtime/src/review/execute.js";
|
|
48
50
|
import { probeReviewTargetDetailed } from "../../../runtime/src/review/probe.js";
|
|
49
51
|
import { resolveReviewScope } from "../../../runtime/src/review/scope.js";
|
|
52
|
+
import { runSurfaceGate } from "../../../runtime/src/surface/discovery.js";
|
|
50
53
|
import { resolveReviewRole } from "../../../runtime/src/review/roles.js";
|
|
51
54
|
import { runTrustCommand } from "./commands/trust.js";
|
|
52
55
|
import { runVerifyCommand } from "./commands/verify.js";
|
|
56
|
+
import { defaultBase } from "./default-base.js";
|
|
53
57
|
import { runAdvanceCommand } from "./commands/advance.js";
|
|
54
58
|
import { runReviewShowCommand } from "./commands/review-show.js";
|
|
55
59
|
import { runEscapesCommand } from "./commands/escapes.js";
|
|
@@ -255,6 +259,16 @@ else {
|
|
|
255
259
|
const root = args.scope === "user"
|
|
256
260
|
? process.env.AGENT_OPS_HOME ?? homedir()
|
|
257
261
|
: process.cwd();
|
|
262
|
+
// Committed work is judged as a range by default; see defaultBase.
|
|
263
|
+
if ((args.command === "verify" || (args.command === "review" && args.action !== "show" && !args.criteriaReview)) && args.base === undefined && args.scope !== "user") {
|
|
264
|
+
const taskId = args.taskId;
|
|
265
|
+
const base = await defaultBase(root, gitRunner(root), async () => taskId === undefined ? undefined :
|
|
266
|
+
(await new TaskService(new FileTaskStore(join(root, ".agent-ops", "tasks", "state.json"), root)).status({ taskId })).createdSourceCommit)
|
|
267
|
+
// Only a default: any failure leaves the command's own scope handling and errors in charge.
|
|
268
|
+
.catch(() => undefined);
|
|
269
|
+
if (base !== undefined)
|
|
270
|
+
args = { ...args, base };
|
|
271
|
+
}
|
|
258
272
|
const isTTY = !args.json &&
|
|
259
273
|
process.stdin.isTTY === true &&
|
|
260
274
|
process.stdout.isTTY === true;
|
|
@@ -495,7 +509,8 @@ else {
|
|
|
495
509
|
return await runTaskCommand({
|
|
496
510
|
args,
|
|
497
511
|
service: taskService,
|
|
498
|
-
|
|
512
|
+
onTaskCreated: async (record, createdRoot) => await reviewCreatedCriteria(createdRoot ?? root, record.task),
|
|
513
|
+
criteriaStatus: async (record) => await criteriaReviewStatus(root, record.task),
|
|
499
514
|
...(policyConfigHash === undefined ? {} : { policyConfigHash }),
|
|
500
515
|
...(sessionId === undefined ? {} : { sessionId }),
|
|
501
516
|
...(fromMain
|
|
@@ -516,6 +531,11 @@ else {
|
|
|
516
531
|
: {})
|
|
517
532
|
});
|
|
518
533
|
}
|
|
534
|
+
if (args.command === "review" && args.criteriaReview) {
|
|
535
|
+
const record = await taskService.status({ taskId: args.taskId });
|
|
536
|
+
const criteriaReview = await reviewCreatedCriteria(root, record.task, args.rerun);
|
|
537
|
+
return { status: "ok", code: "CRITERIA_REVIEW_ADVISORY", data: { criteriaReview, text: renderCriteriaReview(criteriaReview) }, errors: [] };
|
|
538
|
+
}
|
|
519
539
|
const probeTarget = async (target, budget) => await probeReviewTargetDetailed(target, {
|
|
520
540
|
cwd: root,
|
|
521
541
|
deep: true,
|
|
@@ -553,41 +573,16 @@ else {
|
|
|
553
573
|
reviewActivity = undefined;
|
|
554
574
|
}
|
|
555
575
|
});
|
|
556
|
-
const debugRecord = reviewArgs.taskId === undefined ? undefined : await taskService.status({ taskId: reviewArgs.taskId });
|
|
557
|
-
const inDebug = (executor) => debugRecord?.task.debug === undefined ? executor : async (request) => await taskService.debugService().review(debugRecord.task.id, () => executor(request));
|
|
558
576
|
const inSlot = (executor) => slotDir === undefined
|
|
559
|
-
?
|
|
577
|
+
? executor
|
|
560
578
|
: async (request) => await withReviewSlot({
|
|
561
579
|
dir: slotDir,
|
|
562
580
|
signal,
|
|
563
581
|
holder: { root, ...(reviewArgs.taskId === undefined ? {} : { taskId: reviewArgs.taskId }) },
|
|
564
|
-
...(debugRecord?.task.debug === undefined || reviewSessionId === undefined
|
|
565
|
-
? {}
|
|
566
|
-
: (() => {
|
|
567
|
-
let paused = false;
|
|
568
|
-
const debug = taskService.debugService();
|
|
569
|
-
return {
|
|
570
|
-
onWaitStart: async () => {
|
|
571
|
-
if (paused)
|
|
572
|
-
return;
|
|
573
|
-
const current = await debug.root(debugRecord.task.id).catch(() => null);
|
|
574
|
-
if (current?.debug.foreground?.sessionId !== reviewSessionId)
|
|
575
|
-
return;
|
|
576
|
-
await debug.foregroundStop(debugRecord.task.id, reviewSessionId);
|
|
577
|
-
paused = true;
|
|
578
|
-
},
|
|
579
|
-
onWaitEnd: async () => {
|
|
580
|
-
if (!paused)
|
|
581
|
-
return;
|
|
582
|
-
await debug.foregroundStart(debugRecord.task.id, reviewSessionId);
|
|
583
|
-
paused = false;
|
|
584
|
-
}
|
|
585
|
-
};
|
|
586
|
-
})()),
|
|
587
582
|
onWait: (line) => {
|
|
588
583
|
process.stderr.write(`${progressPrefix}${line}\n`);
|
|
589
584
|
}
|
|
590
|
-
}, async () => await
|
|
585
|
+
}, async () => await executor(request));
|
|
591
586
|
return {
|
|
592
587
|
args: reviewArgs,
|
|
593
588
|
authorized: reviewArgs.yes,
|
|
@@ -607,7 +602,6 @@ else {
|
|
|
607
602
|
config: reviewConfig,
|
|
608
603
|
evidenceStore: new FileEvidenceStore(root, root),
|
|
609
604
|
execute: inActivity(inSlot(createReviewExecutor({
|
|
610
|
-
...(debugRecord?.task.debug === undefined ? {} : { remainingBudgetMs: () => taskService.debugService().remaining(debugRecord.task.id) }),
|
|
611
605
|
runner: registeredRunProofRunner(),
|
|
612
606
|
targets: configuredReviewTargets,
|
|
613
607
|
cwd: root,
|
|
@@ -749,6 +743,20 @@ else {
|
|
|
749
743
|
return await runVerifyCommand({
|
|
750
744
|
args,
|
|
751
745
|
taskService,
|
|
746
|
+
surface: async (taskId) => {
|
|
747
|
+
const record = await taskService.status({ taskId });
|
|
748
|
+
// A subtask's surface is its parent's; review checks the tree, so verify stays quiet here.
|
|
749
|
+
if (record.task.parentTaskId !== undefined)
|
|
750
|
+
return [];
|
|
751
|
+
const scope = await resolveReviewScope({ root, runner: gitRunner(root), ...(args.base === undefined ? {} : { base: args.base }) });
|
|
752
|
+
const gate = await runSurfaceGate({ root, taskId, surface: record.task.surface, required: record.task.surfaceRequired === true,
|
|
753
|
+
...(scope.mode === "base" ? { base: scope.resolvedBase } : {}), changedFiles: scope.changedFiles, configHash: calculateConfigHash(config) });
|
|
754
|
+
if (gate.status === "PASS")
|
|
755
|
+
return ["Surface: PASS"];
|
|
756
|
+
const paths = gate.details.flatMap(d => typeof d === "object" && d !== null && "path" in d ? [String(d.path)] : []);
|
|
757
|
+
return [`Surface (advisory; review would stop with ${gate.code}): ${gate.message}`,
|
|
758
|
+
...paths.slice(0, 20).map(path => ` - ${path}`), ...(paths.length > 20 ? [` ... ${paths.length - 20} more`] : [])];
|
|
759
|
+
},
|
|
752
760
|
activity: { commonDir: await officeCommonDir(root), root, ...(verifySessionId === undefined ? {} : { sessionId: verifySessionId }) },
|
|
753
761
|
service: { verify: async (taskId) => {
|
|
754
762
|
const run = await runPolicyContext(root);
|
|
@@ -25,7 +25,7 @@ Commands:
|
|
|
25
25
|
doctor Diagnose an installation
|
|
26
26
|
update Update managed artifacts
|
|
27
27
|
uninstall Remove managed artifacts
|
|
28
|
-
task <create|status|attach|revise|replan|pin-finding|
|
|
28
|
+
task <create|status|attach|revise|replan|pin-finding|advance|complete|archive|export>
|
|
29
29
|
Manage independent task acceptance state
|
|
30
30
|
verify Run configured verification
|
|
31
31
|
review Run an independent review or show recorded reports
|
|
@@ -59,6 +59,7 @@ Options:
|
|
|
59
59
|
--width <n> Batch only: reviews allowed at once (default 2)
|
|
60
60
|
--target-version <version> Update target version (offline-capable)
|
|
61
61
|
--title <text>
|
|
62
|
+
--criteria Review only: one pre-edit advisory, no time budget
|
|
62
63
|
--criterion <json> Repeatable
|
|
63
64
|
--evidence <criterion-id=reference> Repeatable
|
|
64
65
|
--session <id>
|
|
@@ -105,7 +106,7 @@ The URL carries an access token; do not share it.
|
|
|
105
106
|
agent-ops run respond <run-id> --question-id <id> <--answer <text>|--answer-file <path>> [--json]
|
|
106
107
|
|
|
107
108
|
New runs have no total time cap. Manual resume upgrades legacy finite runs and keeps accounting.
|
|
108
|
-
Command/review timeouts, provider limits
|
|
109
|
+
Command/review timeouts, provider limits and stop controls remain effective.
|
|
109
110
|
Native goal state is observational. Completion requires current verification,
|
|
110
111
|
two fresh reviews, task completion and a target-bound integration receipt.
|
|
111
112
|
--retry-approval explicitly retries an intact Codex approval-review usage-limit
|
|
@@ -188,7 +189,7 @@ Options:
|
|
|
188
189
|
--json
|
|
189
190
|
--yes
|
|
190
191
|
`,
|
|
191
|
-
task: `Usage: agent-ops task <create|status|attach|revise|replan|pin-finding|
|
|
192
|
+
task: `Usage: agent-ops task <create|status|attach|revise|replan|pin-finding|advance|complete|archive|export> [options]
|
|
192
193
|
|
|
193
194
|
Manage independent task acceptance state. Task commands accept none of
|
|
194
195
|
--harness, --profile or --dry-run. Only advance accepts --yes.
|
|
@@ -196,16 +197,12 @@ Manage independent task acceptance state. Task commands accept none of
|
|
|
196
197
|
Options:
|
|
197
198
|
--title <text> create
|
|
198
199
|
--goal <text> create: immutable original issue/goal
|
|
199
|
-
--debug create:
|
|
200
|
-
--debug-action <status|hypothesis|strategy|investigate|checkpoint|resume|extend|origin>
|
|
201
|
-
--payload-file <path> debug hypothesis/strategy/origin: bounded JSON
|
|
202
|
-
--command-id <id> debug investigate: trusted configured command
|
|
203
|
-
--event-id <native-event-id|latest> debug extend: authentic human continue event
|
|
200
|
+
--debug create: bug fix; needs --goal and a behavioral criterion with redCheckIds
|
|
204
201
|
--intent <text> create: intended behavior and constraints before editing
|
|
205
202
|
--criterion <json> create, repeatable, two to five total
|
|
206
203
|
--criterion-file <path> create: one criterion object, repeatable; pin-finding: exactly one
|
|
207
204
|
--criteria-file <path> revise: replacement criterion array
|
|
208
|
-
--surface-file <path> create (required
|
|
205
|
+
--surface-file <path> create (required), revise: EVIDENCE-SURFACE-001
|
|
209
206
|
audit JSON, or {"covers":[surface-id]} under a surfaced parent
|
|
210
207
|
--escape-of <commit> create, revise: the commit this bug escaped from (Escape Loop)
|
|
211
208
|
--escape-label <label> with --escape-of: requirement|surface|scenario|criteria|verify|review|flagged|out-of-model
|
|
@@ -216,12 +213,12 @@ Options:
|
|
|
216
213
|
--expected-contract <hash> revise, pin-finding: current task contract from status
|
|
217
214
|
--expected-tree-contract <hash> replan: current tree contract from status
|
|
218
215
|
--reason <text> required for revise/replan; optional for pin-finding
|
|
219
|
-
--baseline <git-ref> create
|
|
216
|
+
--baseline <git-ref> create: baseline for typed criteria; revise: move every typed baseline
|
|
220
217
|
--parent <task-id> create: record a subtask; status: list subtasks
|
|
221
218
|
--task <id> status, attach, revise, replan, pin-finding, complete, archive, export, advance
|
|
222
|
-
--session <id> create, attach, status, advance
|
|
219
|
+
--session <id> create, attach, status, advance
|
|
223
220
|
--evidence <criterion-id=reference> complete, repeatable
|
|
224
|
-
--base <git-ref> complete
|
|
221
|
+
--base <git-ref> complete: a clean committed range
|
|
225
222
|
--yes advance: authorize the two review sessions
|
|
226
223
|
--json
|
|
227
224
|
|
|
@@ -233,7 +230,8 @@ Each legacy criterion needs at least one verifierIds entry naming a verification
|
|
|
233
230
|
command id configured in .agent-ops/config.json. Typed criteria additionally
|
|
234
231
|
accept an acceptance object: mode (behavioral, invariant or review-only),
|
|
235
232
|
bindings and optional baselineCommit/reviewOnlyReason. Mechanical bindings name
|
|
236
|
-
configured acceptance runner IDs, checkIds
|
|
233
|
+
configured acceptance runner IDs, checkIds (the checks this criterion needs;
|
|
234
|
+
other runner output is ignored), committed materials and optional
|
|
237
235
|
redCheckIds; tasks cannot supply commands. Creation freezes the baseline.
|
|
238
236
|
Review-only example (mandatory repository policy commands still run):
|
|
239
237
|
|
|
@@ -241,7 +239,8 @@ Review-only example (mandatory repository policy commands still run):
|
|
|
241
239
|
|
|
242
240
|
Pin a saved finding with task pin-finding --task <id> <report-digest:index>
|
|
243
241
|
--expected-contract <hash> --criterion-file <path>. Revise/replan preserve the
|
|
244
|
-
original goal and
|
|
242
|
+
original goal and existing baselines unless revise --baseline moves them all.
|
|
243
|
+
A revise that keeps the criteria keeps verification; every revise needs review.
|
|
245
244
|
`,
|
|
246
245
|
verify: `Usage: agent-ops verify [options]
|
|
247
246
|
|
|
@@ -254,11 +253,14 @@ Options:
|
|
|
254
253
|
--json
|
|
255
254
|
`,
|
|
256
255
|
review: `Usage: agent-ops review --task <id> --yes [options]
|
|
256
|
+
agent-ops review --criteria --task <id> --yes [--rerun]
|
|
257
257
|
agent-ops review show --task <id> [--json]
|
|
258
258
|
|
|
259
259
|
Run the complete task-bound review: one necessary reviewer followed by one
|
|
260
260
|
fresh adversarial reviewer from the configured pair. Show reads recorded
|
|
261
261
|
reports without running a reviewer, including from the main checkout.
|
|
262
|
+
Criteria mode runs/reads one pre-edit advisory, with no time/token budget;
|
|
263
|
+
only --rerun starts another attempt. It never replaces final proof.
|
|
262
264
|
|
|
263
265
|
Options:
|
|
264
266
|
--task <id> Required task whose original criteria are reviewed
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { PROJECT_LOOP_EVENTS, resolveProjectLoopRoot, runProjectLoop } from "../../../runtime/src/hooks/codex-loop.js";
|
|
2
2
|
import { officeEnabled, officeLoopPhaseHint } from "./office-entry.js";
|
|
3
|
-
import { routeDebugPrompt } from "./debug-intake.js";
|
|
4
3
|
const MAX_LOOP_INPUT_BYTES = 64 * 1024;
|
|
5
4
|
function isLoopHarness(value) {
|
|
6
5
|
return value === "claude" || value === "codex";
|
|
@@ -74,11 +73,6 @@ export async function runLoopProcess(argv, io, dependencies = {}) {
|
|
|
74
73
|
? {}
|
|
75
74
|
: { telemetryMaxBytes: dependencies.telemetryMaxBytes })
|
|
76
75
|
});
|
|
77
|
-
if (event === "UserPromptSubmit" && managedRoot !== null && result.exitCode === 0 && result.stdout === "" && result.stderr === "") {
|
|
78
|
-
const context = await routeDebugPrompt(managedRoot, input, harness).catch(error => `DEBUG intake unavailable: ${error instanceof Error ? error.message : "preserve current state"}`);
|
|
79
|
-
if (context !== null)
|
|
80
|
-
io.writeStdout(harness === "claude" ? JSON.stringify({ hookSpecificOutput: { hookEventName: "UserPromptSubmit", additionalContext: context } }) : context + "\n");
|
|
81
|
-
}
|
|
82
76
|
if (result.stdout.length > 0) {
|
|
83
77
|
io.writeStdout(result.stdout);
|
|
84
78
|
}
|
|
@@ -4,7 +4,7 @@ import { renderReviewResult } from "../../../../runtime/src/review/render.js";
|
|
|
4
4
|
import { reviewReportResults } from "../../../../runtime/src/review/report.js";
|
|
5
5
|
import { findPriorFailingFindings, findReviewAttestation, invalidateReviewAttestation, readReviewReportArtifact, reviewReportDigest, saveReviewAttestation, saveReviewReportArtifact } from "../../../../runtime/src/review/attestation.js";
|
|
6
6
|
import { detectHostTarget, planReviewTargets, resolveReviewRole } from "../../../../runtime/src/review/roles.js";
|
|
7
|
-
import { taskContractHash, treeContractHash, goalHash } from "../../../../runtime/src/task/contract.js";
|
|
7
|
+
import { evidenceMatchesContract, taskContractHash, treeContractHash, goalHash } from "../../../../runtime/src/task/contract.js";
|
|
8
8
|
import { acceptanceCoverage, coverageDigest } from "../../../../runtime/src/verify/acceptance-coverage.js";
|
|
9
9
|
import { resolveGitCommit } from "../../../../runtime/src/verify/change-surface.js";
|
|
10
10
|
import { sha256 } from "../../../../runtime/src/fs/hash.js";
|
|
@@ -15,7 +15,6 @@ import { assertSafeSupportingPaths, isReviewerPolicyPath, resolveReviewScope, re
|
|
|
15
15
|
import { calculateSourceFingerprint } from "../../../../runtime/src/verify/source-fingerprint.js";
|
|
16
16
|
import { calculateConfigHash, isPassingVerificationEvidence } from "../../../../runtime/src/verify/evidence.js";
|
|
17
17
|
import { validateEvidence } from "../../../../runtime/src/schema/validate.js";
|
|
18
|
-
import { loadDebugProof, debugProofDigest } from "../../../../runtime/src/task/debug-state.js";
|
|
19
18
|
import { logSurfaceResidualRisks, runSurfaceGate } from "../../../../runtime/src/surface/discovery.js";
|
|
20
19
|
import { escapeReviewData } from "../../../../runtime/src/escape/link.js";
|
|
21
20
|
import { logGateCatch } from "../../../../runtime/src/logging/local-log.js";
|
|
@@ -187,7 +186,7 @@ async function currentEvidence(options, context, criterionId, commandId, configH
|
|
|
187
186
|
evidence.sourceFingerprint !== sourceFingerprint ||
|
|
188
187
|
(context.records.some(r => r.task.id === (owner?.taskId ?? context.taskId) &&
|
|
189
188
|
(r.task.goal !== undefined || (r.task.contractRevision ?? 0) > 0 || r.task.criteria.some(c => c.acceptance !== undefined))) &&
|
|
190
|
-
evidence.taskContractHash
|
|
189
|
+
!evidenceMatchesContract(evidence.taskContractHash, context.records.find(r => r.task.id === (owner?.taskId ?? context.taskId)).task))) {
|
|
191
190
|
stale = true;
|
|
192
191
|
continue;
|
|
193
192
|
}
|
|
@@ -343,7 +342,7 @@ async function reviewEscapes(options, context, scope) {
|
|
|
343
342
|
const origin = record.task.origin;
|
|
344
343
|
if (origin === undefined)
|
|
345
344
|
continue;
|
|
346
|
-
const base = record.
|
|
345
|
+
const base = record.createdSourceCommit ?? (scope?.mode === "base" ? scope.resolvedBase : undefined);
|
|
347
346
|
try {
|
|
348
347
|
if (base === undefined)
|
|
349
348
|
throw new Error("no base");
|
|
@@ -770,7 +769,6 @@ export async function runReviewCommand(options) {
|
|
|
770
769
|
}
|
|
771
770
|
context.coverage = rows;
|
|
772
771
|
context.binding = {
|
|
773
|
-
...(context.records.every(r => r.task.debug === undefined) || options.root === undefined ? {} : { debugProofs: Object.fromEntries(await Promise.all(context.records.filter(r => r.task.debug !== undefined).map(async (r) => [r.task.id, debugProofDigest(await loadDebugProof(options.root, r.task.debug.rootTaskId))]))) }),
|
|
774
772
|
taskContractHash: taskContractHash(context.records.find(r => r.task.id === context.taskId).task),
|
|
775
773
|
taskContracts: Object.fromEntries(context.records.map(r => [r.task.id, taskContractHash(r.task)])),
|
|
776
774
|
...(context.treeTasks === undefined ? {} : { treeContractHash: treeContractHash(context.records.map(r => r.task)) }),
|
|
@@ -821,8 +819,7 @@ export async function runReviewCommand(options) {
|
|
|
821
819
|
executionPolicy: options.config,
|
|
822
820
|
...(surfacePacket === undefined ? {} : { surface: surfacePacket }),
|
|
823
821
|
artifacts: contractArtifacts.map(({ path, digest }) => ({ path, digest })),
|
|
824
|
-
tasks:
|
|
825
|
-
...(r.task.debug === undefined ? {} : { debugProof: await loadDebugProof(options.root, r.task.debug.rootTaskId) }) }))),
|
|
822
|
+
tasks: context.records.map(r => ({ task: r.task, revisions: r.revisions ?? [] })),
|
|
826
823
|
superseded: context.historyRecords ?? [] }));
|
|
827
824
|
const digest = sha256(content);
|
|
828
825
|
const path = ".agent-ops/tasks/review-contracts/" + digest + ".json";
|
|
@@ -926,9 +923,7 @@ export async function runReviewCommand(options) {
|
|
|
926
923
|
await options.tasks.treeContract(context.taskId) !== context.binding.treeContractHash) ||
|
|
927
924
|
(context !== undefined && options.tasks !== undefined &&
|
|
928
925
|
(await Promise.all(context.records.map(async (r) => taskContractHash((await options.tasks.status({ taskId: r.task.id })).task))))
|
|
929
|
-
.some((hash, i) => hash !== taskContractHash(context.records[i].task)))
|
|
930
|
-
(context?.binding?.debugProofs !== undefined && options.root !== undefined &&
|
|
931
|
-
(await Promise.all(context.records.filter(r => r.task.debug !== undefined).map(async (r) => debugProofDigest(await loadDebugProof(options.root, r.task.debug.rootTaskId)) !== context.binding.debugProofs[r.task.id]))).some(Boolean))) {
|
|
926
|
+
.some((hash, i) => hash !== taskContractHash(context.records[i].task)))) {
|
|
932
927
|
const changed = await persistReviewEvidence(options, sourceChangedResult(result), sourceFingerprint, context);
|
|
933
928
|
return notRunEnvelope(changed);
|
|
934
929
|
}
|
|
@@ -47,7 +47,9 @@ function data(action, result) {
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
function stateData(action, state, message) {
|
|
50
|
-
|
|
50
|
+
const { events, goal: _goal, ...brief } = state;
|
|
51
|
+
return { action, runId: state.runId, state: { ...brief, eventCount: events.length }, recovery: describeRunRecovery(state), message,
|
|
52
|
+
text: textFor(action, state, message) };
|
|
51
53
|
}
|
|
52
54
|
async function goalFromArgs(args, root) {
|
|
53
55
|
if (args.goal !== undefined && args.goalFile !== undefined)
|