@kylecheng3146/agent-ops 0.7.3 → 0.8.3
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 +2 -0
- package/dist/packages/cli/src/args.js +72 -1
- package/dist/packages/cli/src/bin.js +69 -10
- package/dist/packages/cli/src/cli.js +11 -3
- package/dist/packages/cli/src/codex-loop-process.js +6 -0
- package/dist/packages/cli/src/commands/batch.js +3 -1
- package/dist/packages/cli/src/commands/hook.js +4 -1
- package/dist/packages/cli/src/commands/review.js +9 -3
- package/dist/packages/cli/src/commands/task.js +44 -2
- package/dist/packages/cli/src/commands/update.js +18 -0
- package/dist/packages/cli/src/commands/verify.js +3 -1
- package/dist/packages/cli/src/debug-intake.js +245 -0
- package/dist/packages/cli/src/hook-process.js +155 -34
- package/dist/packages/cli/src/office-entry.js +34 -6
- package/dist/packages/cli/src/run-deps.js +8 -0
- package/dist/runtime/src/adapters/agy/config.js +3 -2
- package/dist/runtime/src/adapters/claude/config.js +7 -3
- package/dist/runtime/src/adapters/codex/config.js +10 -3
- package/dist/runtime/src/adapters/codex/events.js +2 -1
- package/dist/runtime/src/adapters/codex/input.js +20 -2
- package/dist/runtime/src/adapters/codex/output.js +4 -1
- package/dist/runtime/src/config/merge.js +1 -1
- package/dist/runtime/src/hooks/completion-gate.js +15 -0
- package/dist/runtime/src/install/harness.js +2 -0
- package/dist/runtime/src/install/plan.js +3 -3
- package/dist/runtime/src/install/profiles.js +2 -0
- package/dist/runtime/src/install/update.js +10 -2
- package/dist/runtime/src/office/activity.js +105 -0
- package/dist/runtime/src/office/art.js +1000 -0
- package/dist/runtime/src/office/collect.js +137 -10
- package/dist/runtime/src/office/layout.js +203 -0
- package/dist/runtime/src/office/page.js +883 -644
- package/dist/runtime/src/office/scene.js +38 -33
- package/dist/runtime/src/office/sessions.js +18 -1
- package/dist/runtime/src/office/snapshot.js +74 -1
- package/dist/runtime/src/parallel/finish.js +4 -2
- package/dist/runtime/src/parallel/manage.js +19 -2
- package/dist/runtime/src/review/attestation.js +4 -1
- package/dist/runtime/src/review/batch.js +10 -0
- package/dist/runtime/src/review/execute.js +10 -1
- package/dist/runtime/src/review/slots.js +59 -44
- package/dist/runtime/src/run/hosts/codex.js +1 -1
- package/dist/runtime/src/run/transport.js +3 -0
- package/dist/runtime/src/schema/validate.js +8 -2
- package/dist/runtime/src/security/permissions.js +3 -1
- package/dist/runtime/src/task/completion.js +20 -1
- package/dist/runtime/src/task/contract.js +1 -1
- package/dist/runtime/src/task/debug-intake.js +12 -0
- package/dist/runtime/src/task/debug-state.js +130 -0
- package/dist/runtime/src/task/debug-user-event.js +93 -0
- package/dist/runtime/src/task/debug.js +586 -0
- package/dist/runtime/src/task/service.js +46 -0
- package/dist/runtime/src/task/store.js +8 -1
- package/dist/runtime/src/verify/acceptance-replay.js +9 -4
- package/dist/runtime/src/verify/service.js +21 -2
- package/docs/en/guides/configuration.md +7 -0
- package/docs/en/guides/task-debug.md +111 -0
- package/docs/en/spec/harness-adapters.md +31 -4
- package/docs/zh-TW/guides/configuration.md +6 -0
- package/docs/zh-TW/guides/task-debug.md +66 -0
- package/docs/zh-TW/spec/harness-adapters.md +23 -4
- package/package.json +1 -1
- package/schemas/config.schema.json +30 -2
- package/schemas/manifest.schema.json +1 -0
- package/schemas/task.schema.json +62 -2
|
@@ -6,6 +6,7 @@ import { validateEvidence, validateTask, validateTaskAgainstConfig } from "../sc
|
|
|
6
6
|
import { renderTaskMarkdown } from "./render.js";
|
|
7
7
|
import { checkTaskCompletionEvidence, findIncompleteSubtask } from "./completion.js";
|
|
8
8
|
import { calculateConfigHash, FileEvidenceStore, isPassingVerificationEvidence } from "../verify/evidence.js";
|
|
9
|
+
import { collectChangeSurface } from "../verify/change-surface.js";
|
|
9
10
|
import { resolveReviewScope } from "../review/scope.js";
|
|
10
11
|
import { calculateSourceFingerprint } from "../verify/source-fingerprint.js";
|
|
11
12
|
import { acceptanceCoverage } from "../verify/acceptance-coverage.js";
|
|
@@ -14,6 +15,9 @@ import { findReviewAttestation } from "../review/attestation.js";
|
|
|
14
15
|
import { advanceFailureFingerprint } from "../verify/fingerprint.js";
|
|
15
16
|
import { taskContractHash, treeContractHash } from "./contract.js";
|
|
16
17
|
import { commitIdentity } from "../schema/acceptance.js";
|
|
18
|
+
import { createDebugState, DEBUG_DEFAULTS } from "./debug-state.js";
|
|
19
|
+
import { DebugService } from "./debug.js";
|
|
20
|
+
import { sameDebugText } from "./debug-intake.js";
|
|
17
21
|
const SESSION_ID_PATTERN = /^[^\0\r\n]{1,256}$/u;
|
|
18
22
|
function defaultTaskId() {
|
|
19
23
|
return `task-${randomUUID()}`;
|
|
@@ -84,6 +88,15 @@ export class TaskService {
|
|
|
84
88
|
this.#now = options.now ?? (() => new Date().toISOString());
|
|
85
89
|
this.#completion = options.completion;
|
|
86
90
|
}
|
|
91
|
+
debugService() {
|
|
92
|
+
if (this.#completion === undefined)
|
|
93
|
+
throw taskError("TASK_DEBUG_CONTEXT_REQUIRED", "Debug operations require repository context.");
|
|
94
|
+
return new DebugService({ store: this.#store, tasks: this, ...this.#completion });
|
|
95
|
+
}
|
|
96
|
+
async assertDebugBaseline() {
|
|
97
|
+
if (this.#completion === undefined || (await collectChangeSurface(this.#completion.gitRunner)).paths.length > 0)
|
|
98
|
+
throw taskError("TASK_DEBUG_BASELINE_DIRTY", "Preserve user edits; debug intake needs a clean committed baseline before worktree allocation.");
|
|
99
|
+
}
|
|
87
100
|
async create(input) {
|
|
88
101
|
if (input.policyConfigHash !== undefined &&
|
|
89
102
|
!/^[a-f0-9]{64}$/u.test(input.policyConfigHash)) {
|
|
@@ -92,6 +105,12 @@ export class TaskService {
|
|
|
92
105
|
if (input.sessionId !== undefined) {
|
|
93
106
|
assertSessionId(input.sessionId);
|
|
94
107
|
}
|
|
108
|
+
// A new session must not restart an active issue's budget and rounds.
|
|
109
|
+
if (input.debug !== undefined && input.parentTaskId === undefined && input.goal !== undefined) {
|
|
110
|
+
const existing = await this.activeDebugRoot(input.goal);
|
|
111
|
+
if (existing !== null)
|
|
112
|
+
return input.sessionId === undefined ? existing : await this.attach(input.sessionId, existing.task.id);
|
|
113
|
+
}
|
|
95
114
|
let createdSourceCommit = input.createdSourceCommit;
|
|
96
115
|
if (createdSourceCommit === undefined && this.#completion !== undefined) {
|
|
97
116
|
const head = await this.#completion.gitRunner.run(["rev-parse", "--verify", "HEAD"]);
|
|
@@ -115,6 +134,18 @@ export class TaskService {
|
|
|
115
134
|
? {}
|
|
116
135
|
: { parentTaskId: input.parentTaskId })
|
|
117
136
|
};
|
|
137
|
+
let debugSettings;
|
|
138
|
+
if (input.debug !== undefined) {
|
|
139
|
+
if (input.goal === undefined || createdSourceCommit === undefined || this.#completion === undefined)
|
|
140
|
+
throw taskError("TASK_DEBUG_CONTEXT_REQUIRED", "Debug intake requires the original issue and a committed baseline.");
|
|
141
|
+
if ((await collectChangeSurface(this.#completion.gitRunner)).paths.length > 0)
|
|
142
|
+
throw taskError("TASK_DEBUG_BASELINE_DIRTY", "Preserve original user edits; investigate read-only or ask for an explicit committed baseline.");
|
|
143
|
+
const config = await this.#completion.loadConfig();
|
|
144
|
+
if (config.features.debug?.enabled === false)
|
|
145
|
+
throw taskError("TASK_DEBUG_DISABLED", "Task debug routing is explicitly disabled.");
|
|
146
|
+
debugSettings = config.features.debug ?? DEBUG_DEFAULTS;
|
|
147
|
+
task.debug = { rootTaskId: task.id, facts: [...(input.debug.facts ?? [])], unknowns: [...(input.debug.unknowns ?? [])] };
|
|
148
|
+
}
|
|
118
149
|
const validation = validateTask(task);
|
|
119
150
|
if (!validation.ok) {
|
|
120
151
|
throw taskError("TASK_INVALID", validation.errors[0]?.message ?? "Task input is invalid.");
|
|
@@ -149,9 +180,15 @@ export class TaskService {
|
|
|
149
180
|
throw taskError("TASK_GOAL_IMMUTABLE", "Subtasks must retain their parent original goal.");
|
|
150
181
|
validation.value.goal = parent.task.goal;
|
|
151
182
|
}
|
|
183
|
+
if (parent.task.debug !== undefined) {
|
|
184
|
+
if (input.debug !== undefined)
|
|
185
|
+
throw taskError("TASK_DEBUG_ROOT_IMMUTABLE", "A child cannot create a new issue budget.");
|
|
186
|
+
validation.value.debug = structuredClone(parent.task.debug);
|
|
187
|
+
}
|
|
152
188
|
}
|
|
153
189
|
const record = {
|
|
154
190
|
task: validation.value,
|
|
191
|
+
...(input.debug === undefined ? {} : { debug: createDebugState(task.id, createdSourceCommit, debugSettings, this.#completion.root) }),
|
|
155
192
|
...(createdSourceCommit === undefined ? {} : { createdSourceCommit }),
|
|
156
193
|
status: "active",
|
|
157
194
|
evidence: {},
|
|
@@ -326,6 +363,8 @@ export class TaskService {
|
|
|
326
363
|
const task = { schemaVersion: TASK_SCHEMA_VERSION, id: planned.id ?? this.#generateId(),
|
|
327
364
|
title: planned.title, intent: planned.intent, goal: root.task.goal ?? root.task.intent ?? root.task.title,
|
|
328
365
|
criteria: structuredClone(planned.criteria), parentTaskId: taskId };
|
|
366
|
+
if (root.task.debug !== undefined)
|
|
367
|
+
task.debug = structuredClone(root.task.debug);
|
|
329
368
|
const checked = config === undefined ? validateTask(task) : validateTaskAgainstConfig(task, config);
|
|
330
369
|
if (!checked.ok)
|
|
331
370
|
throw taskError("TASK_INVALID", checked.errors[0]?.message ?? "Invalid split task.");
|
|
@@ -388,6 +427,8 @@ export class TaskService {
|
|
|
388
427
|
if (record.policyConfigHash !== parent.policyConfigHash || !validateTask(task).ok) {
|
|
389
428
|
throw taskError("TASK_DELIVERY_INVALID", `Task ${task.id} has an incompatible policy or definition.`);
|
|
390
429
|
}
|
|
430
|
+
if (parent.task.debug !== undefined && (task.goal !== parent.task.goal || JSON.stringify(task.debug) !== JSON.stringify(parent.task.debug) || record.debug !== undefined))
|
|
431
|
+
throw taskError("TASK_DEBUG_ROOT_MISMATCH", "A delivery must retain the original issue identity and cannot import an independent debug budget.");
|
|
391
432
|
const existing = state.tasks.find((item) => item.task.id === task.id);
|
|
392
433
|
if (existing !== undefined) {
|
|
393
434
|
if (JSON.stringify(existing.task) !== JSON.stringify(task)) {
|
|
@@ -413,6 +454,11 @@ export class TaskService {
|
|
|
413
454
|
}
|
|
414
455
|
});
|
|
415
456
|
}
|
|
457
|
+
/** The active DEBUG root already holding this original issue, if any. */
|
|
458
|
+
async activeDebugRoot(goal) {
|
|
459
|
+
return (await this.list()).find((record) => record.status === "active" && record.task.debug?.rootTaskId === record.task.id &&
|
|
460
|
+
record.task.goal !== undefined && sameDebugText(record.task.goal, goal)) ?? null;
|
|
461
|
+
}
|
|
416
462
|
async list(filter = {}) {
|
|
417
463
|
const state = await this.#store.read();
|
|
418
464
|
return state.tasks
|
|
@@ -4,6 +4,7 @@ import { AgentOpsError } from "../fs/paths.js";
|
|
|
4
4
|
import { validateTask } from "../schema/validate.js";
|
|
5
5
|
import { readPrivateFile, withPrivateFileLock, writePrivateFile } from "../security/permissions.js";
|
|
6
6
|
import { taskContractHash } from "./contract.js";
|
|
7
|
+
import { assertDebugState } from "./debug-state.js";
|
|
7
8
|
import { commitIdentity, acceptanceSourcePath } from "../schema/acceptance.js";
|
|
8
9
|
const DEFAULT_MAX_BYTES = 1024 * 1024;
|
|
9
10
|
const SESSION_ID_PATTERN = /^[^\0\r\n]{1,256}$/u;
|
|
@@ -68,7 +69,7 @@ function parseTaskRecord(value) {
|
|
|
68
69
|
];
|
|
69
70
|
// Every record written before an optional field existed must still parse, so
|
|
70
71
|
// the accepted shapes are every combination of them.
|
|
71
|
-
const optionalKeys = ["completionBase", "failureFingerprint", "policyConfigHash", "reviewBase", "createdSourceCommit", "revisions", "supersededBy", "noChangePaths"];
|
|
72
|
+
const optionalKeys = ["completionBase", "failureFingerprint", "policyConfigHash", "reviewBase", "createdSourceCommit", "revisions", "supersededBy", "noChangePaths", "debug"];
|
|
72
73
|
if (!isRecord(value) || !baseKeys.every(k => Object.hasOwn(value, k)) ||
|
|
73
74
|
Object.keys(value).some(k => !baseKeys.includes(k) && !optionalKeys.includes(k))) {
|
|
74
75
|
return invalidState("Task state contains an invalid task record.");
|
|
@@ -77,6 +78,11 @@ function parseTaskRecord(value) {
|
|
|
77
78
|
if (!task.ok) {
|
|
78
79
|
return invalidState("Task state contains an invalid task.");
|
|
79
80
|
}
|
|
81
|
+
if (value.debug !== undefined) {
|
|
82
|
+
assertDebugState(value.debug);
|
|
83
|
+
if (value.debug.rootTaskId !== task.value.id || task.value.debug?.rootTaskId !== task.value.id)
|
|
84
|
+
return invalidState("Debug counters belong only to the original issue root.");
|
|
85
|
+
}
|
|
80
86
|
if (!["active", "archived", "complete"].includes(String(value.status)) ||
|
|
81
87
|
!isTimestamp(value.createdAt) ||
|
|
82
88
|
!isTimestamp(value.updatedAt) ||
|
|
@@ -182,6 +188,7 @@ function parseTaskRecord(value) {
|
|
|
182
188
|
}
|
|
183
189
|
return {
|
|
184
190
|
task: task.value,
|
|
191
|
+
...(value.debug === undefined ? {} : { debug: value.debug }),
|
|
185
192
|
...(value.noChangePaths === undefined ? {} : { noChangePaths: value.noChangePaths }),
|
|
186
193
|
...(value.createdSourceCommit === undefined ? {} : { createdSourceCommit: value.createdSourceCommit }),
|
|
187
194
|
...(value.revisions === undefined ? {} : { revisions: value.revisions }),
|
|
@@ -113,9 +113,14 @@ export async function replayAcceptance(options) {
|
|
|
113
113
|
const temp = await mkdtemp(join(tmpdir(), "agent-ops-acceptance-"));
|
|
114
114
|
const checkout = join(temp, "repository");
|
|
115
115
|
const env = replayEnvironment(checkout, phase, executionId);
|
|
116
|
-
const command = async (executable, args, cwd, timeoutMs = 60_000) =>
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
const command = async (executable, args, cwd, timeoutMs = 60_000) => {
|
|
117
|
+
const remaining = await options.remainingMs?.();
|
|
118
|
+
if (remaining === 0)
|
|
119
|
+
throw new AgentOpsError("TASK_DEBUG_BUDGET_EXHAUSTED", "The original issue's active budget is exhausted.");
|
|
120
|
+
return await runVerificationCommand({ id: group.runner.id, command: executable, args: [...args], cwd: ".", required: true,
|
|
121
|
+
evidence: { kind: "exit-code" }, timeoutMs: Math.min(timeoutMs, remaining ?? Infinity) }, { cwd, runner: options.processRunner, env, replaceEnv: true,
|
|
122
|
+
outputLimitBytes: 1024 * 1024, ...(options.signal === undefined ? {} : { signal: options.signal }) });
|
|
123
|
+
};
|
|
119
124
|
let aggregate;
|
|
120
125
|
let failure = "replay-unavailable";
|
|
121
126
|
let output;
|
|
@@ -200,7 +205,7 @@ export async function replayAcceptance(options) {
|
|
|
200
205
|
failure = aggregate?.failureClass ?? output.failureClass;
|
|
201
206
|
}
|
|
202
207
|
catch (error) {
|
|
203
|
-
failure = error instanceof Error && /^[a-z-]+$/u.test(error.message) ? error.message : "replay-unavailable";
|
|
208
|
+
failure = error instanceof AgentOpsError && error.code === "TASK_DEBUG_BUDGET_EXHAUSTED" ? "debug-budget-exhausted" : error instanceof Error && /^[a-z-]+$/u.test(error.message) ? error.message : "replay-unavailable";
|
|
204
209
|
}
|
|
205
210
|
finally {
|
|
206
211
|
await rm(temp, { recursive: true, force: true });
|
|
@@ -70,7 +70,10 @@ export class VerificationService {
|
|
|
70
70
|
}
|
|
71
71
|
async #runCommand(task, command) {
|
|
72
72
|
const startedAt = (this.#options.now ?? (() => new Date().toISOString()))();
|
|
73
|
-
const
|
|
73
|
+
const remaining = task.debug === undefined ? undefined : await this.#options.taskService.debugService().remaining(task.id);
|
|
74
|
+
if (remaining === 0)
|
|
75
|
+
throw verificationError("TASK_DEBUG_BUDGET_EXHAUSTED", "The original issue's active budget is exhausted.");
|
|
76
|
+
const result = await executeConfiguredCommand(remaining === undefined ? command : { ...command, timeoutMs: Math.min(command.timeoutMs ?? 300_000, remaining) }, {
|
|
74
77
|
cwd: await this.#commandCwd(command),
|
|
75
78
|
runner: this.#options.processRunner,
|
|
76
79
|
trusted: this.#options.trusted
|
|
@@ -107,6 +110,11 @@ export class VerificationService {
|
|
|
107
110
|
};
|
|
108
111
|
}
|
|
109
112
|
async verify(taskId) {
|
|
113
|
+
const stored = await this.#options.taskService.status({ taskId });
|
|
114
|
+
return stored.task.debug === undefined ? await this.#verify(taskId) :
|
|
115
|
+
await this.#options.taskService.debugService().verification(taskId, () => this.#verify(taskId));
|
|
116
|
+
}
|
|
117
|
+
async #verify(taskId) {
|
|
110
118
|
const stored = await this.#options.taskService.status({ taskId });
|
|
111
119
|
if (stored.status === "archived") {
|
|
112
120
|
throw verificationError("TASK_NOT_ACTIVE", "An archived task cannot be verified.");
|
|
@@ -145,7 +153,8 @@ export class VerificationService {
|
|
|
145
153
|
results.push(await this.#runCommand(validation.value, commandById(this.#options.config, commandId)));
|
|
146
154
|
}
|
|
147
155
|
const acceptance = validation.value.criteria.some(c => c.acceptance !== undefined && c.acceptance.mode !== "review-only")
|
|
148
|
-
? await replayAcceptance({ ...this.#options, task: validation.value, sourceFingerprint
|
|
156
|
+
? await replayAcceptance({ ...this.#options, task: validation.value, sourceFingerprint,
|
|
157
|
+
...(stored.task.debug === undefined ? {} : { remainingMs: () => this.#options.taskService.debugService().remaining(taskId) }) }) : [];
|
|
149
158
|
let status = aggregateVerificationStatus([...results, ...acceptance.map(a => ({ required: true, status: a.status }))]);
|
|
150
159
|
let sourceChanged = false;
|
|
151
160
|
const postflightScope = await resolveReviewScope({
|
|
@@ -188,6 +197,16 @@ export class VerificationService {
|
|
|
188
197
|
}
|
|
189
198
|
}
|
|
190
199
|
else {
|
|
200
|
+
if (stored.task.debug !== undefined && !sourceChanged) {
|
|
201
|
+
const failures = {};
|
|
202
|
+
for (const result of results) {
|
|
203
|
+
const references = await this.#persistEvidence(validation.value, commandById(this.#options.config, result.commandId), result.startedAt, result.finishedAt, result, sourceFingerprint);
|
|
204
|
+
relevantCriteria(validation.value, result.commandId).forEach((criterion, index) => { const reference = references[index]; if (reference !== undefined)
|
|
205
|
+
failures[criterion.id] = [...(failures[criterion.id] ?? []), reference]; });
|
|
206
|
+
}
|
|
207
|
+
if (Object.keys(failures).length > 0)
|
|
208
|
+
await this.#options.taskService.recordEvidence(taskId, failures);
|
|
209
|
+
}
|
|
191
210
|
const required = [...results.filter((result) => result.required), ...acceptance.map(a => ({ commandId: a.runnerId, required: true,
|
|
192
211
|
status: a.status, failureClass: a.failureClass, exitCode: null, timedOut: false, testCount: null,
|
|
193
212
|
diagnostic: a.criterionId + ":" + a.phase + ":" + a.failureClass, evidenceReferences: [a.reference], startedAt: "", finishedAt: "" }))];
|
|
@@ -448,6 +448,13 @@ To narrow an existing installation, pass the desired list to `agent-ops update
|
|
|
448
448
|
--harness`; shared paths remain managed while removed harness-owned artifacts,
|
|
449
449
|
markers, and hooks are reconciled.
|
|
450
450
|
|
|
451
|
+
Update keeps the configured profiles. To add or remove the opt-in `run`
|
|
452
|
+
profile on an existing installation, pass `agent-ops update --auto-run on` or
|
|
453
|
+
`--auto-run off`; `off` removes only `run`, and the `loop` it brought stays
|
|
454
|
+
(use `init` to change other profiles). Without the flag, an interactive update
|
|
455
|
+
on macOS with Claude Code or Codex installed asks once, defaulting to the
|
|
456
|
+
current choice; `--yes` and non-interactive updates preserve it.
|
|
457
|
+
|
|
451
458
|
To remove only one integrated harness, use `agent-ops uninstall --harness agy`
|
|
452
459
|
(or another installed id). The remaining manifest and shared paths stay in
|
|
453
460
|
place; omit `--harness` to remove the complete managed installation.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Task DEBUG investigations
|
|
2
|
+
|
|
3
|
+
Project `init` and `update` add `features.debug` by default:
|
|
4
|
+
`{"enabled":true,"budgetMs":3600000,"maxRounds":5}` (sixty minutes). Explicit disabled and custom
|
|
5
|
+
settings survive subsequent installs. Old configurations and ordinary tasks
|
|
6
|
+
remain readable. DEBUG is an optional task intake field, not a new run mode.
|
|
7
|
+
|
|
8
|
+
Claude and Codex native prompt hooks conservatively recognize investigation and
|
|
9
|
+
bug-fix requests. They retain the issue verbatim as `goal`, record uncertain
|
|
10
|
+
facts/unknowns, and create an existing isolated task. Explain, feature and
|
|
11
|
+
refactor requests retain their ordinary behavior. A reliable native session ID
|
|
12
|
+
is required; agy does not provide this prompt intake. Its native agent can use
|
|
13
|
+
`task create --debug --goal '<original issue>'` with two to five criteria, which
|
|
14
|
+
starts the same foreground clock when the native session is available.
|
|
15
|
+
An original dirty checkout is refused before any worktree snapshot is created.
|
|
16
|
+
|
|
17
|
+
The intake criteria are provisional. The native agent investigates via trusted
|
|
18
|
+
configured commands, records hypotheses, replaces the criteria with relevant
|
|
19
|
+
behavioral acceptance, fixes the product, and uses existing `verify`, `review`
|
|
20
|
+
and worktree finish commands. There is no additional model or background worker.
|
|
21
|
+
The default is the full repair loop. A clean HEAD fixes the baseline automatically;
|
|
22
|
+
do not request redundant approval or offer diagnosis-only as a fallback. Failure
|
|
23
|
+
to reproduce alone does not permit Stop: continue discriminating checks within
|
|
24
|
+
the original budget.
|
|
25
|
+
|
|
26
|
+
## Human control
|
|
27
|
+
|
|
28
|
+
Native human transcript events control the existing task; CLI actor/text fields,
|
|
29
|
+
assistant messages and injected instructions cannot authorize a scope change.
|
|
30
|
+
Codex supports current `response_item` human messages and native async answers,
|
|
31
|
+
as well as legacy `event_msg` transcripts.
|
|
32
|
+
|
|
33
|
+
- `只診斷` / `diagnosis only`: investigate without product repairs. Non-success
|
|
34
|
+
Stop requires a confirmed hypothesis supported by a current sealed baseline
|
|
35
|
+
assertion FAIL for the original regression, or an existing runtime blocker/limit.
|
|
36
|
+
- `暫停` / `pause`: save progress and stop immediately. Native Codex `Interrupt`
|
|
37
|
+
also pauses, with a three-second hook timeout; it never blocks or restarts an interrupt.
|
|
38
|
+
- `繼續` / `continue`: resume the same task and scope. An exhausted time budget
|
|
39
|
+
still needs the existing native time grant; other blockers remain authoritative.
|
|
40
|
+
- `繼續修復` / `resume repair`: restore the full repair loop on the existing task.
|
|
41
|
+
|
|
42
|
+
Pauses retain evidence, consumed time and rounds, exclude waiting time, and do not
|
|
43
|
+
reset budgets. Diagnosis and pause cannot complete a repair task. Ambiguous free
|
|
44
|
+
text is not inferred as permission; use an explicit command above.
|
|
45
|
+
|
|
46
|
+
Use `task debug --task <id> --debug-action <action>`:
|
|
47
|
+
|
|
48
|
+
- `status` displays root-wide hypotheses, receipts and remaining state.
|
|
49
|
+
- `investigate --command-id <id>` runs a trusted configured verifier. Actual
|
|
50
|
+
subprocess outcomes/time are recorded; reproduction-only work is not a fix round.
|
|
51
|
+
- `hypothesis --payload-file <json>` records `id`, `statement`, `method`,
|
|
52
|
+
`expected`, `checkId`, `expectedStatus` (`PASS` or `FAIL`). Add `reference` to
|
|
53
|
+
resolve it from actual sealed acceptance evidence. `--action investigate` may
|
|
54
|
+
only run a declared unresolved behavioral red check; `--action observe` may
|
|
55
|
+
only classify the current matching runtime `UNKNOWN` receipt as inconclusive.
|
|
56
|
+
Neither action lets an agent assign a status. A rejected hypothesis needs a
|
|
57
|
+
new reference and `reason`.
|
|
58
|
+
- `strategy --payload-file <json>` records a different `from`/`to` method,
|
|
59
|
+
substantive `reason` and discriminating `reference`. Only one switch is allowed.
|
|
60
|
+
- `checkpoint --session <id>` saves the current bound source/policy/stop cause.
|
|
61
|
+
- `resume --session <id>` recovers a matching owned worktree checkpoint. Mismatches
|
|
62
|
+
require human confirmation and remain preserved; this command cannot waive them.
|
|
63
|
+
- `extend --session <native-id> --event-id latest` reads the current native human
|
|
64
|
+
continue event. It grants exactly twenty additional minutes once, only after
|
|
65
|
+
time exhaustion and without waiving any other blocker. No actor/text flag can
|
|
66
|
+
authorize a grant. Missing native transcripts fail closed as unsupported.
|
|
67
|
+
|
|
68
|
+
Limits belong to the original root across revisions, children and sessions.
|
|
69
|
+
Repeating an active issue's text from a new session reattaches that root (or
|
|
70
|
+
resumes the worktree holding it) instead of starting a fresh budget. A
|
|
71
|
+
resolved hypothesis cannot return under a new id with the same statement and
|
|
72
|
+
method; reopen the original with new discriminating evidence.
|
|
73
|
+
Controlled investigation, verification and acquired review execution consume
|
|
74
|
+
active time; observable review-slot waiting is excluded. Claude, Codex, and agy
|
|
75
|
+
use their native session lifecycle to account foreground reasoning, tool use,
|
|
76
|
+
and edits. Accepted idle Stop closes the foreground clock. Rejected Codex Stop
|
|
77
|
+
keeps the clock running, including repeated Stop (`stop_hook_active: true`). OpenCode has no supported
|
|
78
|
+
foreground lifecycle and remains unsupported. Time between stopped turns is
|
|
79
|
+
paused. An interrupted unsealed operation remains blocked until its persisted process
|
|
80
|
+
identity proves death; only then is an UNKNOWN environment receipt sealed and
|
|
81
|
+
the elapsed interval retained.
|
|
82
|
+
|
|
83
|
+
Only changed product candidates followed by verification count toward repair
|
|
84
|
+
rounds. Test/fixture edits do not. New paired original-regression outcomes or
|
|
85
|
+
new discriminating hypothesis outcomes count as progress; renaming a task or
|
|
86
|
+
hypothesis does not. Two stagnant rounds require the strategy switch; two more
|
|
87
|
+
stop, subject to the global round/time ceilings.
|
|
88
|
+
|
|
89
|
+
Success requires baseline assertion red and candidate green for the original
|
|
90
|
+
regression, required related checks, a supported confirmed hypothesis, and two
|
|
91
|
+
fresh independent reviews bound to the current source, contract, config and
|
|
92
|
+
meaningful DEBUG proof. Missing IDs, setup failure, skip, timeout and UNKNOWN
|
|
93
|
+
never prove reproduction. Proof changes invalidate review. Completed tasks and
|
|
94
|
+
worktree finish retain the existing attestation/integration requirements.
|
|
95
|
+
|
|
96
|
+
On Claude, agy and native Codex, Stop checks an active DEBUG issue even without Git net diff,
|
|
97
|
+
with or without the completion gate. Without the gate this check alone runs,
|
|
98
|
+
with one targeted task/config reload on a gate fault. A second failure emits an
|
|
99
|
+
explicit `UNKNOWN` / incomplete diagnostic with `agent-ops doctor` recovery,
|
|
100
|
+
permits non-success Stop and preserves the task, evidence and budgets. It never
|
|
101
|
+
attests completion or retries indefinitely.
|
|
102
|
+
A valid runtime stop checkpoint permits non-success Stop while retaining the
|
|
103
|
+
active task. `allow-stop` cannot waive DEBUG. Gates are never automatically
|
|
104
|
+
enabled for unsupported hosts: the generic Codex completion gate remains
|
|
105
|
+
unsupported, while its DEBUG-specific Stop uses native `decision: "block"`
|
|
106
|
+
with a continuation reason. `continue: false` would stop the harness and is not
|
|
107
|
+
used to continue DEBUG. OpenCode Stop is unsupported. This feature
|
|
108
|
+
persists foreground progress, and does not promise
|
|
109
|
+
execution after the native conversation closes. Native transcripts are trusted
|
|
110
|
+
host evidence, not protection against an attacker with the same filesystem
|
|
111
|
+
authority.
|
|
@@ -118,7 +118,20 @@ installed configuration; the managed OpenCode `tool.execute.before` plugin can
|
|
|
118
118
|
throw its documented denial or unavailable-runtime error for its supported Bash
|
|
119
119
|
surface. Codex remains `unknown` and never emits a denial. Fixture tests assert
|
|
120
120
|
these wire and plugin shapes only; they do not prove that a host honors a
|
|
121
|
-
denial.
|
|
121
|
+
denial. `SessionStart` and ordinary `Stop` failures remain fail-open. Active
|
|
122
|
+
DEBUG Stop faults instead retry one targeted reload, then explicitly report
|
|
123
|
+
UNKNOWN/incomplete and preserve the original state before non-success Stop.
|
|
124
|
+
|
|
125
|
+
Codex DEBUG lifecycle semantics were rechecked on 2026-10-11 against the
|
|
126
|
+
[official hooks reference](https://learn.chatgpt.com/docs/hooks#stop): Stop
|
|
127
|
+
`decision: "block"` with `reason` schedules a continuation; `continue: false`
|
|
128
|
+
halts it. `stop_hook_active` identifies a prior continuation and cannot waive
|
|
129
|
+
the DEBUG check. Native `Interrupt` pauses with a three-second timeout and
|
|
130
|
+
never blocks or restarts the human interruption. This DEBUG-specific policy
|
|
131
|
+
does not enable Codex's generic completion gate or optional Stop verifier.
|
|
132
|
+
Codex separately trusts each exact native hook definition. After managed hook
|
|
133
|
+
updates, review new or changed definitions in `/hooks`; agent-ops repository
|
|
134
|
+
trust alone does not make an unreviewed native hook active.
|
|
122
135
|
|
|
123
136
|
The agy adapter uses native project `GEMINI.md` routing to
|
|
124
137
|
`.agent-ops/GEMINI.md`;
|
|
@@ -130,7 +143,7 @@ unproven final changed conversation. Read-only conversations stop normally.
|
|
|
130
143
|
On Windows the generated command is invoked through `cmd /c`.
|
|
131
144
|
|
|
132
145
|
Stop verification is explicit, trusted, report-only, and disabled by default.
|
|
133
|
-
Every Stop result continues the native harness and may carry only bounded
|
|
146
|
+
Every optional Stop verification result continues the native harness and may carry only bounded
|
|
134
147
|
command evidence; it is never task-completion evidence.
|
|
135
148
|
|
|
136
149
|
The `loop` profile is separate from the ordinary capability matrix above. It
|
|
@@ -145,7 +158,10 @@ The `run` profile MUST be opt-in, select `core` and `loop` with it, and add only
|
|
|
145
158
|
the `auto-run` capability. With it, the Claude Code and Codex managed rules hand
|
|
146
159
|
a change that needs more than five acceptance criteria to `agent-ops run`; five
|
|
147
160
|
or fewer stay in the session. agy rules, and every rule file without `auto-run`,
|
|
148
|
-
MUST stay byte-identical to the rules without the profile.
|
|
161
|
+
MUST stay byte-identical to the rules without the profile. Update MUST preserve
|
|
162
|
+
the profile unless `--auto-run on|off` is supplied or an interactive update on
|
|
163
|
+
macOS with Claude Code or Codex asks (default: the current choice); `off`
|
|
164
|
+
removes only `run` and keeps `loop`.
|
|
149
165
|
|
|
150
166
|
- Trigger: A project selects `run`, and a Claude Code or Codex session meets a
|
|
151
167
|
change that needs more than five acceptance criteria.
|
|
@@ -221,7 +237,18 @@ Claude or Codex transcripts or file contents.
|
|
|
221
237
|
Claude Code also gets a managed `SessionEnd` hook: when the conversation
|
|
222
238
|
closes, its room closes too, even without a task. It writes no output and
|
|
223
239
|
never fails the host. Other hosts publish no end event and rely on the
|
|
224
|
-
activity timeout. A
|
|
240
|
+
activity timeout. A session that has only started is idle; a submitted
|
|
241
|
+
prompt or tool activity marks it active, and agy's per-invocation start
|
|
242
|
+
counts as activity. Outside the project loop, Claude Code and Codex get a
|
|
243
|
+
managed `UserPromptSubmit` hook whenever Office or command policy is enabled:
|
|
244
|
+
Office records the turn's start, and command policy refuses a prompt carrying
|
|
245
|
+
a literal credential in the host's native shape (Claude `decision: "block"`,
|
|
246
|
+
Codex exit code 2). Its runtime failures let the prompt through. Hooks see
|
|
247
|
+
only a command's start, so `agent-ops batch` reports each verify or review
|
|
248
|
+
as it starts to the sessions attached to its tasks (the newest step shows),
|
|
249
|
+
and `task advance` outside a run reports verify, review and integration to
|
|
250
|
+
its own session. These reports update only a known session, never fail the
|
|
251
|
+
command, and record nothing while Office is disabled. A room without a task shows a no-task board rather than
|
|
225
252
|
pending verification and review. Session metadata contains bounded
|
|
226
253
|
identifiers, known work state and activity times, never native transcripts,
|
|
227
254
|
prompts or tool/file contents. Startup and observation failures are advisory.
|
|
@@ -395,6 +395,12 @@ config-hash 與 timestamp evidence,且永遠不會完成 task。Config v1 會
|
|
|
395
395
|
`agent-ops update --harness`;shared path 會繼續受管理,被移除 harness
|
|
396
396
|
擁有的 artifact、marker 與 hook 則會被安全同步。
|
|
397
397
|
|
|
398
|
+
Update 會保留已設定的 profile。若要在既有 installation 加入或移除 opt-in 的
|
|
399
|
+
`run` profile,請使用 `agent-ops update --auto-run on` 或 `--auto-run off`;
|
|
400
|
+
`off` 只移除 `run`,它帶入的 `loop` 會保留(其他 profile 請用 `init` 變更)。
|
|
401
|
+
未提供此旗標時,在已安裝 Claude Code 或 Codex 的 macOS 上互動式 update 會詢問
|
|
402
|
+
一次,預設為目前的選擇;`--yes` 與非互動式 update 會保留原設定。
|
|
403
|
+
|
|
398
404
|
若只要移除單一已整合 harness,可使用
|
|
399
405
|
`agent-ops uninstall --harness agy`(或其他已安裝 id)。剩餘 manifest 與
|
|
400
406
|
shared path 會保留;省略 `--harness` 才會移除整套 managed installation。
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Task DEBUG 調查
|
|
2
|
+
|
|
3
|
+
`init` / `update` 預設加入 `features.debug`:啟用、60 分鐘、5 輪。
|
|
4
|
+
之後保留明確關閉與自訂設定;舊設定和一般 task 保持相容。
|
|
5
|
+
|
|
6
|
+
Claude / Codex prompt hook 會保守辨識調查與修復問題,保留原始 `goal`,
|
|
7
|
+
建立既有隔離 task。說明、功能與重構請求照原流程。
|
|
8
|
+
缺少可靠 session ID,或 agy 沒有 prompt 事件時,使用原生 Agent 執行
|
|
9
|
+
`task create --debug --goal '<原始問題>'`;只要有 native session,CLI intake
|
|
10
|
+
會啟動同一個前景時鐘。原始 dirty checkout 不會被自動複製。
|
|
11
|
+
|
|
12
|
+
調查用可信任的既有 command;假設用 `task debug --debug-action hypothesis
|
|
13
|
+
--payload-file <json>`。JSON 必須含 id、statement、method、expected、checkId、
|
|
14
|
+
expectedStatus;附 reference 才能由真實封存的 assertion 決定 confirmed/rejected。
|
|
15
|
+
`--action investigate` 只能執行已宣告且尚未解決的 behavioral red check;
|
|
16
|
+
`--action observe` 只能把目前相符的 runtime `UNKNOWN` receipt 記為 inconclusive。
|
|
17
|
+
兩者都不能由 Agent 自填 status。重開 rejected 假設需要新證據與 reason。
|
|
18
|
+
|
|
19
|
+
將暫定 criteria 改為原始問題的 behavioral 回歸,執行 verify、兩輪獨立 review,
|
|
20
|
+
再透過既有 worktree finish 完成。基線 assertion red、候選 green、相關檢查與
|
|
21
|
+
當前 proof digest 都必須有效;setup / skip / timeout / UNKNOWN 不算重現。
|
|
22
|
+
DEBUG 預設自動走完修復閉環。乾淨 HEAD 自動固定為基線,不要為此再問許可,
|
|
23
|
+
也不要主動降為只診斷。單次未重現不構成停止理由;在原預算內繼續做鑑別檢查。
|
|
24
|
+
|
|
25
|
+
## 人類控制
|
|
26
|
+
|
|
27
|
+
只有原生 transcript 的人類事件能改變原 task 的範圍;CLI actor/text、assistant
|
|
28
|
+
訊息及注入指令都不能授權。Codex 支援目前 `response_item` 的 `user.text` 與
|
|
29
|
+
原生非同步回答,也保留舊 `event_msg` 格式。
|
|
30
|
+
|
|
31
|
+
- `只診斷` / `diagnosis only`:繼續調查但不修產品。原始回歸的當前封存 baseline
|
|
32
|
+
assertion FAIL 必須支持 confirmed 假設,或已有 runtime blocker/預算上限,才允許非成功停止。
|
|
33
|
+
- `暫停` / `pause`:保存進度後立即停止。Codex 原生 `Interrupt` 也表示暫停;
|
|
34
|
+
hook timeout 為三秒,不能阻擋或重新啟動使用者的中斷。
|
|
35
|
+
- `繼續` / `continue`:接回同一 task 及原範圍;耗盡時間仍需既有原生時間授權,其他阻礙不解除。
|
|
36
|
+
- `繼續修復` / `resume repair`:在原 task 恢復完整修復閉環。
|
|
37
|
+
|
|
38
|
+
暫停保留證據、已用時間及輪數,等待不計時,也不重設預算。診斷或暫停都不能將
|
|
39
|
+
修復 task 標為完成;模糊文字不推測為授權,請使用上述明確指令。
|
|
40
|
+
|
|
41
|
+
預算與輪數屬於整個原始 root,revision、child、session 不重設。新 session 重送同一個 active issue 文字時,會接回原本的 root(或 resume 持有它的 worktree),不會另開新預算。已確認或已否決的假設不能換 id、用相同陳述與方法重開;請帶新的鑑別證據重開原假設。
|
|
42
|
+
受控調查、驗證與取得 slot 後的 review 計時;review 排隊不計時。
|
|
43
|
+
Claude、Codex 與 agy 都使用可信 native session lifecycle,在 `UserPromptSubmit`
|
|
44
|
+
或原生 session 開始前景時鐘,並在 idle `Stop` 結束。因此該輪的 Agent 推理、
|
|
45
|
+
工具與修改都計入,停止回合之間的使用者等待不計入。Codex 被拒絕的 Stop 仍繼續
|
|
46
|
+
計時,重複 Stop 的 `stop_hook_active: true` 不會繞過 DEBUG gate。opencode 沒有支援
|
|
47
|
+
的前景 lifecycle,不可宣稱完整的 active-time 預算。
|
|
48
|
+
中斷且沒有封存結果的操作,必須等持久化 process identity 證實程序已死亡後,
|
|
49
|
+
才封存 UNKNOWN environment receipt;這段經過時間仍會保留,不會退還不明時間。
|
|
50
|
+
測試材料修改不算修復輪;兩輪停滯需要一次實質策略切換,再兩輪停滯停止。
|
|
51
|
+
|
|
52
|
+
checkpoint 保存 task/session/source/contract/config/cause;resume 只接受同一
|
|
53
|
+
擁有者 worktree 的匹配 checkpoint,保留 dirty 內容及預算。失配需要人類確認。
|
|
54
|
+
時間耗盡後明確「繼續」可透過 `extend --event-id latest` 讀取原生人類事件,
|
|
55
|
+
一次增加 20 分鐘;沒有可信事件来源則回報 unsupported,其他阻礙不被解除。
|
|
56
|
+
|
|
57
|
+
Claude / agy / 原生 Codex 的 Stop 都會檢查無 Git 差異的 active DEBUG task,不論是否安裝 completion gate。
|
|
58
|
+
DEBUG gate 故障先針對 task/config 重新載入一次;仍失敗則明確回報 `UNKNOWN/未完成`
|
|
59
|
+
與 `agent-ops doctor` 恢復方法,允許非成功停止並保留 task、證據及預算,不無限重試。
|
|
60
|
+
有效停止 checkpoint 只允許非成功停止,task 仍 active;allow-stop 不能繞過。
|
|
61
|
+
Codex 的一般 completion gate 仍 unsupported;DEBUG 專用 Stop 以原生
|
|
62
|
+
`decision: "block"` 與 reason 續跑,不能使用會停止 harness 的 `continue: false`。
|
|
63
|
+
已接受的 Stop 結束前景時鐘;opencode Stop unsupported。
|
|
64
|
+
只承諾前景對話與持久化進度,不承諾對話關閉後繼續,也不宣稱抵擋同檔案權限攻擊。
|
|
65
|
+
|
|
66
|
+
完整命令及細節見英文 [Task DEBUG](../../en/guides/task-debug.md)。
|
|
@@ -101,7 +101,16 @@ Runtime-failure 處理中,只有 `command-policy` 為 fail-closed。當已安
|
|
|
101
101
|
OpenCode `tool.execute.before` plugin 可在其支援的 Bash surface 上 throw 文件化的
|
|
102
102
|
denial 或 unavailable-runtime error。Codex 維持 `unknown` 且絕不輸出 denial。
|
|
103
103
|
Fixture test 只斷言這些 wire 與 plugin shape;它們不證明 host 會實際遵守 denial。
|
|
104
|
-
|
|
104
|
+
`SessionStart` 與一般 `Stop` failure 維持 fail-open。active DEBUG Stop 故障則
|
|
105
|
+
針對 task/config 重新載入一次,再明確回報 UNKNOWN/未完成並保留原狀態,才允許非成功停止。
|
|
106
|
+
|
|
107
|
+
Codex DEBUG lifecycle 語意於 2026-10-11 依[官方 hook 文件](https://learn.chatgpt.com/docs/hooks#stop)
|
|
108
|
+
重新檢查:Stop 的 `decision: "block"` 加 `reason` 會排程續跑;`continue: false`
|
|
109
|
+
會停止。`stop_hook_active` 表示已經續跑過,不能繞過 DEBUG 檢查。
|
|
110
|
+
原生 `Interrupt` 暫停採三秒 timeout,不得阻擋或重啟人類中斷。
|
|
111
|
+
此 DEBUG 專用政策不會啟用 Codex 的一般 completion gate 或 optional Stop verifier。
|
|
112
|
+
Codex 另以每個原生 hook 的精確定義保存信任。更新受管理 hook 後,必須在 `/hooks`
|
|
113
|
+
審核新增或變更的定義;agent-ops repository trust 不會啟用尚未審核的原生 hook。
|
|
105
114
|
|
|
106
115
|
agy adapter 在 project scope 使用原生 `GEMINI.md` routing 到
|
|
107
116
|
`.agent-ops/GEMINI.md`;user
|
|
@@ -112,7 +121,7 @@ changed conversation 才回傳 `decision: "continue"`;唯讀 conversation 正
|
|
|
112
121
|
結束。在 Windows 透過 `cmd /c` 呼叫產生的 command。
|
|
113
122
|
|
|
114
123
|
Stop verification 必須明確啟用、具備 trust、為 report-only 且預設 disabled。
|
|
115
|
-
每個 Stop 結果都會讓 native harness 繼續,最多攜帶有界 command evidence,永遠
|
|
124
|
+
每個 optional Stop verification 結果都會讓 native harness 繼續,最多攜帶有界 command evidence,永遠
|
|
116
125
|
不是 task-completion evidence。
|
|
117
126
|
|
|
118
127
|
`loop` profile 與上方 ordinary capability matrix 分離。它只保存有界的 local
|
|
@@ -126,7 +135,9 @@ event metadata、回傳有界且 redacted 的 session context,並在 update
|
|
|
126
135
|
capability。啟用後,Claude Code 與 Codex 的 managed rules 會將需要超過五項
|
|
127
136
|
acceptance criteria 的變更交給 `agent-ops run`;五項以內仍留在 session 中處理。
|
|
128
137
|
agy rules,以及所有未含 `auto-run` 的 rule file,MUST 與未啟用此 profile 時
|
|
129
|
-
byte-identical。
|
|
138
|
+
byte-identical。Update MUST 保留此 profile,除非提供 `--auto-run on|off`,或在已
|
|
139
|
+
安裝 Claude Code 或 Codex 的 macOS 上互動式 update 詢問(預設為目前選擇);
|
|
140
|
+
`off` 只移除 `run`,保留 `loop`。
|
|
130
141
|
|
|
131
142
|
- Trigger: Project 選擇 `run`,且 Claude Code 或 Codex session 遇到需要超過五項
|
|
132
143
|
acceptance criteria 的變更。
|
|
@@ -189,7 +200,15 @@ base 的 `git diff`;旁白只使用路徑(`docs/**` 為「writing docs」、
|
|
|
189
200
|
刪除該紀錄,絕不對其 pid 發送訊號。`worktree finish` 成功後會記錄 session
|
|
190
201
|
完成,使房間關閉;只有新的啟動或 active task 會重新開啟。啟用 Office 時,Claude Code
|
|
191
202
|
另有受管理的 `SessionEnd` hook:對話關閉時房間也隨之關閉,即使沒有 task。它不輸出
|
|
192
|
-
任何內容,也絕不讓 host 失敗。其他 host
|
|
203
|
+
任何內容,也絕不讓 host 失敗。其他 host 沒有結束事件,依活動逾時處理。只有啟動的
|
|
204
|
+
session 為閒置;送出 prompt 或 tool 活動才標為 active,agy 以 invocation 為單位
|
|
205
|
+
的啟動視為活動。不在 project loop 時,只要啟用 Office 或 command policy,Claude
|
|
206
|
+
Code 與 Codex 會有受管理的 `UserPromptSubmit` hook:Office 記錄該輪開始,command
|
|
207
|
+
policy 以 host 原生格式拒絕含字面憑證的 prompt(Claude `decision: "block"`、Codex
|
|
208
|
+
exit code 2)。其 runtime 失敗時放行 prompt。Hook 只看得到指令開始,因此
|
|
209
|
+
`agent-ops batch` 在每個 verify 或 review 開始時回報給其 task 綁定的 session
|
|
210
|
+
(顯示最新一步),不在 run 內的 `task advance` 則把驗證、審查、整合回報給自己
|
|
211
|
+
的 session。這些回報只更新已知 session,絕不讓指令失敗,Office 關閉時不記錄。沒有 task 的
|
|
193
212
|
房間白板顯示「無任務」,而非待驗證與待審查。Session metadata
|
|
194
213
|
只記錄有界限的識別資訊、已知工作狀態與活動時間,不包含 native transcript、
|
|
195
214
|
prompt、tool 或檔案內容。啟動與活動觀察失敗均為 advisory。
|
package/package.json
CHANGED
|
@@ -247,9 +247,13 @@
|
|
|
247
247
|
"office": {
|
|
248
248
|
"type": "object",
|
|
249
249
|
"additionalProperties": false,
|
|
250
|
-
"required": [
|
|
250
|
+
"required": [
|
|
251
|
+
"enabled"
|
|
252
|
+
],
|
|
251
253
|
"properties": {
|
|
252
|
-
"enabled": {
|
|
254
|
+
"enabled": {
|
|
255
|
+
"type": "boolean"
|
|
256
|
+
}
|
|
253
257
|
}
|
|
254
258
|
},
|
|
255
259
|
"completionGate": {
|
|
@@ -275,6 +279,30 @@
|
|
|
275
279
|
"type": "boolean"
|
|
276
280
|
}
|
|
277
281
|
}
|
|
282
|
+
},
|
|
283
|
+
"debug": {
|
|
284
|
+
"type": "object",
|
|
285
|
+
"additionalProperties": false,
|
|
286
|
+
"required": [
|
|
287
|
+
"enabled",
|
|
288
|
+
"budgetMs",
|
|
289
|
+
"maxRounds"
|
|
290
|
+
],
|
|
291
|
+
"properties": {
|
|
292
|
+
"enabled": {
|
|
293
|
+
"type": "boolean"
|
|
294
|
+
},
|
|
295
|
+
"budgetMs": {
|
|
296
|
+
"type": "integer",
|
|
297
|
+
"minimum": 1,
|
|
298
|
+
"maximum": 86400000
|
|
299
|
+
},
|
|
300
|
+
"maxRounds": {
|
|
301
|
+
"type": "integer",
|
|
302
|
+
"minimum": 1,
|
|
303
|
+
"maximum": 100
|
|
304
|
+
}
|
|
305
|
+
}
|
|
278
306
|
}
|
|
279
307
|
}
|
|
280
308
|
},
|