ai-obs-cli 0.0.0-stage → 0.5.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 (155) hide show
  1. package/README.md +666 -2
  2. package/bin/ai-obs.js +8 -0
  3. package/dist/adapters/claude-code/analyze.js +189 -0
  4. package/dist/adapters/claude-code/analyze.js.map +1 -0
  5. package/dist/adapters/claude-code/detect.js +60 -0
  6. package/dist/adapters/claude-code/detect.js.map +1 -0
  7. package/dist/adapters/claude-code/handlers.js +183 -0
  8. package/dist/adapters/claude-code/handlers.js.map +1 -0
  9. package/dist/adapters/claude-code/index.js +55 -0
  10. package/dist/adapters/claude-code/index.js.map +1 -0
  11. package/dist/adapters/claude-code/run-links.js +53 -0
  12. package/dist/adapters/claude-code/run-links.js.map +1 -0
  13. package/dist/adapters/claude-code/session.js +427 -0
  14. package/dist/adapters/claude-code/session.js.map +1 -0
  15. package/dist/adapters/claude-code/settings.js +161 -0
  16. package/dist/adapters/claude-code/settings.js.map +1 -0
  17. package/dist/adapters/claude-code/state.js +66 -0
  18. package/dist/adapters/claude-code/state.js.map +1 -0
  19. package/dist/adapters/claude-code/subagents.js +246 -0
  20. package/dist/adapters/claude-code/subagents.js.map +1 -0
  21. package/dist/adapters/claude-code/transcript.js +309 -0
  22. package/dist/adapters/claude-code/transcript.js.map +1 -0
  23. package/dist/adapters/codex/accumulator.js +135 -0
  24. package/dist/adapters/codex/accumulator.js.map +1 -0
  25. package/dist/adapters/codex/detect.js +93 -0
  26. package/dist/adapters/codex/detect.js.map +1 -0
  27. package/dist/adapters/codex/hooks-config.js +158 -0
  28. package/dist/adapters/codex/hooks-config.js.map +1 -0
  29. package/dist/adapters/codex/index.js +270 -0
  30. package/dist/adapters/codex/index.js.map +1 -0
  31. package/dist/adapters/codex/locate.js +72 -0
  32. package/dist/adapters/codex/locate.js.map +1 -0
  33. package/dist/adapters/codex/records.js +194 -0
  34. package/dist/adapters/codex/records.js.map +1 -0
  35. package/dist/adapters/codex/rollout.js +72 -0
  36. package/dist/adapters/codex/rollout.js.map +1 -0
  37. package/dist/adapters/codex/session.js +212 -0
  38. package/dist/adapters/codex/session.js.map +1 -0
  39. package/dist/adapters/codex/subagents.js +139 -0
  40. package/dist/adapters/codex/subagents.js.map +1 -0
  41. package/dist/adapters/codex/summarize.js +84 -0
  42. package/dist/adapters/codex/summarize.js.map +1 -0
  43. package/dist/adapters/codex/tools.js +145 -0
  44. package/dist/adapters/codex/tools.js.map +1 -0
  45. package/dist/adapters/hook-command.js +94 -0
  46. package/dist/adapters/hook-command.js.map +1 -0
  47. package/dist/adapters/index.js +12 -0
  48. package/dist/adapters/index.js.map +1 -0
  49. package/dist/adapters/types.js +2 -0
  50. package/dist/adapters/types.js.map +1 -0
  51. package/dist/analysis/corrections.js +79 -0
  52. package/dist/analysis/corrections.js.map +1 -0
  53. package/dist/analysis/digest.js +16 -0
  54. package/dist/analysis/digest.js.map +1 -0
  55. package/dist/analysis/outcome.js +76 -0
  56. package/dist/analysis/outcome.js.map +1 -0
  57. package/dist/analysis/phases.js +203 -0
  58. package/dist/analysis/phases.js.map +1 -0
  59. package/dist/analysis/summaries.js +350 -0
  60. package/dist/analysis/summaries.js.map +1 -0
  61. package/dist/analysis/tokens.js +67 -0
  62. package/dist/analysis/tokens.js.map +1 -0
  63. package/dist/analysis/usage.js +229 -0
  64. package/dist/analysis/usage.js.map +1 -0
  65. package/dist/cc-cache.js +81 -0
  66. package/dist/cc-cache.js.map +1 -0
  67. package/dist/cli.js +128 -0
  68. package/dist/cli.js.map +1 -0
  69. package/dist/commands/common.js +235 -0
  70. package/dist/commands/common.js.map +1 -0
  71. package/dist/commands/doctor.js +193 -0
  72. package/dist/commands/doctor.js.map +1 -0
  73. package/dist/commands/flush.js +131 -0
  74. package/dist/commands/flush.js.map +1 -0
  75. package/dist/commands/init.js +119 -0
  76. package/dist/commands/init.js.map +1 -0
  77. package/dist/commands/link.js +171 -0
  78. package/dist/commands/link.js.map +1 -0
  79. package/dist/commands/live.js +130 -0
  80. package/dist/commands/live.js.map +1 -0
  81. package/dist/commands/login.js +397 -0
  82. package/dist/commands/login.js.map +1 -0
  83. package/dist/commands/status.js +155 -0
  84. package/dist/commands/status.js.map +1 -0
  85. package/dist/commands/token.js +92 -0
  86. package/dist/commands/token.js.map +1 -0
  87. package/dist/commands/uninstall.js +56 -0
  88. package/dist/commands/uninstall.js.map +1 -0
  89. package/dist/config.js +305 -0
  90. package/dist/config.js.map +1 -0
  91. package/dist/context.js +109 -0
  92. package/dist/context.js.map +1 -0
  93. package/dist/delivery.js +151 -0
  94. package/dist/delivery.js.map +1 -0
  95. package/dist/dotfile.js +187 -0
  96. package/dist/dotfile.js.map +1 -0
  97. package/dist/events.js +259 -0
  98. package/dist/events.js.map +1 -0
  99. package/dist/fsutil.js +50 -0
  100. package/dist/fsutil.js.map +1 -0
  101. package/dist/git.js +139 -0
  102. package/dist/git.js.map +1 -0
  103. package/dist/hook.js +147 -0
  104. package/dist/hook.js.map +1 -0
  105. package/dist/http.js +35 -0
  106. package/dist/http.js.map +1 -0
  107. package/dist/jobs.js +53 -0
  108. package/dist/jobs.js.map +1 -0
  109. package/dist/live/claude-code.js +226 -0
  110. package/dist/live/claude-code.js.map +1 -0
  111. package/dist/live/codex.js +91 -0
  112. package/dist/live/codex.js.map +1 -0
  113. package/dist/live/monitor.js +229 -0
  114. package/dist/live/monitor.js.map +1 -0
  115. package/dist/live/render.js +92 -0
  116. package/dist/live/render.js.map +1 -0
  117. package/dist/lock.js +93 -0
  118. package/dist/lock.js.map +1 -0
  119. package/dist/log.js +36 -0
  120. package/dist/log.js.map +1 -0
  121. package/dist/migrate.js +109 -0
  122. package/dist/migrate.js.map +1 -0
  123. package/dist/nudge.js +231 -0
  124. package/dist/nudge.js.map +1 -0
  125. package/dist/paths.js +73 -0
  126. package/dist/paths.js.map +1 -0
  127. package/dist/pk-cache.js +110 -0
  128. package/dist/pk-cache.js.map +1 -0
  129. package/dist/prices.js +45 -0
  130. package/dist/prices.js.map +1 -0
  131. package/dist/project-key.js +144 -0
  132. package/dist/project-key.js.map +1 -0
  133. package/dist/protocol-types.js +30 -0
  134. package/dist/protocol-types.js.map +1 -0
  135. package/dist/queue.js +649 -0
  136. package/dist/queue.js.map +1 -0
  137. package/dist/routing.js +122 -0
  138. package/dist/routing.js.map +1 -0
  139. package/dist/runtime.js +54 -0
  140. package/dist/runtime.js.map +1 -0
  141. package/dist/sender.js +559 -0
  142. package/dist/sender.js.map +1 -0
  143. package/dist/state.js +59 -0
  144. package/dist/state.js.map +1 -0
  145. package/dist/ulid.js +73 -0
  146. package/dist/ulid.js.map +1 -0
  147. package/dist/version.js +26 -0
  148. package/dist/version.js.map +1 -0
  149. package/dist/warnings.js +71 -0
  150. package/dist/warnings.js.map +1 -0
  151. package/dist/worker-main.js +14 -0
  152. package/dist/worker-main.js.map +1 -0
  153. package/dist/worker.js +295 -0
  154. package/dist/worker.js.map +1 -0
  155. package/package.json +25 -4
@@ -0,0 +1,66 @@
1
+ const KEEP_TURNS = 3;
2
+ export const MAX_TURNS = 50;
3
+ const MAX_AGENT_IDS = 500;
4
+ const MAX_DIGESTS = 200;
5
+ export function newSessionState(sessionId, transcriptPath, cwd, now) {
6
+ return {
7
+ v: 1,
8
+ sessionId,
9
+ transcriptPath,
10
+ cwd,
11
+ pkCwd: cwd,
12
+ offset: 0,
13
+ startedSent: false,
14
+ ended: false,
15
+ lastSeen: now,
16
+ turns: [],
17
+ doneAgents: [],
18
+ emittedAgents: [],
19
+ digests: [],
20
+ };
21
+ }
22
+ export function loadSession(store, sessionId) {
23
+ const s = store.get(sessionId);
24
+ return s && s.v === 1 ? s : null;
25
+ }
26
+ /** Background sub-agents of a turn that have not been reported yet. */
27
+ export function openBackgroundAgents(t) {
28
+ return Object.values(t.agg.agentCalls).some((c) => !c.completed);
29
+ }
30
+ /** The turns `saveSession` keeps: the last few, unsettled ones and those with open sub-agents (capped). */
31
+ function keptTurns(turns) {
32
+ const last = turns.length - KEEP_TURNS;
33
+ return turns.filter((t, i) => i >= last || !t.final || openBackgroundAgents(t)).slice(-MAX_TURNS);
34
+ }
35
+ /**
36
+ * Turns the next `saveSession` would drop although they never got a final outcome (more than
37
+ * MAX_TURNS unsettled turns). Callers settle them first so no turn stays provisional forever.
38
+ */
39
+ export function evictedUnsettledTurns(s) {
40
+ const kept = new Set(keptTurns(s.turns));
41
+ return s.turns.filter((t) => !t.final && !kept.has(t));
42
+ }
43
+ /** Trims lists so the state file stays small, then writes it. */
44
+ export function saveSession(store, s) {
45
+ s.turns = keptTurns(s.turns);
46
+ // Edit fingerprints are only compared against the next human turn: keep them for the latest human
47
+ // turn, the task-notification turns after it, and anything still waiting for its follow-up.
48
+ let group = 0;
49
+ for (let i = s.turns.length - 1; i >= 0; i--) {
50
+ if (s.turns[i].human !== false) {
51
+ group = i;
52
+ break;
53
+ }
54
+ }
55
+ s.turns.forEach((t, i) => {
56
+ if (i < group && t.final)
57
+ t.agg.fp = { added: [], removed: [] };
58
+ });
59
+ s.doneAgents = s.doneAgents.slice(-MAX_AGENT_IDS);
60
+ s.emittedAgents = s.emittedAgents.slice(-MAX_AGENT_IDS);
61
+ if (s.failedAgents)
62
+ s.failedAgents = s.failedAgents.slice(-MAX_AGENT_IDS);
63
+ s.digests = s.digests.slice(-MAX_DIGESTS);
64
+ store.set(s.sessionId, s);
65
+ }
66
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/adapters/claude-code/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,246 @@
1
+ import { readdirSync, readFileSync } from 'node:fs';
2
+ import { basename, dirname, join } from 'node:path';
3
+ import { provisionalOutcome } from '../../analysis/outcome.js';
4
+ import { primaryModel } from '../../analysis/tokens.js';
5
+ import { runEvent } from '../../events.js';
6
+ import { readJsonFile } from '../../fsutil.js';
7
+ import { analyzeEntries, isWorkflowKey, workflowKey } from './analyze.js';
8
+ import { readWholeTranscript } from './transcript.js';
9
+ export function subagentsDir(transcriptPath) {
10
+ return join(dirname(transcriptPath), basename(transcriptPath, '.jsonl'), 'subagents');
11
+ }
12
+ const FILE_RE = /^agent-([A-Za-z0-9_-]+)\.jsonl$/;
13
+ const WF_DIR_RE = /^wf_[A-Za-z0-9_-]{1,64}$/;
14
+ const str = (v) => (typeof v === 'string' && v ? v : null);
15
+ function readDir(dir) {
16
+ try {
17
+ return readdirSync(dir);
18
+ }
19
+ catch {
20
+ return [];
21
+ }
22
+ }
23
+ function readMeta(dir, agentId) {
24
+ return readJsonFile(join(dir, `agent-${agentId}.meta.json`)) ?? {};
25
+ }
26
+ const emptyJournal = () => ({ finished: new Set(), failed: new Set(), phaseOrder: new Map() });
27
+ /**
28
+ * Agents the workflow journal reports as returned / failed, and the order in which phases started.
29
+ * Only `type`, `agentId` and `phase` are looked at.
30
+ */
31
+ function readJournal(dir) {
32
+ const out = emptyJournal();
33
+ const { finished, failed, phaseOrder } = out;
34
+ let text = '';
35
+ try {
36
+ text = readFileSync(join(dir, 'journal.jsonl'), 'utf8');
37
+ }
38
+ catch {
39
+ return out;
40
+ }
41
+ for (const line of text.split('\n')) {
42
+ if (!line.includes('"agentId"') || !(line.includes('"result"') || line.includes('"failed"') || line.includes('"started"')))
43
+ continue;
44
+ try {
45
+ const j = JSON.parse(line);
46
+ const id = str(j.agentId);
47
+ if (!id)
48
+ continue;
49
+ if (j.type === 'result')
50
+ finished.add(id);
51
+ else if (j.type === 'failed')
52
+ (finished.add(id), failed.add(id));
53
+ else if (j.type === 'started') {
54
+ const phase = str(j.phase);
55
+ if (phase && !phaseOrder.has(phase))
56
+ phaseOrder.set(phase, phaseOrder.size);
57
+ }
58
+ }
59
+ catch {
60
+ /* partial last line */
61
+ }
62
+ }
63
+ return out;
64
+ }
65
+ /** All sub-agents of a session, by agent id: direct ones and those of Workflow runs. */
66
+ export function listAgents(transcriptPath) {
67
+ const dir = subagentsDir(transcriptPath);
68
+ const out = new Map();
69
+ for (const n of readDir(dir)) {
70
+ const m = FILE_RE.exec(n);
71
+ if (!m?.[1])
72
+ continue;
73
+ const agentId = m[1];
74
+ const meta = readMeta(dir, agentId);
75
+ out.set(agentId, {
76
+ agentId,
77
+ agentType: str(meta.agentType),
78
+ toolUseId: str(meta.toolUseId),
79
+ parentAgentId: str(meta.parentAgentId),
80
+ spawnDepth: typeof meta.spawnDepth === 'number' ? meta.spawnDepth : null,
81
+ file: join(dir, n),
82
+ workflowId: null,
83
+ label: null,
84
+ workflowPhase: null,
85
+ stageIndex: null,
86
+ finished: false,
87
+ failed: false,
88
+ });
89
+ }
90
+ const wfRoot = join(dir, 'workflows');
91
+ for (const wf of readDir(wfRoot)) {
92
+ if (!WF_DIR_RE.test(wf))
93
+ continue;
94
+ const wfDir = join(wfRoot, wf);
95
+ const names = readDir(wfDir);
96
+ const journal = names.includes('journal.jsonl') ? readJournal(wfDir) : emptyJournal();
97
+ for (const n of names) {
98
+ const m = FILE_RE.exec(n);
99
+ if (!m?.[1] || out.has(m[1]))
100
+ continue;
101
+ const agentId = m[1];
102
+ const meta = readMeta(wfDir, agentId);
103
+ const phase = str(meta.workflowPhase);
104
+ out.set(agentId, {
105
+ agentId,
106
+ agentType: str(meta.agentType),
107
+ toolUseId: workflowKey(wf),
108
+ parentAgentId: str(meta.parentAgentId),
109
+ spawnDepth: typeof meta.spawnDepth === 'number' ? meta.spawnDepth : null,
110
+ file: join(wfDir, n),
111
+ workflowId: wf,
112
+ label: str(meta.description),
113
+ workflowPhase: phase,
114
+ stageIndex: phase ? (journal.phaseOrder.get(phase) ?? null) : null,
115
+ finished: journal.finished.has(agentId),
116
+ failed: journal.failed.has(agentId),
117
+ });
118
+ }
119
+ }
120
+ return out;
121
+ }
122
+ /**
123
+ * Whether the Workflow run is over (`<session>/workflows/<wf_id>.json` has a `status` other than
124
+ * `running`). Reads only that field; the file also holds the workflow script and results.
125
+ */
126
+ export function workflowOver(transcriptPath, workflowId) {
127
+ if (!WF_DIR_RE.test(workflowId))
128
+ return false;
129
+ const file = join(dirname(transcriptPath), basename(transcriptPath, '.jsonl'), 'workflows', `${workflowId}.json`);
130
+ const j = readJsonFile(file);
131
+ return typeof j?.status === 'string' && j.status !== 'running';
132
+ }
133
+ const slug = (s, max) => s
134
+ .toLowerCase()
135
+ .replace(/[^a-z0-9._-]+/g, '-')
136
+ .replace(/^-+|-+$/g, '')
137
+ .slice(0, max);
138
+ const FLOW_LABEL_RE = /^([^:]+):(.+)$/s;
139
+ /**
140
+ * The item a workflow agent works on, from the label convention `<stage>:<item>` (ADR-0020): a slug of
141
+ * `<item>` (≤ 64), or null when the label does not follow the convention or the slug is empty.
142
+ */
143
+ export function flowKeyOf(label) {
144
+ const m = label ? FLOW_LABEL_RE.exec(label) : null;
145
+ const key = m ? slug(m[2], 64) : '';
146
+ return key || null;
147
+ }
148
+ /** `workflow:<label>` (a slug of the agent's label), or the meta agent type, or `workflow-subagent`. */
149
+ export function workflowAgentType(a) {
150
+ const l = a.label ? slug(a.label, 48) : '';
151
+ return l ? `workflow:${l}` : (a.agentType ?? 'workflow-subagent');
152
+ }
153
+ /**
154
+ * The dashboard's read-time grouping rule for a stored `agent_type` (agentGroupOf in
155
+ * apps/web/src/lib/queries/agent-groups.ts, ADR-0019): `workflow:<label>` → `workflow:<verb>` (first token
156
+ * split on `[-:._/ ]`, lower-cased, trailing digits stripped; `workflow` without one), anything else → itself.
157
+ * Kept identical so rows with and without a stored group land in the same group (parity test in workflows.test.ts).
158
+ */
159
+ export function groupOfAgentType(agentType) {
160
+ const m = /^workflow:(.*)$/s.exec(agentType);
161
+ if (!m)
162
+ return agentType;
163
+ const verb = (m[1].split(/[-:._/\s]+/)[0] ?? '').toLowerCase().replace(/\d+$/, '');
164
+ return verb ? `workflow:${verb}` : 'workflow';
165
+ }
166
+ /**
167
+ * `agent_group` of a workflow agent: `workflow:<phase slug>` when the phase is known, else the group the
168
+ * dashboard derives from the stored agent type (`workflow:<verb of the label slug>`). The verb comes from the
169
+ * slug, not the raw label, because the server only ever sees the slug (`Fix(m8) brief` → `workflow:fix`).
170
+ */
171
+ export function workflowAgentGroup(a) {
172
+ const phase = a.workflowPhase ? slug(a.workflowPhase, 48) : '';
173
+ if (phase)
174
+ return `workflow:${phase}`;
175
+ return groupOfAgentType(workflowAgentType(a));
176
+ }
177
+ /** Agents spawned by these tool_use ids, plus all their descendants (nested sub-agents). */
178
+ export function agentsOfToolUses(agents, toolUseIds) {
179
+ const ids = new Set(toolUseIds);
180
+ const picked = new Map();
181
+ for (const a of agents.values())
182
+ if (a.toolUseId && ids.has(a.toolUseId) && !a.parentAgentId)
183
+ picked.set(a.agentId, a);
184
+ for (let grew = true; grew;) {
185
+ grew = false;
186
+ for (const a of agents.values()) {
187
+ if (!picked.has(a.agentId) && a.parentAgentId && picked.has(a.parentAgentId)) {
188
+ picked.set(a.agentId, a);
189
+ grew = true;
190
+ }
191
+ }
192
+ }
193
+ return [...picked.values()];
194
+ }
195
+ export function depthOf(a, agents) {
196
+ let depth = 1;
197
+ const seen = new Set([a.agentId]);
198
+ for (let p = a.parentAgentId; p && !seen.has(p); p = agents.get(p)?.parentAgentId ?? null) {
199
+ seen.add(p);
200
+ depth++;
201
+ }
202
+ if (isWorkflowKey(a.toolUseId) && !a.parentAgentId)
203
+ return 1; // spawned by the main agent's Workflow call
204
+ return a.parentAgentId ? depth : (a.spawnDepth ?? 1);
205
+ }
206
+ /** One `run` event for a sub-agent transcript (null when it has no model activity yet). */
207
+ export function buildRun(a, agents, input) {
208
+ const agg = analyzeEntries(readWholeTranscript(a.file));
209
+ if (!agg.active)
210
+ return null;
211
+ const primary = primaryModel(agg.usage);
212
+ const outcome = provisionalOutcome({ interrupted: agg.interrupted, lastCheck: agg.lastCheck });
213
+ const final = a.failed
214
+ ? { status: 'failed', signals: ['stop_failure'], provisional: false }
215
+ : outcome.status === 'unknown' ? { status: 'success', signals: ['no_failure_signal'], provisional: false } : { ...outcome, provisional: false };
216
+ const event = runEvent({
217
+ harness: input.harness,
218
+ ...(input.idSalt ? { idSalt: input.idSalt } : {}),
219
+ turnId: input.turnId,
220
+ runId: a.agentId,
221
+ parentRunId: a.parentAgentId,
222
+ agentType: a.workflowId ? workflowAgentType(a) : (a.agentType ?? input.agentTypeHint ?? 'unknown'),
223
+ ...(a.workflowId
224
+ ? { agentGroup: workflowAgentGroup(a), orchestrationId: a.workflowId, stageIndex: a.stageIndex, flowKey: flowKeyOf(a.label) }
225
+ : (() => {
226
+ const t = a.agentType ?? input.agentTypeHint ?? null;
227
+ return t && t !== 'unknown' ? { agentGroup: t } : {};
228
+ })()),
229
+ depth: depthOf(a, agents),
230
+ startedAt: agg.startedAt,
231
+ endedAt: agg.endedAt,
232
+ ...(primary ? { model: primary.model, effort: primary.effort } : {}),
233
+ usage: agg.usage,
234
+ phases: agg.phases,
235
+ tools: agg.tools,
236
+ skills: agg.skills,
237
+ mcp: agg.mcp,
238
+ mcpStats: agg.mcpStats,
239
+ outcome: final,
240
+ ...(a.workflowId
241
+ ? { ext: { workflow_id: a.workflowId, ...(a.workflowPhase ? { workflow_phase: slug(a.workflowPhase, 48) } : {}), ...(a.label ? { workflow_label: slug(a.label, 48) } : {}) } }
242
+ : {}),
243
+ });
244
+ return { event, usage: agg.usage };
245
+ }
246
+ //# sourceMappingURL=subagents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagents.js","sourceRoot":"","sources":["../../../src/adapters/claude-code/subagents.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,309 @@
1
+ import { closeSync, fstatSync, openSync, readSync } from 'node:fs';
2
+ import { editFingerprint, isRevertCommand } from '../../analysis/outcome.js';
3
+ import { classify } from '../../analysis/phases.js';
4
+ import { scrubPaths } from '../../analysis/summaries.js';
5
+ import { isDeniedResult } from '../../analysis/usage.js';
6
+ import { cleanSummaryText } from '../../events.js';
7
+ /**
8
+ * Claude Code transcript JSONL (see FORMAT.md) reduced to "slim" entries right after parsing: only
9
+ * timestamps, ids, usage, tool names, phase decisions, counts and hashes survive. Prompt and final
10
+ * message text is kept truncated for the LOCAL digest / correction check only; code, diffs and file
11
+ * contents are dropped here, so they never reach state, the queue or the server.
12
+ */
13
+ /** Max chars of prompt text kept (correction heuristics + digest; local only). */
14
+ const PROMPT_KEEP = 1000;
15
+ const MESSAGE_KEEP = 300;
16
+ const CHUNK = 4 * 1024 * 1024;
17
+ /**
18
+ * Reads complete lines from `offset` to EOF (a trailing partial line is left for the next read) and
19
+ * slims them. Entries of a different session (copied history) are skipped.
20
+ */
21
+ export function readTranscript(path, offset, sessionId) {
22
+ let fd;
23
+ try {
24
+ fd = openSync(path, 'r');
25
+ }
26
+ catch {
27
+ return { lines: [], titles: {}, end: offset, reset: false };
28
+ }
29
+ try {
30
+ const size = fstatSync(fd).size;
31
+ let reset = false;
32
+ let pos = offset;
33
+ if (pos > size) {
34
+ pos = 0;
35
+ reset = true;
36
+ }
37
+ const lines = [];
38
+ const titles = {};
39
+ let carry = Buffer.alloc(0);
40
+ let carryStart = pos;
41
+ const buf = Buffer.allocUnsafe(CHUNK);
42
+ while (pos < size) {
43
+ const n = readSync(fd, buf, 0, Math.min(CHUNK, size - pos), pos);
44
+ if (n <= 0)
45
+ break;
46
+ pos += n;
47
+ const data = carry.length ? Buffer.concat([carry, buf.subarray(0, n)]) : Buffer.from(buf.subarray(0, n));
48
+ let lineStart = 0;
49
+ for (let i = data.indexOf(10); i >= 0; i = data.indexOf(10, lineStart)) {
50
+ const text = data.toString('utf8', lineStart, i);
51
+ const raw = text.trim() ? parseLine(text) : null;
52
+ const title = raw ? titleOf(raw, sessionId) : null;
53
+ if (title)
54
+ titles[title.source] = title.title;
55
+ const entry = raw && !title ? slimEntry(raw, sessionId) : null;
56
+ if (entry)
57
+ lines.push({ start: carryStart + lineStart, entry });
58
+ lineStart = i + 1;
59
+ }
60
+ carry = data.subarray(lineStart);
61
+ carryStart += lineStart;
62
+ }
63
+ return { lines, titles, end: carryStart, reset };
64
+ }
65
+ finally {
66
+ closeSync(fd);
67
+ }
68
+ }
69
+ /** Reads a whole (sub-agent) transcript. */
70
+ export function readWholeTranscript(path) {
71
+ return readTranscript(path, 0).lines.map((l) => l.entry);
72
+ }
73
+ const isObj = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
74
+ const str = (v) => (typeof v === 'string' ? v : null);
75
+ const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : undefined);
76
+ function parseLine(text) {
77
+ let raw;
78
+ try {
79
+ raw = JSON.parse(text);
80
+ }
81
+ catch {
82
+ return null;
83
+ }
84
+ return isObj(raw) ? raw : null;
85
+ }
86
+ export function slimLine(text, sessionId) {
87
+ const raw = parseLine(text);
88
+ return raw ? slimEntry(raw, sessionId) : null;
89
+ }
90
+ /**
91
+ * A session title entry (ADR-0026): `{"type":"custom-title","customTitle":…}` written by `/rename`, or
92
+ * `{"type":"ai-title","aiTitle":…}` generated by Claude Code. Null for any other line, a blank title, or another
93
+ * session's entry. Like a summary, the title is conversation-derived text: path-looking words are reduced to their
94
+ * last segment (scrubPaths), whitespace is collapsed and it is cut to 140 chars here.
95
+ */
96
+ export function titleOf(o, sessionId) {
97
+ if (o.type !== 'custom-title' && o.type !== 'ai-title')
98
+ return null;
99
+ if (sessionId && typeof o.sessionId === 'string' && o.sessionId !== sessionId)
100
+ return null;
101
+ const source = o.type === 'custom-title' ? 'custom' : 'ai';
102
+ const title = cleanSummaryText(scrubPaths(str(source === 'custom' ? o.customTitle : o.aiTitle) ?? ''));
103
+ return title ? { source, title } : null;
104
+ }
105
+ export function slimEntry(o, sessionId) {
106
+ if (sessionId && typeof o.sessionId === 'string' && o.sessionId !== sessionId)
107
+ return null;
108
+ const at = Date.parse(str(o.timestamp) ?? '');
109
+ if (!Number.isFinite(at))
110
+ return null;
111
+ const message = isObj(o.message) ? o.message : null;
112
+ if (o.type === 'assistant' && message)
113
+ return slimAssistant(o, message, at);
114
+ if (o.type === 'user' && message)
115
+ return slimUser(o, message, at);
116
+ if (o.type === 'system' && (o.subtype === 'stop_hook_summary' || o.subtype === 'turn_duration'))
117
+ return { k: 'stop', at };
118
+ return null;
119
+ }
120
+ function slimAssistant(o, m, at) {
121
+ const model = str(m.model) ?? 'unknown';
122
+ const synthetic = model.startsWith('<') || o.isApiErrorMessage === true;
123
+ const u = isObj(m.usage) ? m.usage : null;
124
+ const details = u && isObj(u.output_tokens_details) ? u.output_tokens_details : null;
125
+ const usage = u && !synthetic
126
+ ? {
127
+ responseId: str(m.id) ?? str(o.requestId),
128
+ model,
129
+ effort: str(o.effort) ?? str(o.perTurnEffort),
130
+ input: num(u.input_tokens),
131
+ output: num(u.output_tokens),
132
+ cacheRead: num(u.cache_read_input_tokens),
133
+ cacheWrite: num(u.cache_creation_input_tokens),
134
+ reasoning: num(details?.thinking_tokens),
135
+ }
136
+ : null;
137
+ const blocks = [];
138
+ for (const c of Array.isArray(m.content) ? m.content : []) {
139
+ if (!isObj(c))
140
+ continue;
141
+ if (c.type === 'tool_use')
142
+ blocks.push(slimToolUse(c));
143
+ else if (c.type === 'text')
144
+ blocks.push({ t: 'output', text: str(c.text)?.slice(-MESSAGE_KEEP * 2) ?? null });
145
+ else
146
+ blocks.push({ t: 'output', text: null });
147
+ }
148
+ if (typeof m.content === 'string')
149
+ blocks.push({ t: 'output', text: m.content.slice(-MESSAGE_KEEP * 2) });
150
+ return { k: 'assistant', at, usage, synthetic, blocks };
151
+ }
152
+ const EDIT_TOOLS = new Set(['Edit', 'MultiEdit', 'Write', 'NotebookEdit']);
153
+ function slimToolUse(c) {
154
+ const name = str(c.name) ?? 'unknown';
155
+ const input = isObj(c.input) ? c.input : {};
156
+ const command = name === 'Bash' ? str(input.command) : null;
157
+ const filePath = EDIT_TOOLS.has(name) ? (str(input.file_path) ?? str(input.notebook_path)) : null;
158
+ const phase = classify(name, { ...(command ? { command } : {}), ...(filePath ? { filePaths: [filePath] } : {}) });
159
+ const isAgent = phase === 'delegating';
160
+ return {
161
+ t: 'tool_use',
162
+ id: str(c.id) ?? '',
163
+ name,
164
+ phase,
165
+ command: command && command.length > 4000 ? command.slice(0, 4000) : command,
166
+ filePath,
167
+ edit: filePath ? estimateEdit(name, input, filePath) : null,
168
+ agent: isAgent ? { type: str(input.subagent_type), background: input.run_in_background === true } : null,
169
+ skill: name === 'Skill' ? str(input.skill) : null,
170
+ };
171
+ }
172
+ /** Line counts derived from the tool input (fallback when the result has no structuredPatch). */
173
+ function estimateEdit(name, input, filePath) {
174
+ const pairs = [];
175
+ if (name === 'Write')
176
+ pairs.push(['', str(input.content) ?? '']);
177
+ else if (name === 'Edit')
178
+ pairs.push([str(input.old_string) ?? '', str(input.new_string) ?? '']);
179
+ else if (name === 'MultiEdit' && Array.isArray(input.edits)) {
180
+ for (const e of input.edits)
181
+ if (isObj(e))
182
+ pairs.push([str(e.old_string) ?? '', str(e.new_string) ?? '']);
183
+ }
184
+ else if (name === 'NotebookEdit')
185
+ pairs.push(['', str(input.new_source) ?? '']);
186
+ if (!pairs.length)
187
+ return null;
188
+ const added = [];
189
+ const removed = [];
190
+ for (const [a, b] of pairs) {
191
+ const d = lineDiff(splitLines(a), splitLines(b));
192
+ added.push(...d.added);
193
+ removed.push(...d.removed);
194
+ }
195
+ return { added: added.length, removed: removed.length, fp: editFingerprint([{ filePath, added, removed }]) };
196
+ }
197
+ const splitLines = (s) => (s ? s.replace(/\n$/, '').split('\n') : []);
198
+ /** Multiset line diff: lines only in `b` are added, lines only in `a` removed. */
199
+ export function lineDiff(a, b) {
200
+ const counts = new Map();
201
+ for (const l of a)
202
+ counts.set(l, (counts.get(l) ?? 0) + 1);
203
+ const added = [];
204
+ for (const l of b) {
205
+ const c = counts.get(l) ?? 0;
206
+ if (c > 0)
207
+ counts.set(l, c - 1);
208
+ else
209
+ added.push(l);
210
+ }
211
+ const removed = [];
212
+ for (const [l, c] of counts)
213
+ for (let i = 0; i < c; i++)
214
+ removed.push(l);
215
+ return { added, removed };
216
+ }
217
+ const INTERRUPT_RE = /^\[Request interrupted/;
218
+ const NOT_A_PROMPT_RE = /^<(local-command-(stdout|stderr|caveat)|bash-(stdout|stderr))>/;
219
+ const COMMAND_RE = /<command-name>\/([^<\s]{1,200})<\/command-name>/;
220
+ function slimUser(o, m, at) {
221
+ const content = m.content;
222
+ const parts = Array.isArray(content) ? content.filter(isObj) : [];
223
+ const results = parts.filter((c) => c.type === 'tool_result');
224
+ if (results.length) {
225
+ const infos = results.map((r) => slimToolResult(r, o.toolUseResult));
226
+ const marker = parts.some((c) => c.type === 'text' && INTERRUPT_RE.test((str(c.text) ?? '').trimStart())) || infos.some((r) => r.userInterrupt);
227
+ return { k: 'tool_result', at, results: infos, userInterrupt: marker };
228
+ }
229
+ const text = typeof content === 'string' ? content : parts.filter((c) => c.type === 'text').map((c) => str(c.text) ?? '').join('\n');
230
+ // Compaction summaries can be meta too: never an expansion (a typed `/compact` must not count as a skill).
231
+ if (o.isCompactSummary === true || o.isVisibleInTranscriptOnly === true)
232
+ return null;
233
+ if (o.isMeta === true)
234
+ return isCommandExpansion(o, text) ? { k: 'expansion', at } : null;
235
+ if (INTERRUPT_RE.test(text.trimStart()))
236
+ return { k: 'interrupt', at };
237
+ if (!text.trim() && !parts.length)
238
+ return null;
239
+ if (NOT_A_PROMPT_RE.test(text.trimStart()))
240
+ return null;
241
+ const origin = isObj(o.origin) ? str(o.origin.kind) : null;
242
+ const human = (origin === null || origin === 'human') && !/^<task-notification>/.test(text.trimStart());
243
+ const command = COMMAND_RE.exec(text.slice(0, 2000))?.[1] ?? null;
244
+ return { k: 'prompt', at, uuid: str(o.uuid) ?? `${at}`, text: text.slice(0, PROMPT_KEEP), human, command, promptId: str(o.promptId) };
245
+ }
246
+ /**
247
+ * The meta entry Claude Code writes right after a typed `/skill` or custom command: the skill's body.
248
+ * Not one: a Skill tool's expansion (`sourceToolUseID`; counted from the tool call), local-command
249
+ * caveats / output, and skills the harness loads itself (the meta entry carries its own `<command-name>`).
250
+ */
251
+ function isCommandExpansion(o, text) {
252
+ if (typeof o.sourceToolUseID === 'string')
253
+ return false;
254
+ const t = text.trimStart();
255
+ return Boolean(t) && !NOT_A_PROMPT_RE.test(t) && !/^<command-(name|message)>/.test(t);
256
+ }
257
+ function slimToolResult(r, toolUseResult) {
258
+ const isError = r.is_error === true;
259
+ const text = typeof r.content === 'string' ? r.content : Array.isArray(r.content) ? r.content.map((c) => (isObj(c) ? (str(c.text) ?? '') : '')).join('') : '';
260
+ const exit = /^Exit code (\d+)/.exec(text.slice(0, 40));
261
+ const tur = isObj(toolUseResult) ? toolUseResult : null;
262
+ const userInterrupt = INTERRUPT_RE.test(text.trimStart().slice(0, 60));
263
+ const interrupted = tur?.interrupted === true || userInterrupt;
264
+ return {
265
+ userInterrupt,
266
+ denied: isError && isDeniedResult(text.slice(0, 200)),
267
+ id: str(r.tool_use_id) ?? '',
268
+ isError,
269
+ exitCode: exit ? Number(exit[1]) : isError ? null : 0,
270
+ interrupted,
271
+ edit: tur ? patchStats(tur) : null,
272
+ agentStatus: tur ? str(tur.status) : null,
273
+ workflowRunId: tur && tur.taskType === 'local_workflow' && /^wf_[A-Za-z0-9_-]{1,64}$/.test(str(tur.runId) ?? '') ? str(tur.runId) : null,
274
+ };
275
+ }
276
+ /** Exact counts from Claude Code's structuredPatch (hunks with "+…" / "-…" lines). */
277
+ function patchStats(tur) {
278
+ const hunks = tur.structuredPatch;
279
+ const filePath = str(tur.filePath);
280
+ if (!Array.isArray(hunks) || !filePath)
281
+ return null;
282
+ const added = [];
283
+ const removed = [];
284
+ for (const h of hunks) {
285
+ if (!isObj(h) || !Array.isArray(h.lines))
286
+ continue;
287
+ for (const l of h.lines) {
288
+ if (typeof l !== 'string')
289
+ continue;
290
+ if (l.startsWith('+'))
291
+ added.push(l.slice(1));
292
+ else if (l.startsWith('-'))
293
+ removed.push(l.slice(1));
294
+ }
295
+ }
296
+ if (!added.length && !removed.length) {
297
+ // A new file has no hunks; its content is the added lines.
298
+ const content = tur.type === 'create' ? str(tur.content) : null;
299
+ if (!content)
300
+ return null;
301
+ added.push(...splitLines(content));
302
+ }
303
+ return { added: added.length, removed: removed.length, fp: editFingerprint([{ filePath, added, removed }]) };
304
+ }
305
+ /** Whether any Bash command in these entries throws work away (git restore / checkout -- / reset --hard …). */
306
+ export function hasRevertCommand(entries) {
307
+ return entries.some((e) => e.k === 'assistant' && e.blocks.some((b) => b.t === 'tool_use' && b.command !== null && isRevertCommand(b.command)));
308
+ }
309
+ //# sourceMappingURL=transcript.js.map