@manyoars/oars 0.5.4 → 0.5.5

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 (47) hide show
  1. package/README.md +4 -4
  2. package/dist/cli.js +18 -7
  3. package/dist/club.d.ts +2 -0
  4. package/dist/club.js +10 -4
  5. package/dist/sessions/archive.js +1 -1
  6. package/dist/sessions/card-command.d.ts +2 -2
  7. package/dist/sessions/card-command.js +7 -5
  8. package/dist/sessions/card-line.d.ts +6 -2
  9. package/dist/sessions/card-line.js +6 -3
  10. package/dist/sessions/cell-increments.d.ts +7 -0
  11. package/dist/sessions/cell-increments.js +25 -10
  12. package/dist/sessions/cells.d.ts +7 -0
  13. package/dist/sessions/cells.js +118 -107
  14. package/dist/sessions/crew-mark.d.ts +6 -0
  15. package/dist/sessions/crew-mark.js +22 -0
  16. package/dist/sessions/hook-index.d.ts +2 -1
  17. package/dist/sessions/hook-index.js +2 -2
  18. package/dist/sessions/pool-command.d.ts +22 -4
  19. package/dist/sessions/pool-command.js +516 -82
  20. package/dist/sessions/pool-delta.d.ts +5 -0
  21. package/dist/sessions/pool-delta.js +32 -12
  22. package/dist/sessions/pool-fold.d.ts +22 -0
  23. package/dist/sessions/pool-fold.js +65 -0
  24. package/dist/sessions/pool-outbox.d.ts +9 -0
  25. package/dist/sessions/pool-outbox.js +33 -12
  26. package/dist/sessions/pool.d.ts +10 -5
  27. package/dist/sessions/pool.js +16 -15
  28. package/dist/sessions/read-call-shape.js +3 -2
  29. package/dist/sessions/read-folds.d.ts +21 -0
  30. package/dist/sessions/read-folds.js +37 -0
  31. package/dist/sessions/read-recovery.d.ts +1 -1
  32. package/dist/sessions/read-recovery.js +6 -5
  33. package/dist/sessions/read-support.d.ts +4 -1
  34. package/dist/sessions/recall-consequence.d.ts +2 -1
  35. package/dist/sessions/recall-consequence.js +1 -2
  36. package/dist/sessions/recall-reads.d.ts +23 -1
  37. package/dist/sessions/recall-reads.js +91 -63
  38. package/dist/sessions/reversion.d.ts +41 -2
  39. package/dist/sessions/reversion.js +17 -10
  40. package/dist/sessions/store.d.ts +55 -2
  41. package/dist/sessions/store.js +312 -61
  42. package/dist/telemetry/measures.d.ts +5 -0
  43. package/dist/telemetry/measures.js +16 -2
  44. package/dist/telemetry/schema.js +3 -1
  45. package/dist/telemetry/words.d.ts +6 -0
  46. package/dist/telemetry/words.js +4 -0
  47. package/package.json +1 -1
@@ -160,7 +160,8 @@ const linesOf = (tools) => {
160
160
  }
161
161
  return [...lines.values()].map((line) => line.sort((a, b) => a.n - b.n));
162
162
  };
163
- export const deriveCells = (store, graph = NO_RECALLS) => {
163
+ /** `deriveCells` as a fold: each `add` tallies one run of sessions into the same tallies, and `finish` sorts them. The runs must come in the order the whole store holds its rows, so sums of floats match. */
164
+ export const cellFold = (graph = NO_RECALLS) => {
164
165
  const tallies = new Map();
165
166
  const motifs = new Map();
166
167
  const tallyAt = (motif, frameBucket, grain, day, exposure) => {
@@ -181,128 +182,138 @@ export const deriveCells = (store, graph = NO_RECALLS) => {
181
182
  // Fit's (name, platform, shell) cell: a week cell beside the versioned one, counting the same call.
182
183
  return isPlainCall(motif) ? [...tallies, tallyAt(motif, environmentBucketOf(frame), 'week', weekStartOf(day), exposure)] : tallies;
183
184
  };
184
- const sessions = new Map([...store.sessions, ...store.subagents].map((session) => [session.session, session]));
185
- const prompts = new Map(store.prompts.map((prompt) => [`${prompt.session}#${String(prompt.index)}`, prompt]));
186
- for (const line of linesOf(store.tools)) {
187
- const [first] = line;
188
- const session = first === undefined ? undefined : sessions.get(first.session);
189
- if (first === undefined || session === undefined)
190
- continue;
191
- const harness = harnessOf(session);
192
- const version = first.harnessVersion ?? session.env.harness;
193
- const prompt = prompts.get(`${first.session}#${String(first.prompt)}`);
194
- const frame = prompt === undefined
195
- ? (session.frame ?? oldFrame({ harness, version, model: mostUsedModel(session.models), effort: null, ts: first.ts ?? session.tsFirst, platform: session.env.os }))
196
- : framedPrompt(prompt, harness, version, first.ts, session.env.os);
197
- const placed = line.flatMap((tool) => graph.place(tool, stepsOfCall(tool, harness)));
198
- const steps = placed.map(({ step }) => step);
199
- for (const { motif, start } of motifsOf(steps)) {
200
- const window = steps.slice(start, start + motif.steps.length);
201
- const status = window.at(-1)?.status;
202
- const covered = placed.slice(start, start + motif.steps.length);
203
- for (const tally of talliesOf(motif, frame, exposureOf(covered.some(({ exposed }) => exposed), covered.some((step) => 'held' in step && step.held)))) {
204
- tally.sessions.add(first.parent);
205
- if (status === 'ok')
206
- tally.ok += 1;
207
- if (status === 'error')
208
- tally.failed += 1;
209
- for (const step of window) {
210
- // A step with no recorded cost has no tokens to count, so it joins neither the sum nor the coverage (its duration is still counted).
211
- if (step.tokens !== null) {
212
- tally.tokens += step.tokens;
213
- tally.recorded.add(first.parent);
214
- tally.recordedUnits += 1;
185
+ const add = (store) => {
186
+ const sessions = new Map([...store.sessions, ...store.subagents].map((session) => [session.session, session]));
187
+ const prompts = new Map(store.prompts.map((prompt) => [`${prompt.session}#${String(prompt.index)}`, prompt]));
188
+ for (const line of linesOf(store.tools)) {
189
+ const [first] = line;
190
+ const session = first === undefined ? undefined : sessions.get(first.session);
191
+ if (first === undefined || session === undefined)
192
+ continue;
193
+ const harness = harnessOf(session);
194
+ const version = first.harnessVersion ?? session.env.harness;
195
+ const prompt = prompts.get(`${first.session}#${String(first.prompt)}`);
196
+ const frame = prompt === undefined
197
+ ? (session.frame ?? oldFrame({ harness, version, model: mostUsedModel(session.models), effort: null, ts: first.ts ?? session.tsFirst, platform: session.env.os }))
198
+ : framedPrompt(prompt, harness, version, first.ts, session.env.os);
199
+ const placed = line.flatMap((tool) => graph.place(tool, stepsOfCall(tool, harness)));
200
+ const steps = placed.map(({ step }) => step);
201
+ for (const { motif, start } of motifsOf(steps)) {
202
+ const window = steps.slice(start, start + motif.steps.length);
203
+ const status = window.at(-1)?.status;
204
+ const covered = placed.slice(start, start + motif.steps.length);
205
+ for (const tally of talliesOf(motif, frame, exposureOf(covered.some(({ exposed }) => exposed), covered.some((step) => 'held' in step && step.held)))) {
206
+ tally.sessions.add(first.parent);
207
+ if (status === 'ok')
208
+ tally.ok += 1;
209
+ if (status === 'error')
210
+ tally.failed += 1;
211
+ for (const step of window) {
212
+ // A step with no recorded cost has no tokens to count, so it joins neither the sum nor the coverage (its duration is still counted).
213
+ if (step.tokens !== null) {
214
+ tally.tokens += step.tokens;
215
+ tally.recorded.add(first.parent);
216
+ tally.recordedUnits += 1;
217
+ }
218
+ tally.durationMs += step.durationMs;
215
219
  }
216
- tally.durationMs += step.durationMs;
217
220
  }
218
221
  }
219
- }
220
- // What followed each failed call that has a code (t868): a week cell per (code, failed action, next action).
221
- // A call is read by its first step's action, as a skeleton's is; a record captured before steps has the action its native name maps to.
222
- const calls = line.map((call) => ({ side: call.side, prompt: call.prompt, status: call.status, steps: stepsOfCall(call, harness) }));
223
- for (const [at, tool] of line.entries()) {
224
- const followed = recoveryCallsOf(calls, at);
225
- if (followed === null || tool.errorCode === undefined)
226
- continue;
227
- const code = ERROR_CODES.has(tool.errorCode) ? tool.errorCode : 'other';
228
- const motif = classMotif(recoveryClass(code, calls[at]?.steps[0]?.action ?? 'other', followed.next[0] ?? NO_NEXT));
229
- motifs.set(motif.motifId, motif);
230
- const tally = tallyAt(motif, versionBucketOf(frame), 'week', weekStartOf(frame.day ?? UNKNOWN), 'unknown');
231
- tally.sessions.add(first.parent);
232
- tally.failed += 1;
233
- }
234
- // Each call's shape, by its first step's action: a week cell per facet it holds, counted by the call's own status.
235
- for (const [at, tool] of line.entries()) {
236
- const action = calls[at]?.steps[0]?.action ?? 'other';
237
- for (const [facet, value] of shapeFacetsOf(tool)) {
238
- const motif = classMotif(shapeClass(action, facet, value));
222
+ // What followed each failed call that has a code (t868): a week cell per (code, failed action, next action).
223
+ // A call is read by its first step's action, as a skeleton's is; a record captured before steps has the action its native name maps to.
224
+ const calls = line.map((call) => ({ side: call.side, prompt: call.prompt, status: call.status, steps: stepsOfCall(call, harness) }));
225
+ for (const [at, tool] of line.entries()) {
226
+ const followed = recoveryCallsOf(calls, at);
227
+ if (followed === null || tool.errorCode === undefined)
228
+ continue;
229
+ const code = ERROR_CODES.has(tool.errorCode) ? tool.errorCode : 'other';
230
+ const motif = classMotif(recoveryClass(code, calls[at]?.steps[0]?.action ?? 'other', followed.next[0] ?? NO_NEXT));
239
231
  motifs.set(motif.motifId, motif);
240
232
  const tally = tallyAt(motif, versionBucketOf(frame), 'week', weekStartOf(frame.day ?? UNKNOWN), 'unknown');
241
233
  tally.sessions.add(first.parent);
242
- if (tool.status === 'ok')
243
- tally.ok += 1;
244
- if (tool.status === 'error')
245
- tally.failed += 1;
234
+ tally.failed += 1;
246
235
  }
247
- }
248
- // The failed calls by their `errorCode` (t868). Rule 6: a day cell exists for a known day only.
249
- const failedDay = frame.day ?? UNKNOWN;
250
- for (const tool of failedDay === UNKNOWN ? [] : line) {
251
- if (tool.status !== 'error' || tool.errorCode === undefined)
252
- continue;
253
- const code = ERROR_CODES.has(tool.errorCode) ? tool.errorCode : 'other';
254
- for (const counted of stepsOfCall(tool, harness)) {
255
- if (counted.status !== 'error')
236
+ // Each call's shape, by its first step's action: a week cell per facet it holds, counted by the call's own status.
237
+ for (const [at, tool] of line.entries()) {
238
+ const action = calls[at]?.steps[0]?.action ?? 'other';
239
+ for (const [facet, value] of shapeFacetsOf(tool)) {
240
+ const motif = classMotif(shapeClass(action, facet, value));
241
+ motifs.set(motif.motifId, motif);
242
+ const tally = tallyAt(motif, versionBucketOf(frame), 'week', weekStartOf(frame.day ?? UNKNOWN), 'unknown');
243
+ tally.sessions.add(first.parent);
244
+ if (tool.status === 'ok')
245
+ tally.ok += 1;
246
+ if (tool.status === 'error')
247
+ tally.failed += 1;
248
+ }
249
+ }
250
+ // The failed calls by their `errorCode` (t868). Rule 6: a day cell exists for a known day only.
251
+ const failedDay = frame.day ?? UNKNOWN;
252
+ for (const tool of failedDay === UNKNOWN ? [] : line) {
253
+ if (tool.status !== 'error' || tool.errorCode === undefined)
256
254
  continue;
257
- // The call's name at both levels: its action alone, and a named call (a flag or a pull would be a second dimension, so those steps are named by program and subcommand alone upstream).
258
- for (const { motif: named } of motifsOf([counted])) {
259
- if (named.level === 'named' && counted.identity === null)
255
+ const code = ERROR_CODES.has(tool.errorCode) ? tool.errorCode : 'other';
256
+ for (const counted of stepsOfCall(tool, harness)) {
257
+ if (counted.status !== 'error')
260
258
  continue;
261
- const name = motifName(named);
262
- for (const [prefix, bucket] of [[ERROR_CLASS, `${frame.harness}/${NO_AXES}/${frame.model ?? UNKNOWN}`], [ERROR_VERSION_CLASS, versionBucketOf(frame)]]) {
263
- const motif = classMotif(`${prefix}${code}/${name}`);
264
- motifs.set(motif.motifId, motif);
265
- const tally = tallyAt(motif, bucket, 'day', failedDay, 'unknown');
266
- tally.sessions.add(first.parent);
267
- tally.failed += 1;
268
- if (counted.tokens !== null) {
269
- tally.tokens += counted.tokens;
270
- tally.recorded.add(first.parent);
271
- tally.recordedUnits += 1;
259
+ // The call's name at both levels: its action alone, and a named call (a flag or a pull would be a second dimension, so those steps are named by program and subcommand alone upstream).
260
+ for (const { motif: named } of motifsOf([counted])) {
261
+ if (named.level === 'named' && counted.identity === null)
262
+ continue;
263
+ const name = motifName(named);
264
+ for (const [prefix, bucket] of [[ERROR_CLASS, `${frame.harness}/${NO_AXES}/${frame.model ?? UNKNOWN}`], [ERROR_VERSION_CLASS, versionBucketOf(frame)]]) {
265
+ const motif = classMotif(`${prefix}${code}/${name}`);
266
+ motifs.set(motif.motifId, motif);
267
+ const tally = tallyAt(motif, bucket, 'day', failedDay, 'unknown');
268
+ tally.sessions.add(first.parent);
269
+ tally.failed += 1;
270
+ if (counted.tokens !== null) {
271
+ tally.tokens += counted.tokens;
272
+ tally.recorded.add(first.parent);
273
+ tally.recordedUnits += 1;
274
+ }
275
+ tally.durationMs += counted.durationMs;
272
276
  }
273
- tally.durationMs += counted.durationMs;
274
277
  }
275
278
  }
276
279
  }
277
280
  }
278
- }
279
- for (const prompt of store.prompts) {
280
- const session = sessions.get(prompt.session);
281
- const frame = framedPrompt(prompt, harnessOf(session ?? {}), session?.env.harness ?? null, null, session?.env.os ?? UNKNOWN);
282
- const exposure = exposureOf(graph.exposedPrompt(prompt.session, prompt.index), statesExposure(graph) && graph.heldPrompt(prompt.session, prompt.index));
283
- const effort = frame.effort === null ? [] : talliesOf(classMotif(effortClass(frame.effort, prompt.usage.thinking)), frame, exposure, NO_AXES);
284
- for (const tally of [...talliesOf(classMotif(prompt.cls), frame, exposure), ...effort]) {
285
- tally.sessions.add(prompt.session);
286
- if (prompt.errors > 0)
287
- tally.failed += 1;
288
- else
289
- tally.ok += 1;
290
- // A prompt that recorded no input has no whole usage to count, and a category it never recorded is a placeholder zero, so neither enters the sum.
291
- if (observesUsage(prompt, 'input')) {
292
- tally.recorded.add(prompt.session);
293
- tally.recordedUnits += 1;
294
- for (const field of TOKEN_FIELDS)
295
- if (observesUsage(prompt, field))
296
- tally.tokens += prompt.usage[field];
281
+ for (const prompt of store.prompts) {
282
+ const session = sessions.get(prompt.session);
283
+ const frame = framedPrompt(prompt, harnessOf(session ?? {}), session?.env.harness ?? null, null, session?.env.os ?? UNKNOWN);
284
+ const exposure = exposureOf(graph.exposedPrompt(prompt.session, prompt.index), statesExposure(graph) && graph.heldPrompt(prompt.session, prompt.index));
285
+ const effort = frame.effort === null ? [] : talliesOf(classMotif(effortClass(frame.effort, prompt.usage.thinking)), frame, exposure, NO_AXES);
286
+ for (const tally of [...talliesOf(classMotif(prompt.cls), frame, exposure), ...effort]) {
287
+ tally.sessions.add(prompt.session);
288
+ if (prompt.errors > 0)
289
+ tally.failed += 1;
290
+ else
291
+ tally.ok += 1;
292
+ // A prompt that recorded no input has no whole usage to count, and a category it never recorded is a placeholder zero, so neither enters the sum.
293
+ if (observesUsage(prompt, 'input')) {
294
+ tally.recorded.add(prompt.session);
295
+ tally.recordedUnits += 1;
296
+ for (const field of TOKEN_FIELDS)
297
+ if (observesUsage(prompt, field))
298
+ tally.tokens += prompt.usage[field];
299
+ }
300
+ tally.durationMs += (prompt.wallSec ?? 0) * 1000;
297
301
  }
298
- tally.durationMs += (prompt.wallSec ?? 0) * 1000;
299
302
  }
300
- }
301
- const stated = statesExposure(graph);
302
- const cells = [...tallies.values()]
303
- .sort((a, b) => compare(a.motifId, b.motifId) || compare(a.frameBucket, b.frameBucket) || compare(a.grain, b.grain) || compare(a.day, b.day) || EXPOSURE_CODES[a.exposure] - EXPOSURE_CODES[b.exposure])
304
- .map((tally) => ({ motifId: tally.motifId, frameBucket: tally.frameBucket, grain: tally.grain, day: tally.day, sessions: tally.sessions.size, clusters: ONE_OPERATOR, ok: tally.ok, failed: tally.failed, tokens: tally.recorded.size === 0 ? null : tally.tokens, tokenSessions: tally.recorded.size, tokenPrompts: tally.recordedUnits, durationMs: tally.durationMs, exposed: tally.exposure === 'shown', ...(stated ? { exposure: tally.exposure } : {}) }));
305
- return { cells, motifs: [...motifs.values()].sort((a, b) => compare(a.motifId, b.motifId)) };
303
+ };
304
+ const finish = () => {
305
+ const stated = statesExposure(graph);
306
+ const cells = [...tallies.values()]
307
+ .sort((a, b) => compare(a.motifId, b.motifId) || compare(a.frameBucket, b.frameBucket) || compare(a.grain, b.grain) || compare(a.day, b.day) || EXPOSURE_CODES[a.exposure] - EXPOSURE_CODES[b.exposure])
308
+ .map((tally) => ({ motifId: tally.motifId, frameBucket: tally.frameBucket, grain: tally.grain, day: tally.day, sessions: tally.sessions.size, clusters: ONE_OPERATOR, ok: tally.ok, failed: tally.failed, tokens: tally.recorded.size === 0 ? null : tally.tokens, tokenSessions: tally.recorded.size, tokenPrompts: tally.recordedUnits, durationMs: tally.durationMs, exposed: tally.exposure === 'shown', ...(stated ? { exposure: tally.exposure } : {}) }));
309
+ return { cells, motifs: [...motifs.values()].sort((a, b) => compare(a.motifId, b.motifId)) };
310
+ };
311
+ return { add, finish };
312
+ };
313
+ export const deriveCells = (store, graph = NO_RECALLS) => {
314
+ const fold = cellFold(graph);
315
+ fold.add(store);
316
+ return fold.finish();
306
317
  };
307
318
  export const serializeCells = (cells) => cells.map((cell) => `${JSON.stringify(cell)}\n`).join('');
308
319
  /** The fields of an error row a recovery read depends on, as text: only those, so a change elsewhere is not a new pool, and sorted, so record order is not one either. */
@@ -0,0 +1,6 @@
1
+ /** The crew number held for `account` on `server`, or undefined: no file, another pool or account, a file that is not ours, or an account that is not in the crew. */
2
+ export declare const crewNumberOf: (storeDir: string, server: string, account: string) => Promise<number | undefined>;
3
+ /** Keeps what the pool said, replacing the earlier answer: the number, or none when the account holds none. */
4
+ export declare const keepCrewNumber: (storeDir: string, server: string, account: string, crewNumber: number | undefined) => Promise<void>;
5
+ /** The card's words for a crew number. */
6
+ export declare const crewMarkText: (crewNumber: number) => string;
@@ -0,0 +1,22 @@
1
+ // The founding crew number this machine last learned for a pool (t1229): `GET /v1/me` states it, and `oars pool invite` and `oars pool join` keep it here so the share card, which makes no network call, can show it. A number only; no count of members is ever stored or printed.
2
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { isObject, numberIn } from './pool-replica.js';
5
+ const fileOf = (storeDir) => join(storeDir, 'pool-crew.json');
6
+ /** The crew number held for `account` on `server`, or undefined: no file, another pool or account, a file that is not ours, or an account that is not in the crew. */
7
+ export const crewNumberOf = async (storeDir, server, account) => {
8
+ try {
9
+ const held = JSON.parse(await readFile(fileOf(storeDir), 'utf8'));
10
+ return isObject(held) && Reflect.get(held, 'server') === server && Reflect.get(held, 'account') === account ? numberIn(held, 'crewNumber') : undefined;
11
+ }
12
+ catch {
13
+ return undefined;
14
+ }
15
+ };
16
+ /** Keeps what the pool said, replacing the earlier answer: the number, or none when the account holds none. */
17
+ export const keepCrewNumber = async (storeDir, server, account, crewNumber) => {
18
+ await mkdir(storeDir, { recursive: true });
19
+ await writeFile(fileOf(storeDir), JSON.stringify({ server, account, ...(crewNumber === undefined ? {} : { crewNumber }) }));
20
+ };
21
+ /** The card's words for a crew number. */
22
+ export const crewMarkText = (crewNumber) => `founding crew #${String(crewNumber)}`;
@@ -1,6 +1,7 @@
1
1
  import type { Read } from './read.js';
2
2
  import type { Position, RecoveryEdges } from './recall-next.js';
3
3
  import type { NextByFrame } from './recall-frames.js';
4
+ import type { ReadFolds } from './read-folds.js';
4
5
  import { type RecallRead } from './recall-reads.js';
5
6
  import { type Store } from './store.js';
6
7
  /** A signature is "recurring" once it has been seen in this many sessions. */
@@ -47,7 +48,7 @@ export interface HookIndex {
47
48
  [frame: string]: Position[];
48
49
  };
49
50
  }
50
- export declare const buildIndex: (store: Store, redactDomains: readonly string[], at: string, reads?: readonly Read[], next?: NextByFrame) => HookIndex;
51
+ export declare const buildIndex: (store: Store, redactDomains: readonly string[], at: string, reads?: readonly Read[], next?: NextByFrame, folds?: ReadFolds) => HookIndex;
51
52
  export declare const indexPath: (storeDir: string) => string;
52
53
  /** The index as written, or undefined when there is none (or it is torn): a hook then says nothing. */
53
54
  export declare const readIndex: (storeDir: string) => Promise<HookIndex | undefined>;
@@ -33,7 +33,7 @@ const repositoryOf = (projects) => {
33
33
  return bases.find((candidate) => base === candidate || base.startsWith(`${candidate}-`)) ?? base;
34
34
  };
35
35
  };
36
- export const buildIndex = (store, redactDomains, at, reads = [], next) => {
36
+ export const buildIndex = (store, redactDomains, at, reads = [], next, folds) => {
37
37
  const projectOf = new Map(store.sessions.map((session) => [session.session, session.project]));
38
38
  // The latest 300 of each repository, not of the whole store: another repository's deposits must not move this one's ranking.
39
39
  const family = repositoryOf([...new Set(store.sessions.map((session) => session.project))]);
@@ -92,7 +92,7 @@ export const buildIndex = (store, redactDomains, at, reads = [], next) => {
92
92
  redactDomains: [...redactDomains],
93
93
  projects,
94
94
  signatures,
95
- fit: Object.fromEntries(resolveReads(aboveBaseRate(store, fitReads(store, PUSH_FLOORS)), reads).map((read) => [toolShapeId(read.tool, read.keys), read])),
95
+ fit: Object.fromEntries(resolveReads(folds?.push ?? aboveBaseRate(store, fitReads(store, PUSH_FLOORS)), reads).map((read) => [toolShapeId(read.tool, read.keys), read])),
96
96
  ...(next === undefined ? {} : { recoveryByFrame: next.recovery, positionsByFrame: next.positions }),
97
97
  };
98
98
  };
@@ -70,6 +70,7 @@ export declare const poolLogin: (deps: RunDeps, options: {
70
70
  server?: string | undefined;
71
71
  provider?: string | undefined;
72
72
  verbose?: boolean;
73
+ signal?: AbortSignal;
73
74
  }) => Promise<Outcome>;
74
75
  /**
75
76
  * `oars pool logout` (S8): revokes this machine's credential at the pool (`DELETE /v1/login`), then removes the local entry. A pool that cannot be reached does not keep the entry: the person asked to be signed out here, so the entry
@@ -117,6 +118,9 @@ export interface SendLock {
117
118
  times: Partial<LockTimes>;
118
119
  fs: LockFs;
119
120
  }
121
+ /** What `--send-all` does with the pool's busy answers: a 429 or a 5xx is asked again, up to `SEND_ATTEMPTS` in all, after the wait the pool names in `retry-after` (seconds, at most 30) or else 1 second, then 2. Any other answer, and a request that never got one, goes up unchanged. A batch or a part sent again is harmless, so the retry covers every request alike. */
122
+ export type Pause = (ms: number, signal?: AbortSignal) => Promise<void>;
123
+ export declare const sleeps: Pause;
120
124
  /**
121
125
  * How long the pool gets to answer one request of a send (an interactive `--send` and the background sender) before the send ends. It is a deadline for the whole request, the upload and the answer together,
122
126
  * not an idle timeout: a batch is one session's skeleton, whose largest fixture is 887 KB (docs/pool.md), which a link of 240 kbit/s or better uploads inside 30 seconds. A slower link fails each try with the stall
@@ -152,6 +156,8 @@ export declare const poolDeposit: (deps: RunDeps, options: {
152
156
  acceptCovenant?: string | undefined;
153
157
  inEuEeaUk?: string | undefined;
154
158
  send?: string | undefined;
159
+ sendAll?: boolean | undefined;
160
+ pause?: Pause | undefined;
155
161
  via?: Sender | undefined;
156
162
  git?: Git | undefined;
157
163
  session?: string | undefined;
@@ -174,9 +180,11 @@ export declare const poolFetch: (deps: RunDeps, options: {
174
180
  harnessVersion: string;
175
181
  }) => Promise<Outcome>;
176
182
  /**
177
- * `oars pool join`: the five commands of a first deposit in one run, each by the handler the command itself uses: `login`, `deposit`, `outbox --explain`, `deposit --send <hash>` for each batch not yet sent, then `fetch`. It asks and checks
178
- * nothing they do not, and a step that stops ends the run with that step's own error. Run again it skips what stands: a held sign-in, a prepared batch not yet sent, a batch in `sent.log`.
179
- * Each step's text prints as it finishes, so the explanation is on screen before the send asks anything: on stdout, or with `json` on stderr, so stdout holds the one document.
183
+ * `oars pool join`: the first deposit in one run, each step by the handler its own command uses: sign-in (`login`), the region question, the exclusion term file (scaffolded empty, never filled in), the batch (`deposit`), what it holds (`outbox
184
+ * --explain`), the covenant (the screen and typed phrase `deposit --send` shows), and the send (`deposit --send <hash>` for each batch not yet sent), then `fetch` when a harness version is named. A step that stops ends the run with its own
185
+ * error. Run again, each step that stands is skipped: a held sign-in, terms in the file, a prepared batch, an accepted covenant, a batch in `sent.log`. Acceptance is the person's own: this never types the phrase, and off a terminal it stops.
186
+ * Each step's text prints as it finishes, so the explanation is on screen before the send asks anything: on stdout, or with `json` on stderr, so stdout holds the one document. One `join-step` event per step says which of the closed steps ran and
187
+ * what it did (done, skipped, or stopped), and nothing else.
180
188
  */
181
189
  export declare const poolJoin: (deps: RunDeps, options: {
182
190
  server?: string | undefined;
@@ -185,8 +193,18 @@ export declare const poolJoin: (deps: RunDeps, options: {
185
193
  acceptCovenant?: string | undefined;
186
194
  inEuEeaUk?: string | undefined;
187
195
  harness?: string | undefined;
188
- harnessVersion: string;
196
+ harnessVersion?: string | undefined;
197
+ invite?: string | undefined;
189
198
  json?: boolean;
199
+ plan?: boolean;
200
+ }) => Promise<Outcome>;
201
+ /** The one line an invitee runs. `<version>` stays a placeholder: the harness version is the invitee's own to name. */
202
+ export declare const joinLineOf: (code: string) => string;
203
+ /**
204
+ * `oars pool invite`: makes one invite code (`POST /v1/invites`, a founding or standing account, three open at most) and prints it once with the line to hand on and what the vouch gives. The code is the only secret here, so it is the one thing the pool never shows again.
205
+ */
206
+ export declare const poolInvite: (deps: RunDeps, options: {
207
+ server?: string | undefined;
190
208
  }) => Promise<Outcome>;
191
209
  /** The raw prompt, in plain words: shown by `oars pool raw` until it is answered, and what the detached sender relies on. Redaction is best effort and the text says so. */
192
210
  export declare const RAW_PROMPT: string;