@nxuss/lemma 1.44.0 → 1.45.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 (199) hide show
  1. package/README.md +25 -7
  2. package/bin/brain-ingest.js +30 -13
  3. package/dist/cjs/cli/hostHooks.d.ts +38 -7
  4. package/dist/cjs/cli/hostHooks.d.ts.map +1 -1
  5. package/dist/cjs/cli/hostHooks.js +189 -9
  6. package/dist/cjs/cli/hostHooks.js.map +1 -1
  7. package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
  8. package/dist/cjs/cli/lemma-proxy.js +13 -16
  9. package/dist/cjs/cli/lemma-proxy.js.map +1 -1
  10. package/dist/cjs/cloud/billing/stripeApi.d.ts.map +1 -1
  11. package/dist/cjs/cloud/billing/stripeApi.js +6 -1
  12. package/dist/cjs/cloud/billing/stripeApi.js.map +1 -1
  13. package/dist/cjs/cloud/billing/stripeWebhook.d.ts.map +1 -1
  14. package/dist/cjs/cloud/billing/stripeWebhook.js +5 -0
  15. package/dist/cjs/cloud/billing/stripeWebhook.js.map +1 -1
  16. package/dist/cjs/cloud/console/consoleRouter.d.ts +2 -2
  17. package/dist/cjs/cloud/console/consoleRouter.d.ts.map +1 -1
  18. package/dist/cjs/cloud/console/consoleRouter.js +39 -1
  19. package/dist/cjs/cloud/console/consoleRouter.js.map +1 -1
  20. package/dist/cjs/cloud/identity/PlanPolicy.d.ts +26 -5
  21. package/dist/cjs/cloud/identity/PlanPolicy.d.ts.map +1 -1
  22. package/dist/cjs/cloud/identity/PlanPolicy.js +56 -16
  23. package/dist/cjs/cloud/identity/PlanPolicy.js.map +1 -1
  24. package/dist/cjs/cloud/identity/TenantResolver.d.ts +2 -0
  25. package/dist/cjs/cloud/identity/TenantResolver.d.ts.map +1 -1
  26. package/dist/cjs/cloud/identity/TenantResolver.js +1 -1
  27. package/dist/cjs/cloud/identity/TenantResolver.js.map +1 -1
  28. package/dist/cjs/cloud/identity/authenticateV2.js +1 -1
  29. package/dist/cjs/cloud/identity/authenticateV2.js.map +1 -1
  30. package/dist/cjs/cloud/identity/identityStore.d.ts +11 -0
  31. package/dist/cjs/cloud/identity/identityStore.d.ts.map +1 -1
  32. package/dist/cjs/cloud/identity/identityStore.js +26 -9
  33. package/dist/cjs/cloud/identity/identityStore.js.map +1 -1
  34. package/dist/cjs/cloud/metering/PostgresLedgerStore.d.ts +3 -1
  35. package/dist/cjs/cloud/metering/PostgresLedgerStore.d.ts.map +1 -1
  36. package/dist/cjs/cloud/metering/PostgresLedgerStore.js +12 -0
  37. package/dist/cjs/cloud/metering/PostgresLedgerStore.js.map +1 -1
  38. package/dist/cjs/cloud/metering/UsageLedger.d.ts +37 -0
  39. package/dist/cjs/cloud/metering/UsageLedger.d.ts.map +1 -1
  40. package/dist/cjs/cloud/metering/UsageLedger.js +42 -0
  41. package/dist/cjs/cloud/metering/UsageLedger.js.map +1 -1
  42. package/dist/cjs/cloud/store/FileBrainStore.d.ts +5 -1
  43. package/dist/cjs/cloud/store/FileBrainStore.d.ts.map +1 -1
  44. package/dist/cjs/cloud/store/FileBrainStore.js +5 -0
  45. package/dist/cjs/cloud/store/FileBrainStore.js.map +1 -1
  46. package/dist/cjs/cloud/store/PostgresBrainStore.d.ts +10 -1
  47. package/dist/cjs/cloud/store/PostgresBrainStore.d.ts.map +1 -1
  48. package/dist/cjs/cloud/store/PostgresBrainStore.js +28 -0
  49. package/dist/cjs/cloud/store/PostgresBrainStore.js.map +1 -1
  50. package/dist/cjs/cloud/store/memorySummary.d.ts +29 -0
  51. package/dist/cjs/cloud/store/memorySummary.d.ts.map +1 -0
  52. package/dist/cjs/cloud/store/memorySummary.js +66 -0
  53. package/dist/cjs/cloud/store/memorySummary.js.map +1 -0
  54. package/dist/cjs/cloud/v2/routes/admin.d.ts.map +1 -1
  55. package/dist/cjs/cloud/v2/routes/admin.js +20 -5
  56. package/dist/cjs/cloud/v2/routes/admin.js.map +1 -1
  57. package/dist/cjs/cloud/v2/routes/memories.js +1 -1
  58. package/dist/cjs/cloud/v2/routes/memories.js.map +1 -1
  59. package/dist/cjs/cloud/v2/routes/tenant.d.ts +1 -0
  60. package/dist/cjs/cloud/v2/routes/tenant.d.ts.map +1 -1
  61. package/dist/cjs/cloud/v2/routes/tenant.js +4 -1
  62. package/dist/cjs/cloud/v2/routes/tenant.js.map +1 -1
  63. package/dist/cjs/contracts/api-v2.types.d.ts +2 -0
  64. package/dist/cjs/contracts/api-v2.types.d.ts.map +1 -1
  65. package/dist/cjs/contracts/store.d.ts +35 -0
  66. package/dist/cjs/contracts/store.d.ts.map +1 -1
  67. package/dist/cjs/mcp/index.js +4 -1
  68. package/dist/cjs/mcp/index.js.map +1 -1
  69. package/dist/cjs/mcp/tools/misc.d.ts.map +1 -1
  70. package/dist/cjs/mcp/tools/misc.js +2 -1
  71. package/dist/cjs/mcp/tools/misc.js.map +1 -1
  72. package/dist/cjs/mcp/tools/workspace.d.ts.map +1 -1
  73. package/dist/cjs/mcp/tools/workspace.js +4 -1
  74. package/dist/cjs/mcp/tools/workspace.js.map +1 -1
  75. package/dist/cjs/mcp/tools.d.ts +10 -0
  76. package/dist/cjs/mcp/tools.d.ts.map +1 -1
  77. package/dist/cjs/mcp/tools.js +47 -12
  78. package/dist/cjs/mcp/tools.js.map +1 -1
  79. package/dist/cjs/subconscious/HostRecall.d.ts.map +1 -1
  80. package/dist/cjs/subconscious/HostRecall.js +11 -4
  81. package/dist/cjs/subconscious/HostRecall.js.map +1 -1
  82. package/dist/cjs/utils/FixCapture.d.ts +7 -5
  83. package/dist/cjs/utils/FixCapture.d.ts.map +1 -1
  84. package/dist/cjs/utils/FixCapture.js +64 -32
  85. package/dist/cjs/utils/FixCapture.js.map +1 -1
  86. package/dist/cjs/utils/NoTerminal.d.ts +27 -0
  87. package/dist/cjs/utils/NoTerminal.d.ts.map +1 -0
  88. package/dist/cjs/utils/NoTerminal.js +37 -0
  89. package/dist/cjs/utils/NoTerminal.js.map +1 -0
  90. package/dist/cjs/utils/OpenCodeSession.d.ts +70 -0
  91. package/dist/cjs/utils/OpenCodeSession.d.ts.map +1 -0
  92. package/dist/cjs/utils/OpenCodeSession.js +154 -0
  93. package/dist/cjs/utils/OpenCodeSession.js.map +1 -0
  94. package/dist/cjs/utils/SessionCapture.d.ts +29 -7
  95. package/dist/cjs/utils/SessionCapture.d.ts.map +1 -1
  96. package/dist/cjs/utils/SessionCapture.js +180 -32
  97. package/dist/cjs/utils/SessionCapture.js.map +1 -1
  98. package/dist/cjs/utils/TestAutofixInterceptor.d.ts.map +1 -1
  99. package/dist/cjs/utils/TestAutofixInterceptor.js +2 -0
  100. package/dist/cjs/utils/TestAutofixInterceptor.js.map +1 -1
  101. package/dist/esm/cli/hostHooks.d.ts +38 -7
  102. package/dist/esm/cli/hostHooks.d.ts.map +1 -1
  103. package/dist/esm/cli/hostHooks.js +187 -8
  104. package/dist/esm/cli/hostHooks.js.map +1 -1
  105. package/dist/esm/cli/lemma-proxy.d.ts.map +1 -1
  106. package/dist/esm/cli/lemma-proxy.js +14 -17
  107. package/dist/esm/cli/lemma-proxy.js.map +1 -1
  108. package/dist/esm/cloud/billing/stripeApi.d.ts.map +1 -1
  109. package/dist/esm/cloud/billing/stripeApi.js +6 -1
  110. package/dist/esm/cloud/billing/stripeApi.js.map +1 -1
  111. package/dist/esm/cloud/billing/stripeWebhook.d.ts.map +1 -1
  112. package/dist/esm/cloud/billing/stripeWebhook.js +5 -0
  113. package/dist/esm/cloud/billing/stripeWebhook.js.map +1 -1
  114. package/dist/esm/cloud/console/consoleRouter.d.ts +2 -2
  115. package/dist/esm/cloud/console/consoleRouter.d.ts.map +1 -1
  116. package/dist/esm/cloud/console/consoleRouter.js +39 -1
  117. package/dist/esm/cloud/console/consoleRouter.js.map +1 -1
  118. package/dist/esm/cloud/identity/PlanPolicy.d.ts +26 -5
  119. package/dist/esm/cloud/identity/PlanPolicy.d.ts.map +1 -1
  120. package/dist/esm/cloud/identity/PlanPolicy.js +54 -15
  121. package/dist/esm/cloud/identity/PlanPolicy.js.map +1 -1
  122. package/dist/esm/cloud/identity/TenantResolver.d.ts +2 -0
  123. package/dist/esm/cloud/identity/TenantResolver.d.ts.map +1 -1
  124. package/dist/esm/cloud/identity/TenantResolver.js +1 -1
  125. package/dist/esm/cloud/identity/TenantResolver.js.map +1 -1
  126. package/dist/esm/cloud/identity/authenticateV2.js +1 -1
  127. package/dist/esm/cloud/identity/authenticateV2.js.map +1 -1
  128. package/dist/esm/cloud/identity/identityStore.d.ts +11 -0
  129. package/dist/esm/cloud/identity/identityStore.d.ts.map +1 -1
  130. package/dist/esm/cloud/identity/identityStore.js +26 -9
  131. package/dist/esm/cloud/identity/identityStore.js.map +1 -1
  132. package/dist/esm/cloud/metering/PostgresLedgerStore.d.ts +3 -1
  133. package/dist/esm/cloud/metering/PostgresLedgerStore.d.ts.map +1 -1
  134. package/dist/esm/cloud/metering/PostgresLedgerStore.js +12 -0
  135. package/dist/esm/cloud/metering/PostgresLedgerStore.js.map +1 -1
  136. package/dist/esm/cloud/metering/UsageLedger.d.ts +37 -0
  137. package/dist/esm/cloud/metering/UsageLedger.d.ts.map +1 -1
  138. package/dist/esm/cloud/metering/UsageLedger.js +41 -0
  139. package/dist/esm/cloud/metering/UsageLedger.js.map +1 -1
  140. package/dist/esm/cloud/store/FileBrainStore.d.ts +5 -1
  141. package/dist/esm/cloud/store/FileBrainStore.d.ts.map +1 -1
  142. package/dist/esm/cloud/store/FileBrainStore.js +5 -0
  143. package/dist/esm/cloud/store/FileBrainStore.js.map +1 -1
  144. package/dist/esm/cloud/store/PostgresBrainStore.d.ts +10 -1
  145. package/dist/esm/cloud/store/PostgresBrainStore.d.ts.map +1 -1
  146. package/dist/esm/cloud/store/PostgresBrainStore.js +28 -0
  147. package/dist/esm/cloud/store/PostgresBrainStore.js.map +1 -1
  148. package/dist/esm/cloud/store/memorySummary.d.ts +29 -0
  149. package/dist/esm/cloud/store/memorySummary.d.ts.map +1 -0
  150. package/dist/esm/cloud/store/memorySummary.js +60 -0
  151. package/dist/esm/cloud/store/memorySummary.js.map +1 -0
  152. package/dist/esm/cloud/v2/routes/admin.d.ts.map +1 -1
  153. package/dist/esm/cloud/v2/routes/admin.js +20 -5
  154. package/dist/esm/cloud/v2/routes/admin.js.map +1 -1
  155. package/dist/esm/cloud/v2/routes/memories.js +1 -1
  156. package/dist/esm/cloud/v2/routes/memories.js.map +1 -1
  157. package/dist/esm/cloud/v2/routes/tenant.d.ts +1 -0
  158. package/dist/esm/cloud/v2/routes/tenant.d.ts.map +1 -1
  159. package/dist/esm/cloud/v2/routes/tenant.js +4 -1
  160. package/dist/esm/cloud/v2/routes/tenant.js.map +1 -1
  161. package/dist/esm/contracts/api-v2.types.d.ts +2 -0
  162. package/dist/esm/contracts/api-v2.types.d.ts.map +1 -1
  163. package/dist/esm/contracts/store.d.ts +35 -0
  164. package/dist/esm/contracts/store.d.ts.map +1 -1
  165. package/dist/esm/mcp/index.js +5 -2
  166. package/dist/esm/mcp/index.js.map +1 -1
  167. package/dist/esm/mcp/tools/misc.d.ts.map +1 -1
  168. package/dist/esm/mcp/tools/misc.js +2 -1
  169. package/dist/esm/mcp/tools/misc.js.map +1 -1
  170. package/dist/esm/mcp/tools/workspace.d.ts.map +1 -1
  171. package/dist/esm/mcp/tools/workspace.js +4 -1
  172. package/dist/esm/mcp/tools/workspace.js.map +1 -1
  173. package/dist/esm/mcp/tools.d.ts +10 -0
  174. package/dist/esm/mcp/tools.d.ts.map +1 -1
  175. package/dist/esm/mcp/tools.js +46 -12
  176. package/dist/esm/mcp/tools.js.map +1 -1
  177. package/dist/esm/subconscious/HostRecall.d.ts.map +1 -1
  178. package/dist/esm/subconscious/HostRecall.js +11 -4
  179. package/dist/esm/subconscious/HostRecall.js.map +1 -1
  180. package/dist/esm/utils/FixCapture.d.ts +7 -5
  181. package/dist/esm/utils/FixCapture.d.ts.map +1 -1
  182. package/dist/esm/utils/FixCapture.js +64 -33
  183. package/dist/esm/utils/FixCapture.js.map +1 -1
  184. package/dist/esm/utils/NoTerminal.d.ts +27 -0
  185. package/dist/esm/utils/NoTerminal.d.ts.map +1 -0
  186. package/dist/esm/utils/NoTerminal.js +33 -0
  187. package/dist/esm/utils/NoTerminal.js.map +1 -0
  188. package/dist/esm/utils/OpenCodeSession.d.ts +70 -0
  189. package/dist/esm/utils/OpenCodeSession.d.ts.map +1 -0
  190. package/dist/esm/utils/OpenCodeSession.js +146 -0
  191. package/dist/esm/utils/OpenCodeSession.js.map +1 -0
  192. package/dist/esm/utils/SessionCapture.d.ts +29 -7
  193. package/dist/esm/utils/SessionCapture.d.ts.map +1 -1
  194. package/dist/esm/utils/SessionCapture.js +180 -32
  195. package/dist/esm/utils/SessionCapture.js.map +1 -1
  196. package/dist/esm/utils/TestAutofixInterceptor.d.ts.map +1 -1
  197. package/dist/esm/utils/TestAutofixInterceptor.js +2 -0
  198. package/dist/esm/utils/TestAutofixInterceptor.js.map +1 -1
  199. package/package.json +1 -1
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ /**
3
+ * OpenCode sessions → the hand-off. OpenCode has no SessionEnd hook and no transcript file: its
4
+ * plugin gets the session from the SDK (`client.session.messages`, `client.session.children`) as
5
+ * `{ info, parts }` messages, and subagents are child sessions the `task` tool creates. The plugin
6
+ * `lemma init` installs sends that snapshot here when the session goes idle (and before a
7
+ * compaction); it is rewritten as a Claude Code shaped transcript, subagents beside it in
8
+ * `<name>/subagents/agent-<id>.jsonl` + `.meta.json`, so captureSession builds the same hand-off
9
+ * it builds for Claude Code instead of a second implementation drifting from the first.
10
+ *
11
+ * Measured on OpenCode 1.19.3 (session snapshot from a real `opencode run` with a `task` subagent):
12
+ * tool parts carry `state.input` with `filePath` (read/edit/write) or `command` (bash), and `patch`
13
+ * parts list the files the step changed on disk, which also catches edits made through bash.
14
+ */
15
+ var __importDefault = (this && this.__importDefault) || function (mod) {
16
+ return (mod && mod.__esModule) ? mod : { "default": mod };
17
+ };
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.toClaudeEvents = toClaudeEvents;
20
+ exports.writeClaudeTranscript = writeClaudeTranscript;
21
+ exports.captureOpenCodeSession = captureOpenCodeSession;
22
+ const fs_1 = __importDefault(require("fs"));
23
+ const os_1 = __importDefault(require("os"));
24
+ const path_1 = __importDefault(require("path"));
25
+ const SessionCapture_1 = require("./SessionCapture");
26
+ const HostRecall_1 = require("../subconscious/HostRecall");
27
+ const AtomicFile_1 = require("./AtomicFile");
28
+ const TRANSCRIPT_DIR = path_1.default.join(os_1.default.tmpdir(), 'lemma-opencode');
29
+ const KEEP_MS = 3 * 24 * 60 * 60 * 1000;
30
+ /** OpenCode tool ids → the names SessionCapture knows. MCP tools arrive as `<server>_<tool>`. */
31
+ const TOOL_NAMES = { read: 'Read', edit: 'Edit', write: 'Write', multiedit: 'MultiEdit', bash: 'Bash', task: 'Agent', grep: 'Grep', glob: 'Glob', list: 'LS' };
32
+ const FILE_TOOLS = new Set(['Read', 'Edit', 'Write', 'MultiEdit']);
33
+ function toolName(tool) {
34
+ if (TOOL_NAMES[tool])
35
+ return TOOL_NAMES[tool];
36
+ const lemma = /^lemma_(.+)$/.exec(tool);
37
+ return lemma ? `mcp__lemma__${lemma[1]}` : tool;
38
+ }
39
+ /** Files named in an apply_patch body (the tool GPT models get in OpenCode). */
40
+ function patchFiles(patchText) {
41
+ if (typeof patchText !== 'string')
42
+ return [];
43
+ return [...patchText.matchAll(/^\*\*\* (?:Add|Update|Delete) File: (.+)$/gm)].map((m) => m[1].trim());
44
+ }
45
+ const iso = (ms, fallback) => (typeof ms === 'number' && ms > 0 ? new Date(ms).toISOString() : fallback);
46
+ /** One OpenCode session as Claude Code transcript events (one JSON object per line). */
47
+ function toClaudeEvents(messages, directory, sidechain) {
48
+ const lines = [];
49
+ const extra = sidechain ? { isSidechain: true, agentId: sidechain } : {};
50
+ const push = (timestamp, o) => lines.push(JSON.stringify({ timestamp, ...extra, ...o }));
51
+ const abs = (f) => path_1.default.resolve(directory, f);
52
+ let n = 0;
53
+ for (const m of messages ?? []) {
54
+ const role = m?.info?.role;
55
+ const at = iso(m?.info?.time?.created, new Date(0).toISOString());
56
+ for (const p of m?.parts ?? []) {
57
+ if (p?.type === 'text' && typeof p.text === 'string' && p.text.trim() && !p.synthetic) {
58
+ push(at, role === 'user'
59
+ ? { type: 'user', message: { role: 'user', content: p.text } }
60
+ : { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: p.text }] } });
61
+ }
62
+ else if (p?.type === 'patch' && Array.isArray(p.files)) {
63
+ // The step's on-disk changes, whichever tool made them.
64
+ for (const f of p.files)
65
+ if (typeof f === 'string')
66
+ push(at, { type: 'assistant', message: { role: 'assistant', content: [{ type: 'tool_use', id: `patch-${n++}`, name: 'Edit', input: { file_path: abs(f) } }] } });
67
+ }
68
+ else if (p?.type === 'tool' && typeof p.tool === 'string') {
69
+ const st = p.state ?? {};
70
+ const start = iso(st.time?.start, at);
71
+ const name = toolName(p.tool);
72
+ const input = { ...(st.input && typeof st.input === 'object' ? st.input : {}) };
73
+ if (FILE_TOOLS.has(name) && typeof input.filePath === 'string')
74
+ input.file_path = abs(input.filePath);
75
+ const id = p.callID || `call-${n++}`;
76
+ const files = /^(apply_)?patch$/.test(p.tool) ? patchFiles(input.patchText) : [];
77
+ if (files.length) {
78
+ for (const f of files)
79
+ push(start, { type: 'assistant', message: { role: 'assistant', content: [{ type: 'tool_use', id: `${id}-${n++}`, name: 'Edit', input: { file_path: abs(f) } }] } });
80
+ continue;
81
+ }
82
+ push(start, { type: 'assistant', message: { role: 'assistant', content: [{ type: 'tool_use', id, name, input }] } });
83
+ if (st.status === 'completed' || st.status === 'error') {
84
+ const content = st.status === 'error' ? String(st.error ?? '') : String(st.output ?? '');
85
+ push(iso(st.time?.end, start), { type: 'user', message: { role: 'user', content: [{ type: 'tool_result', tool_use_id: id, content }] } });
86
+ }
87
+ }
88
+ }
89
+ }
90
+ return lines;
91
+ }
92
+ /** The description the parent gave each subagent: the `task` call whose metadata names the child session. */
93
+ function taskDescriptions(messages) {
94
+ const out = new Map();
95
+ for (const m of messages ?? [])
96
+ for (const p of m?.parts ?? []) {
97
+ if (p?.type !== 'tool' || p.tool !== 'task')
98
+ continue;
99
+ const child = p.state?.metadata?.sessionId;
100
+ const description = p.state?.input?.description;
101
+ if (typeof child === 'string' && typeof description === 'string')
102
+ out.set(child, description);
103
+ }
104
+ return out;
105
+ }
106
+ function prune(dir) {
107
+ try {
108
+ for (const name of fs_1.default.readdirSync(dir)) {
109
+ const f = path_1.default.join(dir, name);
110
+ if (Date.now() - fs_1.default.statSync(f).mtimeMs > KEEP_MS)
111
+ fs_1.default.rmSync(f, { recursive: true, force: true });
112
+ }
113
+ }
114
+ catch {
115
+ // a leftover temp file costs nothing
116
+ }
117
+ }
118
+ /** Writes the snapshot as `<dir>/<session>.jsonl` plus its subagents, and returns the transcript path. */
119
+ function writeClaudeTranscript(snap, dir = TRANSCRIPT_DIR) {
120
+ fs_1.default.mkdirSync(dir, { recursive: true });
121
+ prune(dir);
122
+ const safe = (id) => id.replace(/[^A-Za-z0-9_-]/g, '_');
123
+ const base = path_1.default.join(dir, safe(snap.sessionID));
124
+ (0, AtomicFile_1.writeFileAtomic)(`${base}.jsonl`, toClaudeEvents(snap.messages, snap.directory).join('\n'));
125
+ const subdir = path_1.default.join(base, 'subagents');
126
+ fs_1.default.rmSync(subdir, { recursive: true, force: true });
127
+ const descriptions = taskDescriptions(snap.messages);
128
+ for (const child of snap.children ?? []) {
129
+ const id = child?.session?.id;
130
+ if (typeof id !== 'string')
131
+ continue;
132
+ fs_1.default.mkdirSync(subdir, { recursive: true });
133
+ fs_1.default.writeFileSync(path_1.default.join(subdir, `agent-${safe(id)}.jsonl`), toClaudeEvents(child.messages, snap.directory, id).join('\n'));
134
+ const description = descriptions.get(id) ?? child.session.title?.replace(/\s*\(@\S+ subagent\)\s*$/, '');
135
+ fs_1.default.writeFileSync(path_1.default.join(subdir, `agent-${safe(id)}.meta.json`), JSON.stringify({ ...(child.session.agent ? { agentType: child.session.agent } : {}), ...(description ? { description } : {}) }));
136
+ }
137
+ return `${base}.jsonl`;
138
+ }
139
+ /**
140
+ * Save (or refresh) the session's hand-off. OpenCode has no session end: `session.idle` fires after
141
+ * every turn, so this runs in the mid-session mode (PreCompact's): it refreshes one hand-off in place
142
+ * and never marks the session done. `compacting` also returns the text to put back in the context.
143
+ */
144
+ function captureOpenCodeSession(snap, opts = {}) {
145
+ if (!snap || typeof snap.sessionID !== 'string' || typeof snap.directory !== 'string' || !Array.isArray(snap.messages))
146
+ return { skipped: 'no-transcript' };
147
+ const transcript = writeClaudeTranscript(snap);
148
+ const res = (0, SessionCapture_1.captureSession)({ session_id: snap.sessionID, transcript_path: transcript, cwd: snap.directory }, snap.directory, undefined, { preCompact: true });
149
+ if (!opts.compacting)
150
+ return res;
151
+ const recall = (0, HostRecall_1.compactRecall)({ source: 'compact', session_id: snap.sessionID, cwd: snap.directory }, snap.directory);
152
+ return recall ? { ...res, text: recall.text } : res;
153
+ }
154
+ //# sourceMappingURL=OpenCodeSession.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpenCodeSession.js","sourceRoot":"","sources":["../../../src/utils/OpenCodeSession.ts"],"names":[],"mappings":"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"}
@@ -33,14 +33,36 @@ export interface SessionDigest {
33
33
  /** Timestamp of the first transcript event: where "commits made during this session" starts. */
34
34
  startedAt?: string;
35
35
  /** The last test/build/lint run the agent made and how it ended; unset when the output didn't say. */
36
- lastVerdict?: {
37
- command: string;
38
- passed: boolean;
39
- headline: string;
40
- };
36
+ lastVerdict?: Verdict;
37
+ /** What the session's subagents did, when asked for (digestTranscript's `subagents` option). Not counted in the fields above. */
38
+ subagents?: SubagentDigest[];
41
39
  }
42
- /** Reads the transcript (JSONL, one event per line) into the few facts capture needs. */
43
- export declare function digestTranscript(transcriptPath: string): SessionDigest | null;
40
+ export interface Verdict {
41
+ command: string;
42
+ passed: boolean;
43
+ headline: string;
44
+ /** Timestamp of the run's result, when the transcript had one: which of several agents ran last. */
45
+ at?: string;
46
+ }
47
+ /** One subagent's share of the work, from its own transcript. */
48
+ export interface SubagentDigest {
49
+ agentId: string;
50
+ /** What the parent called it (the Agent tool's description), from the host's meta file. */
51
+ description?: string;
52
+ agentType?: string;
53
+ startedAt?: string;
54
+ toolCalls: number;
55
+ edited: string[];
56
+ lastVerdict?: Verdict;
57
+ }
58
+ /**
59
+ * Reads the transcript (JSONL, one event per line) into the few facts capture needs. The fields
60
+ * describe the main agent only; `subagents: true` adds what each subagent did, read from the
61
+ * sidechain events in this file and from the host's per-subagent transcripts next to it.
62
+ */
63
+ export declare function digestTranscript(transcriptPath: string, opts?: {
64
+ subagents?: boolean;
65
+ }): SessionDigest | null;
44
66
  /**
45
67
  * Where a session that edited code left off, from the transcript and git alone. The task is the
46
68
  * person's first prompt, uncut up to a limit, because that is where a multi-part plan lives; "what
@@ -1 +1 @@
1
- {"version":3,"file":"SessionCapture.d.ts","sourceRoot":"","sources":["../../../src/utils/SessionCapture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,EAA0C,UAAU,EAAE,MAAM,4BAA4B,CAAC;AA2BhG,eAAO,MAAM,YAAY,wBAAwB,CAAC;AASlD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,sGAAsG;IACtG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,eAAe,EAAE,OAAO,CAAC;IACzB,0GAA0G;IAC1G,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gGAAgG;IAChG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sGAAsG;IACtG,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CACtE;AAkCD,yFAAyF;AACzF,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CA+D7E;AA+BD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CA0B9F;AAED,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAIhE;AAsBD,MAAM,WAAW,oBAAoB;IACnC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,eAAe,GAAG,UAAU,GAAG,kBAAkB,GAAG,cAAc,GAAG,eAAe,GAAG,WAAW,GAAG,YAAY,GAAG,SAAS,CAAC;CACzI;AAED,0GAA0G;AAC1G,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,EAAE,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,oBAAoB,CAqE5I;AAuBD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAWtH"}
1
+ {"version":3,"file":"SessionCapture.d.ts","sourceRoot":"","sources":["../../../src/utils/SessionCapture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,EAA0C,UAAU,EAAE,MAAM,4BAA4B,CAAC;AA2BhG,eAAO,MAAM,YAAY,wBAAwB,CAAC;AAkBlD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,sGAAsG;IACtG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,eAAe,EAAE,OAAO,CAAC;IACzB,0GAA0G;IAC1G,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gGAAgG;IAChG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sGAAsG;IACtG,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iIAAiI;IACjI,SAAS,CAAC,EAAE,cAAc,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,oGAAoG;IACpG,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,iEAAiE;AACjE,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,2FAA2F;IAC3F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA2CD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,aAAa,GAAG,IAAI,CAYjH;AA2LD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAyC9F;AAED,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAIhE;AAsBD,MAAM,WAAW,oBAAoB;IACnC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,eAAe,GAAG,UAAU,GAAG,kBAAkB,GAAG,cAAc,GAAG,eAAe,GAAG,WAAW,GAAG,YAAY,GAAG,SAAS,CAAC;CACzI;AAED,0GAA0G;AAC1G,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,EAAE,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,oBAAoB,CAwE5I;AAuBD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAWtH"}
@@ -62,9 +62,18 @@ const AGENT_NOTE_MARK = "AGENT'S NOTE";
62
62
  const MAX_AGENT_NOTE_CHARS = 1500;
63
63
  const MAX_HANDOFF_FILES = 10;
64
64
  const MAX_SAID_CHARS = 700;
65
+ /** Subagent transcripts read per session (newest first) and the bytes read from each: an orchestrator can spawn dozens. */
66
+ const MAX_SUBAGENT_TRANSCRIPTS = 30;
67
+ const MAX_SUBAGENT_BYTES = 2 * 1024 * 1024;
68
+ const SUBAGENT_HEAD_BYTES = 128 * 1024;
69
+ const SUBAGENT_TAIL_BYTES = 1536 * 1024;
70
+ const MAX_SUBAGENT_LINES = 6;
71
+ const MAX_DESCRIPTION_CHARS = 80;
65
72
  const REMEMBERED_SESSIONS = 50;
66
73
  /** Tools that mean the agent already wrote (or deliberately declined to write) its own memory. */
67
74
  const OWN_MEMORY_TOOL = /^mcp__lemma__(store|update|forget)_memory$/;
75
+ /** Lemma's own editing tools: `filePath` is relative to the project root (multi_file_patch carries a list of them). */
76
+ const LEMMA_EDIT_TOOL = /^mcp__lemma__(write_workspace_file|create_workspace_file|apply_workspace_patch|multi_file_patch)$/;
68
77
  function textOf(content) {
69
78
  if (typeof content === 'string')
70
79
  return content;
@@ -81,13 +90,13 @@ function textOf(content) {
81
90
  * done; dropping them would leave no hand-off where it matters most. Counts such as `toolCalls`
82
91
  * then describe the tail only.
83
92
  */
84
- function readEnds(file, size) {
93
+ function readEnds(file, size, headBytes = HEAD_BYTES, tailBytes = TAIL_BYTES) {
85
94
  const fd = fs_1.default.openSync(file, 'r');
86
95
  try {
87
- const head = Buffer.alloc(HEAD_BYTES);
88
- fs_1.default.readSync(fd, head, 0, HEAD_BYTES, 0);
89
- const tail = Buffer.alloc(TAIL_BYTES);
90
- fs_1.default.readSync(fd, tail, 0, TAIL_BYTES, size - TAIL_BYTES);
96
+ const head = Buffer.alloc(headBytes);
97
+ fs_1.default.readSync(fd, head, 0, headBytes, 0);
98
+ const tail = Buffer.alloc(tailBytes);
99
+ fs_1.default.readSync(fd, tail, 0, tailBytes, size - tailBytes);
91
100
  const h = head.toString('utf8');
92
101
  const t = tail.toString('utf8');
93
102
  // Drop the partial line at each cut; an unparseable line would be skipped anyway, but a cut
@@ -98,8 +107,24 @@ function readEnds(file, size) {
98
107
  fs_1.default.closeSync(fd);
99
108
  }
100
109
  }
101
- /** Reads the transcript (JSONL, one event per line) into the few facts capture needs. */
102
- function digestTranscript(transcriptPath) {
110
+ function parseLines(raw) {
111
+ const events = [];
112
+ for (const line of raw.split('\n')) {
113
+ if (!line)
114
+ continue;
115
+ try {
116
+ events.push(JSON.parse(line));
117
+ }
118
+ catch { /* a cut or garbled line */ }
119
+ }
120
+ return events;
121
+ }
122
+ /**
123
+ * Reads the transcript (JSONL, one event per line) into the few facts capture needs. The fields
124
+ * describe the main agent only; `subagents: true` adds what each subagent did, read from the
125
+ * sidechain events in this file and from the host's per-subagent transcripts next to it.
126
+ */
127
+ function digestTranscript(transcriptPath, opts = {}) {
103
128
  let raw;
104
129
  try {
105
130
  const stat = fs_1.default.statSync(transcriptPath);
@@ -108,21 +133,19 @@ function digestTranscript(transcriptPath) {
108
133
  catch {
109
134
  return null;
110
135
  }
136
+ const events = parseLines(raw);
137
+ const digest = scan(events.filter((ev) => !ev?.isSidechain));
138
+ if (opts.subagents)
139
+ digest.subagents = digestSubagents(transcriptPath, events.filter((ev) => ev?.isSidechain));
140
+ return digest;
141
+ }
142
+ function scan(events) {
111
143
  const digest = { prompt: '', answer: '', toolCalls: 0, edited: [], read: [], storedOwnMemory: false };
112
144
  const readCount = new Map();
113
145
  const verdictCommands = new Map(); // tool_use id -> command, for verdict-shaped Bash runs
114
146
  let edited = false;
115
- for (const line of raw.split('\n')) {
116
- if (!line)
117
- continue;
118
- let ev;
119
- try {
120
- ev = JSON.parse(line);
121
- }
122
- catch {
123
- continue;
124
- }
125
- if (ev.isSidechain)
147
+ for (const ev of events) {
148
+ if (!ev || typeof ev !== 'object')
126
149
  continue;
127
150
  if (!digest.startedAt && typeof ev.timestamp === 'string')
128
151
  digest.startedAt = ev.timestamp;
@@ -138,7 +161,7 @@ function digestTranscript(transcriptPath) {
138
161
  const passed = (0, FixCapture_1.inferVerdict)(output);
139
162
  if (passed === null)
140
163
  continue;
141
- digest.lastVerdict = { command, passed, headline: passed ? '' : (0, FixCapture_1.extractFailure)(output).headline };
164
+ digest.lastVerdict = { command, passed, headline: passed ? '' : (0, FixCapture_1.extractFailure)(output).headline, ...(typeof ev.timestamp === 'string' ? { at: ev.timestamp } : {}) };
142
165
  }
143
166
  }
144
167
  if (ev.type === 'user') {
@@ -170,11 +193,15 @@ function digestTranscript(transcriptPath) {
170
193
  }
171
194
  if (name === 'Bash')
172
195
  digest.ranAfterEdit = edited;
173
- if (typeof file === 'string' && /^(Edit|Write|MultiEdit|NotebookEdit)$/.test(name)) {
196
+ const lemmaEdits = LEMMA_EDIT_TOOL.test(name)
197
+ ? [block.input?.filePath, ...(Array.isArray(block.input?.patches) ? block.input.patches.map((x) => x?.filePath) : [])].filter((f) => typeof f === 'string' && !!f)
198
+ : [];
199
+ if ((typeof file === 'string' && /^(Edit|Write|MultiEdit|NotebookEdit)$/.test(name)) || lemmaEdits.length) {
174
200
  edited = true;
175
201
  digest.ranAfterEdit = false;
176
- if (!digest.edited.includes(file))
177
- digest.edited.push(file);
202
+ for (const f of lemmaEdits.length ? lemmaEdits : [file])
203
+ if (!digest.edited.includes(f))
204
+ digest.edited.push(f);
178
205
  }
179
206
  else if (typeof file === 'string' && name === 'Read') {
180
207
  readCount.set(file, (readCount.get(file) ?? 0) + 1);
@@ -184,6 +211,94 @@ function digestTranscript(transcriptPath) {
184
211
  digest.read = [...readCount.entries()].sort((a, b) => b[1] - a[1]).map(([f]) => f);
185
212
  return digest;
186
213
  }
214
+ function readMeta(file) {
215
+ try {
216
+ const m = JSON.parse(fs_1.default.readFileSync(file, 'utf8'));
217
+ return {
218
+ ...(typeof m?.description === 'string' && m.description.trim() ? { description: m.description.trim() } : {}),
219
+ ...(typeof m?.agentType === 'string' && m.agentType ? { agentType: m.agentType } : {}),
220
+ };
221
+ }
222
+ catch {
223
+ return {};
224
+ }
225
+ }
226
+ function subagentDigest(agentId, events, meta) {
227
+ const d = scan(events);
228
+ return { agentId, ...meta, ...(d.startedAt ? { startedAt: d.startedAt } : {}), toolCalls: d.toolCalls, edited: d.edited, ...(d.lastVerdict ? { lastVerdict: d.lastVerdict } : {}) };
229
+ }
230
+ /**
231
+ * What the session's subagents did. Claude Code writes each one to
232
+ * `<transcript without .jsonl>/subagents/agent-<id>.jsonl`, with a `.meta.json` beside it naming
233
+ * the description the parent gave; older builds put the same events in the main transcript,
234
+ * flagged isSidechain. A subagent found in both is read from its own file, which is whole.
235
+ * An orchestrator that delegated the edits used to leave an empty hand-off: its own transcript
236
+ * holds the Agent calls, not the files they changed or the tests they ran.
237
+ */
238
+ function digestSubagents(transcriptPath, sidechain) {
239
+ const byId = new Map();
240
+ const dir = path_1.default.join(transcriptPath.replace(/\.jsonl$/, ''), 'subagents');
241
+ let files = [];
242
+ try {
243
+ files = fs_1.default.readdirSync(dir)
244
+ .map((name) => /^agent-(.+)\.jsonl$/.exec(name))
245
+ .filter((m) => !!m)
246
+ .map((m) => {
247
+ const file = path_1.default.join(dir, m[0]);
248
+ const st = fs_1.default.statSync(file);
249
+ return { id: m[1], file, mtime: st.mtimeMs, size: st.size };
250
+ })
251
+ .sort((a, b) => b.mtime - a.mtime)
252
+ .slice(0, MAX_SUBAGENT_TRANSCRIPTS);
253
+ }
254
+ catch {
255
+ // no subagents directory: a session that delegated nothing, or an older host
256
+ }
257
+ for (const f of files) {
258
+ try {
259
+ const raw = f.size > MAX_SUBAGENT_BYTES ? readEnds(f.file, f.size, SUBAGENT_HEAD_BYTES, SUBAGENT_TAIL_BYTES) : fs_1.default.readFileSync(f.file, 'utf8');
260
+ byId.set(f.id, subagentDigest(f.id, parseLines(raw), readMeta(f.file.replace(/\.jsonl$/, '.meta.json'))));
261
+ }
262
+ catch {
263
+ // unreadable: that subagent simply isn't listed
264
+ }
265
+ }
266
+ const inline = new Map();
267
+ for (const ev of sidechain) {
268
+ const id = typeof ev.agentId === 'string' && ev.agentId ? ev.agentId : 'sidechain';
269
+ if (byId.has(id))
270
+ continue;
271
+ if (!inline.has(id))
272
+ inline.set(id, []);
273
+ inline.get(id).push(ev);
274
+ }
275
+ for (const [id, evs] of inline)
276
+ byId.set(id, subagentDigest(id, evs, {}));
277
+ return [...byId.values()].sort((a, b) => String(a.startedAt ?? '').localeCompare(String(b.startedAt ?? '')));
278
+ }
279
+ /** Files edited by the main agent first, then by its subagents, once each. */
280
+ function allEdited(digest) {
281
+ const out = [...digest.edited];
282
+ for (const s of digest.subagents ?? [])
283
+ for (const f of s.edited)
284
+ if (!out.includes(f))
285
+ out.push(f);
286
+ return out;
287
+ }
288
+ /** The latest test/build run by any agent of the session, and which subagent ran it (unset for the main agent). */
289
+ function latestVerdict(digest) {
290
+ let best = digest.lastVerdict ? { verdict: digest.lastVerdict } : null;
291
+ for (const s of digest.subagents ?? []) {
292
+ const v = s.lastVerdict;
293
+ if (!v)
294
+ continue;
295
+ // Without timestamps on both there is no telling who ran last, and the run already chosen stands.
296
+ if (!best || (v.at && best.verdict.at && v.at > best.verdict.at))
297
+ best = { verdict: v, by: s };
298
+ }
299
+ return best;
300
+ }
301
+ const subagentName = (s) => `${s.description ? `"${clip(s.description, MAX_DESCRIPTION_CHARS)}"` : `agent ${s.agentId}`}${s.agentType ? ` (${s.agentType})` : ''}`;
187
302
  function seen(root) {
188
303
  try {
189
304
  const parsed = JSON.parse(fs_1.default.readFileSync(path_1.default.join(root, STATE_FILE), 'utf8'));
@@ -212,6 +327,20 @@ function git(root, args) {
212
327
  }
213
328
  }
214
329
  const clip = (text, max) => (text.length > max ? `${text.slice(0, max)}…` : text);
330
+ /**
331
+ * A path as the filesystem resolves it. git reports the root resolved (macOS /var and /tmp are links to
332
+ * /private/…) while a host can report the same files through the link, and path.relative between the two
333
+ * spellings gave `../../../../var/folders/…/src/tax.js`, and the file was not tracked.
334
+ */
335
+ function realPath(root, f) {
336
+ const abs = path_1.default.resolve(root, f);
337
+ try {
338
+ return fs_1.default.realpathSync(abs);
339
+ }
340
+ catch {
341
+ return abs; // deleted since: the spelling the host gave is the best there is
342
+ }
343
+ }
215
344
  /**
216
345
  * Where a session that edited code left off, from the transcript and git alone. The task is the
217
346
  * person's first prompt, uncut up to a limit, because that is where a multi-part plan lives; "what
@@ -219,8 +348,9 @@ const clip = (text, max) => (text.length > max ? `${text.slice(0, max)}…` : te
219
348
  * against what changed. Exported for tests.
220
349
  */
221
350
  function buildHandoff(root, digest, carriedPlan) {
222
- const rel = (f) => path_1.default.relative(root, f) || f;
223
- const edited = digest.edited.map(rel);
351
+ // Lemma's edit tools record paths relative to the project root, the host's are absolute.
352
+ const rel = (f) => path_1.default.relative(root, realPath(root, f)) || f;
353
+ const edited = allEdited(digest).map(rel);
224
354
  // A continuation's own prompt ("continue where we left off") says nothing: the plan is the one
225
355
  // the previous hand-off held, carried forward as it was first given.
226
356
  const taskBlock = carriedPlan
@@ -238,9 +368,24 @@ function buildHandoff(root, digest, carriedPlan) {
238
368
  const commits = digest.startedAt ? git(root, ['log', `--since=${digest.startedAt}`, '-5', '--format=%h %s']) : '';
239
369
  const dirty = git(root, ['status', '--porcelain']).split('\n').filter((l) => l && !/\s\.lemma(\/|$)/.test(l));
240
370
  lines.push(`GIT${branch ? ` (${branch})` : ''}: ${commits ? `commits this session: ${commits.split('\n').join('; ')}` : 'no commits this session'}; ${dirty.length} uncommitted file(s)${dirty.length ? `: ${dirty.slice(0, MAX_HANDOFF_FILES).map((l) => l.slice(3)).join(', ')}` : ''}`);
241
- const v = digest.lastVerdict;
242
- if (v)
243
- lines.push(`LAST TEST/BUILD RUN: \`${clip(v.command, 100)}\` ${v.passed ? 'passed' : `FAILED${v.headline ? ` — ${clip(v.headline, 160)}` : ''}`}`);
371
+ const ran = (v) => `\`${clip(v.command, 100)}\` ${v.passed ? 'passed' : `FAILED${v.headline ? ` — ${clip(v.headline, 160)}` : ''}`}`;
372
+ const last = latestVerdict(digest);
373
+ if (last)
374
+ lines.push(`LAST TEST/BUILD RUN${last.by ? ` (by subagent ${subagentName(last.by)})` : ''}: ${ran(last.verdict)}`);
375
+ const subs = digest.subagents ?? [];
376
+ const worked = subs.filter((s) => s.edited.length > 0 || s.lastVerdict);
377
+ if (worked.length) {
378
+ lines.push('', `SUBAGENTS (${subs.length} ran, ${subs.filter((s) => s.edited.length > 0).length} edited code):`);
379
+ for (const s of worked.slice(0, MAX_SUBAGENT_LINES)) {
380
+ const parts = [
381
+ ...(s.edited.length ? [`edited ${s.edited.slice(0, MAX_FILES).map(rel).join(', ')}${s.edited.length > MAX_FILES ? ', …' : ''}`] : []),
382
+ ...(s.lastVerdict ? [`last run ${ran(s.lastVerdict)}`] : []),
383
+ ];
384
+ lines.push(`- ${subagentName(s)}: ${parts.join('; ')}`);
385
+ }
386
+ if (worked.length > MAX_SUBAGENT_LINES)
387
+ lines.push(`- …and ${worked.length - MAX_SUBAGENT_LINES} more`);
388
+ }
244
389
  if (digest.answer)
245
390
  lines.push('', 'LAST SAID BY THE AGENT:', clip(digest.answer, MAX_SAID_CHARS));
246
391
  return lines.join('\n');
@@ -294,10 +439,11 @@ function captureSession(payload, cwd, brain, opts = {}) {
294
439
  // PreCompact runs mid-session and again at SessionEnd: it must neither be stopped by nor leave the "captured" mark.
295
440
  if (!opts.preCompact && sessionId && seen(root).includes(sessionId))
296
441
  return { skipped: 'already-captured' };
297
- const digest = digestTranscript(p.transcript_path);
442
+ const digest = digestTranscript(p.transcript_path, { subagents: true });
298
443
  if (!digest)
299
444
  return { skipped: 'no-transcript' };
300
- const handoff = digest.edited.length > 0;
445
+ const edited = allEdited(digest);
446
+ const handoff = edited.length > 0;
301
447
  if (opts.preCompact && !handoff)
302
448
  return { skipped: 'no-edits' }; // only a hand-off is worth re-injecting
303
449
  // The closing answer is skipped when the agent wrote its own memory, but the hand-off is not:
@@ -306,7 +452,9 @@ function captureSession(payload, cwd, brain, opts = {}) {
306
452
  // task verbatim. Both live side by side: the note is the agent's reading, this is the source.
307
453
  if (digest.storedOwnMemory && !handoff)
308
454
  return { skipped: 'agent-stored' };
309
- if (digest.toolCalls < MIN_TOOL_CALLS)
455
+ // An orchestrator's own calls are few (it delegates); its subagents' calls are the session's work too.
456
+ const toolCalls = digest.toolCalls + (digest.subagents ?? []).reduce((n, s) => n + s.toolCalls, 0);
457
+ if (toolCalls < MIN_TOOL_CALLS)
310
458
  return { skipped: 'small-session' };
311
459
  if (!digest.prompt)
312
460
  return { skipped: 'no-answer' };
@@ -314,10 +462,10 @@ function captureSession(payload, cwd, brain, opts = {}) {
314
462
  if (!handoff && digest.answer.length < MIN_ANSWER_CHARS)
315
463
  return { skipped: 'no-answer' };
316
464
  const inRoot = (f) => {
317
- const abs = path_1.default.resolve(root, f);
465
+ const abs = realPath(root, f);
318
466
  return abs.startsWith(root + path_1.default.sep) && fs_1.default.existsSync(abs) ? abs : null;
319
467
  };
320
- const tracked = (digest.edited.length > 0 ? digest.edited : digest.read)
468
+ const tracked = (edited.length > 0 ? edited : digest.read)
321
469
  .map(inRoot)
322
470
  .filter((f) => !!f)
323
471
  .slice(0, MAX_FILES);