@kylecheng3146/agent-ops 0.8.0 → 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/dist/packages/cli/src/args.js +56 -1
- package/dist/packages/cli/src/bin.js +29 -2
- package/dist/packages/cli/src/cli.js +9 -3
- package/dist/packages/cli/src/codex-loop-process.js +6 -0
- 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/debug-intake.js +245 -0
- package/dist/packages/cli/src/hook-process.js +110 -37
- package/dist/runtime/src/adapters/agy/config.js +3 -2
- package/dist/runtime/src/adapters/claude/config.js +4 -3
- package/dist/runtime/src/adapters/codex/config.js +7 -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/office/art.js +2 -1
- package/dist/runtime/src/office/layout.js +6 -3
- package/dist/runtime/src/office/page.js +133 -9
- package/dist/runtime/src/office/scene.js +13 -4
- package/dist/runtime/src/office/snapshot.js +12 -0
- package/dist/runtime/src/parallel/manage.js +19 -2
- package/dist/runtime/src/review/attestation.js +4 -1
- package/dist/runtime/src/review/execute.js +1 -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/task-debug.md +111 -0
- package/docs/en/spec/harness-adapters.md +15 -2
- package/docs/zh-TW/guides/task-debug.md +66 -0
- package/docs/zh-TW/spec/harness-adapters.md +11 -2
- 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
|
@@ -9,7 +9,7 @@ export function criterionContractHash(criterion) {
|
|
|
9
9
|
export function taskContractHash(task) {
|
|
10
10
|
return sha256(canonicalJson({ goalHash: goalHash(task), title: task.title,
|
|
11
11
|
intent: task.intent ?? null, revision: task.contractRevision ?? 0,
|
|
12
|
-
criteria: task.criteria.map(criterionContractHash) }));
|
|
12
|
+
criteria: task.criteria.map(criterionContractHash), ...(task.debug === undefined ? {} : { debug: task.debug }) }));
|
|
13
13
|
}
|
|
14
14
|
export function treeContractHash(tasks) {
|
|
15
15
|
return sha256(canonicalJson(tasks.map(task => ({ id: task.id, hash: taskContractHash(task) }))
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Conservative intake only; the existing native Agent reasons about the issue. */
|
|
2
|
+
export function isDebugIssue(prompt) {
|
|
3
|
+
if (prompt.length === 0 || prompt.length > 64 * 1024 || prompt.includes("\0"))
|
|
4
|
+
return false;
|
|
5
|
+
const value = prompt.normalize("NFKC").toLowerCase();
|
|
6
|
+
if (/\b(?:explain|describe|what is|how does|refactor|feature|implement|design)\b|說明|解释|解釋|重構|新增功能|設計|實作功能/u.test(value))
|
|
7
|
+
return false;
|
|
8
|
+
return /\b(?:bug|error|fail(?:s|ed|ure)?|exception|crash|regression|500)\b|錯誤|错误|失敗|失败|異常|异常|問題|问题|回傳\s*500/u.test(value) &&
|
|
9
|
+
/\b(?:fix|debug|investigate|reproduce|diagnose|resolve|repair)\b|修復|修复|調查|调查|重現|重现|診斷|诊断|查明|找出原因|解決|解决/u.test(value);
|
|
10
|
+
}
|
|
11
|
+
/** One issue or cause in different spacing or case is still the same one. */
|
|
12
|
+
export const sameDebugText = (left, right) => left.normalize("NFKC").trim().replace(/\s+/gu, " ").toLowerCase() === right.normalize("NFKC").trim().replace(/\s+/gu, " ").toLowerCase();
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { canonicalJson } from "../config/hash.js";
|
|
2
|
+
import { sha256 } from "../fs/hash.js";
|
|
3
|
+
import { AgentOpsError } from "../fs/paths.js";
|
|
4
|
+
export const DEBUG_DEFAULTS = { enabled: true, budgetMs: 60 * 60_000, maxRounds: 5 };
|
|
5
|
+
export const DEBUG_EXTENSION_MS = 20 * 60_000;
|
|
6
|
+
const object = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
7
|
+
const text = (v) => typeof v === "string" && v.length > 0 && v.length <= 4096 && !v.includes("\0");
|
|
8
|
+
const hash = (v) => typeof v === "string" && /^[a-f0-9]{64}$/u.test(v);
|
|
9
|
+
const integer = (v) => Number.isSafeInteger(v) && v >= 0;
|
|
10
|
+
const strings = (v) => Array.isArray(v) && v.length <= 128 && v.every(text) && new Set(v).size === v.length;
|
|
11
|
+
const exact = (v, names) => object(v) && Object.keys(v).length === names.length && names.every(k => Object.hasOwn(v, k));
|
|
12
|
+
const statuses = ["proposed", "investigating", "confirmed", "rejected", "inconclusive"];
|
|
13
|
+
export const debugCauses = ["budget", "iterations", "strategy-required", "stagnation", "environment", "permission", "user-pause", "diagnosed"];
|
|
14
|
+
export function validDebugSettings(v) {
|
|
15
|
+
return exact(v, ["enabled", "budgetMs", "maxRounds"]) && typeof v.enabled === "boolean" &&
|
|
16
|
+
integer(v.budgetMs) && v.budgetMs > 0 && v.budgetMs <= 86_400_000 &&
|
|
17
|
+
integer(v.maxRounds) && v.maxRounds > 0 && v.maxRounds <= 100;
|
|
18
|
+
}
|
|
19
|
+
export function validDebugIntake(v) {
|
|
20
|
+
return exact(v, ["rootTaskId", "facts", "unknowns"]) && text(v.rootTaskId) && strings(v.facts) && strings(v.unknowns);
|
|
21
|
+
}
|
|
22
|
+
export function validDebugState(v) {
|
|
23
|
+
const baseKeys = ["schemaVersion", "rootTaskId", "ownerRoot", "baselineCommit", "budgetMs", "maxRounds", "consumedMs", "regressionChecks", "hypotheses", "receipts", "strategy", "grants", "operation", "checkpoint"];
|
|
24
|
+
if (!object(v) || Object.keys(v).some(k => !baseKeys.includes(k) && k !== "foreground" && k !== "control") || !baseKeys.every(k => Object.hasOwn(v, k)) ||
|
|
25
|
+
v.schemaVersion !== 1 || !text(v.rootTaskId) || !text(v.ownerRoot) || typeof v.baselineCommit !== "string" || !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/u.test(v.baselineCommit) ||
|
|
26
|
+
!validDebugSettings({ enabled: true, budgetMs: v.budgetMs, maxRounds: v.maxRounds }) || !integer(v.consumedMs) || !strings(v.regressionChecks) ||
|
|
27
|
+
!Array.isArray(v.hypotheses) || v.hypotheses.length > 32 || !Array.isArray(v.receipts) || v.receipts.length > 256 ||
|
|
28
|
+
!Array.isArray(v.grants) || v.grants.length > 128)
|
|
29
|
+
return false;
|
|
30
|
+
if (Object.hasOwn(v, "control") && (!exact(v.control, ["mode", "paused", "sessionId", "eventId", "sourceDigest", "timestamp"]) ||
|
|
31
|
+
!["full", "diagnose"].includes(String(v.control.mode)) || typeof v.control.paused !== "boolean" || !text(v.control.sessionId) ||
|
|
32
|
+
v.control.sessionId.length > 256 || !hash(v.control.eventId) || !hash(v.control.sourceDigest) || !integer(v.control.timestamp)))
|
|
33
|
+
return false;
|
|
34
|
+
for (const h of v.hypotheses) {
|
|
35
|
+
if (!exact(h, ["id", "statement", "method", "expected", "checkId", "expectedStatus", "status", "supporting", "opposing", "actual", "history"]) ||
|
|
36
|
+
![h.id, h.statement, h.method, h.expected, h.checkId].every(text) || !["PASS", "FAIL"].includes(String(h.expectedStatus)) || !statuses.includes(String(h.status)) ||
|
|
37
|
+
!strings(h.supporting) || !strings(h.opposing) || typeof h.actual !== "string" || h.actual.length > 4096 ||
|
|
38
|
+
!Array.isArray(h.history) || h.history.length > 128 || h.history.some(entry => !exact(entry, ["status", "actual", "references", "reason"]) ||
|
|
39
|
+
!statuses.includes(String(entry.status)) || typeof entry.actual !== "string" || entry.actual.length > 4096 || !strings(entry.references) || !text(entry.reason)))
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
if (new Set(v.hypotheses.map(h => h.id)).size !== v.hypotheses.length)
|
|
43
|
+
return false;
|
|
44
|
+
for (const r of v.receipts)
|
|
45
|
+
if (!exact(r, ["id", "taskId", "kind", "contractHash", "configHash", "sourceFingerprint", "productDigest", "durationMs", "finishedAt", "round", "progress", "references", "outcome", "cause"]) ||
|
|
46
|
+
!text(r.id) || !text(r.taskId) || !["investigate", "verify", "review"].includes(String(r.kind)) ||
|
|
47
|
+
![r.contractHash, r.configHash, r.sourceFingerprint, r.productDigest].every(hash) || !integer(r.durationMs) || !integer(r.finishedAt) || typeof r.round !== "boolean" ||
|
|
48
|
+
!strings(r.progress) || !strings(r.references) || !["PASS", "FAIL", "UNKNOWN"].includes(String(r.outcome)) || ![null, "environment", "permission"].includes(r.cause))
|
|
49
|
+
return false;
|
|
50
|
+
if (new Set(v.receipts.map(r => r.id)).size !== v.receipts.length || v.receipts.filter(r => r.round).length > v.maxRounds)
|
|
51
|
+
return false;
|
|
52
|
+
for (const g of v.grants)
|
|
53
|
+
if (!exact(g, ["eventId", "sessionId", "sourceDigest", "grantedMs"]) || !text(g.eventId) || !text(g.sessionId) || !hash(g.sourceDigest) || g.grantedMs !== DEBUG_EXTENSION_MS)
|
|
54
|
+
return false;
|
|
55
|
+
if (new Set(v.grants.map(g => g.eventId)).size !== v.grants.length)
|
|
56
|
+
return false;
|
|
57
|
+
if (v.strategy !== null && (!exact(v.strategy, ["from", "to", "reason", "references", "afterRound"]) ||
|
|
58
|
+
![v.strategy.from, v.strategy.to, v.strategy.reason].every(text) || v.strategy.from === v.strategy.to ||
|
|
59
|
+
!strings(v.strategy.references) || !integer(v.strategy.afterRound) || v.strategy.afterRound < 2))
|
|
60
|
+
return false;
|
|
61
|
+
if (v.foreground !== undefined && v.foreground !== null && (!exact(v.foreground, ["sessionId", "startedAt"]) || !text(v.foreground.sessionId) || v.foreground.sessionId.length > 256 || !integer(v.foreground.startedAt)))
|
|
62
|
+
return false;
|
|
63
|
+
if (v.operation !== null && (!((exact(v.operation, ["id", "startedAt", "taskId", "contractHash", "configHash", "sourceFingerprint", "productDigest", "kind"])) ||
|
|
64
|
+
exact(v.operation, ["id", "startedAt", "taskId", "contractHash", "configHash", "sourceFingerprint", "productDigest", "kind", "processId", "processIdentity"])) ||
|
|
65
|
+
!text(v.operation.id) || !integer(v.operation.startedAt) || !text(v.operation.taskId) ||
|
|
66
|
+
![v.operation.contractHash, v.operation.configHash, v.operation.sourceFingerprint, v.operation.productDigest].every(hash) ||
|
|
67
|
+
!["investigate", "verify", "review"].includes(String(v.operation.kind)) ||
|
|
68
|
+
(Object.hasOwn(v.operation, "processId") && (!integer(v.operation.processId) || v.operation.processId < 1)) ||
|
|
69
|
+
(Object.hasOwn(v.operation, "processIdentity") && (!text(v.operation.processIdentity) || v.operation.processIdentity.length > 256))))
|
|
70
|
+
return false;
|
|
71
|
+
if (v.checkpoint !== null && (!exact(v.checkpoint, ["taskId", "sessionId", "root", "sourceFingerprint", "contractHash", "configHash", "cause", "receiptId"]) ||
|
|
72
|
+
![v.checkpoint.taskId, v.checkpoint.sessionId, v.checkpoint.root, v.checkpoint.receiptId].every(text) ||
|
|
73
|
+
![v.checkpoint.sourceFingerprint, v.checkpoint.contractHash, v.checkpoint.configHash].every(hash) || (v.checkpoint.cause !== null && !debugCauses.includes(v.checkpoint.cause))))
|
|
74
|
+
return false;
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
export function createDebugState(rootTaskId, baselineCommit, settings = DEBUG_DEFAULTS, ownerRoot = ".") {
|
|
78
|
+
return { schemaVersion: 1, rootTaskId, ownerRoot, baselineCommit, budgetMs: settings.budgetMs, maxRounds: settings.maxRounds, consumedMs: 0, regressionChecks: [],
|
|
79
|
+
hypotheses: [], receipts: [], strategy: null, grants: [], foreground: null, operation: null, checkpoint: null };
|
|
80
|
+
}
|
|
81
|
+
export function debugRounds(state) { return state.receipts.filter(r => r.round).length; }
|
|
82
|
+
export function debugRemainingMs(state, now = Date.now()) {
|
|
83
|
+
const starts = [state.foreground?.startedAt, state.operation?.startedAt].filter((startedAt) => startedAt !== undefined);
|
|
84
|
+
const active = starts.length === 0 ? 0 : Math.max(0, now - Math.min(...starts));
|
|
85
|
+
return Math.max(0, state.budgetMs + state.grants.reduce((n, g) => n + g.grantedMs, 0) - state.consumedMs - active);
|
|
86
|
+
}
|
|
87
|
+
export function debugStopCause(state, now = Date.now()) {
|
|
88
|
+
if (state.control?.paused === true)
|
|
89
|
+
return "user-pause";
|
|
90
|
+
const latest = state.receipts.at(-1);
|
|
91
|
+
if (latest?.cause !== null && latest?.cause !== undefined)
|
|
92
|
+
return latest.cause;
|
|
93
|
+
if (debugRemainingMs(state, now) === 0)
|
|
94
|
+
return "budget";
|
|
95
|
+
if (debugRounds(state) >= state.maxRounds)
|
|
96
|
+
return "iterations";
|
|
97
|
+
const rounds = state.receipts.filter(r => r.round);
|
|
98
|
+
const since = rounds.slice(state.strategy?.afterRound ?? 0);
|
|
99
|
+
let stagnant = 0;
|
|
100
|
+
for (const round of [...since].reverse()) {
|
|
101
|
+
if (round.progress.length > 0)
|
|
102
|
+
break;
|
|
103
|
+
stagnant++;
|
|
104
|
+
}
|
|
105
|
+
return stagnant >= 2 ? state.strategy === null ? "strategy-required" : "stagnation" : null;
|
|
106
|
+
}
|
|
107
|
+
/** Only reviewed reasoning/proof belongs in the digest; operations and clocks do not. */
|
|
108
|
+
export function debugProofDigest(state) {
|
|
109
|
+
return sha256(canonicalJson({ rootTaskId: state.rootTaskId, baselineCommit: state.baselineCommit, regressionChecks: state.regressionChecks, hypotheses: state.hypotheses,
|
|
110
|
+
strategy: state.strategy, proof: state.receipts.filter(r => r.kind === "verify").map(r => ({ taskId: r.taskId, contractHash: r.contractHash,
|
|
111
|
+
configHash: r.configHash, sourceFingerprint: r.sourceFingerprint, productDigest: r.productDigest, references: r.references, outcome: r.outcome })) }));
|
|
112
|
+
}
|
|
113
|
+
export function assertDebugState(value) {
|
|
114
|
+
if (!validDebugState(value))
|
|
115
|
+
throw new AgentOpsError("TASK_DEBUG_STATE_INVALID", "Debug state must be strict, bounded and root-bound.");
|
|
116
|
+
}
|
|
117
|
+
export async function loadDebugProof(root, rootTaskId) {
|
|
118
|
+
const { FileTaskStore } = await import("./store.js");
|
|
119
|
+
const { join } = await import("node:path");
|
|
120
|
+
const record = (await new FileTaskStore(join(root, ".agent-ops/tasks/state.json"), root).read()).tasks.find(r => r.task.id === rootTaskId);
|
|
121
|
+
if (record?.debug === undefined || record.task.debug?.rootTaskId !== rootTaskId)
|
|
122
|
+
throw new AgentOpsError("TASK_DEBUG_ROOT_MISMATCH", "Original issue proof is unavailable; do not reset its limits.");
|
|
123
|
+
if (record.debug.ownerRoot !== root) {
|
|
124
|
+
const owned = (await new FileTaskStore(join(record.debug.ownerRoot, ".agent-ops/tasks/state.json"), record.debug.ownerRoot).read()).tasks.find(r => r.task.id === rootTaskId);
|
|
125
|
+
if (owned?.debug === undefined || owned.debug.ownerRoot !== record.debug.ownerRoot || owned.task.goal !== record.task.goal)
|
|
126
|
+
throw new AgentOpsError("TASK_DEBUG_ROOT_MISMATCH", "Original owned issue state is unavailable; do not recreate its budget.");
|
|
127
|
+
return owned.debug;
|
|
128
|
+
}
|
|
129
|
+
return record.debug;
|
|
130
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { lstat, readFile, readdir, realpath } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { isAbsolute, join, relative, sep } from "node:path";
|
|
4
|
+
import { sha256 } from "../fs/hash.js";
|
|
5
|
+
import { AgentOpsError } from "../fs/paths.js";
|
|
6
|
+
export function debugUserControl(text) {
|
|
7
|
+
const value = text.trim();
|
|
8
|
+
if (/^(?:暫停|暂停|先暫停|先暂停|停止|stop|pause)[。.!!]?$/iu.test(value))
|
|
9
|
+
return "pause";
|
|
10
|
+
if (/^(?:先)?只診斷(?:,等我補失敗時證據)?[。.!!]?$/u.test(value) || /^(?:先)?只诊断[。.!!]?$/u.test(value) || /^diagnos(?:e|is)[ -]only[.!]?$/iu.test(value))
|
|
11
|
+
return "diagnose";
|
|
12
|
+
if (/^(?:繼續|继续|continue|resume)[。.!!]?$/iu.test(value))
|
|
13
|
+
return "resume";
|
|
14
|
+
if (/^(?:繼續修復|继续修复|開始修復|开始修复|resume repair)[。.!!]?$/iu.test(value))
|
|
15
|
+
return "repair";
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
function codexUserText(row) {
|
|
19
|
+
if (row?.type === "event_msg" && row.payload?.type === "user_message")
|
|
20
|
+
return row.payload.message;
|
|
21
|
+
const message = row?.payload;
|
|
22
|
+
if (row?.type !== "response_item" || message?.type !== "message" || message.role !== "user")
|
|
23
|
+
return undefined;
|
|
24
|
+
const kinds = message.internal_chat_message_metadata_passthrough?.content_item_kinds;
|
|
25
|
+
if (!Array.isArray(kinds) || kinds.length === 0 || !kinds.every(k => k === "user.text") || !Array.isArray(message.content) ||
|
|
26
|
+
message.content.length !== kinds.length || !message.content.every((c) => c?.type === "input_text" && typeof c.text === "string"))
|
|
27
|
+
return undefined;
|
|
28
|
+
const text = message.content.map((c) => c.text).join("\n");
|
|
29
|
+
const reply = /^<send_user_message_question_reply>\s*([\s\S]*)\s*<\/send_user_message_question_reply>$/u.exec(text.trim());
|
|
30
|
+
if (reply === null)
|
|
31
|
+
return text;
|
|
32
|
+
try {
|
|
33
|
+
const answers = JSON.parse(reply[1]);
|
|
34
|
+
if (Array.isArray(answers) && answers.length === 1 && typeof answers[0]?.answer === "string")
|
|
35
|
+
return answers[0].answer;
|
|
36
|
+
}
|
|
37
|
+
catch { /* An ambiguous reply cannot authorize a control change. */ }
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
/** Host transcripts are the user-event authority; CLI actor/text fields are never accepted. */
|
|
41
|
+
export async function readDebugUserEvent(root, sessionId, home = homedir(), kind = "latest") {
|
|
42
|
+
if (!/^[a-f0-9-]{36}$/u.test(sessionId))
|
|
43
|
+
throw new AgentOpsError("TASK_DEBUG_GRANT_UNSUPPORTED", "This host has no identifiable native user-event source.");
|
|
44
|
+
const canonicalHome = await realpath(home).catch(() => home);
|
|
45
|
+
const paths = [join(home, ".claude/projects", root.replace(/[^a-zA-Z0-9]/gu, "-"), sessionId + ".jsonl")];
|
|
46
|
+
const codex = join(home, ".codex/sessions");
|
|
47
|
+
async function visit(path, depth) {
|
|
48
|
+
if (paths.length > 4096)
|
|
49
|
+
return;
|
|
50
|
+
try {
|
|
51
|
+
for (const entry of await readdir(path, { withFileTypes: true })) {
|
|
52
|
+
if (entry.isDirectory() && depth > 0)
|
|
53
|
+
await visit(join(path, entry.name), depth - 1);
|
|
54
|
+
else if (entry.isFile() && entry.name.endsWith(sessionId + ".jsonl"))
|
|
55
|
+
paths.push(join(path, entry.name));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch { /* The host need not be installed. */ }
|
|
59
|
+
}
|
|
60
|
+
await visit(codex, 3);
|
|
61
|
+
for (const path of paths) {
|
|
62
|
+
try {
|
|
63
|
+
const stat = await lstat(path), canonical = await realpath(path);
|
|
64
|
+
const fromHome = relative(canonicalHome, canonical);
|
|
65
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 16 * 1024 * 1024 || fromHome.startsWith("..") || isAbsolute(fromHome))
|
|
66
|
+
continue;
|
|
67
|
+
const lines = (await readFile(path, "utf8")).split("\n");
|
|
68
|
+
const rows = lines.map(line => { try {
|
|
69
|
+
return JSON.parse(line);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return null;
|
|
73
|
+
} });
|
|
74
|
+
const codexSource = path.startsWith(codex + sep);
|
|
75
|
+
if (codexSource && !rows.some(row => row?.type === "session_meta" && row.payload?.id === sessionId && row.payload?.cwd === root))
|
|
76
|
+
continue;
|
|
77
|
+
for (let index = rows.length - 1; index >= 0; index--) {
|
|
78
|
+
const row = rows[index];
|
|
79
|
+
const text = codexSource ? codexUserText(row) :
|
|
80
|
+
row?.type === "user" && row.sessionId === sessionId && row.cwd === root && row.message?.role === "user" ? row.message.content : undefined;
|
|
81
|
+
if (typeof text !== "string" || text.length > 64 * 1024 || (kind === "control" && debugUserControl(text) === null))
|
|
82
|
+
continue;
|
|
83
|
+
const timestamp = Date.parse(row.timestamp);
|
|
84
|
+
if (!Number.isFinite(timestamp))
|
|
85
|
+
break;
|
|
86
|
+
const sourceDigest = sha256(lines[index]);
|
|
87
|
+
return { id: sha256(relative(home, path) + ":" + index + ":" + sourceDigest), sourceDigest, text, timestamp };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch { /* An unavailable native source cannot grant time. */ }
|
|
91
|
+
}
|
|
92
|
+
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.");
|
|
93
|
+
}
|