@oharness/engine 0.1.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 (256) hide show
  1. package/dist/agent/agent.d.ts +196 -0
  2. package/dist/agent/agent.js +758 -0
  3. package/dist/agent/agent.js.map +1 -0
  4. package/dist/agent/compaction.d.ts +68 -0
  5. package/dist/agent/compaction.js +179 -0
  6. package/dist/agent/compaction.js.map +1 -0
  7. package/dist/agent/hooks.d.ts +77 -0
  8. package/dist/agent/hooks.js +152 -0
  9. package/dist/agent/hooks.js.map +1 -0
  10. package/dist/agent/memory.d.ts +45 -0
  11. package/dist/agent/memory.js +147 -0
  12. package/dist/agent/memory.js.map +1 -0
  13. package/dist/agent/overflow.d.ts +35 -0
  14. package/dist/agent/overflow.js +69 -0
  15. package/dist/agent/overflow.js.map +1 -0
  16. package/dist/agent/permissions.d.ts +181 -0
  17. package/dist/agent/permissions.js +289 -0
  18. package/dist/agent/permissions.js.map +1 -0
  19. package/dist/agent/prompt.d.ts +25 -0
  20. package/dist/agent/prompt.js +63 -0
  21. package/dist/agent/prompt.js.map +1 -0
  22. package/dist/agent/session.d.ts +108 -0
  23. package/dist/agent/session.js +231 -0
  24. package/dist/agent/session.js.map +1 -0
  25. package/dist/agent/settings.d.ts +37 -0
  26. package/dist/agent/settings.js +113 -0
  27. package/dist/agent/settings.js.map +1 -0
  28. package/dist/agent/skills.d.ts +58 -0
  29. package/dist/agent/skills.js +225 -0
  30. package/dist/agent/skills.js.map +1 -0
  31. package/dist/agent/store.d.ts +308 -0
  32. package/dist/agent/store.js +596 -0
  33. package/dist/agent/store.js.map +1 -0
  34. package/dist/agent/subagent.d.ts +44 -0
  35. package/dist/agent/subagent.js +138 -0
  36. package/dist/agent/subagent.js.map +1 -0
  37. package/dist/agent/titles.d.ts +8 -0
  38. package/dist/agent/titles.js +23 -0
  39. package/dist/agent/titles.js.map +1 -0
  40. package/dist/agent/workspace.d.ts +60 -0
  41. package/dist/agent/workspace.js +156 -0
  42. package/dist/agent/workspace.js.map +1 -0
  43. package/dist/artifacts/publish.d.ts +94 -0
  44. package/dist/artifacts/publish.js +104 -0
  45. package/dist/artifacts/publish.js.map +1 -0
  46. package/dist/artifacts/store.d.ts +324 -0
  47. package/dist/artifacts/store.js +500 -0
  48. package/dist/artifacts/store.js.map +1 -0
  49. package/dist/auth/index.d.ts +13 -0
  50. package/dist/auth/index.js +10 -0
  51. package/dist/auth/index.js.map +1 -0
  52. package/dist/auth/manager.d.ts +83 -0
  53. package/dist/auth/manager.js +266 -0
  54. package/dist/auth/manager.js.map +1 -0
  55. package/dist/auth/oauth.d.ts +83 -0
  56. package/dist/auth/oauth.js +327 -0
  57. package/dist/auth/oauth.js.map +1 -0
  58. package/dist/auth/providers/account.d.ts +59 -0
  59. package/dist/auth/providers/account.js +282 -0
  60. package/dist/auth/providers/account.js.map +1 -0
  61. package/dist/auth/providers/anthropic.d.ts +24 -0
  62. package/dist/auth/providers/anthropic.js +209 -0
  63. package/dist/auth/providers/anthropic.js.map +1 -0
  64. package/dist/auth/providers/generic.d.ts +57 -0
  65. package/dist/auth/providers/generic.js +155 -0
  66. package/dist/auth/providers/generic.js.map +1 -0
  67. package/dist/auth/providers/official.d.ts +3 -0
  68. package/dist/auth/providers/official.js +90 -0
  69. package/dist/auth/providers/official.js.map +1 -0
  70. package/dist/auth/providers/ottoport.d.ts +3 -0
  71. package/dist/auth/providers/ottoport.js +60 -0
  72. package/dist/auth/providers/ottoport.js.map +1 -0
  73. package/dist/auth/store.d.ts +22 -0
  74. package/dist/auth/store.js +138 -0
  75. package/dist/auth/store.js.map +1 -0
  76. package/dist/auth/types.d.ts +167 -0
  77. package/dist/auth/types.js +16 -0
  78. package/dist/auth/types.js.map +1 -0
  79. package/dist/config.d.ts +197 -0
  80. package/dist/config.js +195 -0
  81. package/dist/config.js.map +1 -0
  82. package/dist/core/errors.d.ts +46 -0
  83. package/dist/core/errors.js +83 -0
  84. package/dist/core/errors.js.map +1 -0
  85. package/dist/core/tokens.d.ts +12 -0
  86. package/dist/core/tokens.js +56 -0
  87. package/dist/core/tokens.js.map +1 -0
  88. package/dist/core/types.d.ts +91 -0
  89. package/dist/core/types.js +49 -0
  90. package/dist/core/types.js.map +1 -0
  91. package/dist/harness.d.ts +302 -0
  92. package/dist/harness.js +695 -0
  93. package/dist/harness.js.map +1 -0
  94. package/dist/index.d.ts +32 -0
  95. package/dist/index.js +21 -0
  96. package/dist/index.js.map +1 -0
  97. package/dist/mcp/client.d.ts +55 -0
  98. package/dist/mcp/client.js +199 -0
  99. package/dist/mcp/client.js.map +1 -0
  100. package/dist/mcp/index.d.ts +13 -0
  101. package/dist/mcp/index.js +8 -0
  102. package/dist/mcp/index.js.map +1 -0
  103. package/dist/mcp/manager.d.ts +77 -0
  104. package/dist/mcp/manager.js +325 -0
  105. package/dist/mcp/manager.js.map +1 -0
  106. package/dist/mcp/oauth.d.ts +63 -0
  107. package/dist/mcp/oauth.js +302 -0
  108. package/dist/mcp/oauth.js.map +1 -0
  109. package/dist/mcp/protocol.d.ts +130 -0
  110. package/dist/mcp/protocol.js +14 -0
  111. package/dist/mcp/protocol.js.map +1 -0
  112. package/dist/mcp/registry.d.ts +52 -0
  113. package/dist/mcp/registry.js +100 -0
  114. package/dist/mcp/registry.js.map +1 -0
  115. package/dist/mcp/registry.json +293 -0
  116. package/dist/mcp/secrets.d.ts +20 -0
  117. package/dist/mcp/secrets.js +75 -0
  118. package/dist/mcp/secrets.js.map +1 -0
  119. package/dist/mcp/transport.d.ts +51 -0
  120. package/dist/mcp/transport.js +158 -0
  121. package/dist/mcp/transport.js.map +1 -0
  122. package/dist/paths.d.ts +37 -0
  123. package/dist/paths.js +63 -0
  124. package/dist/paths.js.map +1 -0
  125. package/dist/plugins.d.ts +55 -0
  126. package/dist/plugins.js +201 -0
  127. package/dist/plugins.js.map +1 -0
  128. package/dist/providers/builtin.d.ts +23 -0
  129. package/dist/providers/builtin.js +141 -0
  130. package/dist/providers/builtin.js.map +1 -0
  131. package/dist/providers/catalog.d.ts +129 -0
  132. package/dist/providers/catalog.js +254 -0
  133. package/dist/providers/catalog.js.map +1 -0
  134. package/dist/providers/catalog.json +472 -0
  135. package/dist/providers/discovery.d.ts +16 -0
  136. package/dist/providers/discovery.js +161 -0
  137. package/dist/providers/discovery.js.map +1 -0
  138. package/dist/providers/http.d.ts +24 -0
  139. package/dist/providers/http.js +139 -0
  140. package/dist/providers/http.js.map +1 -0
  141. package/dist/providers/index.d.ts +10 -0
  142. package/dist/providers/index.js +8 -0
  143. package/dist/providers/index.js.map +1 -0
  144. package/dist/providers/piai.d.ts +124 -0
  145. package/dist/providers/piai.js +551 -0
  146. package/dist/providers/piai.js.map +1 -0
  147. package/dist/providers/registry.d.ts +65 -0
  148. package/dist/providers/registry.js +195 -0
  149. package/dist/providers/registry.js.map +1 -0
  150. package/dist/providers/resilient.d.ts +46 -0
  151. package/dist/providers/resilient.js +117 -0
  152. package/dist/providers/resilient.js.map +1 -0
  153. package/dist/providers/types.d.ts +182 -0
  154. package/dist/providers/types.js +50 -0
  155. package/dist/providers/types.js.map +1 -0
  156. package/dist/schedule/cron.d.ts +35 -0
  157. package/dist/schedule/cron.js +125 -0
  158. package/dist/schedule/cron.js.map +1 -0
  159. package/dist/schedule/scheduler.d.ts +87 -0
  160. package/dist/schedule/scheduler.js +237 -0
  161. package/dist/schedule/scheduler.js.map +1 -0
  162. package/dist/storage/kv.d.ts +56 -0
  163. package/dist/storage/kv.js +173 -0
  164. package/dist/storage/kv.js.map +1 -0
  165. package/dist/storage/migrate.d.ts +11 -0
  166. package/dist/storage/migrate.js +93 -0
  167. package/dist/storage/migrate.js.map +1 -0
  168. package/dist/storage/project.d.ts +9 -0
  169. package/dist/storage/project.js +57 -0
  170. package/dist/storage/project.js.map +1 -0
  171. package/dist/storage/records.d.ts +32 -0
  172. package/dist/storage/records.js +48 -0
  173. package/dist/storage/records.js.map +1 -0
  174. package/dist/sync/client.d.ts +79 -0
  175. package/dist/sync/client.js +118 -0
  176. package/dist/sync/client.js.map +1 -0
  177. package/dist/sync/mirror.d.ts +85 -0
  178. package/dist/sync/mirror.js +389 -0
  179. package/dist/sync/mirror.js.map +1 -0
  180. package/dist/sync/protocol.d.ts +67 -0
  181. package/dist/sync/protocol.js +16 -0
  182. package/dist/sync/protocol.js.map +1 -0
  183. package/dist/sync/settings.d.ts +56 -0
  184. package/dist/sync/settings.js +60 -0
  185. package/dist/sync/settings.js.map +1 -0
  186. package/dist/tools/builtin/artifact.d.ts +16 -0
  187. package/dist/tools/builtin/artifact.js +134 -0
  188. package/dist/tools/builtin/artifact.js.map +1 -0
  189. package/dist/tools/builtin/bash.d.ts +3 -0
  190. package/dist/tools/builtin/bash.js +142 -0
  191. package/dist/tools/builtin/bash.js.map +1 -0
  192. package/dist/tools/builtin/fs.d.ts +19 -0
  193. package/dist/tools/builtin/fs.js +280 -0
  194. package/dist/tools/builtin/fs.js.map +1 -0
  195. package/dist/tools/builtin/index.d.ts +22 -0
  196. package/dist/tools/builtin/index.js +42 -0
  197. package/dist/tools/builtin/index.js.map +1 -0
  198. package/dist/tools/builtin/mcp-resource.d.ts +3 -0
  199. package/dist/tools/builtin/mcp-resource.js +220 -0
  200. package/dist/tools/builtin/mcp-resource.js.map +1 -0
  201. package/dist/tools/builtin/memory.d.ts +10 -0
  202. package/dist/tools/builtin/memory.js +76 -0
  203. package/dist/tools/builtin/memory.js.map +1 -0
  204. package/dist/tools/builtin/recall.d.ts +13 -0
  205. package/dist/tools/builtin/recall.js +163 -0
  206. package/dist/tools/builtin/recall.js.map +1 -0
  207. package/dist/tools/builtin/search.d.ts +3 -0
  208. package/dist/tools/builtin/search.js +240 -0
  209. package/dist/tools/builtin/search.js.map +1 -0
  210. package/dist/tools/builtin/skill.d.ts +10 -0
  211. package/dist/tools/builtin/skill.js +46 -0
  212. package/dist/tools/builtin/skill.js.map +1 -0
  213. package/dist/tools/builtin/task.d.ts +9 -0
  214. package/dist/tools/builtin/task.js +68 -0
  215. package/dist/tools/builtin/task.js.map +1 -0
  216. package/dist/tools/builtin/todo.d.ts +20 -0
  217. package/dist/tools/builtin/todo.js +63 -0
  218. package/dist/tools/builtin/todo.js.map +1 -0
  219. package/dist/tools/define.d.ts +19 -0
  220. package/dist/tools/define.js +39 -0
  221. package/dist/tools/define.js.map +1 -0
  222. package/dist/tools/index.d.ts +7 -0
  223. package/dist/tools/index.js +5 -0
  224. package/dist/tools/index.js.map +1 -0
  225. package/dist/tools/registry.d.ts +39 -0
  226. package/dist/tools/registry.js +101 -0
  227. package/dist/tools/registry.js.map +1 -0
  228. package/dist/tools/types.d.ts +112 -0
  229. package/dist/tools/types.js +22 -0
  230. package/dist/tools/types.js.map +1 -0
  231. package/dist/util/id.d.ts +9 -0
  232. package/dist/util/id.js +19 -0
  233. package/dist/util/id.js.map +1 -0
  234. package/dist/util/json.d.ts +25 -0
  235. package/dist/util/json.js +100 -0
  236. package/dist/util/json.js.map +1 -0
  237. package/dist/util/jwt.d.ts +74 -0
  238. package/dist/util/jwt.js +177 -0
  239. package/dist/util/jwt.js.map +1 -0
  240. package/dist/util/logger.d.ts +13 -0
  241. package/dist/util/logger.js +59 -0
  242. package/dist/util/logger.js.map +1 -0
  243. package/dist/util/queue.d.ts +26 -0
  244. package/dist/util/queue.js +106 -0
  245. package/dist/util/queue.js.map +1 -0
  246. package/dist/util/schema.d.ts +17 -0
  247. package/dist/util/schema.js +98 -0
  248. package/dist/util/schema.js.map +1 -0
  249. package/dist/util/shell.d.ts +20 -0
  250. package/dist/util/shell.js +24 -0
  251. package/dist/util/shell.js.map +1 -0
  252. package/official.json +4 -0
  253. package/package.json +38 -0
  254. package/skills/artifact-authoring/SKILL.md +81 -0
  255. package/skills/canvas/SKILL.md +85 -0
  256. package/skills/skill-authoring/SKILL.md +85 -0
@@ -0,0 +1,225 @@
1
+ import { userPath } from "../paths.js";
2
+ import { readFile, readdir, stat } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { createLogger } from "../util/logger.js";
6
+ const SKILL_FILE = "SKILL.md";
7
+ /**
8
+ * Skills that ship with the harness.
9
+ *
10
+ * Three, and the bar for a fourth is high: a skill's *description* sits in the
11
+ * system prompt of every request forever, so anything a model already knows
12
+ * from pretraining — reviewing a diff, debugging, writing tests — is a tax
13
+ * with no return. What is here is only what cannot be guessed: the rules this
14
+ * harness imposes on artifacts, the artboard format a canvas is written in,
15
+ * and the file format skills themselves use.
16
+ *
17
+ * `canvas` earned its own entry rather than a section inside
18
+ * artifact-authoring because it is not advice about a mime — it is a format
19
+ * the model has no way to derive (which element, which attributes, who writes
20
+ * the editor) plus a set of consequences that only exist for canvases (a
21
+ * person's edits become revisions, and a revision of yours can replace them).
22
+ *
23
+ * The path is the same from `src/agent/` and from `dist/agent/`, which is why
24
+ * these live beside the package rather than inside src — no build step has to
25
+ * remember to copy them, and forgetting one would ship an install whose
26
+ * built-in skills silently do not exist.
27
+ */
28
+ function builtinSkillsDir() {
29
+ return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "skills");
30
+ }
31
+ /** Enough for the model to know what is there without dumping a tree. */
32
+ const MAX_LISTED_FILES = 20;
33
+ export class SkillRegistry {
34
+ skills = new Map();
35
+ logger;
36
+ constructor(logger) {
37
+ this.logger = (logger ?? createLogger("skills")).child("registry");
38
+ }
39
+ register(skill) {
40
+ this.skills.set(skill.name, skill);
41
+ return this;
42
+ }
43
+ get(name) {
44
+ return this.skills.get(name);
45
+ }
46
+ list() {
47
+ return [...this.skills.values()].sort((a, b) => a.name.localeCompare(b.name));
48
+ }
49
+ get size() {
50
+ return this.skills.size;
51
+ }
52
+ /**
53
+ * Discover skills from the conventional locations. Project skills override
54
+ * user skills of the same name, so a repo can pin its own version.
55
+ */
56
+ async discover(cwd, extraDirs = []) {
57
+ const roots = [
58
+ // First, so everything else can override one by name. A user who
59
+ // disagrees with a built-in replaces it rather than being stuck with it.
60
+ { dir: builtinSkillsDir(), source: "builtin" },
61
+ { dir: userPath("skills"), source: "user" },
62
+ { dir: path.join(cwd, ".oharness", "skills"), source: "project" },
63
+ ...extraDirs.map((dir) => ({ dir: path.resolve(cwd, dir), source: "config" })),
64
+ ];
65
+ for (const root of roots) {
66
+ for (const skill of await scanDirectory(root.dir, root.source, this.logger)) {
67
+ this.register(skill);
68
+ }
69
+ }
70
+ }
71
+ /**
72
+ * The block injected into the system prompt: names and descriptions only.
73
+ * Returns undefined when there is nothing to advertise, so the prompt does
74
+ * not carry an empty section.
75
+ */
76
+ promptSection() {
77
+ const skills = this.list();
78
+ if (skills.length === 0)
79
+ return undefined;
80
+ const lines = [
81
+ "## Available skills",
82
+ "",
83
+ "Skills are packaged instructions for specific kinds of work. When a task matches",
84
+ "one of the descriptions below, call the `skill` tool with its name to load the full",
85
+ "instructions before you start. Do not guess at a skill's contents from its description.",
86
+ "",
87
+ ];
88
+ for (const skill of skills) {
89
+ lines.push(`- **${skill.name}** — ${skill.description}`);
90
+ }
91
+ return lines.join("\n");
92
+ }
93
+ }
94
+ /**
95
+ * Render a loaded skill for the model.
96
+ *
97
+ * The base directory and file listing matter: skills routinely say "run
98
+ * `scripts/check.py`", and without knowing where they live the model cannot
99
+ * act on that.
100
+ */
101
+ export async function renderSkill(skill) {
102
+ const files = await listSkillFiles(skill.directory);
103
+ const parts = [
104
+ `<skill name="${skill.name}">`,
105
+ skill.body.trim(),
106
+ "",
107
+ `Base directory: ${skill.directory}`,
108
+ "Relative paths in this skill (scripts/, reference/, …) resolve against that directory.",
109
+ ];
110
+ if (files.length > 0) {
111
+ parts.push("", "Files in this skill:");
112
+ for (const file of files.slice(0, MAX_LISTED_FILES))
113
+ parts.push(` ${file}`);
114
+ if (files.length > MAX_LISTED_FILES) {
115
+ parts.push(` … ${files.length - MAX_LISTED_FILES} more`);
116
+ }
117
+ }
118
+ parts.push("</skill>");
119
+ return parts.join("\n");
120
+ }
121
+ // ---------------------------------------------------------------------------
122
+ async function scanDirectory(root, source, logger) {
123
+ let entries;
124
+ try {
125
+ entries = await readdir(root, { withFileTypes: true });
126
+ }
127
+ catch {
128
+ return []; // The directory simply does not exist.
129
+ }
130
+ const out = [];
131
+ for (const entry of entries) {
132
+ if (!entry.isDirectory() && !entry.isSymbolicLink())
133
+ continue;
134
+ const directory = path.join(root, entry.name);
135
+ const file = path.join(directory, SKILL_FILE);
136
+ let raw;
137
+ try {
138
+ const info = await stat(file);
139
+ if (!info.isFile())
140
+ continue;
141
+ raw = await readFile(file, "utf8");
142
+ }
143
+ catch {
144
+ continue; // A directory without SKILL.md is not a skill.
145
+ }
146
+ const parsed = parseSkillFile(raw, entry.name);
147
+ if (!parsed) {
148
+ // A skill with no description can never be selected, since the
149
+ // description is the only thing the model sees.
150
+ logger.warn("skill has no description; skipping", { directory });
151
+ continue;
152
+ }
153
+ out.push({ ...parsed, directory, source });
154
+ }
155
+ return out;
156
+ }
157
+ /** Parse YAML-ish frontmatter. Only `name` and `description` are meaningful. */
158
+ export function parseSkillFile(raw, fallbackName) {
159
+ const { frontmatter, body } = splitFrontmatter(raw);
160
+ const name = frontmatter["name"]?.trim() || fallbackName;
161
+ const description = frontmatter["description"]?.trim();
162
+ if (!description)
163
+ return undefined;
164
+ return { name, description, body };
165
+ }
166
+ export function splitFrontmatter(raw) {
167
+ const normalized = raw.replace(/^/, "");
168
+ if (!normalized.startsWith("---"))
169
+ return { frontmatter: {}, body: normalized };
170
+ const end = normalized.indexOf("\n---", 3);
171
+ if (end === -1)
172
+ return { frontmatter: {}, body: normalized };
173
+ const block = normalized.slice(normalized.indexOf("\n") + 1, end);
174
+ const body = normalized.slice(normalized.indexOf("\n", end + 1) + 1);
175
+ const frontmatter = {};
176
+ let currentKey;
177
+ for (const line of block.split("\n")) {
178
+ const match = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line);
179
+ if (match) {
180
+ currentKey = match[1];
181
+ frontmatter[currentKey] = stripQuotes(match[2].trim());
182
+ continue;
183
+ }
184
+ // Continuation of a folded value, e.g. a description wrapped over lines.
185
+ if (currentKey && /^\s+\S/.test(line)) {
186
+ frontmatter[currentKey] = `${frontmatter[currentKey]} ${line.trim()}`.trim();
187
+ }
188
+ }
189
+ return { frontmatter, body };
190
+ }
191
+ function stripQuotes(value) {
192
+ if (value.length >= 2 && (value.startsWith('"') || value.startsWith("'"))) {
193
+ const quote = value[0];
194
+ if (value.endsWith(quote))
195
+ return value.slice(1, -1);
196
+ }
197
+ return value;
198
+ }
199
+ /** Relative paths of the files a skill ships, excluding SKILL.md itself. */
200
+ async function listSkillFiles(directory, prefix = "") {
201
+ let entries;
202
+ try {
203
+ entries = await readdir(directory, { withFileTypes: true });
204
+ }
205
+ catch {
206
+ return [];
207
+ }
208
+ const out = [];
209
+ for (const entry of entries) {
210
+ if (entry.name.startsWith("."))
211
+ continue;
212
+ const relative = prefix ? `${prefix}/${entry.name}` : entry.name;
213
+ if (entry.isDirectory()) {
214
+ // One level deep is enough to show structure without walking a tree.
215
+ if (prefix === "")
216
+ out.push(...(await listSkillFiles(path.join(directory, entry.name), relative)));
217
+ continue;
218
+ }
219
+ if (relative === SKILL_FILE)
220
+ continue;
221
+ out.push(relative);
222
+ }
223
+ return out.sort();
224
+ }
225
+ //# sourceMappingURL=skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/agent/skills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAwBjD,MAAM,UAAU,GAAG,UAAU,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,SAAS,gBAAgB;IACvB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC1F,CAAC;AACD,yEAAyE;AACzE,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAE5B,MAAM,OAAO,aAAa;IACP,MAAM,GAAG,IAAI,GAAG,EAAiB,CAAC;IAClC,MAAM,CAAS;IAEhC,YAAY,MAAe;QACzB,IAAI,CAAC,MAAM,GAAG,CAAC,MAAM,IAAI,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACrE,CAAC;IAED,QAAQ,CAAC,KAAY;QACnB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAChF,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;IAC1B,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,QAAQ,CAAC,GAAW,EAAE,YAAsB,EAAE;QAClD,MAAM,KAAK,GAAoD;YAC7D,iEAAiE;YACjE,yEAAyE;YACzE,EAAE,GAAG,EAAE,gBAAgB,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE;YAC9C,EAAE,GAAG,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE;YAC3C,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE;YACjE,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,QAAiB,EAAE,CAAC,CAAC;SACxF,CAAC;QAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,KAAK,MAAM,KAAK,IAAI,MAAM,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC5E,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,aAAa;QACX,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC3B,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QAE1C,MAAM,KAAK,GAAG;YACZ,qBAAqB;YACrB,EAAE;YACF,kFAAkF;YAClF,qFAAqF;YACrF,yFAAyF;YACzF,EAAE;SACH,CAAC;QACF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,IAAI,QAAQ,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAAY;IAC5C,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAEpD,MAAM,KAAK,GAAG;QACZ,gBAAgB,KAAK,CAAC,IAAI,IAAI;QAC9B,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE;QACjB,EAAE;QACF,mBAAmB,KAAK,CAAC,SAAS,EAAE;QACpC,wFAAwF;KACzF,CAAC;IAEF,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,sBAAsB,CAAC,CAAC;QACvC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QAC7E,IAAI,KAAK,CAAC,MAAM,GAAG,gBAAgB,EAAE,CAAC;YACpC,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,MAAM,GAAG,gBAAgB,OAAO,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,8EAA8E;AAE9E,KAAK,UAAU,aAAa,CAC1B,IAAY,EACZ,MAAuB,EACvB,MAAc;IAEd,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC,CAAC,uCAAuC;IACpD,CAAC;IAED,MAAM,GAAG,GAAY,EAAE,CAAC;IACxB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE;YAAE,SAAS;QAE9D,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QAC9C,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;gBAAE,SAAS;YAC7B,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACrC,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,CAAC,+CAA+C;QAC3D,CAAC;QAED,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,+DAA+D;YAC/D,gDAAgD;YAChD,MAAM,CAAC,IAAI,CAAC,oCAAoC,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;YACjE,SAAS;QACX,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,cAAc,CAC5B,GAAW,EACX,YAAoB;IAEpB,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAEpD,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,YAAY,CAAC;IACzD,MAAM,WAAW,GAAG,WAAW,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC;IACvD,IAAI,CAAC,WAAW;QAAE,OAAO,SAAS,CAAC;IAEnC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAI1C,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACzC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAEhF,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC3C,IAAI,GAAG,KAAK,CAAC,CAAC;QAAE,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAE7D,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IAClE,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAErE,MAAM,WAAW,GAA2B,EAAE,CAAC;IAC/C,IAAI,UAA8B,CAAC;IAEnC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,4BAA4B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtD,IAAI,KAAK,EAAE,CAAC;YACV,UAAU,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;YACvB,WAAW,CAAC,UAAU,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,CAAC;YACxD,SAAS;QACX,CAAC;QACD,yEAAyE;QACzE,IAAI,UAAU,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,WAAW,CAAC,UAAU,CAAC,GAAG,GAAG,WAAW,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;QAC/E,CAAC;IACH,CAAC;IAED,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;AAC/B,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC1E,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QACxB,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,4EAA4E;AAC5E,KAAK,UAAU,cAAc,CAAC,SAAiB,EAAE,MAAM,GAAG,EAAE;IAC1D,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACzC,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;QAEjE,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,qEAAqE;YACrE,IAAI,MAAM,KAAK,EAAE;gBAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;YACnG,SAAS;QACX,CAAC;QACD,IAAI,QAAQ,KAAK,UAAU;YAAE,SAAS;QACtC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC"}
@@ -0,0 +1,308 @@
1
+ import type { Message, Usage } from "../core/types.ts";
2
+ import type { AuditEntry } from "./permissions.ts";
3
+ import type { StoredArtifactRevision } from "../artifacts/store.ts";
4
+ import type { MessageRecord } from "../storage/records.ts";
5
+ export interface SessionMeta {
6
+ id: string;
7
+ createdAt: number;
8
+ updatedAt: number;
9
+ cwd: string;
10
+ model: string;
11
+ title?: string;
12
+ turns: number;
13
+ usage: Usage;
14
+ costUsd: number;
15
+ /** Session this one was branched from, if any. */
16
+ forkedFrom?: string;
17
+ /** How many messages the parent had at the branch point. */
18
+ forkedAt?: number;
19
+ /**
20
+ * The project the session belongs to — a repository, not a path. Sessions
21
+ * recorded before this existed are assigned one when they are read.
22
+ */
23
+ projectId?: string;
24
+ /**
25
+ * Bumped when the transcript is rewritten rather than appended to, which is
26
+ * what compaction does. A merge takes the highest generation whole: replaying
27
+ * appends from before a rewrite would resurrect what compaction dropped.
28
+ */
29
+ gen?: number;
30
+ /** Highest sequence number handed out in this session's log. */
31
+ seq?: number;
32
+ }
33
+ /**
34
+ * Cap on how much of an archive a single `recall` loads. Generous enough that
35
+ * ordinary sessions read in full, small enough that a runaway one cannot
36
+ * exhaust memory.
37
+ */
38
+ export declare const MAX_ARCHIVE_READ_BYTES: number;
39
+ /**
40
+ * Cap on how much of a permission trail one read loads. Smaller than the
41
+ * archive's: these are short lines, and a page showing the trail wants the
42
+ * recent end of it, not a session's entire history of tool calls.
43
+ */
44
+ export declare const MAX_DECISION_READ_BYTES: number;
45
+ export interface SessionRecord {
46
+ meta: SessionMeta;
47
+ messages: Message[];
48
+ }
49
+ /** One archived message, as written when compaction dropped it. */
50
+ export interface ArchivedMessage {
51
+ /** Position in the original transcript, stable across compactions. */
52
+ seq: number;
53
+ /** When the compaction that archived it ran. */
54
+ archivedAt: number;
55
+ message: Message;
56
+ }
57
+ /**
58
+ * Where sessions live.
59
+ *
60
+ * Keyed files under one root rather than a directory per session:
61
+ *
62
+ * storage/migration how many layout changes have run
63
+ * storage/device.json this machine's id, for merges
64
+ * storage/project/<projectId>.json a repository, by its root commit
65
+ * storage/session/<projectId>/<id>.json session metadata
66
+ * storage/message/<id>.jsonl the transcript, one record a line
67
+ * storage/archive/<id>.jsonl what compaction dropped
68
+ * storage/decision/<id>.jsonl every permission decision
69
+ *
70
+ * Grouping sessions under a project makes listing one project's history a
71
+ * single directory read instead of opening every session on the machine, and
72
+ * gives sync something to scope to. Messages stay in a flat log per session
73
+ * because appending is the hot path and appending must not rewrite.
74
+ */
75
+ /**
76
+ * A local write, as reported to whoever is watching.
77
+ *
78
+ * `meta` is carried on a deletion too, and captured before the files go: a
79
+ * tombstone has to travel or the other device restores what was deleted, and
80
+ * after `remove` there is nothing left to describe.
81
+ */
82
+ export interface StoreChange {
83
+ kind: "write" | "delete";
84
+ id: string;
85
+ projectId: string;
86
+ meta: SessionMeta;
87
+ }
88
+ export declare class SessionStore {
89
+ private readonly kv;
90
+ private readonly legacyRoot;
91
+ private ready;
92
+ private deviceId;
93
+ /** Which project a session was found under, so a second read is a direct hit. */
94
+ private readonly located;
95
+ private readonly watchers;
96
+ constructor(root?: string);
97
+ get root(): string;
98
+ /**
99
+ * Watch local writes. Returns the unsubscribe.
100
+ *
101
+ * Sync needs to know when to push, and the alternatives are worse: polling
102
+ * the directory turns an event into a delay, and calling a client from the
103
+ * write paths would put a network dependency inside the only thing here that
104
+ * must not be able to fail. A watcher is told what changed and decides for
105
+ * itself, so nothing above this file is reachable from below it.
106
+ *
107
+ * Listeners are called after the write has landed, and one that throws is
108
+ * ignored — a mirror falling over is not a reason to lose a transcript.
109
+ */
110
+ watch(listener: (change: StoreChange) => void): () => void;
111
+ private announce;
112
+ /** A session's transcript as records, ordered and deduplicated. */
113
+ recordsOf(id: string): Promise<MessageRecord[]>;
114
+ /** A session's metadata, without being told which project it is under. */
115
+ readMeta(id: string): Promise<SessionMeta | undefined>;
116
+ /**
117
+ * Take records that were written somewhere else.
118
+ *
119
+ * Distinct from `appendMessage`, which mints an id, a seq and this device —
120
+ * exactly the three fields that must survive the trip for a merge to work.
121
+ * The log is append-only and `orderRecords` deduplicates on read, so writing
122
+ * a record that is already held costs a line and changes nothing, which is
123
+ * what makes a retried pull safe.
124
+ */
125
+ mergeRecords(id: string, incoming: MessageRecord[]): Promise<number>;
126
+ /**
127
+ * Take metadata written somewhere else, if it is newer.
128
+ *
129
+ * Generation first, then wall clock. A generation is bumped by a rewrite, so
130
+ * a device that compacted a session wins over one that merely kept appending
131
+ * to it — no amount of clock skew should let a stale full transcript
132
+ * overwrite a summary that replaced it.
133
+ */
134
+ mergeMeta(incoming: SessionMeta): Promise<boolean>;
135
+ /** Layout changes run once, on first use, before anything reads. */
136
+ private prepare;
137
+ /**
138
+ * This machine's id.
139
+ *
140
+ * Two devices appending to one session need to be distinguishable, and the
141
+ * tiebreak has to survive restarts — so it is stored rather than generated
142
+ * per process.
143
+ */
144
+ private device;
145
+ private projectOf;
146
+ /** Find a session's metadata without being told which project it is in. */
147
+ private findMeta;
148
+ create(meta: SessionMeta): Promise<void>;
149
+ writeMeta(meta: SessionMeta): Promise<void>;
150
+ /**
151
+ * The write itself, without telling anyone.
152
+ *
153
+ * Applying what a pull returned goes through here: announcing it would have
154
+ * the mirror push back what it had just received, and while the server
155
+ * deduplicates that, a loop that is merely harmless is still a loop.
156
+ */
157
+ private putMeta;
158
+ private touchProject;
159
+ appendMessage(id: string, message: Message): Promise<void>;
160
+ /**
161
+ * Record one artifact revision on disk.
162
+ *
163
+ * A log rather than a document, for the same reason the transcript is one:
164
+ * a revision is a thing that happened, appending never rewrites what is
165
+ * already there, and a crashed process still leaves everything written
166
+ * before it. It also means the version history comes free — the file is the
167
+ * history, in order.
168
+ *
169
+ * Keyed by session alone, like `message` and `archive`, because an artifact
170
+ * belongs to the conversation that made it and is found the same way.
171
+ *
172
+ * This is what makes an artifact something other than a by-product of the
173
+ * transcript. A model's write was always recoverable, because the content is
174
+ * the argument of a tool call and tool calls are in the transcript. A
175
+ * revision a person made in the canvas is not a tool call and was therefore
176
+ * nowhere: a restart brought the model's version back as the newest one and
177
+ * said nothing about what it had replaced.
178
+ */
179
+ appendArtifactRevision(id: string, revision: StoredArtifactRevision): Promise<void>;
180
+ /**
181
+ * Take artifact revisions written on another device.
182
+ *
183
+ * The counterpart to `mergeRecords`, and deliberately its shape: dedupe on
184
+ * the record id, append what is new, and let the reader fold the file
185
+ * together. A revision already held costs a line and changes nothing, which
186
+ * is what makes a retried pull safe.
187
+ */
188
+ mergeArtifactRevisions(id: string, incoming: StoredArtifactRevision[]): Promise<number>;
189
+ /**
190
+ * Sessions that have written an artifact, whether or not they are loaded.
191
+ *
192
+ * A directory read, not a transcript read. Artifacts belong to sessions and
193
+ * a session's store is memory, so "everything on this machine" used to mean
194
+ * "everything currently open" — which is a listing that changes depending on
195
+ * which tabs happen to be open, and that is not a listing anybody can trust.
196
+ * The logs are the durable record; this says which ones exist so a caller
197
+ * can fold the ones it wants without opening a session to ask.
198
+ */
199
+ artifactSessions(): Promise<string[]>;
200
+ /**
201
+ * Every artifact revision recorded for a session, oldest first.
202
+ *
203
+ * `maxBytes` reads the tail, for a caller that is building a listing rather
204
+ * than restoring a session: the newest lines are the ones a listing shows,
205
+ * and a log with a megabyte of superseded revisions in it should not cost a
206
+ * megabyte to summarise.
207
+ */
208
+ artifactRevisions(id: string, maxBytes?: number): Promise<StoredArtifactRevision[]>;
209
+ /**
210
+ * Record one permission decision, with the session it happened in.
211
+ *
212
+ * Beside the transcript rather than in a file of its own somewhere else,
213
+ * because a decision is a thing that happened *in this conversation*: kept
214
+ * here it is found the way everything else about the session is found,
215
+ * deleted when the session is deleted, and readable next to the tool call it
216
+ * was about. A separate audit file is a second truth that has to be
217
+ * correlated by timestamp, and correlating by timestamp is how two records
218
+ * of the same event end up disagreeing.
219
+ *
220
+ * No seq and no device, unlike messages and artifact revisions: nothing
221
+ * merges this log. It stays on the machine that wrote it — see `SessionMirror`,
222
+ * which pushes messages and artifacts and deliberately not this.
223
+ */
224
+ appendDecision(id: string, entry: AuditEntry): Promise<void>;
225
+ /**
226
+ * A session's permission trail, oldest first.
227
+ *
228
+ * Bounded like the archive read: the trail grows for the life of a session —
229
+ * every tool call adds a line — and a long unattended run should not be able
230
+ * to make reading it the most expensive thing the harness does.
231
+ */
232
+ decisions(id: string, maxBytes?: number): Promise<AuditEntry[]>;
233
+ /**
234
+ * Append messages to the archive before they leave the live transcript.
235
+ *
236
+ * Compaction replaces history with a lossy summary; without this the
237
+ * originals would be unrecoverable — not just to the agent, but to anyone
238
+ * debugging the session afterwards. The archive is append-only and is never
239
+ * rewritten.
240
+ */
241
+ archiveMessages(id: string, entries: Array<{
242
+ seq: number;
243
+ message: Message;
244
+ }>): Promise<void>;
245
+ /**
246
+ * Read the archive, newest-biased and bounded.
247
+ *
248
+ * The archive is append-only and grows for the life of a session — every
249
+ * compaction and every oversized tool result adds to it. Reading all of it
250
+ * into memory to answer one `recall` would eventually be the most expensive
251
+ * thing the harness does, so only the tail is loaded past a threshold.
252
+ */
253
+ readArchive(id: string, maxBytes?: number): Promise<ArchivedMessage[]>;
254
+ /**
255
+ * Branch a session into a new one.
256
+ *
257
+ * The parent is never touched — that is the whole point. Exploring a
258
+ * different approach should not cost you the transcript that got you here,
259
+ * and mutating in place makes "go back and try the other thing" impossible.
260
+ */
261
+ fork(sourceId: string, opts?: {
262
+ keepMessages?: number;
263
+ title?: string;
264
+ newId?: string;
265
+ }): Promise<{
266
+ id: string;
267
+ meta: SessionMeta;
268
+ messages: Message[];
269
+ } | undefined>;
270
+ /** Permanently remove a session and everything it recorded. */
271
+ delete(id: string): Promise<boolean>;
272
+ /**
273
+ * Every session recorded in one tree, deleted.
274
+ *
275
+ * By working directory rather than by project id, because that is what the
276
+ * sidebar groups by and therefore what somebody is looking at when they ask
277
+ * for this. Deleting one at a time is the same work either way — there is no
278
+ * bulk primitive underneath — but each one still announces its own tombstone,
279
+ * so a device syncing with this one loses exactly the same set.
280
+ *
281
+ * Returns the ids that went, so a caller holding open tabs knows which of
282
+ * them are now pointing at nothing.
283
+ */
284
+ deleteIn(cwd: string): Promise<string[]>;
285
+ rename(id: string, title: string): Promise<boolean>;
286
+ /** Sessions branched from a given one. */
287
+ children(id: string): Promise<SessionMeta[]>;
288
+ /**
289
+ * Delete old sessions.
290
+ *
291
+ * Nothing reclaimed these before, so a machine used daily accumulates every
292
+ * transcript and archive it ever produced. Returns the ids removed.
293
+ */
294
+ prune(opts?: {
295
+ maxAgeDays?: number;
296
+ keep?: number;
297
+ }): Promise<string[]>;
298
+ /** Rewrite the transcript wholesale — used after compaction. */
299
+ replaceMessages(id: string, messages: Message[]): Promise<void>;
300
+ private records;
301
+ load(id: string): Promise<SessionRecord | undefined>;
302
+ list(limit?: number, opts?: {
303
+ projectId?: string;
304
+ }): Promise<SessionMeta[]>;
305
+ private backfillTitle;
306
+ /** Most recently updated session for a directory, for `--continue`. */
307
+ mostRecent(cwd?: string): Promise<SessionMeta | undefined>;
308
+ }