@manyoars/oars 0.2.0 → 0.4.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 (320) hide show
  1. package/README.md +147 -28
  2. package/dist/api.js +1 -1
  3. package/dist/bin.js +6 -29
  4. package/dist/cli.d.ts +10 -4
  5. package/dist/cli.js +440 -91
  6. package/dist/commands/keys.d.ts +31 -0
  7. package/dist/commands/keys.js +65 -0
  8. package/dist/commands/login.d.ts +1 -1
  9. package/dist/commands/login.js +3 -3
  10. package/dist/errors.d.ts +13 -13
  11. package/dist/errors.js +28 -11
  12. package/dist/gate/checks.d.ts +68 -0
  13. package/dist/gate/checks.js +584 -0
  14. package/dist/gate/command.d.ts +14 -0
  15. package/dist/gate/command.js +61 -0
  16. package/dist/gate/hook-fill-main.d.ts +1 -0
  17. package/dist/gate/hook-fill-main.js +16 -0
  18. package/dist/gate/hook-fill.d.ts +38 -0
  19. package/dist/gate/hook-fill.js +94 -0
  20. package/dist/gate/hook-main.d.ts +1 -0
  21. package/dist/gate/hook-main.js +47 -0
  22. package/dist/gate/hook-process.d.ts +24 -0
  23. package/dist/gate/hook-process.js +112 -0
  24. package/dist/gate/hook-record.d.ts +3 -0
  25. package/dist/gate/hook-record.js +8 -0
  26. package/dist/gate/hook.d.ts +48 -0
  27. package/dist/gate/hook.js +259 -0
  28. package/dist/gate/install.d.ts +25 -0
  29. package/dist/gate/install.js +126 -0
  30. package/dist/gate/local-bin.d.ts +8 -0
  31. package/dist/gate/local-bin.js +36 -0
  32. package/dist/gate/notes.d.ts +19 -0
  33. package/dist/gate/notes.js +27 -0
  34. package/dist/gate/novelty.d.ts +35 -0
  35. package/dist/gate/novelty.js +99 -0
  36. package/dist/gate/policy.d.ts +25 -0
  37. package/dist/gate/policy.js +73 -0
  38. package/dist/gate/record.d.ts +120 -0
  39. package/dist/gate/record.js +125 -0
  40. package/dist/gate/registry-state.d.ts +10 -0
  41. package/dist/gate/registry-state.js +30 -0
  42. package/dist/gate/registry.d.ts +56 -0
  43. package/dist/gate/registry.js +94 -0
  44. package/dist/gate/replay.d.ts +20 -0
  45. package/dist/gate/replay.js +254 -0
  46. package/dist/gate/report.d.ts +50 -0
  47. package/dist/gate/report.js +89 -0
  48. package/dist/gate/scope.d.ts +7 -0
  49. package/dist/gate/scope.js +27 -0
  50. package/dist/gate/transcripts.d.ts +68 -0
  51. package/dist/gate/transcripts.js +232 -0
  52. package/dist/hook-bin.d.ts +1 -0
  53. package/dist/hook-bin.js +50 -0
  54. package/dist/issuer.d.ts +10 -6
  55. package/dist/issuer.js +19 -24
  56. package/dist/main.d.ts +1 -0
  57. package/dist/main.js +67 -0
  58. package/dist/oidc.js +1 -1
  59. package/dist/sessions/actions.d.ts +35 -0
  60. package/dist/sessions/actions.js +429 -0
  61. package/dist/sessions/allow.d.ts +23 -0
  62. package/dist/sessions/allow.js +71 -0
  63. package/dist/sessions/archive.d.ts +11 -0
  64. package/dist/sessions/archive.js +38 -0
  65. package/dist/sessions/audit.d.ts +42 -0
  66. package/dist/sessions/audit.js +104 -0
  67. package/dist/sessions/baseline.d.ts +62 -0
  68. package/dist/sessions/baseline.js +190 -0
  69. package/dist/sessions/call-shape.d.ts +19 -0
  70. package/dist/sessions/call-shape.js +85 -0
  71. package/dist/sessions/capture-runtime.d.ts +10 -0
  72. package/dist/sessions/capture-runtime.js +12 -0
  73. package/dist/sessions/cell-increments.d.ts +47 -0
  74. package/dist/sessions/cell-increments.js +243 -0
  75. package/dist/sessions/cells.d.ts +109 -0
  76. package/dist/sessions/cells.js +261 -0
  77. package/dist/sessions/codex.d.ts +53 -0
  78. package/dist/sessions/codex.js +745 -0
  79. package/dist/sessions/command.d.ts +127 -0
  80. package/dist/sessions/command.js +419 -0
  81. package/dist/sessions/copilot.d.ts +40 -0
  82. package/dist/sessions/copilot.js +432 -0
  83. package/dist/sessions/coverage.d.ts +19 -0
  84. package/dist/sessions/coverage.js +56 -0
  85. package/dist/sessions/cursor.d.ts +24 -0
  86. package/dist/sessions/cursor.js +324 -0
  87. package/dist/sessions/derive.d.ts +57 -0
  88. package/dist/sessions/derive.js +386 -0
  89. package/dist/sessions/exclusion.d.ts +87 -0
  90. package/dist/sessions/exclusion.js +361 -0
  91. package/dist/sessions/extract.d.ts +181 -0
  92. package/dist/sessions/extract.js +1806 -0
  93. package/dist/sessions/fold.d.ts +17 -0
  94. package/dist/sessions/fold.js +71 -0
  95. package/dist/sessions/frame.d.ts +24 -0
  96. package/dist/sessions/frame.js +95 -0
  97. package/dist/sessions/gemini.d.ts +37 -0
  98. package/dist/sessions/gemini.js +613 -0
  99. package/dist/sessions/gold-arms.d.ts +67 -0
  100. package/dist/sessions/gold-arms.js +172 -0
  101. package/dist/sessions/gold.d.ts +197 -0
  102. package/dist/sessions/gold.js +463 -0
  103. package/dist/sessions/graph.d.ts +12 -0
  104. package/dist/sessions/graph.js +34 -0
  105. package/dist/sessions/grok.d.ts +36 -0
  106. package/dist/sessions/grok.js +421 -0
  107. package/dist/sessions/hook-calls.d.ts +3 -0
  108. package/dist/sessions/hook-calls.js +47 -0
  109. package/dist/sessions/hook-index.d.ts +66 -0
  110. package/dist/sessions/hook-index.js +164 -0
  111. package/dist/sessions/hooks.d.ts +17 -0
  112. package/dist/sessions/hooks.js +281 -0
  113. package/dist/sessions/identity.d.ts +27 -0
  114. package/dist/sessions/identity.js +131 -0
  115. package/dist/sessions/install.d.ts +70 -0
  116. package/dist/sessions/install.js +246 -0
  117. package/dist/sessions/lineage.d.ts +19 -0
  118. package/dist/sessions/lineage.js +118 -0
  119. package/dist/sessions/mcp.d.ts +7 -0
  120. package/dist/sessions/mcp.js +157 -0
  121. package/dist/sessions/merkle.d.ts +7 -0
  122. package/dist/sessions/merkle.js +28 -0
  123. package/dist/sessions/motif.d.ts +44 -0
  124. package/dist/sessions/motif.js +59 -0
  125. package/dist/sessions/opencode.d.ts +33 -0
  126. package/dist/sessions/opencode.js +319 -0
  127. package/dist/sessions/otel.d.ts +60 -0
  128. package/dist/sessions/otel.js +494 -0
  129. package/dist/sessions/package-books.d.ts +48 -0
  130. package/dist/sessions/package-books.js +120 -0
  131. package/dist/sessions/package-resolve.d.ts +32 -0
  132. package/dist/sessions/package-resolve.js +185 -0
  133. package/dist/sessions/pending.d.ts +34 -0
  134. package/dist/sessions/pending.js +142 -0
  135. package/dist/sessions/pi.d.ts +36 -0
  136. package/dist/sessions/pi.js +353 -0
  137. package/dist/sessions/pool-books.d.ts +21 -0
  138. package/dist/sessions/pool-books.js +75 -0
  139. package/dist/sessions/pool-choices.d.ts +5 -0
  140. package/dist/sessions/pool-choices.js +11 -0
  141. package/dist/sessions/pool-command.d.ts +115 -0
  142. package/dist/sessions/pool-command.js +669 -0
  143. package/dist/sessions/pool-delta.d.ts +54 -0
  144. package/dist/sessions/pool-delta.js +190 -0
  145. package/dist/sessions/pool-nightly-deps.d.ts +3 -0
  146. package/dist/sessions/pool-nightly-deps.js +11 -0
  147. package/dist/sessions/pool-nightly.d.ts +10 -0
  148. package/dist/sessions/pool-nightly.js +85 -0
  149. package/dist/sessions/pool-outbox.d.ts +76 -0
  150. package/dist/sessions/pool-outbox.js +313 -0
  151. package/dist/sessions/pool-print.d.ts +26 -0
  152. package/dist/sessions/pool-print.js +68 -0
  153. package/dist/sessions/pool-raw.d.ts +27 -0
  154. package/dist/sessions/pool-raw.js +68 -0
  155. package/dist/sessions/pool-replica.d.ts +35 -0
  156. package/dist/sessions/pool-replica.js +91 -0
  157. package/dist/sessions/pool-send-lock.d.ts +20 -0
  158. package/dist/sessions/pool-send-lock.js +87 -0
  159. package/dist/sessions/pool-sent.d.ts +4 -0
  160. package/dist/sessions/pool-sent.js +26 -0
  161. package/dist/sessions/pool-totals.d.ts +10 -0
  162. package/dist/sessions/pool-totals.js +21 -0
  163. package/dist/sessions/pool-wire.d.ts +435 -0
  164. package/dist/sessions/pool-wire.js +1100 -0
  165. package/dist/sessions/pool.d.ts +32 -0
  166. package/dist/sessions/pool.js +164 -0
  167. package/dist/sessions/projection.d.ts +75 -0
  168. package/dist/sessions/projection.js +165 -0
  169. package/dist/sessions/public-vocabulary.d.ts +72 -0
  170. package/dist/sessions/public-vocabulary.js +295 -0
  171. package/dist/sessions/query.d.ts +123 -0
  172. package/dist/sessions/query.js +268 -0
  173. package/dist/sessions/read-adoption.d.ts +2 -0
  174. package/dist/sessions/read-adoption.js +27 -0
  175. package/dist/sessions/read-call-shape.d.ts +2 -0
  176. package/dist/sessions/read-call-shape.js +25 -0
  177. package/dist/sessions/read-change.d.ts +2 -0
  178. package/dist/sessions/read-change.js +30 -0
  179. package/dist/sessions/read-cost.d.ts +4 -0
  180. package/dist/sessions/read-cost.js +48 -0
  181. package/dist/sessions/read-day-move.d.ts +3 -0
  182. package/dist/sessions/read-day-move.js +65 -0
  183. package/dist/sessions/read-fit.d.ts +7 -0
  184. package/dist/sessions/read-fit.js +41 -0
  185. package/dist/sessions/read-interaction.d.ts +5 -0
  186. package/dist/sessions/read-interaction.js +62 -0
  187. package/dist/sessions/read-lead-lag.d.ts +15 -0
  188. package/dist/sessions/read-lead-lag.js +81 -0
  189. package/dist/sessions/read-live.d.ts +6 -0
  190. package/dist/sessions/read-live.js +38 -0
  191. package/dist/sessions/read-method.d.ts +2 -0
  192. package/dist/sessions/read-method.js +29 -0
  193. package/dist/sessions/read-next-step.d.ts +2 -0
  194. package/dist/sessions/read-next-step.js +35 -0
  195. package/dist/sessions/read-order.d.ts +54 -0
  196. package/dist/sessions/read-order.js +62 -0
  197. package/dist/sessions/read-readable.d.ts +8 -0
  198. package/dist/sessions/read-readable.js +24 -0
  199. package/dist/sessions/read-recovery-edge.d.ts +2 -0
  200. package/dist/sessions/read-recovery-edge.js +26 -0
  201. package/dist/sessions/read-recovery.d.ts +20 -0
  202. package/dist/sessions/read-recovery.js +97 -0
  203. package/dist/sessions/read-rows.d.ts +13 -0
  204. package/dist/sessions/read-rows.js +47 -0
  205. package/dist/sessions/read-substitution.d.ts +5 -0
  206. package/dist/sessions/read-substitution.js +51 -0
  207. package/dist/sessions/read-support.d.ts +48 -0
  208. package/dist/sessions/read-support.js +73 -0
  209. package/dist/sessions/read-tool-day.d.ts +2 -0
  210. package/dist/sessions/read-tool-day.js +26 -0
  211. package/dist/sessions/read-version-boundary.d.ts +31 -0
  212. package/dist/sessions/read-version-boundary.js +95 -0
  213. package/dist/sessions/read.d.ts +86 -0
  214. package/dist/sessions/read.js +87 -0
  215. package/dist/sessions/recall-bundle.d.ts +12 -0
  216. package/dist/sessions/recall-bundle.js +33 -0
  217. package/dist/sessions/recall-consequence.d.ts +45 -0
  218. package/dist/sessions/recall-consequence.js +179 -0
  219. package/dist/sessions/recall-frames.d.ts +18 -0
  220. package/dist/sessions/recall-frames.js +44 -0
  221. package/dist/sessions/recall-next.d.ts +145 -0
  222. package/dist/sessions/recall-next.js +241 -0
  223. package/dist/sessions/recall-object.d.ts +139 -0
  224. package/dist/sessions/recall-object.js +78 -0
  225. package/dist/sessions/recall-pool.d.ts +19 -0
  226. package/dist/sessions/recall-pool.js +96 -0
  227. package/dist/sessions/recall-push.d.ts +26 -0
  228. package/dist/sessions/recall-push.js +47 -0
  229. package/dist/sessions/recall-reads.d.ts +124 -0
  230. package/dist/sessions/recall-reads.js +417 -0
  231. package/dist/sessions/recall-render.d.ts +19 -0
  232. package/dist/sessions/recall-render.js +168 -0
  233. package/dist/sessions/recovery-calls.d.ts +21 -0
  234. package/dist/sessions/recovery-calls.js +25 -0
  235. package/dist/sessions/redact.d.ts +17 -0
  236. package/dist/sessions/redact.js +113 -0
  237. package/dist/sessions/repo-root.d.ts +10 -0
  238. package/dist/sessions/repo-root.js +101 -0
  239. package/dist/sessions/schema.d.ts +694 -0
  240. package/dist/sessions/schema.js +167 -0
  241. package/dist/sessions/session-shape.d.ts +76 -0
  242. package/dist/sessions/session-shape.js +355 -0
  243. package/dist/sessions/sources.d.ts +50 -0
  244. package/dist/sessions/sources.js +233 -0
  245. package/dist/sessions/steps.d.ts +105 -0
  246. package/dist/sessions/steps.js +250 -0
  247. package/dist/sessions/store.d.ts +309 -0
  248. package/dist/sessions/store.js +1498 -0
  249. package/dist/sessions/strata.d.ts +26 -0
  250. package/dist/sessions/strata.js +77 -0
  251. package/dist/sessions/survival.d.ts +27 -0
  252. package/dist/sessions/survival.js +230 -0
  253. package/dist/sessions/transcript-tail.d.ts +14 -0
  254. package/dist/sessions/transcript-tail.js +64 -0
  255. package/dist/sessions/verify.d.ts +53 -0
  256. package/dist/sessions/verify.js +286 -0
  257. package/dist/sessions/vocab-load.d.ts +26 -0
  258. package/dist/sessions/vocab-load.js +61 -0
  259. package/dist/sessions/vscode-copilot.d.ts +38 -0
  260. package/dist/sessions/vscode-copilot.js +494 -0
  261. package/dist/telemetry/command.d.ts +8 -0
  262. package/dist/telemetry/command.js +85 -0
  263. package/dist/telemetry/consent.d.ts +17 -0
  264. package/dist/telemetry/consent.js +63 -0
  265. package/dist/telemetry/crash.d.ts +8 -0
  266. package/dist/telemetry/crash.js +52 -0
  267. package/dist/telemetry/emit.d.ts +31 -0
  268. package/dist/telemetry/emit.js +42 -0
  269. package/dist/telemetry/fields.d.ts +16 -0
  270. package/dist/telemetry/fields.js +26 -0
  271. package/dist/telemetry/health.d.ts +21 -0
  272. package/dist/telemetry/health.js +108 -0
  273. package/dist/telemetry/hook-emit.d.ts +4 -0
  274. package/dist/telemetry/hook-emit.js +18 -0
  275. package/dist/telemetry/measures.d.ts +32 -0
  276. package/dist/telemetry/measures.js +128 -0
  277. package/dist/telemetry/observe.d.ts +13 -0
  278. package/dist/telemetry/observe.js +51 -0
  279. package/dist/telemetry/read-events.d.ts +9 -0
  280. package/dist/telemetry/read-events.js +17 -0
  281. package/dist/telemetry/record.d.ts +17 -0
  282. package/dist/telemetry/record.js +26 -0
  283. package/dist/telemetry/report.d.ts +77 -0
  284. package/dist/telemetry/report.js +188 -0
  285. package/dist/telemetry/schema.d.ts +40 -0
  286. package/dist/telemetry/schema.js +187 -0
  287. package/dist/telemetry/send-main.d.ts +1 -0
  288. package/dist/telemetry/send-main.js +11 -0
  289. package/dist/telemetry/send.d.ts +36 -0
  290. package/dist/telemetry/send.js +172 -0
  291. package/dist/telemetry/spool.d.ts +17 -0
  292. package/dist/telemetry/spool.js +70 -0
  293. package/dist/telemetry/wire.d.ts +25 -0
  294. package/dist/telemetry/wire.js +76 -0
  295. package/package.json +4 -5
  296. package/vocab/crates.json +1 -0
  297. package/vocab/go.json +1 -0
  298. package/vocab/harness.json +1 -0
  299. package/vocab/homebrew.json +1 -0
  300. package/vocab/manifest.json +1 -0
  301. package/vocab/maven.json +1 -0
  302. package/vocab/models.json +1 -0
  303. package/vocab/npm.json +1 -0
  304. package/vocab/programs.json +1 -0
  305. package/vocab/pypi.json +1 -0
  306. package/vocab/rubygems.json +1 -0
  307. package/dist/commands/deploy-input.d.ts +0 -38
  308. package/dist/commands/deploy-input.js +0 -150
  309. package/dist/commands/deploy.d.ts +0 -22
  310. package/dist/commands/deploy.js +0 -124
  311. package/dist/commands/status.d.ts +0 -7
  312. package/dist/commands/status.js +0 -18
  313. package/dist/commands/templates.d.ts +0 -11
  314. package/dist/commands/templates.js +0 -32
  315. package/dist/commands/test-charge.d.ts +0 -11
  316. package/dist/commands/test-charge.js +0 -34
  317. package/dist/journal.d.ts +0 -26
  318. package/dist/journal.js +0 -58
  319. package/parity/deploy-plans/paid-api.json +0 -34
  320. package/parity/deploy-plans/per-task-agent.json +0 -34
@@ -0,0 +1,20 @@
1
+ import { type FileHandle } from 'node:fs/promises';
2
+ export interface LockTimes {
3
+ /** A lock file untouched for longer than this was left by a run that died; its holder touches it every quarter of this. */
4
+ staleMs: number;
5
+ /** How long a run waits for the lock before it gives up. */
6
+ waitMs: number;
7
+ pollMs: number;
8
+ }
9
+ /** What a test replaces: `stat` to read a lock as stale at the moment a waiter looks, `open` to make the create fail the way Windows does, `platform` to pick the platform's rules. */
10
+ export interface LockFs {
11
+ stat: (path: string) => Promise<{
12
+ mtimeMs: number;
13
+ }>;
14
+ open?: (path: string, flags: 'wx') => Promise<FileHandle>;
15
+ platform?: NodeJS.Platform;
16
+ }
17
+ export declare class SendLockBusy extends Error {
18
+ constructor();
19
+ }
20
+ export declare const withSendLock: <T>(outbox: string, run: () => Promise<T>, times?: Partial<LockTimes>, fs?: LockFs) => Promise<T>;
@@ -0,0 +1,87 @@
1
+ // One sender at a time per outbox (t884): the nightly task and `deposit --send` both take this lock, so a batch is never posted twice by two runs that overlap. The lock is a file created
2
+ // with `wx` beside `sent.log`, so it holds across processes as well as inside one. A run that finds it held waits; a lock older than `staleMs` belongs to a run that died, and is taken over by one waiter only, under a guard file. The holder touches the file while it runs, so a long send (a first night of raw parts) is never mistaken for a dead run, and it removes the file only if the file still names its own run.
3
+ import { randomUUID } from 'node:crypto';
4
+ import { mkdir, open, readFile, rm, stat, utimes } from 'node:fs/promises';
5
+ import { join } from 'node:path';
6
+ import { setTimeout as sleep } from 'node:timers/promises';
7
+ const DEFAULT_TIMES = { staleMs: 10 * 60 * 1000, waitMs: 2 * 60 * 1000, pollMs: 50 };
8
+ export class SendLockBusy extends Error {
9
+ constructor() {
10
+ super('Another oars run is sending from this outbox.');
11
+ }
12
+ }
13
+ // EEXIST means another run holds the file. On Windows an open of a file whose delete is still pending (the holder just released it) answers EPERM or EACCES instead: the same contention, so retry within waitMs.
14
+ const contended = (error, platform) => {
15
+ if (!(error instanceof Error))
16
+ return false;
17
+ const code = Reflect.get(error, 'code');
18
+ return code === 'EEXIST' || (platform === 'win32' && (code === 'EPERM' || code === 'EACCES'));
19
+ };
20
+ /**
21
+ * A stale lock is removed under a takeover guard (`send.lock.takeover`, created with `wx`), so one waiter at a time reads the lock again and removes it only if it is still stale. A live lock is never moved
22
+ * or removed: a waiter that read a lock as stale, but meets a lock another run took over and refreshed in the meantime, finds it fresh under the guard and leaves it. A guard left by a taker that died is
23
+ * removed once it is older than `staleMs`.
24
+ */
25
+ const takeOver = async (file, staleMs, fs) => {
26
+ const guard = `${file}.takeover`;
27
+ try {
28
+ await (await open(guard, 'wx')).close();
29
+ }
30
+ catch {
31
+ const left = await stat(guard).catch(() => undefined);
32
+ if (left !== undefined && Date.now() - left.mtimeMs > staleMs)
33
+ await rm(guard, { force: true });
34
+ return false;
35
+ }
36
+ try {
37
+ const held = await fs.stat(file).catch(() => undefined);
38
+ if (held === undefined || Date.now() - held.mtimeMs <= staleMs)
39
+ return false;
40
+ await rm(file, { force: true });
41
+ return true;
42
+ }
43
+ finally {
44
+ await rm(guard, { force: true });
45
+ }
46
+ };
47
+ export const withSendLock = async (outbox, run, times = {}, fs = { stat }) => {
48
+ const { staleMs, waitMs, pollMs } = { ...DEFAULT_TIMES, ...times };
49
+ const file = join(outbox, 'send.lock');
50
+ const runId = randomUUID();
51
+ const create = fs.open ?? open;
52
+ const platform = fs.platform ?? process.platform;
53
+ await mkdir(outbox, { recursive: true });
54
+ const giveUp = Date.now() + waitMs;
55
+ for (;;) {
56
+ try {
57
+ const handle = await create(file, 'wx');
58
+ await handle.writeFile(runId);
59
+ await handle.close();
60
+ break;
61
+ }
62
+ catch (error) {
63
+ if (!contended(error, platform))
64
+ throw error;
65
+ }
66
+ const held = await fs.stat(file).catch(() => undefined);
67
+ // A takeover that cleared the lock goes straight back to taking it; any other outcome waits, and gives up once waitMs has passed.
68
+ if (held !== undefined && Date.now() - held.mtimeMs > staleMs && (await takeOver(file, staleMs, fs)))
69
+ continue;
70
+ if (Date.now() >= giveUp)
71
+ throw new SendLockBusy();
72
+ await sleep(pollMs);
73
+ }
74
+ const beat = setInterval(() => {
75
+ const now = new Date();
76
+ utimes(file, now, now).catch(() => undefined);
77
+ }, Math.max(1, Math.floor(staleMs / 4)));
78
+ try {
79
+ return await run();
80
+ }
81
+ finally {
82
+ clearInterval(beat);
83
+ // The file is ours only while it still names this run; a lock another run took over is theirs.
84
+ if ((await readFile(file, 'utf8').catch(() => undefined)) === runId)
85
+ await rm(file, { force: true });
86
+ }
87
+ };
@@ -0,0 +1,4 @@
1
+ /** Each hash that was sent, with the time it was sent. A hash sent twice keeps its first time. */
2
+ export declare const sentLog: (outbox: string) => Promise<Map<string, string>>;
3
+ export declare const sentHashes: (outbox: string) => Promise<Set<string>>;
4
+ export declare const markSent: (outbox: string, hash: string, at: number) => Promise<void>;
@@ -0,0 +1,26 @@
1
+ // What the nightly send has already sent, so a night after a failure sends only what was not taken: one line for each hash, in the outbox beside `shown.log`. A batch that is rebuilt hashes differently and so is sent again, which is what a deposit that changed should do.
2
+ import { appendFile, mkdir, readFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { isMissing } from './pool-outbox.js';
5
+ const sentFile = (outbox) => join(outbox, 'sent.log');
6
+ /** Each hash that was sent, with the time it was sent. A hash sent twice keeps its first time. */
7
+ export const sentLog = async (outbox) => {
8
+ const log = new Map();
9
+ try {
10
+ for (const line of (await readFile(sentFile(outbox), 'utf8')).split('\n')) {
11
+ const [hash = '', at = ''] = line.split(' ');
12
+ if (hash !== '' && !log.has(hash))
13
+ log.set(hash, at);
14
+ }
15
+ }
16
+ catch (error) {
17
+ if (!isMissing(error))
18
+ throw error;
19
+ }
20
+ return log;
21
+ };
22
+ export const sentHashes = async (outbox) => new Set((await sentLog(outbox)).keys());
23
+ export const markSent = async (outbox, hash, at) => {
24
+ await mkdir(outbox, { recursive: true });
25
+ await appendFile(sentFile(outbox), `${hash} ${new Date(at).toISOString()}\n`, 'utf8');
26
+ };
@@ -0,0 +1,10 @@
1
+ export interface OutboxTotals {
2
+ /** Batches of the current stamp whose hash `sent.log` holds. */
3
+ sentBatches: number;
4
+ /** Raw copies of the current stamp whose hash `sent.log` holds. */
5
+ sentRaw: number;
6
+ /** Raw copies queued now, and the placeholders in their bodies: the values masked locally before anything is sent. */
7
+ rawCopies: number;
8
+ rawMasked: number;
9
+ }
10
+ export declare const outboxTotals: (storeDir: string) => Promise<OutboxTotals>;
@@ -0,0 +1,21 @@
1
+ // What the outboxes hold across every server, for `oars sessions verify`: how much of it was already sent, and how many values the raw copies masked. Counts only.
2
+ import { readdir } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { heldBatches } from './pool-outbox.js';
5
+ import { heldRaw } from './pool-raw.js';
6
+ import { sentHashes } from './pool-sent.js';
7
+ import { placeholders } from './redact.js';
8
+ export const outboxTotals = async (storeDir) => {
9
+ const root = join(storeDir, 'pool-outbox');
10
+ const totals = { sentBatches: 0, sentRaw: 0, rawCopies: 0, rawMasked: 0 };
11
+ for (const name of await readdir(root).catch(() => [])) {
12
+ const outbox = join(root, name);
13
+ const done = await sentHashes(outbox);
14
+ totals.sentBatches += (await heldBatches(outbox)).batches.filter(({ hash }) => done.has(hash)).length;
15
+ const { copies } = await heldRaw(outbox);
16
+ totals.sentRaw += copies.filter(({ hash }) => done.has(hash)).length;
17
+ totals.rawCopies += copies.length;
18
+ totals.rawMasked += copies.reduce((sum, { body }) => sum + placeholders(body), 0);
19
+ }
20
+ return totals;
21
+ };
@@ -0,0 +1,435 @@
1
+ import { VOCAB_VERSION, type RepoSize } from './public-vocabulary.js';
2
+ import { type ArgClass, type TargetClass } from './call-shape.js';
3
+ import type { Cell } from './cells.js';
4
+ import type { Redactor } from './redact.js';
5
+ import type { Motif } from './motif.js';
6
+ import type { Read, ReadAsOf } from './read.js';
7
+ import { type RecoveryCalls } from './recovery-calls.js';
8
+ import { type PromptIntervention, type Frame, type FrameStack, type Harness, type LanguageShare, type PromptClass, type PromptObservation, type SessionObservation, type StepEdge, type StepObservation, type ToolObservation, type ToolStatus, type Usage } from './schema.js';
9
+ export declare const SKELETON_FORMAT = 3;
10
+ /**
11
+ * The version of the field list: every field name a batch can hold (`BATCH_FIELDS` in `pool-outbox.ts`). It moves with any field added, so a server told the version knows which names a batch may carry,
12
+ * and a test pins the list to it (`pool-wire-additive.test.ts`). A field is deprecated and left on the list, never removed (conclusion section 17, rule 3).
13
+ */
14
+ export declare const FIELD_LIST_VERSION = 8;
15
+ export declare const BUNDLE_FORMAT = 1;
16
+ /** One constant: the pool serves only the vocabulary version the client holds. */
17
+ export { VOCAB_VERSION };
18
+ /**
19
+ * Section 7's edges. A bucket is the count of edges a value reaches, so bucket 0 is under the first edge and the last is the first edge's doubling past the cap. `tokens`: under 1k, then
20
+ * doubling to 1.024M and over. `ms`: under 100 ms, then doubling to 6,553.6 s and over. `seconds`: under 1 s, then doubling to 65,536 s and over. `count`: 0, 1, 2, 3-4, 5-8, 9-16, 17-32, over 32.
21
+ */
22
+ declare const EDGES: {
23
+ readonly tokens: number[];
24
+ readonly ms: number[];
25
+ readonly seconds: number[];
26
+ readonly count: readonly [1, 2, 3, 5, 9, 17, 33];
27
+ readonly calls: readonly [1, 2, 3, 5, 9, 17, 33, 65, 129, 257, 513, 1025, 2049, 4097];
28
+ };
29
+ export type BucketKind = keyof typeof EDGES;
30
+ /** The bucket a value falls in. */
31
+ export declare const bucketOf: (kind: BucketKind, value: number) => number;
32
+ /** A stored `explore` count as it pools: its `calls` bucket, or null for a value no count holds (a record captured before the strata has none). The exact count never leaves. */
33
+ export declare const projectExplore: (stored: number | undefined) => number | null;
34
+ /** The lowest value of a bucket, which is what a derivation counts a bucketed value as: it never reads above what the sender held. */
35
+ export declare const floorOf: (kind: BucketKind, bucket: number) => number;
36
+ /** The closed list of how a step came about (`schema.ts` `StepEdge`). */
37
+ export declare const EDGE_KINDS: readonly ["next", "spawned", "retried", "after-error"];
38
+ /** One edge arriving at a step: its kind, and the position of the step it names, counted over the steps of one transcript in call order (the batch's own ordinal, never a call number). A `spawned` edge names a step of the spawning transcript. */
39
+ export interface SkeletonEdge {
40
+ /** `other` for a kind the reader does not know (see `OTHER`); it names no step the parser can check, so only its shape is held. */
41
+ kind: StepEdge['kind'] | typeof OTHER;
42
+ target: number;
43
+ }
44
+ /**
45
+ * A step without what a server can compute: `n` is its call's position, `seg` its place in the call (the array position), and `frameId` hashes a frame the skeleton holds. Its cost is
46
+ * two buckets, never the exact counts. `parent` is the position of the spawning call in the `tools` of the transcript its transcript's `spawnedIn` names (Section 1.2's `parent` ordinal).
47
+ */
48
+ export type SkeletonStep = Omit<StepObservation, 'n' | 'seg' | 'frameId' | 'cost' | 'edges' | 'status'> & {
49
+ /** The edges that arrive at this step, `retried` already decided by the client (the pool holds no raw key to compare); absent when none arrive. */
50
+ edges?: SkeletonEdge[];
51
+ /** `other` for a status the reader does not know (see `OTHER`). */
52
+ status: ToolStatus | typeof OTHER;
53
+ /** A `tokens` bucket of the step's tokens. */
54
+ tokens: number | null;
55
+ /** An `ms` bucket of the step's time. */
56
+ ms: number | null;
57
+ /** True when `tokens` was divided among the calls of one response, not measured for this step; absent otherwise. */
58
+ allocated?: true;
59
+ /** A `tokens` bucket of the tokens the run spent after this step (spec Section 1.2): the bucket of the exact sum, never a sum of buckets, summed over the later steps that reported tokens (a lower bound). Null when steps follow and none reported tokens; 0 only when nothing follows or the later steps reported zero. */
60
+ remainingTokens: number | null;
61
+ /** The projected type of the subagent whose run holds the step, a built-in or `custom`; null on a main-run step. */
62
+ agentType: string | null;
63
+ /** The step's frame: a position in its transcript's `frames`, the frame of the call that made it, with that call's own model and harness version (t867). The parser refuses a position outside the list. */
64
+ frame: number;
65
+ /** The skill whose run the step's call sits in, the name after redaction or `private`; absent outside a run. The harness marks no end to a run but stops naming the skill on the next line, and a step carries the skill its call's line named. */
66
+ skill?: string;
67
+ };
68
+ /** A call's order is its position in `tools`, and its `prompt` is a position in the same transcript's `prompts`: ordinals inside the batch, never the session's own numbers. */
69
+ export interface SkeletonTool {
70
+ side: boolean;
71
+ prompt: number | null;
72
+ status: ToolStatus | typeof OTHER;
73
+ /** Exact, per tool: the one exact time on the wire, which W reads on `ledger` (Section 1.2, `durationMs`). A cell never reads it. */
74
+ durationMs: number | null;
75
+ /** The failed call's class, a member of `ERROR_CODES` (a user refusal, a policy denial, and a real failure are three codes); null on a call that did not fail and on one whose capture holds none. Never result text. */
76
+ errorCode: string | null;
77
+ /** For a failed call, what followed it: the actions of up to the next 5 calls of its prompt and whether an edit was among them. Absent on a call that did not fail. Never a signature, command, or text. */
78
+ recoveryCalls?: RecoveryCalls;
79
+ /** The call's input key names, sorted (t870): a public tool's from its pinned list in `TOOL_INPUT_KEYS`, any other tool's from the name gate, and none for a tool that went as `private`. Never a value. Absent when none is sent. */
80
+ keys?: string[];
81
+ /** A `count` bucket of the key names the call held that were not sent: off a public tool's pinned list, or kept `private` by the gate. Absent when none was dropped. */
82
+ keysDropped?: number;
83
+ /** The class of the call's path target, derived at capture from the target's base and suffix (`targetClassOf`): the base name itself does not go. Absent on a call captured before it. */
84
+ targetClass?: TargetClass | typeof OTHER;
85
+ /** The classes of the call's arguments as a sorted set, derived at capture (`argClassOf`); never a value. Absent on a call captured before it. */
86
+ argClass?: Array<ArgClass | typeof OTHER>;
87
+ steps: SkeletonStep[];
88
+ }
89
+ /**
90
+ * A prompt's usage as `tokens` buckets: `thinking` is a token count (the thinking tokens of the output), so it takes the same edges as the rest. A category the source
91
+ * never recorded is null, never the bucket of zero: a prompt with no counts at all sends five nulls, and a partial total sends a number only for the categories it observed.
92
+ */
93
+ export type SkeletonUsage = {
94
+ [field in keyof Usage]: number | null;
95
+ };
96
+ /** A language of the stack with its share of the touched files (spec Section 7): one of four buckets, never a count. */
97
+ export interface LanguageShareEntry {
98
+ language: string;
99
+ languageShare: LanguageShare | typeof OTHER;
100
+ }
101
+ /**
102
+ * The MCP servers, skills, and plugins in reach of a session, each the name after redaction or `private` (a plugin has no pinned list, so it is always `private`). `filled` says whether the harness recorded a
103
+ * `setup` or a `funnel` at all: an unfilled reach lists nothing and must not be read as a session with no tools.
104
+ */
105
+ export interface Reach {
106
+ filled: boolean;
107
+ mcp: string[];
108
+ skills: string[];
109
+ plugins: string[];
110
+ }
111
+ /** How many people the operator's organization has (an operator's setting, read locally, never looked up). */
112
+ export declare const ORG_SIZES: readonly ["solo", "2-10", "11-100", "101-1000", "over-1000", "unknown"];
113
+ export type OrgSize = (typeof ORG_SIZES)[number];
114
+ /** Whether the repository's local git log shows one commit author or several (distinct authors, counted and then dropped). */
115
+ export declare const TEAMS: readonly ["solo", "team", "unknown"];
116
+ export type Team = (typeof TEAMS)[number];
117
+ /** Whether the repository is public or private (an operator's setting, read locally, never looked up). */
118
+ export declare const REPO_VISIBILITIES: readonly ["public", "private", "unknown"];
119
+ export type RepoVisibility = (typeof REPO_VISIBILITIES)[number];
120
+ /** What the pool can state about whom a deposit covers. It is not a filter: no read uses it. Anything not read locally is `unknown`. */
121
+ export interface Coverage {
122
+ orgSize: OrgSize;
123
+ team: Team;
124
+ repoVisibility: RepoVisibility;
125
+ }
126
+ export declare const NO_COVERAGE: Coverage;
127
+ /** The UTC hour of day, 0 to 23, of a timestamp, whatever offset it was written in; null when there is none or it does not parse. */
128
+ export declare const hourOf: (ts: string | null | undefined) => number | null;
129
+ /**
130
+ * The frame a skeleton holds: the stored frame, with the stack's shares beside its languages. A language whose share is not known is in `languages` and has no entry here. `reach`, `repoKey`, and the coverage bands
131
+ * are on the transcript's frame and the frames of its list, not on a prompt's. `hour` is the UTC hour beside `day` and joins no cell. Every one is optional: an absent field was not sent.
132
+ */
133
+ export type SkeletonFrame = Omit<Frame, 'stack' | 'harness'> & {
134
+ harness: WireHarness;
135
+ stack: Omit<FrameStack, 'size'> & {
136
+ size: RepoSize | typeof OTHER | null;
137
+ languageShares: LanguageShareEntry[];
138
+ };
139
+ reach?: Reach;
140
+ repoKey?: string;
141
+ orgSize?: OrgSize | typeof OTHER;
142
+ team?: Team | typeof OTHER;
143
+ repoVisibility?: RepoVisibility | typeof OTHER;
144
+ hour?: number;
145
+ };
146
+ export interface SkeletonPrompt {
147
+ cls: PromptClass | typeof OTHER;
148
+ /** A `count` bucket. */
149
+ errors: number;
150
+ /** A `seconds` bucket. */
151
+ wallSec: number | null;
152
+ usage: SkeletonUsage;
153
+ /** A `calls` bucket of the prompt's steps that only look (`file.read`, `file.search`, `web.search`, `web.fetch`); null when the record holds no count. */
154
+ explore: number | null;
155
+ /** A `count` bucket of the responses that reported usage: what a partial `usage` sum covers. Absent only when the record predates the coverage (capture version 20). */
156
+ usageResponses?: number;
157
+ /** Whether a human stepped in after this prompt's work and how, one of the closed list (t882; docs/sessions-store.md "Prompt intervention"). Absent when the stored prompt predates the signal: absent means not computed, `unknown` means computed and not sayable. An outcome row, never a grade. */
158
+ intervention?: PromptIntervention | typeof OTHER;
159
+ frame: SkeletonFrame;
160
+ }
161
+ export interface SkeletonTranscript {
162
+ /** The transcript's Merkle root and the count of its lines it covers (Section 2, "Subagents"): one pair for each transcript, the main's first. */
163
+ merkleRoot: string;
164
+ leaves: number;
165
+ harness: WireHarness;
166
+ frame: SkeletonFrame;
167
+ /** The distinct frames the transcript's steps ran in, the transcript's own first; a step's `frame` is a position in it. */
168
+ frames: SkeletonFrame[];
169
+ /** The position, in the batch's `transcripts`, of the transcript that spawned this one; null for the main run, and for a subagent whose spawner the batch does not hold or the harness does not name. */
170
+ spawnedIn: number | null;
171
+ prompts: SkeletonPrompt[];
172
+ tools: SkeletonTool[];
173
+ }
174
+ /** What a delta entry may change (spec Section 4, rule 4): `permissionMode` and `effort` by name only, a `plugin` as added or removed, and a `package` as removed. */
175
+ export type DeltaField = 'plugin' | 'package' | 'permissionMode' | 'effort';
176
+ export type DeltaKind = 'added' | 'removed' | 'changed';
177
+ /** The one metric an entry's change is in: `callsPerSession` (tool calls in a session, or calls naming the package) in `count` buckets, `thinking` (thinking tokens in a prompt) in `tokens` buckets. */
178
+ export type DeltaMetric = 'callsPerSession' | 'thinking';
179
+ /**
180
+ * One change between a client's own before and after windows. It holds no `sid`, root, window date, or exact value: `name` is the public package or the word `private` (a plugin has no pinned registry, so
181
+ * always `private`), null when the field is named alone; `registry` is the package's registry and is null for a `private` name; `change` is the signed bucket of the metric's move, never the metric.
182
+ */
183
+ export interface DeltaEntry {
184
+ deltaKind: DeltaKind | typeof OTHER;
185
+ field: DeltaField | typeof OTHER;
186
+ name: string | null;
187
+ registry: string | null;
188
+ metric: DeltaMetric | typeof OTHER;
189
+ change: number;
190
+ }
191
+ export interface SessionSkeleton {
192
+ format: typeof SKELETON_FORMAT;
193
+ /** The vocabulary version the client built the batch under: stored with it, so a read can tell a batch of an older list from a current one. */
194
+ vocabVersion: string;
195
+ /** `FIELD_LIST_VERSION` as the client held it: the fields a batch of this version may carry. */
196
+ fieldsVersion: number;
197
+ /** A hash the client computes per account, so a redeposit replaces and two accounts' sessions cannot be joined. Never the session's own id. */
198
+ id: string;
199
+ /** The main transcript first, then each subagent's. */
200
+ transcripts: SkeletonTranscript[];
201
+ /** One for the batch: a hash of the main transcript's non-null call durations in call order, only with `MIN_TIMED_CALLS` of them; a subagent's calls never join it. */
202
+ timingPrint: string | null;
203
+ /** The batch's client-built delta entries (Section 2, "Deltas"): changes between the client's own before and after windows, each carried once, by the batch of the session that filled the after window. */
204
+ deltas: DeltaEntry[];
205
+ }
206
+ /** One main session and the subagents it spawned, as the local store holds them. */
207
+ export interface SessionRecords {
208
+ main: SessionObservation;
209
+ /** The main transcript first, then each subagent's, ordered by session so one store builds one batch. */
210
+ transcripts: {
211
+ session: SessionObservation;
212
+ prompts: PromptObservation[];
213
+ tools: ToolObservation[];
214
+ root: TranscriptRoot | null;
215
+ }[];
216
+ }
217
+ /** What a skeleton file holds of a transcript that may leave the machine: its root and the count of lines under it. */
218
+ export interface TranscriptRoot {
219
+ merkleRoot: string;
220
+ leaves: number;
221
+ }
222
+ /** W's minimum for a timing print (deposit-weighting spec, layer 5): fewer timed calls than this say too little to tell sessions apart. */
223
+ export declare const MIN_TIMED_CALLS = 50;
224
+ /** A session has ended once no transcript of it has gained a line for this long (`session_idle_hours`, spec Section 7). */
225
+ export declare const SESSION_IDLE_HOURS = 6;
226
+ /**
227
+ * Whether a session's batch may be built at `now`: no transcript of it, main or subagent, has gained a line for `SESSION_IDLE_HOURS`. A transcript gains a line when capture sees it change, which
228
+ * stamps a new `rev` in epoch milliseconds, but a transcript can grow after the last capture, so `written` (the newest write to any raw file of the session, 0 when none is known) counts too: the
229
+ * session was last active at the later of the two. A subagent that died without an end marker holds its session back at most that long.
230
+ */
231
+ export declare const hasEnded: (records: SessionRecords, now: number, written: number) => boolean;
232
+ /** Every main session of a store, with its records. */
233
+ export declare const sessionsOf: (store: {
234
+ sessions: readonly SessionObservation[];
235
+ subagents: readonly SessionObservation[];
236
+ prompts: readonly PromptObservation[];
237
+ tools: readonly ToolObservation[];
238
+ }, roots: ReadonlyMap<string, TranscriptRoot>) => SessionRecords[];
239
+ /** One command of a shell call as the projection reads it (`projectStoredShell`): a program, its subcommand, and its flags, each the name after redaction or `private`. */
240
+ export interface ShellCommand {
241
+ program: string;
242
+ subcommand: string | null;
243
+ flags: readonly string[];
244
+ }
245
+ /** Reads a stored shell call's commands in order. The client passes the projection; this file holds no vocabulary snapshot, so the Worker can bundle it. */
246
+ export type ShellReader = (tool: ToolObservation) => readonly ShellCommand[];
247
+ /** The most characters a name may hold on the wire. A longer value is not a name: it counts as redacted. */
248
+ export declare const NAME_MAX = 128;
249
+ /** Whether a value has the shape of a name (and so can be told from a command line, a path, or an email) and is within `NAME_MAX`. */
250
+ export declare const isWireName: (name: string) => boolean;
251
+ /** The shape of a pull's package name: a name, or a scope or module path with `/` in it. */
252
+ export declare const isWirePullName: (name: string) => boolean;
253
+ /** What the client holds against a value before it leaves: the redactor, and the exclusion terms the operator keeps. This is the one gate; the pool assigns every other class. */
254
+ export interface WireGate {
255
+ readonly redact: Redactor;
256
+ readonly terms: readonly string[];
257
+ }
258
+ /** No redaction and no terms: the name shape and the length limit still apply. A local read, which sends nothing, uses it. */
259
+ export declare const OPEN_GATE: WireGate;
260
+ /**
261
+ * A name as the wire holds it: the value itself, or `private` when it counts as redacted. A value counts as redacted when the redactor changes it, it holds an exclusion term, it fails the name shape, or it is over
262
+ * `NAME_MAX` characters, because a secret, an employer's name, or a mis-parsed command is not a name and nothing downstream can take it back. `keys` (t870) sends its names through this same function.
263
+ */
264
+ export declare const wireName: (gate: WireGate, name: string, hasShape?: (name: string) => boolean) => string;
265
+ /**
266
+ * A stored tool's steps as the wire holds them: each shell step's identity is replaced by the projected command, and every long-tail name (a native name, MCP server, skill, host, program, or pull name) is
267
+ * sent as it is after the gate, or `private` when it counts as redacted. A `private` program holds no pull. The one place a deposit and a local read take it from.
268
+ */
269
+ export declare const projectedSteps: (tool: ToolObservation, read: ShellReader, harness: Harness, gate?: WireGate) => StepObservation[];
270
+ /** The hash a batch's timing print is made with, over the durations in call order: the client's `contentHash` (D1's `hP`). */
271
+ export type Print = (durations: readonly number[]) => string;
272
+ /**
273
+ * The skeleton of a session; null when any record of it was captured before steps and frames, or a transcript of it has no root, which holds no closed reading to send. The batch holds the main
274
+ * transcript's run first, then each subagent's as its own run; its `id` is the main session's, and its one `timingPrint` is over the main transcript's calls alone.
275
+ */
276
+ export declare const skeletonOf: (records: SessionRecords, id: string, read: ShellReader, print: Print, deltas?: readonly DeltaEntry[], gate?: WireGate, coverage?: Coverage) => SessionSkeleton | null;
277
+ /**
278
+ * The version bucket of a session that recorded none: Cursor, Gemini, and Grok Build keep no harness version in their local files, and neither does a VS Code Copilot Chat session whose requests carry no `agent.extensionVersion`, so their cells are
279
+ * `<harness>/unknown/<model>`. It is the one non-numeric word a bundle can be asked for, so those harnesses are served the cells they have.
280
+ */
281
+ export declare const UNKNOWN_VERSION = "unknown";
282
+ /** A harness version a bundle can be asked for: public, so the request carries nothing private, or `unknown` for a harness with none on record. The admitted list replaces this shape rule when it ships. */
283
+ export declare const isBundleVersion: (version: string) => boolean;
284
+ /** Whether `name` is a harness the schema names. */
285
+ export declare const isHarness: (name: string) => name is Harness;
286
+ export interface Refusal {
287
+ ok: false;
288
+ field: string;
289
+ reason: string;
290
+ }
291
+ /** The value a reader gives an enumerated field whose value it does not know (conclusion section 17, rule 3). */
292
+ export declare const OTHER = "other";
293
+ /** The harness of a transcript or a frame: one the schema names, or `other` for one a newer client names. */
294
+ export type WireHarness = Harness | typeof OTHER;
295
+ /**
296
+ * What one parse sets aside: a field the reader does not know, a value it does not know, and a literal a client sent beside `other`. The bag is the stored form of these (the `extensions` column), the fields kept as sent and
297
+ * read by nothing that serves: no cell, read, or bundle is derived from it. `at` is the field's path, as a refusal names it.
298
+ */
299
+ export interface Extension {
300
+ at: string;
301
+ kind: 'field' | 'value' | 'literal';
302
+ value: unknown;
303
+ }
304
+ /** The fields a client may send a literal beside (`<field>Literal`) when the field itself says `other`: the enumerated fields, whose values a closed list holds. */
305
+ export declare const ENUMERATED_FIELDS: ReadonlySet<string>;
306
+ /**
307
+ * The fields that may be absent at format 3, by the level of the batch that holds them. An absent one reads as null, or as empty for a list, exactly as a sent null does, so a batch with all of them left out is
308
+ * served from what remains. A field the cells need to be sound (a harness, a status, a bucket of tokens) is never here.
309
+ */
310
+ export declare const OPTIONAL: {
311
+ readonly batch: readonly ["timingPrint", "deltas"];
312
+ readonly frame: readonly ["harnessVersion", "model", "effort", "day", "shell", "reach", "repoKey", "orgSize", "team", "repoVisibility", "hour"];
313
+ readonly stack: readonly ["size", "languageShares"];
314
+ readonly prompt: readonly ["wallSec", "explore", "usageResponses", "intervention"];
315
+ readonly tool: readonly ["prompt", "durationMs", "errorCode", "recoveryCalls", "keys", "keysDropped", "targetClass", "argClass"];
316
+ readonly step: readonly ["tokens", "ms", "allocated", "parent", "skill", "edges", "remainingTokens"];
317
+ readonly transcript: readonly ["spawnedIn"];
318
+ };
319
+ /** The most bytes (as JSON) one field this reader does not know, or one literal, may hold: an enumerated value or a literal never needs more, and a newer client's larger structure belongs in a new format version. The deposit's own cap is no part of this. */
320
+ export declare const MAX_EXTENSION_BYTES: number;
321
+ export declare const HARNESSES: readonly ["claude-code", "cursor", "codex", "copilot", "gemini", "grok", "opencode", "pi", "vscode-copilot", "otel"];
322
+ /** The registries a package comes from; a pull may also come from a skill, plugin, or MCP registry. */
323
+ export declare const PACKAGE_REGISTRIES: readonly ["npm", "pypi", "crates", "go", "rubygems", "homebrew"];
324
+ /** A parsed batch: the skeleton every derivation reads, the bag of what this reader did not know, and the count of values it read as `other`. Only the skeleton is ever served from. */
325
+ export interface ParsedSkeleton {
326
+ ok: true;
327
+ skeleton: SessionSkeleton;
328
+ extensions: Extension[];
329
+ others: number;
330
+ }
331
+ /** The skeleton in `value`, rebuilt from the fields read, with the bag of what was set aside; else the first field that is a shape error, and why. */
332
+ export declare const parseSkeleton: (value: unknown) => ParsedSkeleton | Refusal;
333
+ /**
334
+ * One count of a stack stratum (spec Section 4, rule 3), across every harness version, so it is never joined to one. The two pairs hold two values each, joined by a slash:
335
+ * `languageShare` is `<language>/<share bucket>` and `repoFilesExplore` is `<stack size, xs to xl>/<explore calls bucket>`.
336
+ */
337
+ export interface StrataCell {
338
+ dimension: 'language' | 'framework' | 'repoFiles' | 'languageShare' | 'repoFilesExplore';
339
+ value: string;
340
+ sessions: number;
341
+ clusters: number;
342
+ }
343
+ /**
344
+ * The floors a pool served the bundle at, stated so a client can print them and holds none of its own. `public` is the clusters a cell needs to be served and a read to count it, `disclosure` the clusters that must hold a signature's hash
345
+ * before its text may be requested, and `families` the read families that have a public floor of their own (a family not listed is at `public`). Both are the deploy's to set, so a client reads them here and never applies one.
346
+ */
347
+ export interface BundleFloors {
348
+ public: number;
349
+ disclosure: number;
350
+ families?: FamilyFloors;
351
+ }
352
+ /** A floor per read family, keyed by the family's name as the pool states it. A client applies none and prints only the families it knows, so a name this version has never heard of (a family a later pool adds) is carried as stated and not refused. */
353
+ export interface FamilyFloors {
354
+ [family: string]: number;
355
+ }
356
+ /** A number the pool can state, or the plain statement that nothing measures it yet. `no source yet` is never a zero: a measured nothing and an unmeasured one must not read alike. */
357
+ export type Sourced = {
358
+ state: 'value';
359
+ value: number;
360
+ } | {
361
+ state: 'no source yet';
362
+ reason: string;
363
+ };
364
+ /**
365
+ * The set of four (direction conclusion, section 20 rule 1), as totals. Served cells and the below-floor backlog are totals over the cells this bundle derived from; the backlog is one number and names no
366
+ * tuple, week, or value. The panel is one account count and names no account. `demand` and `goal` hold a place each for a source a later brief adds. `floor`, the versions, and `asOf` are its lineage (section 17 rule 6).
367
+ */
368
+ export interface Books {
369
+ floor: number;
370
+ format: typeof BUNDLE_FORMAT;
371
+ vocabVersion: string;
372
+ asOf: ReadAsOf;
373
+ served: {
374
+ total: number;
375
+ byWeek: {
376
+ week: string;
377
+ cells: number;
378
+ }[];
379
+ byTuple: {
380
+ tuple: string;
381
+ cells: number;
382
+ }[];
383
+ };
384
+ backlog: number;
385
+ panel: Sourced;
386
+ /** The UTC day the panel was counted: the pool reads its deposits once a day. */
387
+ panelDay: string | null;
388
+ demand: {
389
+ comparisonReadOwners: Sourced;
390
+ followOnReads: Sourced;
391
+ };
392
+ goal: {
393
+ readYield: Sourced;
394
+ costPerServedCell: Sourced;
395
+ yieldPerServedCell: Sourced;
396
+ };
397
+ }
398
+ /** One harness version's replica, as the server signs it. It names its harness, which the signature covers, so a bundle for one harness cannot stand in for another's. Nothing in it is keyed by stack. */
399
+ export interface Bundle {
400
+ format: typeof BUNDLE_FORMAT;
401
+ harness: Harness;
402
+ harnessVersion: string;
403
+ vocabVersion: string;
404
+ /** The public floor the pool applied (`floors.public`): every cell and read is at or above it except a family on a lower floor of its own, which states its floor on each read and in `floors.families`. */
405
+ floor: number;
406
+ asOf: ReadAsOf;
407
+ cells: Cell[];
408
+ motifs: Motif[];
409
+ reads: Read[];
410
+ strata: StrataCell[];
411
+ /** Additive: a bundle from a pool before the floors were bindings holds none, and a client that does not know the field ignores it. */
412
+ floors?: BundleFloors;
413
+ /** Absent in a bundle signed before the set of four existed, which `oars sessions books` reads as no pool books. */
414
+ books?: Books;
415
+ }
416
+ /** The signature covers these exact bytes, so nothing has to be canonicalized on either side. */
417
+ export interface SignedBundle {
418
+ payload: string;
419
+ /** Ed25519 over the payload's UTF-8 bytes, in base64. */
420
+ signature: string;
421
+ }
422
+ /** The public half of the pool's key, as a client pins it at login. */
423
+ export interface PinnedKey {
424
+ kty: string;
425
+ crv: string;
426
+ x: string;
427
+ }
428
+ /** The bundle, only when the pinned key signed these bytes for the harness and version that were asked for: another harness's or version's bundle, under a good signature, is still the wrong answer. */
429
+ export declare const verifyBundle: (signed: unknown, key: PinnedKey, harness: Harness, harnessVersion: string) => Promise<{
430
+ ok: true;
431
+ bundle: Bundle;
432
+ } | {
433
+ ok: false;
434
+ reason: string;
435
+ }>;