@kylecheng3146/agent-ops 0.10.2 → 0.11.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.
Files changed (47) hide show
  1. package/dist/packages/cli/src/args.js +8 -43
  2. package/dist/packages/cli/src/bin.js +28 -29
  3. package/dist/packages/cli/src/cli.js +12 -14
  4. package/dist/packages/cli/src/codex-loop-process.js +0 -6
  5. package/dist/packages/cli/src/commands/review.js +5 -10
  6. package/dist/packages/cli/src/commands/run.js +3 -1
  7. package/dist/packages/cli/src/commands/task.js +6 -50
  8. package/dist/packages/cli/src/commands/verify.js +7 -3
  9. package/dist/packages/cli/src/default-base.js +26 -0
  10. package/dist/packages/cli/src/hook-process.js +19 -83
  11. package/dist/packages/cli/src/run-entry.js +66 -21
  12. package/dist/runtime/src/adapters/agy/config.js +2 -3
  13. package/dist/runtime/src/adapters/claude/config.js +2 -2
  14. package/dist/runtime/src/adapters/codex/config.js +2 -5
  15. package/dist/runtime/src/hooks/completion-gate.js +0 -15
  16. package/dist/runtime/src/install/harness.js +3 -3
  17. package/dist/runtime/src/install/plan.js +3 -3
  18. package/dist/runtime/src/install/profiles.js +0 -2
  19. package/dist/runtime/src/office/snapshot.js +4 -1
  20. package/dist/runtime/src/parallel/manage.js +0 -3
  21. package/dist/runtime/src/review/execute.js +68 -4
  22. package/dist/runtime/src/run/verification.js +2 -2
  23. package/dist/runtime/src/schema/validate.js +10 -5
  24. package/dist/runtime/src/task/completion.js +4 -42
  25. package/dist/runtime/src/task/contract.js +11 -0
  26. package/dist/runtime/src/task/service.js +34 -57
  27. package/dist/runtime/src/task/store.js +3 -7
  28. package/dist/runtime/src/verify/acceptance-coverage.js +2 -3
  29. package/dist/runtime/src/verify/acceptance-protocol.js +4 -3
  30. package/dist/runtime/src/verify/acceptance-replay.js +4 -7
  31. package/dist/runtime/src/verify/service.js +6 -23
  32. package/dist/runtime/src/verify/source-fingerprint.js +17 -4
  33. package/docs/en/guides/task-debug.md +39 -133
  34. package/docs/en/spec/acceptance-and-evidence.md +31 -23
  35. package/docs/en/spec/harness-adapters.md +9 -11
  36. package/docs/en/spec/loop-engineering.md +11 -5
  37. package/docs/zh-TW/guides/task-debug.md +27 -73
  38. package/docs/zh-TW/spec/acceptance-and-evidence.md +18 -18
  39. package/docs/zh-TW/spec/harness-adapters.md +7 -8
  40. package/docs/zh-TW/spec/loop-engineering.md +9 -4
  41. package/package.json +1 -1
  42. package/schemas/config.schema.json +1 -21
  43. package/dist/packages/cli/src/debug-intake.js +0 -245
  44. package/dist/runtime/src/task/debug-intake.js +0 -15
  45. package/dist/runtime/src/task/debug-state.js +0 -135
  46. package/dist/runtime/src/task/debug-user-event.js +0 -160
  47. package/dist/runtime/src/task/debug.js +0 -632
@@ -1,160 +0,0 @@
1
- import { constants } from "node:fs";
2
- import { lstat, open, readdir, realpath } from "node:fs/promises";
3
- import { homedir } from "node:os";
4
- import { isAbsolute, join, relative, sep } from "node:path";
5
- import { sha256 } from "../fs/hash.js";
6
- import { AgentOpsError } from "../fs/paths.js";
7
- export function debugUserControl(text) {
8
- const value = text.trim();
9
- if (/^(?:不要給預算時間(?:\s+直接完整驗證與review)?|取消時間限制|no time limit)[。.!!]?$/iu.test(value))
10
- return "unlimited";
11
- if (/^(?:暫停|暂停|先暫停|先暂停|停止|stop|pause)[。.!!]?$/iu.test(value))
12
- return "pause";
13
- if (/^(?:先)?只診斷(?:,等我補失敗時證據)?[。.!!]?$/u.test(value) || /^(?:先)?只诊断[。.!!]?$/u.test(value) || /^diagnos(?:e|is)[ -]only[.!]?$/iu.test(value))
14
- return "diagnose";
15
- if (/^(?:繼續|继续|continue|resume)[。.!!]?$/iu.test(value))
16
- return "resume";
17
- if (/^(?:繼續修復|继续修复|開始修復|开始修复|resume repair)[。.!!]?$/iu.test(value))
18
- return "repair";
19
- return null;
20
- }
21
- function codexUserText(row) {
22
- if (row?.type === "event_msg" && row.payload?.type === "user_message")
23
- return row.payload.message;
24
- const message = row?.payload;
25
- if (row?.type !== "response_item" || message?.type !== "message" || message.role !== "user")
26
- return undefined;
27
- const kinds = message.internal_chat_message_metadata_passthrough?.content_item_kinds;
28
- if (!Array.isArray(kinds) || kinds.length === 0 || !kinds.every(k => k === "user.text") || !Array.isArray(message.content) ||
29
- message.content.length !== kinds.length || !message.content.every((c) => c?.type === "input_text" && typeof c.text === "string"))
30
- return undefined;
31
- const text = message.content.map((c) => c.text).join("\n");
32
- const reply = /^<send_user_message_question_reply>\s*([\s\S]*)\s*<\/send_user_message_question_reply>$/u.exec(text.trim());
33
- if (reply === null)
34
- return text;
35
- try {
36
- const answers = JSON.parse(reply[1]);
37
- if (Array.isArray(answers) && answers.length === 1 && typeof answers[0]?.answer === "string")
38
- return answers[0].answer;
39
- }
40
- catch { /* An ambiguous reply cannot authorize a control change. */ }
41
- return undefined;
42
- }
43
- /** Bound each row, not the transcript: assistant/tool output must not hide later human input. */
44
- async function* nativeLines(path) {
45
- const file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
46
- try {
47
- if (!(await file.stat()).isFile())
48
- return;
49
- let pending = Buffer.alloc(0), oversized = false, index = 0;
50
- const started = Date.now();
51
- for await (const chunk of file.createReadStream({ autoClose: false })) {
52
- if (Date.now() - started > 10_000)
53
- throw new Error("Native transcript read timed out");
54
- const bytes = chunk;
55
- let start = 0;
56
- while (start < bytes.length) {
57
- const end = bytes.indexOf(10, start), stop = end < 0 ? bytes.length : end;
58
- if (!oversized) {
59
- if (pending.length + stop - start > 1024 * 1024) {
60
- pending = Buffer.alloc(0);
61
- oversized = true;
62
- }
63
- else
64
- pending = Buffer.concat([pending, bytes.subarray(start, stop)]);
65
- }
66
- if (end < 0)
67
- break;
68
- if (!oversized)
69
- yield { line: pending.toString("utf8"), index };
70
- index++;
71
- pending = Buffer.alloc(0);
72
- oversized = false;
73
- start = end + 1;
74
- }
75
- }
76
- if (!oversized && pending.length > 0)
77
- yield { line: pending.toString("utf8"), index };
78
- }
79
- finally {
80
- await file.close();
81
- }
82
- }
83
- async function ownedNativeSession(root, worktreeRoot, sessionId) {
84
- if (process.env.CODEX_THREAD_ID !== sessionId || worktreeRoot === undefined)
85
- return false;
86
- const { readWorktreeRecord } = await import("../parallel/service.js");
87
- const record = await readWorktreeRecord(worktreeRoot);
88
- if (record === null || record.sessionId !== sessionId || record.agentId !== undefined || record.runId !== undefined)
89
- return false;
90
- const [main, worktree, recordMain, recordPath] = await Promise.all([root, worktreeRoot, record.mainRoot, record.path].map(path => realpath(path)));
91
- const inside = relative(join(main, ".worktrees"), worktree);
92
- return main === recordMain && worktree === recordPath && inside.length > 0 && !inside.startsWith("..") && !isAbsolute(inside);
93
- }
94
- /** Host transcripts are the user-event authority; CLI actor/text fields are never accepted. */
95
- /** `worktreeRoot` is the session's owned worktree: Claude stamps each row with the shell's cwd, which moves there after `cd`. */
96
- export async function readDebugUserEvent(root, sessionId, home = homedir(), kind = "latest", worktreeRoot) {
97
- if (!/^[a-f0-9-]{36}$/u.test(sessionId))
98
- throw new AgentOpsError("TASK_DEBUG_GRANT_UNSUPPORTED", "This host has no identifiable native user-event source.");
99
- const canonicalHome = await realpath(home).catch(() => home);
100
- const paths = [join(home, ".claude/projects", root.replace(/[^a-zA-Z0-9]/gu, "-"), sessionId + ".jsonl")];
101
- const codex = join(home, ".codex/sessions");
102
- async function visit(path, depth) {
103
- if (paths.length > 4096)
104
- return;
105
- try {
106
- for (const entry of await readdir(path, { withFileTypes: true })) {
107
- if (entry.isDirectory() && depth > 0)
108
- await visit(join(path, entry.name), depth - 1);
109
- else if (entry.isFile() && entry.name.endsWith(sessionId + ".jsonl"))
110
- paths.push(join(path, entry.name));
111
- }
112
- }
113
- catch { /* The host need not be installed. */ }
114
- }
115
- await visit(codex, 3);
116
- const owned = await ownedNativeSession(root, worktreeRoot, sessionId).catch(() => false);
117
- for (const path of paths) {
118
- try {
119
- const stat = await lstat(path), canonical = await realpath(path);
120
- const fromHome = relative(canonicalHome, canonical);
121
- const codexSource = path.startsWith(codex + sep);
122
- if (!stat.isFile() || stat.isSymbolicLink() || (!codexSource && stat.size > 16 * 1024 * 1024) || fromHome.startsWith("..") || isAbsolute(fromHome))
123
- continue;
124
- let matchedSession = false, event;
125
- for await (const { line, index } of nativeLines(path)) {
126
- let row;
127
- try {
128
- row = JSON.parse(line);
129
- }
130
- catch {
131
- continue;
132
- }
133
- if (codexSource && row?.type === "session_meta") {
134
- if (row.payload?.id !== sessionId || (row.payload?.cwd !== root && !owned)) {
135
- event = undefined;
136
- matchedSession = false;
137
- break;
138
- }
139
- matchedSession = true;
140
- }
141
- const text = codexSource ? codexUserText(row) :
142
- row?.type === "user" && row.sessionId === sessionId && (row.cwd === root || (worktreeRoot !== undefined && row.cwd === worktreeRoot)) && row.message?.role === "user" &&
143
- row.isMeta !== true && (row.origin === undefined || row.origin?.kind === "human") ? row.message.content : undefined;
144
- if (typeof text !== "string" || text.length > 64 * 1024 || (kind === "control" && debugUserControl(text) === null))
145
- continue;
146
- const timestamp = Date.parse(row.timestamp);
147
- if (!Number.isFinite(timestamp)) {
148
- event = undefined;
149
- continue;
150
- }
151
- const sourceDigest = sha256(line);
152
- event = { id: sha256(relative(home, path) + ":" + index + ":" + sourceDigest), sourceDigest, text, timestamp };
153
- }
154
- if (event !== undefined && (!codexSource || matchedSession))
155
- return event;
156
- }
157
- catch { /* An unavailable native source cannot grant time. */ }
158
- }
159
- throw new AgentOpsError("TASK_DEBUG_GRANT_UNSUPPORTED", "No current native human event can authorize this extension. Request explicit host approval; this CLI cannot self-grant it.");
160
- }