@kylecheng3146/agent-ops 0.8.3 → 0.8.5
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/dist/packages/cli/src/hook-process.js +5 -2
- package/dist/packages/cli/src/office-entry.js +38 -7
- package/dist/runtime/src/adapters/codex/config.js +3 -0
- package/dist/runtime/src/install/plan.js +1 -1
- package/dist/runtime/src/task/debug-state.js +3 -4
- package/dist/runtime/src/task/debug.js +3 -3
- package/dist/runtime/src/verify/change-surface.js +4 -1
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@ import { resolveContainedPath } from "../../../runtime/src/fs/paths.js";
|
|
|
9
9
|
import { redactSecrets } from "../../../runtime/src/security/redact.js";
|
|
10
10
|
import { claudeStopRecursionMarker } from "../../../runtime/src/adapters/claude/input.js";
|
|
11
11
|
import { HARNESS_IDS, harnessDescriptor } from "../../../runtime/src/install/harness.js";
|
|
12
|
-
import { officePhaseHint } from "./office-entry.js";
|
|
12
|
+
import { officePhaseHint, officeLoopPhaseHint } from "./office-entry.js";
|
|
13
13
|
import { evaluateWorktreeEnter } from "../../../runtime/src/parallel/enter.js";
|
|
14
14
|
import { evaluateWorktreeWrite, resolveMainRoot } from "../../../runtime/src/parallel/guard.js";
|
|
15
15
|
import { ensureSessionWorktree, recordSessionOrigin } from "../../../runtime/src/parallel/service.js";
|
|
@@ -461,7 +461,7 @@ export async function runHookProcess(argv, io, cliVersion, dependencies = {}) {
|
|
|
461
461
|
await startDebugSession(root, normalized.sessionId, harnessId).catch(() => undefined);
|
|
462
462
|
if (dependencies.office !== undefined && config.features.office?.enabled === true) {
|
|
463
463
|
if (normalized !== null) {
|
|
464
|
-
const phase = officePhaseHint(normalized);
|
|
464
|
+
const phase = officePhaseHint(normalized) ?? (harnessId === "codex" ? officeLoopPhaseHint(parsedInput, root) : undefined);
|
|
465
465
|
void dependencies.office({
|
|
466
466
|
root,
|
|
467
467
|
harness: harnessId,
|
|
@@ -472,6 +472,9 @@ export async function runHookProcess(argv, io, cliVersion, dependencies = {}) {
|
|
|
472
472
|
...(normalized.agentId === undefined ? {} : { agentId: normalized.agentId }),
|
|
473
473
|
...(phase === undefined ? {} : { phase })
|
|
474
474
|
}).catch(() => undefined);
|
|
475
|
+
// Native patch hooks added for Office must not emit unsupported-policy messages.
|
|
476
|
+
if (harnessId === "codex" && normalized.event === "unsupported" && phase === "implementing")
|
|
477
|
+
return 0;
|
|
475
478
|
}
|
|
476
479
|
}
|
|
477
480
|
const gitRunner = dependencies.gitRunner ?? defaultGitRunner(root);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
|
-
import { basename, join } from "node:path";
|
|
2
|
+
import { basename, join, resolve } from "node:path";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { rm } from "node:fs/promises";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
@@ -64,17 +64,46 @@ export function officePhaseHint(event) {
|
|
|
64
64
|
: [];
|
|
65
65
|
let phase;
|
|
66
66
|
for (const command of commands) {
|
|
67
|
-
|
|
67
|
+
let argv = [command.command, ...command.args];
|
|
68
|
+
// Unwrap display hints here; command-policy normalization stays unchanged.
|
|
69
|
+
while (basename(argv[0] ?? "") === "env") {
|
|
70
|
+
argv = argv.slice(1);
|
|
71
|
+
while (argv.length > 0) {
|
|
72
|
+
if (argv[0] === "-u" || argv[0] === "--unset") {
|
|
73
|
+
if (argv[1] === undefined || !/^[A-Za-z_][A-Za-z0-9_]*$/u.test(argv[1]))
|
|
74
|
+
break;
|
|
75
|
+
argv = argv.slice(2);
|
|
76
|
+
}
|
|
77
|
+
else if (/^--unset=[A-Za-z_][A-Za-z0-9_]*$/u.test(argv[0] ?? ""))
|
|
78
|
+
argv = argv.slice(1);
|
|
79
|
+
else
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
if (argv[0] === "--")
|
|
83
|
+
argv = argv.slice(1);
|
|
84
|
+
while (/^[A-Za-z_][A-Za-z0-9_]*=/u.test(argv[0] ?? ""))
|
|
85
|
+
argv = argv.slice(1);
|
|
86
|
+
}
|
|
87
|
+
if (basename(argv[0] ?? "") === "node" || basename(argv[0] ?? "") === "node.exe") {
|
|
88
|
+
const script = argv[1];
|
|
89
|
+
if (script === undefined || ![
|
|
90
|
+
fileURLToPath(new URL("./bin.js", import.meta.url)),
|
|
91
|
+
resolve(event.projectRoot, "dist/packages/cli/src/bin.js")
|
|
92
|
+
].includes(resolve(event.projectRoot, script)))
|
|
93
|
+
continue;
|
|
94
|
+
argv = ["agent-ops", ...argv.slice(2)];
|
|
95
|
+
}
|
|
96
|
+
if (basename(argv[0] ?? "") !== "agent-ops")
|
|
68
97
|
continue;
|
|
69
|
-
const action =
|
|
98
|
+
const action = argv[1];
|
|
70
99
|
// batch and advance report their later steps themselves; this is their first.
|
|
71
100
|
const next = action === "verify" || action === "batch"
|
|
72
101
|
? "verifying"
|
|
73
102
|
: action === "review"
|
|
74
103
|
? "reviewing"
|
|
75
|
-
: action === "worktree" &&
|
|
104
|
+
: action === "worktree" && argv[2] === "finish"
|
|
76
105
|
? "integrating"
|
|
77
|
-
: action === "task" && (
|
|
106
|
+
: action === "task" && (argv[2] === "complete" || argv[2] === "advance")
|
|
78
107
|
? "integrating"
|
|
79
108
|
: action === "task"
|
|
80
109
|
? "planning"
|
|
@@ -93,10 +122,12 @@ export function officeLoopPhaseHint(input, root) {
|
|
|
93
122
|
const tool = input.tool_name;
|
|
94
123
|
if (tool === "Edit" || tool === "Write" || tool === "NotebookEdit" || tool === "FileWrite")
|
|
95
124
|
return "implementing";
|
|
125
|
+
const toolInput = recordValue(input, "tool_input");
|
|
126
|
+
const command = recordValue(toolInput, "command") ?? recordValue(toolInput, "cmd");
|
|
127
|
+
if (tool === "apply_patch")
|
|
128
|
+
return typeof command === "string" && command.length > 0 ? "implementing" : undefined;
|
|
96
129
|
if (tool !== "Bash")
|
|
97
130
|
return undefined;
|
|
98
|
-
const toolInput = recordValue(input, "tool_input");
|
|
99
|
-
const command = recordValue(toolInput, "command");
|
|
100
131
|
return typeof command === "string" ? officePhaseHint(normalizeShellHookEvent(command, root)) : undefined;
|
|
101
132
|
}
|
|
102
133
|
/** Records hook presence and starts Office at the production process boundary. */
|
|
@@ -96,6 +96,9 @@ export function buildCodexHookConfig(capabilities, runtimePath) {
|
|
|
96
96
|
if (capabilities.includes("task-debug")) {
|
|
97
97
|
hooks.Interrupt = [{ hooks: [{ ...commandHook("Interrupt", runtimePath), timeout: 3 }] }];
|
|
98
98
|
}
|
|
99
|
+
if (capabilities.includes("office-presence") && hooks.PreToolUse !== undefined) {
|
|
100
|
+
hooks.PreToolUse = hooks.PreToolUse.map(group => ({ ...group, matcher: "^(Bash|apply_patch)$" }));
|
|
101
|
+
}
|
|
99
102
|
return {
|
|
100
103
|
description: "agent-ops lifecycle hooks.",
|
|
101
104
|
hooks
|
|
@@ -483,7 +483,7 @@ export async function createInstallPlan(options) {
|
|
|
483
483
|
});
|
|
484
484
|
const contribution = {
|
|
485
485
|
artifacts: [
|
|
486
|
-
...(options.scope === "project" ? [{ id: "runtime-ignore", path: ".agent-ops/.gitignore", content: "/tasks/\n/reviews/\n" }] : []),
|
|
486
|
+
...(options.scope === "project" ? [{ id: "runtime-ignore", path: ".agent-ops/.gitignore", content: "/tasks/\n/reviews/\n/state/\n" }] : []),
|
|
487
487
|
...baseContribution.artifacts,
|
|
488
488
|
...loopContribution.artifacts
|
|
489
489
|
],
|
|
@@ -87,9 +87,6 @@ export function debugRemainingMs(state, now = Date.now()) {
|
|
|
87
87
|
export function debugStopCause(state, now = Date.now()) {
|
|
88
88
|
if (state.control?.paused === true)
|
|
89
89
|
return "user-pause";
|
|
90
|
-
const latest = state.receipts.at(-1);
|
|
91
|
-
if (latest?.cause !== null && latest?.cause !== undefined)
|
|
92
|
-
return latest.cause;
|
|
93
90
|
if (debugRemainingMs(state, now) === 0)
|
|
94
91
|
return "budget";
|
|
95
92
|
if (debugRounds(state) >= state.maxRounds)
|
|
@@ -102,7 +99,9 @@ export function debugStopCause(state, now = Date.now()) {
|
|
|
102
99
|
break;
|
|
103
100
|
stagnant++;
|
|
104
101
|
}
|
|
105
|
-
|
|
102
|
+
if (stagnant >= 2)
|
|
103
|
+
return state.strategy === null ? "strategy-required" : "stagnation";
|
|
104
|
+
return state.receipts.at(-1)?.cause ?? null;
|
|
106
105
|
}
|
|
107
106
|
/** Only reviewed reasoning/proof belongs in the digest; operations and clocks do not. */
|
|
108
107
|
export function debugProofDigest(state) {
|
|
@@ -349,9 +349,9 @@ export class DebugService {
|
|
|
349
349
|
fail("TASK_DEBUG_HISTORY_LIMIT", "Execution history is full; retain it and report the blocker.");
|
|
350
350
|
const productDigest = await this.#productDigest(root.debug);
|
|
351
351
|
const previousCandidate = root.debug.receipts.filter(r => r.kind === "verify" && (r.outcome === "PASS" || r.outcome === "FAIL")).at(-1)?.productDigest ?? sha256("[]");
|
|
352
|
-
const latestAttempt = root.debug.receipts.filter(r => r.kind === "verify").at(-1)?.productDigest ?? sha256("[]");
|
|
353
352
|
const cause = debugStopCause(root.debug);
|
|
354
|
-
|
|
353
|
+
// Recovery reruns the original verifier; changed product content still needs fresh proof.
|
|
354
|
+
if (cause !== null && !(["environment", "permission"].includes(cause) && (kind === "investigate" || kind === "verify")) && !(cause === "iterations" && kind === "review" && productDigest === previousCandidate))
|
|
355
355
|
fail("TASK_DEBUG_STOP_REQUIRED", `Runtime stop predicate: ${cause}. Record a checkpoint; resume alone cannot reset it.`);
|
|
356
356
|
const processIdentity = await nativeProcessIdentity(process.pid);
|
|
357
357
|
if (processIdentity === null)
|
|
@@ -579,7 +579,7 @@ export class DebugService {
|
|
|
579
579
|
const exhaustedAtEvent = foregroundActive
|
|
580
580
|
? remainingAtEvent === 0
|
|
581
581
|
: debugRemainingMs(root.debug) === 0 && event.timestamp > lastSealedAt;
|
|
582
|
-
if (!exhaustedAtEvent || otherCause !== null || !/^(?:繼續|继续|continue)[。.!!]?$/iu.test(event.text.trim()))
|
|
582
|
+
if (!exhaustedAtEvent || (otherCause !== null && !["environment", "permission"].includes(otherCause)) || !/^(?:繼續|继续|continue)[。.!!]?$/iu.test(event.text.trim()))
|
|
583
583
|
fail("TASK_DEBUG_GRANT_INVALID", "Only a contemporaneous human continue event extends exhausted time; other blockers remain authoritative.");
|
|
584
584
|
await this.#mutate(root, { ...root.debug, grants: [...root.debug.grants, { eventId: event.id, sessionId, sourceDigest: event.sourceDigest, grantedMs: DEBUG_EXTENSION_MS }], checkpoint: null });
|
|
585
585
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TextDecoder } from "node:util";
|
|
2
2
|
import { AgentOpsError } from "../fs/paths.js";
|
|
3
3
|
const WINDOWS_RESERVED_SEGMENT = /^(?:aux|com[1-9]|con|lpt[1-9]|nul|prn)(?:\..*)?$/i;
|
|
4
|
+
const HARNESS_PROCESS_HEARTBEAT = /^\.(?:claude|codex)\/\.agent-ops-process-[1-9]\d*-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
|
|
4
5
|
function sortedUnique(values) {
|
|
5
6
|
return [...new Set(values)].sort();
|
|
6
7
|
}
|
|
@@ -91,7 +92,9 @@ export async function collectChangeSurface(runner) {
|
|
|
91
92
|
])).filter(
|
|
92
93
|
// Indexed runtime was rejected above; only generated, untracked output is excluded.
|
|
93
94
|
(path) => !path.startsWith(".agent-ops/tasks/") &&
|
|
94
|
-
!path.startsWith(".agent-ops/reviews/")
|
|
95
|
+
!path.startsWith(".agent-ops/reviews/") &&
|
|
96
|
+
// Private marker locks keep these runtime heartbeats until process exit.
|
|
97
|
+
!HARNESS_PROCESS_HEARTBEAT.test(path));
|
|
95
98
|
return {
|
|
96
99
|
staged,
|
|
97
100
|
unstaged,
|