@earendil-works/pi-durable 1.2.0 → 1.3.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 (51) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +17 -0
  3. package/dist/env/index.d.ts +1 -0
  4. package/dist/env/index.d.ts.map +1 -1
  5. package/dist/env/index.js.map +1 -1
  6. package/dist/harness/context.d.ts +5 -0
  7. package/dist/harness/context.d.ts.map +1 -1
  8. package/dist/harness/context.js +46 -12
  9. package/dist/harness/context.js.map +1 -1
  10. package/dist/harness/tool.d.ts.map +1 -1
  11. package/dist/harness/tool.js +1 -1
  12. package/dist/harness/tool.js.map +1 -1
  13. package/dist/storage/sqlite/cloudflare.d.ts +17 -5
  14. package/dist/storage/sqlite/cloudflare.d.ts.map +1 -1
  15. package/dist/storage/sqlite/cloudflare.js +57 -21
  16. package/dist/storage/sqlite/cloudflare.js.map +1 -1
  17. package/dist/storage/sqlite/index.d.ts +1 -1
  18. package/dist/storage/sqlite/index.d.ts.map +1 -1
  19. package/dist/storage/sqlite/index.js.map +1 -1
  20. package/dist/storage/sqlite/migrations.d.ts +5 -1
  21. package/dist/storage/sqlite/migrations.d.ts.map +1 -1
  22. package/dist/storage/sqlite/migrations.js +94 -84
  23. package/dist/storage/sqlite/migrations.js.map +1 -1
  24. package/dist/storage/sqlite/node.d.ts +6 -4
  25. package/dist/storage/sqlite/node.d.ts.map +1 -1
  26. package/dist/storage/sqlite/node.js +6 -2
  27. package/dist/storage/sqlite/node.js.map +1 -1
  28. package/dist/storage/sqlite/schema.d.ts +47 -0
  29. package/dist/storage/sqlite/schema.d.ts.map +1 -0
  30. package/dist/storage/sqlite/schema.js +57 -0
  31. package/dist/storage/sqlite/schema.js.map +1 -0
  32. package/dist/storage/sqlite/storage.d.ts +9 -1
  33. package/dist/storage/sqlite/storage.d.ts.map +1 -1
  34. package/dist/storage/sqlite/storage.js +46 -43
  35. package/dist/storage/sqlite/storage.js.map +1 -1
  36. package/dist/tools/image-processor.d.ts +4 -1
  37. package/dist/tools/image-processor.d.ts.map +1 -1
  38. package/dist/tools/image-processor.js +8 -18
  39. package/dist/tools/image-processor.js.map +1 -1
  40. package/dist/tools/read.d.ts.map +1 -1
  41. package/dist/tools/read.js +1 -1
  42. package/dist/tools/read.js.map +1 -1
  43. package/dist/truncate.d.ts +3 -2
  44. package/dist/truncate.d.ts.map +1 -1
  45. package/dist/truncate.js +4 -34
  46. package/dist/truncate.js.map +1 -1
  47. package/package.json +4 -4
  48. package/dist/harness/output.d.ts +0 -69
  49. package/dist/harness/output.d.ts.map +0 -1
  50. package/dist/harness/output.js +0 -318
  51. package/dist/harness/output.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.3.0] - 2026-10-15
4
+
5
+ ### Added
6
+
7
+ - `SqliteStorage.open(database, { prefix })` can namespace every table and index; `openNodeSqliteStorage()` and `openDurableObjectSqliteStorage()` forward it, for databases shared with host tables.
8
+
9
+ ### Changed
10
+
11
+ - Cloudflare Durable Object SQLite adapters created for the same storage object now coordinate their operations, allowing multiple Pi storages to safely share one Durable Object database.
12
+
13
+ ### Fixed
14
+
15
+ - The system baseline written after a compaction now leads the model context when the kept history includes an assistant message, as does a first baseline written after earlier answers, instead of following the new input; Anthropic and OpenAI Codex requests sent it as a mid-conversation system message without an initial prompt or tool set. Other system messages after an assistant message stay in place ([#10904](https://github.com/earendil-works/pi/issues/10904)).
16
+ - `toBase64`, used by `read` and the Photon image processor, no longer takes about 140 MB of heap to encode a 3 MiB image, which exceeded the 128 MB memory limit of a Cloudflare Durable Object; it now builds a binary string 8 KiB at a time and encodes it with `btoa` ([#10923](https://github.com/earendil-works/pi/issues/10923)).
17
+
3
18
  ## [1.2.0] - 2026-10-12
4
19
 
5
20
  ### Breaking Changes
package/README.md CHANGED
@@ -662,6 +662,23 @@ const usage = await harness.usage(context); // { models: { "openai/gpt-6-sol": U
662
662
  | JSONL | `openNodeJsonlStorage(directory, context)` from `@earendil-works/pi-durable/storage/jsonl/node` | Append-only files in one directory. Pass `{ fsync: true }` to flush before each commit marker. Keeps the whole history in memory too, so memory grows with it; use SQLite for long-running agents. |
663
663
  | Cloudflare Durable Object | `openDurableObjectSqliteStorage(ctx.storage)` from `@earendil-works/pi-durable/storage/sqlite/cloudflare` | The SQLite storage of one SQLite-backed Durable Object; commits are its storage transactions. |
664
664
 
665
+ SQLite storage can namespace its tables and indexes when sharing a database: pass `{ prefix: "pi_" }` to
666
+ `SqliteStorage.open(database, options)`, `openNodeSqliteStorage(path, options)`, or
667
+ `openDurableObjectSqliteStorage(ctx.storage, options)`. Storage renders the prefixed names into its SQL, so adapters
668
+ need no support. A prefix must be a SQLite identifier and cannot begin with `sqlite_`; a host may reserve more, such
669
+ as `_cf_` in Durable Objects. Changing the prefix does not migrate existing data.
670
+
671
+ Multiple Pi storages opened through the Cloudflare adapter with the same storage object automatically share an
672
+ operation queue. To share the database with host tables, create an adapter and use it for both Pi and all host queries;
673
+ direct storage calls bypass the queue and may be rolled back with a Pi transaction:
674
+
675
+ ```typescript
676
+ const database = new DurableObjectSqliteDatabase(ctx.storage);
677
+ const piStorage = await SqliteStorage.open(database, { prefix: "pi_" });
678
+ await database.exec("CREATE TABLE IF NOT EXISTS host_notes (text TEXT)");
679
+ await database.run("INSERT INTO host_notes (text) VALUES (?)", text);
680
+ ```
681
+
665
682
  One process owns a storage at a time; there is no cross-process locking. The portable SQLite and JSONL cores (`/storage/sqlite`, `/storage/jsonl`) run without Node APIs, for example on Bun or in Cloudflare Durable Objects, given an asynchronous `SqliteDatabase` facade or a `FileSystem` from `@earendil-works/pi-durable/env`.
666
683
 
667
684
  SQLite adapters implement promise-based `exec`, `run`, `get`, `all`, `transaction`, and `close`. `run`, `get`, and `all` take SQL text plus positional bindings; adapters may cache prepared statements by SQL text. A transaction callback receives a transaction handle; all work in the transaction must use it, and the handle expires when the callback settles. Adapters must queue unrelated operations and other transactions until the transaction finishes, so calling `database` itself inside the callback never settles:
@@ -269,6 +269,7 @@ export interface ShellOutputWindow {
269
269
  * Output an environment omitted, measured on the decoded text `onOutput` would have received: every U+FFFD counts as
270
270
  * three bytes, and no sanitizing is applied.
271
271
  */
272
+ /** The shape of `OutputSkip` from `@earendil-works/chord/output`, which `OutputBuffer.push()` accepts. */
272
273
  export interface ShellOutputSkip {
273
274
  /** UTF-8 byte length of the omitted text. */
274
275
  bytes: number;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/env/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/env/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/env/index.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,EAAE,CAAiB,KAAa;IAC/C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,GAAG,CAAiB,KAAa;IAChD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,UAAU,CAAiB,MAA8B;IACxE,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,MAAM,MAAM,CAAC,KAAK,CAAC;IACnC,OAAO,MAAM,CAAC,KAAK,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,cAAc,CAAgC,MAA8B;IAC3F,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,KAAc;IACrC,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,KAAK,CAAC;IACzC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IACvD,IAAI,CAAC;QACJ,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACjC,CAAC;AACF,CAAC;AAcD,MAAM,OAAO,SAAU,SAAQ,KAAK;IAC5B,IAAI,CAAgB;IACpB,IAAI,CAAU;IAErB,YAAY,IAAmB,EAAE,OAAe,EAAE,IAAa,EAAE,KAAa;QAC7E,KAAK,CAAC,OAAO,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;CACD;AAUD,MAAM,OAAO,cAAe,SAAQ,KAAK;IACjC,IAAI,CAAqB;IAChC,2GAA2G;IACpG,SAAS,CAAU;IAE1B,YAAY,IAAwB,EAAE,OAAe,EAAE,KAAa;QACnE,KAAK,CAAC,OAAO,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;CACD;AA4SD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\n\n/** Result of a fallible operation. Expected failures are returned instead of thrown. */\nexport type Result<TValue, TError> = { ok: true; value: TValue } | { ok: false; error: TError };\n\nexport function ok<TValue, TError>(value: TValue): Result<TValue, TError> {\n\treturn { ok: true, value };\n}\n\nexport function err<TValue, TError>(error: TError): Result<TValue, TError> {\n\treturn { ok: false, error };\n}\n\nexport function getOrThrow<TValue, TError>(result: Result<TValue, TError>): TValue {\n\tif (!result.ok) throw result.error;\n\treturn result.value;\n}\n\nexport function getOrUndefined<TValue extends object, TError>(result: Result<TValue, TError>): TValue | undefined {\n\treturn result.ok ? result.value : undefined;\n}\n\nexport function toError(error: unknown): Error {\n\tif (error instanceof Error) return error;\n\tif (typeof error === \"string\") return new Error(error);\n\ttry {\n\t\treturn new Error(JSON.stringify(error));\n\t} catch {\n\t\treturn new Error(String(error));\n\t}\n}\n\nexport type FileKind = \"file\" | \"directory\" | \"symlink\";\n\nexport type FileErrorCode =\n\t| \"aborted\"\n\t| \"not_found\"\n\t| \"permission_denied\"\n\t| \"not_directory\"\n\t| \"is_directory\"\n\t| \"invalid\"\n\t| \"not_supported\"\n\t| \"unknown\";\n\nexport class FileError extends Error {\n\tpublic code: FileErrorCode;\n\tpublic path?: string;\n\n\tconstructor(code: FileErrorCode, message: string, path?: string, cause?: Error) {\n\t\tsuper(message, cause === undefined ? undefined : { cause });\n\t\tthis.name = \"FileError\";\n\t\tthis.code = code;\n\t\tthis.path = path;\n\t}\n}\n\nexport type ExecutionErrorCode =\n\t| \"aborted\"\n\t| \"timeout\"\n\t| \"shell_unavailable\"\n\t| \"spawn_error\"\n\t| \"callback_error\"\n\t| \"unknown\";\n\nexport class ExecutionError extends Error {\n\tpublic code: ExecutionErrorCode;\n\t/** Spill file of a command that timed out or was aborted after its output crossed the spill thresholds. */\n\tpublic spillPath?: string;\n\n\tconstructor(code: ExecutionErrorCode, message: string, cause?: Error) {\n\t\tsuper(message, cause === undefined ? undefined : { cause });\n\t\tthis.name = \"ExecutionError\";\n\t\tthis.code = code;\n\t}\n}\n\nexport interface FileInfo {\n\tname: string;\n\tpath: string;\n\tkind: FileKind;\n\tsize: number;\n\tmtimeMs: number;\n}\n\nexport interface TextLine {\n\ttext: string;\n\tterminated: boolean;\n}\n\nexport interface TextLineReader {\n\treadLine(context: Context): Promise<Result<TextLine | undefined, FileError>>;\n\tclose(context: Context): Promise<void>;\n}\n\n/** Positional reads from one opened regular file; all calls see the same file even if its path is renamed. */\nexport interface BinaryReader {\n\t/** Metadata of the opened file, not of whatever its path names now. */\n\tinfo(context: Context): Promise<Result<FileInfo, FileError>>;\n\t/** Up to `length` bytes at `offset`; fewer only at end of file. */\n\tread(offset: number, length: number, context: Context): Promise<Result<Uint8Array, FileError>>;\n\t/**\n\t * One pass over the file that locates lines `[startLine, endLine)` (`endLine` absent: to the end), 0-based, where line\n\t * `k` starts after the `k`-th newline byte. Decoded sizes are those of the text `new TextDecoder().decode(file)` would\n\t * produce for that range, so a byte-order mark at the start of the file is not counted.\n\t */\n\tscanLines(options: { startLine: number; endLine?: number }, context: Context): Promise<Result<LineScan, FileError>>;\n\tclose(context: Context): Promise<void>;\n}\n\n/** A file or directory to watch. It may be missing; creating it is a change. */\nexport interface WatchTarget {\n\tpath: string;\n\t/** Watch everything below a directory, not only its entries. Symbolic links below it are not followed. */\n\trecursive?: boolean;\n\t/** Entries below `path` that are neither watched nor reported: names starting with `.`, or these names. */\n\texclude?: { hidden?: boolean; names?: readonly string[] };\n}\n\n/**\n * What changed:\n * - `paths`: something at or below each path may have changed (a directory path covers its whole subtree). Calls may be\n * spurious; a change is never missed while the watcher is healthy.\n * - `overflow`: coverage was uncertain for a while (lost events, reconnect); rescan everything that is watched.\n * - `error`: the watcher stopped, for example because the watched tree grew past the environment's limit; no calls\n * follow.\n */\nexport type WatchChange = { paths: string[] } | { overflow: true } | { error: FileError };\n\nexport interface FileWatcher {\n\t/**\n\t * `native`: changes are reported within about two seconds. `polling`: the environment compares snapshots, because\n\t * the file system does not report changes reliably (network and FUSE file systems); a change undone between two\n\t * snapshots can be missed.\n\t */\n\treadonly mode: \"native\" | \"polling\";\n\t/** Stop watching; no `onChange` call starts after this resolves. Idempotent. */\n\tclose(context: Context): Promise<void>;\n}\n\n/** Where lines of a file are, as `BinaryReader.scanLines` found them. */\nexport interface LineScan {\n\t/** Newline bytes in the whole file; it has `newlines + 1` lines. */\n\tnewlines: number;\n\t/**\n\t * Byte range of the selected lines: from the start of the first to the end of the last, without the newline that\n\t * ends it. A selection past the last line is empty at the end of the file.\n\t */\n\tstart: number;\n\tend: number;\n\t/** Where the first selected line ends: its newline, or the end of the file. */\n\tfirstLineEnd: number;\n\t/** Where the last selected line starts. */\n\tlastLineStart: number;\n\t/** UTF-8 byte length of the decoded selection and of its first line. */\n\tselectedBytes: number;\n\tfirstLineBytes: number;\n}\n\n/** Pages of one directory's entries. */\nexport interface DirReader {\n\t/**\n\t * Up to `maxEntries` entries in the order the file system returns them, continuing where the previous call stopped.\n\t * `done` marks the end; it may come with the last entries or with an empty page. An entry that disappears before its\n\t * metadata is read is skipped, as are entries of unsupported kinds. After a failed or aborted call, close the reader.\n\t */\n\tnext(maxEntries: number, context: Context): Promise<Result<{ entries: FileInfo[]; done: boolean }, FileError>>;\n\tclose(context: Context): Promise<void>;\n}\n\n/** What `FileSystem.listFiles` lists. */\nexport interface ListFilesOptions {\n\t/**\n\t * Include names starting with `.` (on Windows also files with the hidden attribute); default true. Entries named\n\t * `.git` are never listed.\n\t */\n\thidden?: boolean;\n\t/**\n\t * Inside a git work tree, skip what git ignores: the `.gitignore` files of the directory, its subdirectories and its\n\t * parents, `.git/info/exclude`, and the global excludes file; default true. Unlike `git ls-files`, a tracked file that\n\t * matches an ignore rule is skipped too. Outside work trees, `.gitignore` files have no effect.\n\t */\n\tgitignore?: boolean;\n\t/** Names of entries to skip with everything below them, such as `node_modules`. */\n\texclude?: readonly string[];\n\t/** Most entries to list; default 50 000. */\n\tmaxEntries?: number;\n\t/** Most bytes of the listing: each entry's UTF-8 path plus two; default 4 MiB, at most 15.9 MiB. */\n\tmaxBytes?: number;\n\t/** Time after which the listing stops; default 10 000 ms. */\n\ttimeoutMs?: number;\n}\n\n/** One entry of a `FileListing`. */\nexport interface FileListEntry {\n\t/** Relative to the listed directory, with `/` separators on every system. */\n\tpath: string;\n\t/** A symbolic link is listed as such and not followed. */\n\tkind: FileKind;\n}\n\nexport interface FileListing {\n\t/** Depth first, parents before their entries, siblings in byte order of their names. */\n\tentries: FileListEntry[];\n\t/** A bound in `ListFilesOptions` ended the listing early. */\n\ttruncated: boolean;\n}\n\n/** Portable filesystem capability. Operations return failures rather than throwing. */\nexport interface FileSystem {\n\t/**\n\t * The file namespace: equal ids see the same files at the same paths, whatever their `cwd`. Every local Node\n\t * environment shares one id; each container or remote host has its own.\n\t */\n\treadonly id: string;\n\tcwd: string;\n\tabsolutePath(path: string, context: Context): Promise<Result<string, FileError>>;\n\tjoinPath(parts: string[], context: Context): Promise<Result<string, FileError>>;\n\treadTextFile(path: string, context: Context): Promise<Result<string, FileError>>;\n\topenTextLineReader(path: string, context: Context): Promise<Result<TextLineReader, FileError>>;\n\treadTextLines(\n\t\tpath: string,\n\t\toptions: { maxLines?: number } | undefined,\n\t\tcontext: Context,\n\t): Promise<Result<string[], FileError>>;\n\treadBinaryFile(path: string, context: Context): Promise<Result<Uint8Array, FileError>>;\n\t/**\n\t * Open a regular file for bounded positional reads. A directory fails with `is_directory`, other non-regular files\n\t * with `invalid`. With `noFollow`, a symbolic link as the final path component fails with `invalid` instead of being\n\t * followed; earlier components are still resolved.\n\t */\n\topenBinaryReader(\n\t\tpath: string,\n\t\toptions: { noFollow?: boolean } | undefined,\n\t\tcontext: Context,\n\t): Promise<Result<BinaryReader, FileError>>;\n\twriteFile(path: string, content: string | Uint8Array, context: Context): Promise<Result<void, FileError>>;\n\tappendFile(path: string, content: string | Uint8Array, context: Context): Promise<Result<void, FileError>>;\n\t/** Truncate or extend a file to exactly `size` bytes. */\n\ttruncateFile(path: string, size: number, context: Context): Promise<Result<void, FileError>>;\n\t/** Flush file contents and metadata needed to retrieve them from an open file handle. */\n\tflushFile(path: string, context: Context): Promise<Result<void, FileError>>;\n\trenameFile(sourcePath: string, destinationPath: string, context: Context): Promise<Result<void, FileError>>;\n\tfileInfo(path: string, context: Context): Promise<Result<FileInfo, FileError>>;\n\tlistDir(path: string, context: Context): Promise<Result<FileInfo[], FileError>>;\n\t/**\n\t * Optional: the files, directories and symbolic links below a directory, at any depth, for indexes such as file\n\t * completion. Symbolic links are not followed; other kinds of files are not listed; entries that cannot be read are\n\t * skipped. See `ListFilesOptions` for hidden and ignored files and for the bounds. Fails with `not_supported` when\n\t * the environment cannot list right now (an older remote daemon), so callers fall back to their own walk.\n\t */\n\tlistFiles?(\n\t\tpath: string,\n\t\toptions: ListFilesOptions | undefined,\n\t\tcontext: Context,\n\t): Promise<Result<FileListing, FileError>>;\n\topenDirReader(path: string, context: Context): Promise<Result<DirReader, FileError>>;\n\t/**\n\t * Report changes to files and directories, for hosts that load resources from the environment. When the returned\n\t * watcher exists, coverage is established: a host that watches before it loads cannot miss a change made during the\n\t * load. See `WatchChange` for what is reported and `FileWatcher.mode` for how reliably.\n\t */\n\twatch(\n\t\ttargets: readonly WatchTarget[],\n\t\tonChange: (change: WatchChange) => void,\n\t\tcontext: Context,\n\t): Promise<Result<FileWatcher, FileError>>;\n\tcanonicalPath(path: string, context: Context): Promise<Result<string, FileError>>;\n\texists(path: string, context: Context): Promise<Result<boolean, FileError>>;\n\tcreateDir(\n\t\tpath: string,\n\t\toptions: { recursive?: boolean } | undefined,\n\t\tcontext: Context,\n\t): Promise<Result<void, FileError>>;\n\tremove(\n\t\tpath: string,\n\t\toptions: { recursive?: boolean; force?: boolean } | undefined,\n\t\tcontext: Context,\n\t): Promise<Result<void, FileError>>;\n\tcreateTempDir(prefix: string | undefined, context: Context): Promise<Result<string, FileError>>;\n\tcreateTempFile(\n\t\toptions: { prefix?: string; suffix?: string } | undefined,\n\t\tcontext: Context,\n\t): Promise<Result<string, FileError>>;\n\tcleanup(context: Context): Promise<void>;\n}\n\n/** Spill the complete output to a temporary file once it exceeds either threshold. */\nexport interface ShellSpillOptions {\n\tafterBytes: number;\n\t/** Complete or partial lines. */\n\tafterLines: number;\n}\n\nexport interface ShellExecResult {\n\texitCode: number;\n\t/** Temporary file holding the complete raw output, when the spill thresholds were exceeded. */\n\tspillPath?: string;\n}\n\nexport interface ShellExecOptions {\n\tcwd?: string;\n\tenv?: Record<string, string>;\n\tinheritEnv?: boolean;\n\ttimeout?: number;\n\t/**\n\t * Every decoded chunk of stdout and stderr as it arrives, in arrival order, with the stream it came from: raw,\n\t * unbounded, and unthrottled. Each stream is decoded separately, so a character split across chunks survives.\n\t */\n\tonOutput?: (text: string, context: Context, info: ShellOutputInfo) => void;\n\tspill?: ShellSpillOptions;\n\t/**\n\t * The caller keeps only this tail of the output, so the environment may omit output outside it and report the omission\n\t * as `info.skipped`. Without it, every chunk is delivered.\n\t */\n\twindow?: ShellOutputWindow;\n}\n\n/**\n * The tail of the combined output a caller keeps, and how often it samples it. An environment that transfers output\n * over a slow link uses it to omit what the caller would drop anyway and to send no faster than the caller commits.\n */\nexport interface ShellOutputWindow {\n\t/** UTF-8 bytes of decoded text kept at the end of the output. */\n\tmaxBytes: number;\n\t/** Lines kept at the end of the output. */\n\tmaxLines: number;\n\t/** Minimum pause between the caller's samples of the output. */\n\tminIntervalMs: number;\n\t/** Each sample also pauses the caller in proportion to its size at this rate. */\n\tbytesPerSecond: number;\n}\n\n/**\n * Output an environment omitted, measured on the decoded text `onOutput` would have received: every U+FFFD counts as\n * three bytes, and no sanitizing is applied.\n */\nexport interface ShellOutputSkip {\n\t/** UTF-8 byte length of the omitted text. */\n\tbytes: number;\n\t/** Newlines (U+000A) in the omitted text. */\n\tnewlines: number;\n\t/** Whether the omitted text ends with a newline. */\n\tendsWithNewline: boolean;\n}\n\nexport interface ShellOutputInfo {\n\tstream: \"stdout\" | \"stderr\";\n\t/**\n\t * Output omitted immediately before this chunk, only with `window`. The chunk then holds all output after the\n\t * omission up to its end, and that is more than the window by at least one byte or one line: more than\n\t * `window.maxBytes` bytes or more than `window.maxLines` newlines. So the omitted text can never be in the kept tail.\n\t * The omission and such a chunk may span both streams in arrival order; `stream` then names the chunk's last stream.\n\t * Callers that need the streams apart do not pass `window`.\n\t */\n\tskipped?: ShellOutputSkip;\n}\n\nexport interface Shell {\n\t/**\n\t * Run a command. A string runs through the environment's shell. An array runs `command[0]` directly with the rest\n\t * as its arguments, without a shell, so they reach the program unparsed. Aborting the context or a timeout kills\n\t * only this command's processes.\n\t */\n\texec(\n\t\tcommand: string | readonly string[],\n\t\toptions: ShellExecOptions | undefined,\n\t\tcontext: Context,\n\t): Promise<Result<ShellExecResult, ExecutionError>>;\n\t/** Kill every command this environment still runs; for its owner's shutdown, never for one request. */\n\tcleanup(context: Context): Promise<void>;\n}\n\nexport interface ExecutionEnv extends FileSystem, Shell {}\n\nexport { rangeDecoder, StreamDecoder, startsWithBom } from \"./decode.ts\";\nexport { LineScanner } from \"./line-scan.ts\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/env/index.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,EAAE,CAAiB,KAAa;IAC/C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,GAAG,CAAiB,KAAa;IAChD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,UAAU,CAAiB,MAA8B;IACxE,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,MAAM,MAAM,CAAC,KAAK,CAAC;IACnC,OAAO,MAAM,CAAC,KAAK,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,cAAc,CAAgC,MAA8B;IAC3F,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,KAAc;IACrC,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,KAAK,CAAC;IACzC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IACvD,IAAI,CAAC;QACJ,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACjC,CAAC;AACF,CAAC;AAcD,MAAM,OAAO,SAAU,SAAQ,KAAK;IAC5B,IAAI,CAAgB;IACpB,IAAI,CAAU;IAErB,YAAY,IAAmB,EAAE,OAAe,EAAE,IAAa,EAAE,KAAa;QAC7E,KAAK,CAAC,OAAO,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;CACD;AAUD,MAAM,OAAO,cAAe,SAAQ,KAAK;IACjC,IAAI,CAAqB;IAChC,2GAA2G;IACpG,SAAS,CAAU;IAE1B,YAAY,IAAwB,EAAE,OAAe,EAAE,KAAa;QACnE,KAAK,CAAC,OAAO,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;CACD;AA6SD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\n\n/** Result of a fallible operation. Expected failures are returned instead of thrown. */\nexport type Result<TValue, TError> = { ok: true; value: TValue } | { ok: false; error: TError };\n\nexport function ok<TValue, TError>(value: TValue): Result<TValue, TError> {\n\treturn { ok: true, value };\n}\n\nexport function err<TValue, TError>(error: TError): Result<TValue, TError> {\n\treturn { ok: false, error };\n}\n\nexport function getOrThrow<TValue, TError>(result: Result<TValue, TError>): TValue {\n\tif (!result.ok) throw result.error;\n\treturn result.value;\n}\n\nexport function getOrUndefined<TValue extends object, TError>(result: Result<TValue, TError>): TValue | undefined {\n\treturn result.ok ? result.value : undefined;\n}\n\nexport function toError(error: unknown): Error {\n\tif (error instanceof Error) return error;\n\tif (typeof error === \"string\") return new Error(error);\n\ttry {\n\t\treturn new Error(JSON.stringify(error));\n\t} catch {\n\t\treturn new Error(String(error));\n\t}\n}\n\nexport type FileKind = \"file\" | \"directory\" | \"symlink\";\n\nexport type FileErrorCode =\n\t| \"aborted\"\n\t| \"not_found\"\n\t| \"permission_denied\"\n\t| \"not_directory\"\n\t| \"is_directory\"\n\t| \"invalid\"\n\t| \"not_supported\"\n\t| \"unknown\";\n\nexport class FileError extends Error {\n\tpublic code: FileErrorCode;\n\tpublic path?: string;\n\n\tconstructor(code: FileErrorCode, message: string, path?: string, cause?: Error) {\n\t\tsuper(message, cause === undefined ? undefined : { cause });\n\t\tthis.name = \"FileError\";\n\t\tthis.code = code;\n\t\tthis.path = path;\n\t}\n}\n\nexport type ExecutionErrorCode =\n\t| \"aborted\"\n\t| \"timeout\"\n\t| \"shell_unavailable\"\n\t| \"spawn_error\"\n\t| \"callback_error\"\n\t| \"unknown\";\n\nexport class ExecutionError extends Error {\n\tpublic code: ExecutionErrorCode;\n\t/** Spill file of a command that timed out or was aborted after its output crossed the spill thresholds. */\n\tpublic spillPath?: string;\n\n\tconstructor(code: ExecutionErrorCode, message: string, cause?: Error) {\n\t\tsuper(message, cause === undefined ? undefined : { cause });\n\t\tthis.name = \"ExecutionError\";\n\t\tthis.code = code;\n\t}\n}\n\nexport interface FileInfo {\n\tname: string;\n\tpath: string;\n\tkind: FileKind;\n\tsize: number;\n\tmtimeMs: number;\n}\n\nexport interface TextLine {\n\ttext: string;\n\tterminated: boolean;\n}\n\nexport interface TextLineReader {\n\treadLine(context: Context): Promise<Result<TextLine | undefined, FileError>>;\n\tclose(context: Context): Promise<void>;\n}\n\n/** Positional reads from one opened regular file; all calls see the same file even if its path is renamed. */\nexport interface BinaryReader {\n\t/** Metadata of the opened file, not of whatever its path names now. */\n\tinfo(context: Context): Promise<Result<FileInfo, FileError>>;\n\t/** Up to `length` bytes at `offset`; fewer only at end of file. */\n\tread(offset: number, length: number, context: Context): Promise<Result<Uint8Array, FileError>>;\n\t/**\n\t * One pass over the file that locates lines `[startLine, endLine)` (`endLine` absent: to the end), 0-based, where line\n\t * `k` starts after the `k`-th newline byte. Decoded sizes are those of the text `new TextDecoder().decode(file)` would\n\t * produce for that range, so a byte-order mark at the start of the file is not counted.\n\t */\n\tscanLines(options: { startLine: number; endLine?: number }, context: Context): Promise<Result<LineScan, FileError>>;\n\tclose(context: Context): Promise<void>;\n}\n\n/** A file or directory to watch. It may be missing; creating it is a change. */\nexport interface WatchTarget {\n\tpath: string;\n\t/** Watch everything below a directory, not only its entries. Symbolic links below it are not followed. */\n\trecursive?: boolean;\n\t/** Entries below `path` that are neither watched nor reported: names starting with `.`, or these names. */\n\texclude?: { hidden?: boolean; names?: readonly string[] };\n}\n\n/**\n * What changed:\n * - `paths`: something at or below each path may have changed (a directory path covers its whole subtree). Calls may be\n * spurious; a change is never missed while the watcher is healthy.\n * - `overflow`: coverage was uncertain for a while (lost events, reconnect); rescan everything that is watched.\n * - `error`: the watcher stopped, for example because the watched tree grew past the environment's limit; no calls\n * follow.\n */\nexport type WatchChange = { paths: string[] } | { overflow: true } | { error: FileError };\n\nexport interface FileWatcher {\n\t/**\n\t * `native`: changes are reported within about two seconds. `polling`: the environment compares snapshots, because\n\t * the file system does not report changes reliably (network and FUSE file systems); a change undone between two\n\t * snapshots can be missed.\n\t */\n\treadonly mode: \"native\" | \"polling\";\n\t/** Stop watching; no `onChange` call starts after this resolves. Idempotent. */\n\tclose(context: Context): Promise<void>;\n}\n\n/** Where lines of a file are, as `BinaryReader.scanLines` found them. */\nexport interface LineScan {\n\t/** Newline bytes in the whole file; it has `newlines + 1` lines. */\n\tnewlines: number;\n\t/**\n\t * Byte range of the selected lines: from the start of the first to the end of the last, without the newline that\n\t * ends it. A selection past the last line is empty at the end of the file.\n\t */\n\tstart: number;\n\tend: number;\n\t/** Where the first selected line ends: its newline, or the end of the file. */\n\tfirstLineEnd: number;\n\t/** Where the last selected line starts. */\n\tlastLineStart: number;\n\t/** UTF-8 byte length of the decoded selection and of its first line. */\n\tselectedBytes: number;\n\tfirstLineBytes: number;\n}\n\n/** Pages of one directory's entries. */\nexport interface DirReader {\n\t/**\n\t * Up to `maxEntries` entries in the order the file system returns them, continuing where the previous call stopped.\n\t * `done` marks the end; it may come with the last entries or with an empty page. An entry that disappears before its\n\t * metadata is read is skipped, as are entries of unsupported kinds. After a failed or aborted call, close the reader.\n\t */\n\tnext(maxEntries: number, context: Context): Promise<Result<{ entries: FileInfo[]; done: boolean }, FileError>>;\n\tclose(context: Context): Promise<void>;\n}\n\n/** What `FileSystem.listFiles` lists. */\nexport interface ListFilesOptions {\n\t/**\n\t * Include names starting with `.` (on Windows also files with the hidden attribute); default true. Entries named\n\t * `.git` are never listed.\n\t */\n\thidden?: boolean;\n\t/**\n\t * Inside a git work tree, skip what git ignores: the `.gitignore` files of the directory, its subdirectories and its\n\t * parents, `.git/info/exclude`, and the global excludes file; default true. Unlike `git ls-files`, a tracked file that\n\t * matches an ignore rule is skipped too. Outside work trees, `.gitignore` files have no effect.\n\t */\n\tgitignore?: boolean;\n\t/** Names of entries to skip with everything below them, such as `node_modules`. */\n\texclude?: readonly string[];\n\t/** Most entries to list; default 50 000. */\n\tmaxEntries?: number;\n\t/** Most bytes of the listing: each entry's UTF-8 path plus two; default 4 MiB, at most 15.9 MiB. */\n\tmaxBytes?: number;\n\t/** Time after which the listing stops; default 10 000 ms. */\n\ttimeoutMs?: number;\n}\n\n/** One entry of a `FileListing`. */\nexport interface FileListEntry {\n\t/** Relative to the listed directory, with `/` separators on every system. */\n\tpath: string;\n\t/** A symbolic link is listed as such and not followed. */\n\tkind: FileKind;\n}\n\nexport interface FileListing {\n\t/** Depth first, parents before their entries, siblings in byte order of their names. */\n\tentries: FileListEntry[];\n\t/** A bound in `ListFilesOptions` ended the listing early. */\n\ttruncated: boolean;\n}\n\n/** Portable filesystem capability. Operations return failures rather than throwing. */\nexport interface FileSystem {\n\t/**\n\t * The file namespace: equal ids see the same files at the same paths, whatever their `cwd`. Every local Node\n\t * environment shares one id; each container or remote host has its own.\n\t */\n\treadonly id: string;\n\tcwd: string;\n\tabsolutePath(path: string, context: Context): Promise<Result<string, FileError>>;\n\tjoinPath(parts: string[], context: Context): Promise<Result<string, FileError>>;\n\treadTextFile(path: string, context: Context): Promise<Result<string, FileError>>;\n\topenTextLineReader(path: string, context: Context): Promise<Result<TextLineReader, FileError>>;\n\treadTextLines(\n\t\tpath: string,\n\t\toptions: { maxLines?: number } | undefined,\n\t\tcontext: Context,\n\t): Promise<Result<string[], FileError>>;\n\treadBinaryFile(path: string, context: Context): Promise<Result<Uint8Array, FileError>>;\n\t/**\n\t * Open a regular file for bounded positional reads. A directory fails with `is_directory`, other non-regular files\n\t * with `invalid`. With `noFollow`, a symbolic link as the final path component fails with `invalid` instead of being\n\t * followed; earlier components are still resolved.\n\t */\n\topenBinaryReader(\n\t\tpath: string,\n\t\toptions: { noFollow?: boolean } | undefined,\n\t\tcontext: Context,\n\t): Promise<Result<BinaryReader, FileError>>;\n\twriteFile(path: string, content: string | Uint8Array, context: Context): Promise<Result<void, FileError>>;\n\tappendFile(path: string, content: string | Uint8Array, context: Context): Promise<Result<void, FileError>>;\n\t/** Truncate or extend a file to exactly `size` bytes. */\n\ttruncateFile(path: string, size: number, context: Context): Promise<Result<void, FileError>>;\n\t/** Flush file contents and metadata needed to retrieve them from an open file handle. */\n\tflushFile(path: string, context: Context): Promise<Result<void, FileError>>;\n\trenameFile(sourcePath: string, destinationPath: string, context: Context): Promise<Result<void, FileError>>;\n\tfileInfo(path: string, context: Context): Promise<Result<FileInfo, FileError>>;\n\tlistDir(path: string, context: Context): Promise<Result<FileInfo[], FileError>>;\n\t/**\n\t * Optional: the files, directories and symbolic links below a directory, at any depth, for indexes such as file\n\t * completion. Symbolic links are not followed; other kinds of files are not listed; entries that cannot be read are\n\t * skipped. See `ListFilesOptions` for hidden and ignored files and for the bounds. Fails with `not_supported` when\n\t * the environment cannot list right now (an older remote daemon), so callers fall back to their own walk.\n\t */\n\tlistFiles?(\n\t\tpath: string,\n\t\toptions: ListFilesOptions | undefined,\n\t\tcontext: Context,\n\t): Promise<Result<FileListing, FileError>>;\n\topenDirReader(path: string, context: Context): Promise<Result<DirReader, FileError>>;\n\t/**\n\t * Report changes to files and directories, for hosts that load resources from the environment. When the returned\n\t * watcher exists, coverage is established: a host that watches before it loads cannot miss a change made during the\n\t * load. See `WatchChange` for what is reported and `FileWatcher.mode` for how reliably.\n\t */\n\twatch(\n\t\ttargets: readonly WatchTarget[],\n\t\tonChange: (change: WatchChange) => void,\n\t\tcontext: Context,\n\t): Promise<Result<FileWatcher, FileError>>;\n\tcanonicalPath(path: string, context: Context): Promise<Result<string, FileError>>;\n\texists(path: string, context: Context): Promise<Result<boolean, FileError>>;\n\tcreateDir(\n\t\tpath: string,\n\t\toptions: { recursive?: boolean } | undefined,\n\t\tcontext: Context,\n\t): Promise<Result<void, FileError>>;\n\tremove(\n\t\tpath: string,\n\t\toptions: { recursive?: boolean; force?: boolean } | undefined,\n\t\tcontext: Context,\n\t): Promise<Result<void, FileError>>;\n\tcreateTempDir(prefix: string | undefined, context: Context): Promise<Result<string, FileError>>;\n\tcreateTempFile(\n\t\toptions: { prefix?: string; suffix?: string } | undefined,\n\t\tcontext: Context,\n\t): Promise<Result<string, FileError>>;\n\tcleanup(context: Context): Promise<void>;\n}\n\n/** Spill the complete output to a temporary file once it exceeds either threshold. */\nexport interface ShellSpillOptions {\n\tafterBytes: number;\n\t/** Complete or partial lines. */\n\tafterLines: number;\n}\n\nexport interface ShellExecResult {\n\texitCode: number;\n\t/** Temporary file holding the complete raw output, when the spill thresholds were exceeded. */\n\tspillPath?: string;\n}\n\nexport interface ShellExecOptions {\n\tcwd?: string;\n\tenv?: Record<string, string>;\n\tinheritEnv?: boolean;\n\ttimeout?: number;\n\t/**\n\t * Every decoded chunk of stdout and stderr as it arrives, in arrival order, with the stream it came from: raw,\n\t * unbounded, and unthrottled. Each stream is decoded separately, so a character split across chunks survives.\n\t */\n\tonOutput?: (text: string, context: Context, info: ShellOutputInfo) => void;\n\tspill?: ShellSpillOptions;\n\t/**\n\t * The caller keeps only this tail of the output, so the environment may omit output outside it and report the omission\n\t * as `info.skipped`. Without it, every chunk is delivered.\n\t */\n\twindow?: ShellOutputWindow;\n}\n\n/**\n * The tail of the combined output a caller keeps, and how often it samples it. An environment that transfers output\n * over a slow link uses it to omit what the caller would drop anyway and to send no faster than the caller commits.\n */\nexport interface ShellOutputWindow {\n\t/** UTF-8 bytes of decoded text kept at the end of the output. */\n\tmaxBytes: number;\n\t/** Lines kept at the end of the output. */\n\tmaxLines: number;\n\t/** Minimum pause between the caller's samples of the output. */\n\tminIntervalMs: number;\n\t/** Each sample also pauses the caller in proportion to its size at this rate. */\n\tbytesPerSecond: number;\n}\n\n/**\n * Output an environment omitted, measured on the decoded text `onOutput` would have received: every U+FFFD counts as\n * three bytes, and no sanitizing is applied.\n */\n/** The shape of `OutputSkip` from `@earendil-works/chord/output`, which `OutputBuffer.push()` accepts. */\nexport interface ShellOutputSkip {\n\t/** UTF-8 byte length of the omitted text. */\n\tbytes: number;\n\t/** Newlines (U+000A) in the omitted text. */\n\tnewlines: number;\n\t/** Whether the omitted text ends with a newline. */\n\tendsWithNewline: boolean;\n}\n\nexport interface ShellOutputInfo {\n\tstream: \"stdout\" | \"stderr\";\n\t/**\n\t * Output omitted immediately before this chunk, only with `window`. The chunk then holds all output after the\n\t * omission up to its end, and that is more than the window by at least one byte or one line: more than\n\t * `window.maxBytes` bytes or more than `window.maxLines` newlines. So the omitted text can never be in the kept tail.\n\t * The omission and such a chunk may span both streams in arrival order; `stream` then names the chunk's last stream.\n\t * Callers that need the streams apart do not pass `window`.\n\t */\n\tskipped?: ShellOutputSkip;\n}\n\nexport interface Shell {\n\t/**\n\t * Run a command. A string runs through the environment's shell. An array runs `command[0]` directly with the rest\n\t * as its arguments, without a shell, so they reach the program unparsed. Aborting the context or a timeout kills\n\t * only this command's processes.\n\t */\n\texec(\n\t\tcommand: string | readonly string[],\n\t\toptions: ShellExecOptions | undefined,\n\t\tcontext: Context,\n\t): Promise<Result<ShellExecResult, ExecutionError>>;\n\t/** Kill every command this environment still runs; for its owner's shutdown, never for one request. */\n\tcleanup(context: Context): Promise<void>;\n}\n\nexport interface ExecutionEnv extends FileSystem, Shell {}\n\nexport { rangeDecoder, StreamDecoder, startsWithBom } from \"./decode.ts\";\nexport { LineScanner } from \"./line-scan.ts\";\n"]}
@@ -34,7 +34,11 @@ export type ContextRange = {
34
34
  readonly settled: readonly Message[];
35
35
  /** Contributed messages from the last assistant message on, before tool result ordering. */
36
36
  readonly open: readonly Message[];
37
+ /** The baseline's system message; see `findBaseline()`. */
38
+ readonly baseline: Baseline;
37
39
  };
40
+ /** A baseline's system message, `null` when the range cannot have one, or `undefined` while a later entry may be one. */
41
+ type Baseline = Message | null | undefined;
38
42
  /**
39
43
  * `readContext()` that reuses `previous`, an earlier range of the same conversation: with the same head marker, only
40
44
  * entries after its tail are scanned. Returns the view and the range to pass to the next read. A range outlives the
@@ -60,4 +64,5 @@ export declare function activeEntries(storage: Storage, conversationId: Conversa
60
64
  * the next assistant; a missing result is synthesized and unmatched results are dropped.
61
65
  */
62
66
  export declare function orderToolResults(messages: readonly Message[]): Message[];
67
+ export {};
63
68
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/harness/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAoB,OAAO,EAA+B,MAAM,uBAAuB,CAAC;AAEpG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAe,cAAc,EAAU,OAAO,EAAc,WAAW,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAClH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAM9C,iFAAiF;AACjF,MAAM,MAAM,aAAa,GAAG;IAC3B,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,GAAG;QAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,GAAG,SAAS,CAAC;IACtE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,OAAO,EAChB,EAAE,CAAC,EAAE,OAAO,GACV,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAYpC;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG;IAC1B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,yCAAyC;IACzC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IACtC;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;IACrC,4FAA4F;IAC5F,QAAQ,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,CAAC;CAClC,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,eAAe,CACpC,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,OAAO,GAAG,SAAS,EACvB,QAAQ,EAAE,YAAY,GAAG,SAAS,GAChC,OAAO,CAAC;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,CAAA;CAAE,CAAC,CA6BnF;AAED,0GAA0G;AAC1G,wBAAsB,WAAW,CAChC,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,OAAO,EAChB,EAAE,CAAC,EAAE,OAAO,GACV,OAAO,CAAC,WAAW,CAAC,CAGtB;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAClC,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,WAAW,CAAC,CAGtB;AAqFD,qFAAqF;AACrF,wBAAsB,aAAa,CAClC,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,SAAS,WAAW,EAAE,CAAC,CAGjC;AA8BD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,EAAE,CAsBxE"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/harness/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAoB,OAAO,EAA+B,MAAM,uBAAuB,CAAC;AAGpG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAe,cAAc,EAAU,OAAO,EAAc,WAAW,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAClH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAM9C,iFAAiF;AACjF,MAAM,MAAM,aAAa,GAAG;IAC3B,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,GAAG;QAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,GAAG,SAAS,CAAC;IACtE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,OAAO,EAChB,EAAE,CAAC,EAAE,OAAO,GACV,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAYpC;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG;IAC1B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,yCAAyC;IACzC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IACtC;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;IACrC,4FAA4F;IAC5F,QAAQ,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,CAAC;IAClC,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;CAC5B,CAAC;AAEF,yHAAyH;AACzH,KAAK,QAAQ,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAE3C;;;;GAIG;AACH,wBAAsB,eAAe,CACpC,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,OAAO,GAAG,SAAS,EACvB,QAAQ,EAAE,YAAY,GAAG,SAAS,GAChC,OAAO,CAAC;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,CAAA;CAAE,CAAC,CA6BnF;AAED,0GAA0G;AAC1G,wBAAsB,WAAW,CAChC,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,OAAO,EAChB,EAAE,CAAC,EAAE,OAAO,GACV,OAAO,CAAC,WAAW,CAAC,CAGtB;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAClC,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,WAAW,CAAC,CAGtB;AAuHD,qFAAqF;AACrF,wBAAsB,aAAa,CAClC,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,SAAS,WAAW,EAAE,CAAC,CAGjC;AA8BD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,EAAE,CAsBxE"}
@@ -1,3 +1,4 @@
1
+ import { SystemEntry } from "../entries.js";
1
2
  import { idFromNumber } from "../ids.js";
2
3
  const SCAN_PAGE_SIZE = 256;
3
4
  const EXCLUDED_STOP_REASONS = new Set(["aborted", "error", "deferred"]);
@@ -87,9 +88,10 @@ function deriveRange(bounds, entries) {
87
88
  const active = selectActive(bounds.head, entries);
88
89
  const contributions = active.map((entry) => contribute(entry, edits.get(entry.id)));
89
90
  const { settled, open } = settle([], contributions.flat());
90
- const messages = leadWithSystem([...settled, ...orderToolResults(open)]);
91
+ const baseline = findBaseline(bounds.head, active);
92
+ const messages = leadWithSystem([...settled, ...orderToolResults(open)], baseline);
91
93
  const view = { head: bounds.head, entries: active, contributions, messages };
92
- return { bounds, entries, view, edited: new Set(edits.keys()), settled, open };
94
+ return { bounds, entries, view, edited: new Set(edits.keys()), settled, open, baseline };
93
95
  }
94
96
  /**
95
97
  * `previous` extended by the entries `added` after its tail under the same head marker: only their contributions and
@@ -102,25 +104,57 @@ function extendRange(previous, bounds, added) {
102
104
  }
103
105
  const contributions = added.map((entry) => contribute(entry, undefined));
104
106
  const { settled, open } = settle(previous.settled, [...previous.open, ...contributions.flat()]);
107
+ const baseline = previous.baseline !== undefined ? previous.baseline : findBaseline(bounds.head, added);
105
108
  const view = {
106
109
  head: bounds.head,
107
110
  entries: [...previous.view.entries, ...added],
108
111
  contributions: [...previous.view.contributions, ...contributions],
109
- messages: leadWithSystem([...settled, ...orderToolResults(open)]),
112
+ messages: leadWithSystem([...settled, ...orderToolResults(open)], baseline),
110
113
  };
111
- return { bounds, entries, view, edited: previous.edited, settled, open };
114
+ return { bounds, entries, view, edited: previous.edited, settled, open, baseline };
112
115
  }
113
116
  /**
114
- * Move a system message that only user messages precede to the front. A run's input is committed before generation
115
- * renders the system prompt, so a transcript, or the range after a compaction or reset, starts with user messages
116
- * followed by the baseline system message. Providers treat only a leading system message as the initial prompt and tool
117
- * set; without it, a later tool change rewrites the request's tool list and invalidates the whole prompt cache.
117
+ * The baseline among `entries`, oldest first: the system message of the first `pi.system` entry after the head
118
+ * marker, or of the transcript without one, when generation wrote it with every section and tool in effect. After a
119
+ * head marker it always does, and omits the retained `pi.system` entries. Without one it does when it saw no system
120
+ * message before, which holds unless an earlier entry carries one, even if an edit omits it later. An edit that targets
121
+ * the baseline contributes other messages, or none, so none leads; a later entry does not take its place.
118
122
  */
119
- function leadWithSystem(messages) {
120
- const index = messages.findIndex((message) => message.role !== "user");
121
- if (index <= 0 || messages[index].role !== "system")
123
+ function findBaseline(head, entries) {
124
+ for (const entry of entries) {
125
+ if (head !== undefined && entry.id <= head.id)
126
+ continue;
127
+ if (SystemEntry.is(entry))
128
+ return entry.model?.find((message) => message.role === "system") ?? null;
129
+ if (head === undefined && carriesSystemMessage(entry))
130
+ return null;
131
+ }
132
+ return undefined;
133
+ }
134
+ /** Whether an entry's own messages, or the replacement messages of its edits, include a system message. */
135
+ function carriesSystemMessage(entry) {
136
+ const isSystem = (message) => message.role === "system";
137
+ return ((entry.model?.some(isSystem) ?? false) ||
138
+ (entry.edits?.some((edit) => edit.action === "replace" && edit.messages.some(isSystem)) ?? false));
139
+ }
140
+ /**
141
+ * Move the first system message to the front when only user messages precede it, or when it is the baseline's. A run's
142
+ * input is committed before generation renders the system prompt, so a transcript starts with the input followed by
143
+ * the baseline, and the range after a compaction with the kept history, assistant messages included. Providers treat
144
+ * only a leading system message as the initial prompt and tool set; without it, a later tool change rewrites the
145
+ * request's tool list and invalidates the whole prompt cache. Only the first system message moves, so replaying
146
+ * system messages in order gives the same sections and tools. Any other system message, before an assistant message,
147
+ * stays where it was committed: moving it would change what it means to the model.
148
+ */
149
+ function leadWithSystem(messages, baseline) {
150
+ const index = messages.findIndex((message) => message.role === "system");
151
+ if (index <= 0)
122
152
  return messages;
123
- return [messages[index], ...messages.slice(0, index), ...messages.slice(index + 1)];
153
+ const system = messages[index];
154
+ if (system !== baseline && messages.slice(0, index).some((message) => message.role !== "user")) {
155
+ return messages;
156
+ }
157
+ return [system, ...messages.slice(0, index), ...messages.slice(index + 1)];
124
158
  }
125
159
  /** One active entry's model messages after its edit and excluded stop reasons, before tool result ordering. */
126
160
  function contribute(entry, edit) {
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/harness/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport type { AssistantMessage, Message, ToolCall, ToolResultMessage } from \"@earendil-works/pi-ai\";\nimport { idFromNumber } from \"../ids.ts\";\nimport type { SessionImpl } from \"../session/session.ts\";\nimport type { ContextEdit, ConversationId, Cursor, EntryId, EntryQuery, EntryRecord, Storage } from \"../types.ts\";\nimport type { ContextView } from \"./types.ts\";\n\nconst SCAN_PAGE_SIZE = 256;\nconst EXCLUDED_STOP_REASONS: ReadonlySet<AssistantMessage[\"stopReason\"]> = new Set([\"aborted\", \"error\", \"deferred\"]);\nconst MISSING_RESULT_TEXT = \"Tool result unavailable: history ends before this call completed.\";\n\n/** Head marker and newest visible entry that fix one committed context range. */\nexport type ContextBounds = {\n\treadonly head: (EntryRecord & { readonly head: EntryId }) | undefined;\n\treadonly tail: EntryId;\n};\n\n/**\n * Capture the bounds of the current context, or of the context cut off at the visible entry `at`, with two O(1)\n * reads. Run this on the Session line; entries at or below the tail are immutable, so `deriveContext()` can then scan\n * them off the line.\n */\nexport async function captureContextBounds(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat?: EntryId,\n): Promise<ContextBounds | undefined> {\n\tlet tail: EntryId | undefined;\n\tif (at === undefined) {\n\t\ttail = (await storage.scanEntries({ conversationId }, 1, undefined, context)).items[0]?.id;\n\t\tif (tail === undefined) return undefined;\n\t} else {\n\t\tif ((await storage.entry(conversationId, at, context)) === undefined) {\n\t\t\tthrow new Error(`Entry ${at} is not visible from conversation ${conversationId}`);\n\t\t}\n\t\ttail = at;\n\t}\n\treturn { head: await storage.findLatestHeadMarker(conversationId, tail, context), tail };\n}\n\n/**\n * One context read: the visible entries it scanned, from the head marker's head, or transcript start, through\n * `bounds.tail`, oldest first, and the view derived from them. Entries at or below the tail never change, so a later\n * read with the same head marker extends both.\n */\nexport type ContextRange = {\n\treadonly bounds: ContextBounds;\n\treadonly entries: readonly EntryRecord[];\n\treadonly view: ContextView;\n\t/** Targets of the edits in `entries`. */\n\treadonly edited: ReadonlySet<EntryId>;\n\t/**\n\t * `view.messages` from the contributed messages before the last assistant message. Tool results are ordered within\n\t * the messages up to the next assistant message, so later entries cannot change these.\n\t */\n\treadonly settled: readonly Message[];\n\t/** Contributed messages from the last assistant message on, before tool result ordering. */\n\treadonly open: readonly Message[];\n};\n\n/**\n * `readContext()` that reuses `previous`, an earlier range of the same conversation: with the same head marker, only\n * entries after its tail are scanned. Returns the view and the range to pass to the next read. A range outlives the\n * read, so its entries are frozen like `MemoryStorage` records, and the returned view has its own arrays.\n */\nexport async function readContextFrom(\n\tsession: SessionImpl,\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat: EntryId | undefined,\n\tprevious: ContextRange | undefined,\n): Promise<{ readonly view: ContextView; readonly range: ContextRange | undefined }> {\n\tconst bounds = await session.readOnLine(() => captureContextBounds(storage, conversationId, context, at));\n\tif (bounds === undefined) return { view: emptyView(), range: undefined };\n\tfreezeJson(bounds.head);\n\tlet range: ContextRange;\n\tif (previous === undefined || previous.bounds.head?.id !== bounds.head?.id) {\n\t\trange = deriveRange(bounds, freezeJson(await scanRange(storage, conversationId, rangeQuery(bounds), context)));\n\t} else if (bounds.tail === previous.bounds.tail) {\n\t\trange = previous;\n\t} else if (bounds.tail < previous.bounds.tail) {\n\t\trange = deriveRange(\n\t\t\tbounds,\n\t\t\tprevious.entries.filter((entry) => entry.id <= bounds.tail),\n\t\t);\n\t} else {\n\t\tconst minEntryId = idFromNumber<EntryId>(previous.bounds.tail + 1);\n\t\tconst added = await scanRange(storage, conversationId, { minEntryId, maxEntryId: bounds.tail }, context);\n\t\trange = extendRange(previous, bounds, freezeJson(added));\n\t}\n\tconst { view } = range;\n\treturn {\n\t\tview: {\n\t\t\t...view,\n\t\t\tentries: [...view.entries],\n\t\t\tcontributions: [...view.contributions],\n\t\t\tmessages: [...view.messages],\n\t\t},\n\t\trange,\n\t};\n}\n\n/** Committed context of one conversation: bounds captured on the Session line, entries derived off it. */\nexport async function readContext(\n\tsession: SessionImpl,\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat?: EntryId,\n): Promise<ContextView> {\n\tconst bounds = await session.readOnLine(() => captureContextBounds(storage, conversationId, context, at));\n\treturn deriveContext(storage, conversationId, bounds, context);\n}\n\n/**\n * Derive the active transcript and model context of one conversation within captured bounds.\n *\n * H = newest visible head marker; the range runs from `H.head` (or transcript start) through the tail. Per target,\n * the newest edit in the range wins. Context entries are H followed by the range's non-head entries.\n */\nexport async function deriveContext(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tbounds: ContextBounds | undefined,\n\tcontext: Context,\n): Promise<ContextView> {\n\tif (bounds === undefined) return emptyView();\n\treturn deriveRange(bounds, await scanRange(storage, conversationId, rangeQuery(bounds), context)).view;\n}\n\nfunction emptyView(): ContextView {\n\treturn { head: undefined, entries: [], contributions: [], messages: [] };\n}\n\n/** Derive the context view from the entries scanned within `bounds`. */\nfunction deriveRange(bounds: ContextBounds, entries: readonly EntryRecord[]): ContextRange {\n\tconst edits = new Map<EntryId, ContextEdit>();\n\t// Edits of every entry in the range count, including older head markers that `selectActive()` drops.\n\tfor (const entry of entries) for (const edit of entry.edits ?? []) edits.set(edit.target, edit);\n\tconst active = selectActive(bounds.head, entries);\n\tconst contributions = active.map((entry) => contribute(entry, edits.get(entry.id)));\n\tconst { settled, open } = settle([], contributions.flat());\n\tconst messages = leadWithSystem([...settled, ...orderToolResults(open)]);\n\tconst view = { head: bounds.head, entries: active, contributions, messages };\n\treturn { bounds, entries, view, edited: new Set(edits.keys()), settled, open };\n}\n\n/**\n * `previous` extended by the entries `added` after its tail under the same head marker: only their contributions and\n * the open messages are derived. An added edit can change an earlier entry, so it derives the whole range again.\n */\nfunction extendRange(previous: ContextRange, bounds: ContextBounds, added: readonly EntryRecord[]): ContextRange {\n\tconst entries = [...previous.entries, ...added];\n\tif (added.some((entry) => entry.edits !== undefined || entry.head !== undefined || previous.edited.has(entry.id))) {\n\t\treturn deriveRange(bounds, entries);\n\t}\n\tconst contributions = added.map((entry) => contribute(entry, undefined));\n\tconst { settled, open } = settle(previous.settled, [...previous.open, ...contributions.flat()]);\n\tconst view = {\n\t\thead: bounds.head,\n\t\tentries: [...previous.view.entries, ...added],\n\t\tcontributions: [...previous.view.contributions, ...contributions],\n\t\tmessages: leadWithSystem([...settled, ...orderToolResults(open)]),\n\t};\n\treturn { bounds, entries, view, edited: previous.edited, settled, open };\n}\n\n/**\n * Move a system message that only user messages precede to the front. A run's input is committed before generation\n * renders the system prompt, so a transcript, or the range after a compaction or reset, starts with user messages\n * followed by the baseline system message. Providers treat only a leading system message as the initial prompt and tool\n * set; without it, a later tool change rewrites the request's tool list and invalidates the whole prompt cache.\n */\nfunction leadWithSystem(messages: Message[]): Message[] {\n\tconst index = messages.findIndex((message) => message.role !== \"user\");\n\tif (index <= 0 || messages[index]!.role !== \"system\") return messages;\n\treturn [messages[index]!, ...messages.slice(0, index), ...messages.slice(index + 1)];\n}\n\n/** One active entry's model messages after its edit and excluded stop reasons, before tool result ordering. */\nfunction contribute(entry: EntryRecord, edit: ContextEdit | undefined): readonly Message[] {\n\tif (edit?.action === \"omit\") return Object.freeze([]);\n\tconst contributed = edit?.action === \"replace\" ? edit.messages : (entry.model ?? []);\n\treturn Object.freeze(\n\t\tcontributed.filter((message) => message.role !== \"assistant\" || !EXCLUDED_STOP_REASONS.has(message.stopReason)),\n\t);\n}\n\n/** Objects `freezeJson()` froze with everything in them. `Object.isFrozen()` holds for shallow freezes too. */\nconst deeplyFrozen = new WeakSet<object>();\n\n/** Freeze a JSON value and everything in it. */\nfunction freezeJson<T>(value: T): T {\n\tif (value === null || typeof value !== \"object\" || deeplyFrozen.has(value)) return value;\n\tfor (const child of Object.values(value)) freezeJson(child);\n\tObject.freeze(value);\n\tdeeplyFrozen.add(value);\n\treturn value;\n}\n\n/**\n * Move the ordered messages before the last assistant message of `open` to `settled`. `orderToolResults()` of a\n * sequence equals the concatenation over its parts when each later part starts with an assistant message.\n */\nfunction settle(\n\tsettled: readonly Message[],\n\topen: readonly Message[],\n): { readonly settled: readonly Message[]; readonly open: readonly Message[] } {\n\tconst last = open.findLastIndex((message) => message.role === \"assistant\");\n\tif (last <= 0) return { settled, open };\n\treturn { settled: [...settled, ...orderToolResults(open.slice(0, last))], open: open.slice(last) };\n}\n\n/** The raw active entries within captured bounds, without deriving model context. */\nexport async function activeEntries(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tbounds: ContextBounds | undefined,\n\tcontext: Context,\n): Promise<readonly EntryRecord[]> {\n\tif (bounds === undefined) return [];\n\treturn selectActive(bounds.head, await scanRange(storage, conversationId, rangeQuery(bounds), context));\n}\n\n/** The visible range of `bounds`: from the head marker's head, or transcript start, through the tail. */\nfunction rangeQuery(bounds: ContextBounds): Omit<EntryQuery, \"conversationId\"> {\n\tconst head = bounds.head;\n\treturn head === undefined ? { maxEntryId: bounds.tail } : { minEntryId: head.head, maxEntryId: bounds.tail };\n}\n\n/** Visible entries within `range`, oldest first. */\nasync function scanRange(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\trange: Omit<EntryQuery, \"conversationId\">,\n\tcontext: Context,\n): Promise<EntryRecord[]> {\n\tconst entries: EntryRecord[] = [];\n\tlet cursor: Cursor | undefined;\n\tdo {\n\t\tconst page = await storage.scanEntries({ conversationId, ...range }, SCAN_PAGE_SIZE, cursor, context);\n\t\tentries.push(...page.items);\n\t\tcursor = page.next;\n\t} while (cursor !== undefined);\n\treturn entries.reverse();\n}\n\n/** The head marker followed by the range's non-head entries, or the whole range without a marker. */\nfunction selectActive(head: ContextBounds[\"head\"], range: readonly EntryRecord[]): readonly EntryRecord[] {\n\treturn head === undefined ? range : [head, ...range.filter((entry) => entry.head === undefined)];\n}\n\n/**\n * Place each assistant's tool results directly after it in call order. Results are taken from the messages before\n * the next assistant; a missing result is synthesized and unmatched results are dropped.\n */\nexport function orderToolResults(messages: readonly Message[]): Message[] {\n\tconst ordered: Message[] = [];\n\tfor (let index = 0; index < messages.length; index++) {\n\t\tconst message = messages[index]!;\n\t\tif (message.role === \"toolResult\") continue;\n\t\tordered.push(message);\n\t\tif (message.role !== \"assistant\") continue;\n\t\tconst calls = message.content.filter((content): content is ToolCall => content.type === \"toolCall\");\n\t\tif (calls.length === 0) continue;\n\t\tconst results = new Map<string, number>();\n\t\tfor (let next = index + 1; next < messages.length && messages[next]!.role !== \"assistant\"; next++) {\n\t\t\tconst candidate = messages[next]!;\n\t\t\tif (candidate.role === \"toolResult\" && !results.has(candidate.toolCallId)) {\n\t\t\t\tresults.set(candidate.toolCallId, next);\n\t\t\t}\n\t\t}\n\t\tfor (const call of calls) {\n\t\t\tconst resultIndex = results.get(call.id);\n\t\t\tordered.push(resultIndex === undefined ? missingResult(call, message.timestamp) : messages[resultIndex]!);\n\t\t}\n\t}\n\treturn ordered;\n}\n\nfunction missingResult(call: ToolCall, timestamp: number): ToolResultMessage {\n\treturn freezeJson({\n\t\trole: \"toolResult\",\n\t\ttoolCallId: call.id,\n\t\ttoolName: call.name,\n\t\tcontent: [{ type: \"text\", text: MISSING_RESULT_TEXT }],\n\t\tisError: true,\n\t\tdetails: { reason: \"missing_result\" },\n\t\ttimestamp,\n\t});\n}\n"]}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/harness/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport type { AssistantMessage, Message, ToolCall, ToolResultMessage } from \"@earendil-works/pi-ai\";\nimport { SystemEntry } from \"../entries.ts\";\nimport { idFromNumber } from \"../ids.ts\";\nimport type { SessionImpl } from \"../session/session.ts\";\nimport type { ContextEdit, ConversationId, Cursor, EntryId, EntryQuery, EntryRecord, Storage } from \"../types.ts\";\nimport type { ContextView } from \"./types.ts\";\n\nconst SCAN_PAGE_SIZE = 256;\nconst EXCLUDED_STOP_REASONS: ReadonlySet<AssistantMessage[\"stopReason\"]> = new Set([\"aborted\", \"error\", \"deferred\"]);\nconst MISSING_RESULT_TEXT = \"Tool result unavailable: history ends before this call completed.\";\n\n/** Head marker and newest visible entry that fix one committed context range. */\nexport type ContextBounds = {\n\treadonly head: (EntryRecord & { readonly head: EntryId }) | undefined;\n\treadonly tail: EntryId;\n};\n\n/**\n * Capture the bounds of the current context, or of the context cut off at the visible entry `at`, with two O(1)\n * reads. Run this on the Session line; entries at or below the tail are immutable, so `deriveContext()` can then scan\n * them off the line.\n */\nexport async function captureContextBounds(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat?: EntryId,\n): Promise<ContextBounds | undefined> {\n\tlet tail: EntryId | undefined;\n\tif (at === undefined) {\n\t\ttail = (await storage.scanEntries({ conversationId }, 1, undefined, context)).items[0]?.id;\n\t\tif (tail === undefined) return undefined;\n\t} else {\n\t\tif ((await storage.entry(conversationId, at, context)) === undefined) {\n\t\t\tthrow new Error(`Entry ${at} is not visible from conversation ${conversationId}`);\n\t\t}\n\t\ttail = at;\n\t}\n\treturn { head: await storage.findLatestHeadMarker(conversationId, tail, context), tail };\n}\n\n/**\n * One context read: the visible entries it scanned, from the head marker's head, or transcript start, through\n * `bounds.tail`, oldest first, and the view derived from them. Entries at or below the tail never change, so a later\n * read with the same head marker extends both.\n */\nexport type ContextRange = {\n\treadonly bounds: ContextBounds;\n\treadonly entries: readonly EntryRecord[];\n\treadonly view: ContextView;\n\t/** Targets of the edits in `entries`. */\n\treadonly edited: ReadonlySet<EntryId>;\n\t/**\n\t * `view.messages` from the contributed messages before the last assistant message. Tool results are ordered within\n\t * the messages up to the next assistant message, so later entries cannot change these.\n\t */\n\treadonly settled: readonly Message[];\n\t/** Contributed messages from the last assistant message on, before tool result ordering. */\n\treadonly open: readonly Message[];\n\t/** The baseline's system message; see `findBaseline()`. */\n\treadonly baseline: Baseline;\n};\n\n/** A baseline's system message, `null` when the range cannot have one, or `undefined` while a later entry may be one. */\ntype Baseline = Message | null | undefined;\n\n/**\n * `readContext()` that reuses `previous`, an earlier range of the same conversation: with the same head marker, only\n * entries after its tail are scanned. Returns the view and the range to pass to the next read. A range outlives the\n * read, so its entries are frozen like `MemoryStorage` records, and the returned view has its own arrays.\n */\nexport async function readContextFrom(\n\tsession: SessionImpl,\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat: EntryId | undefined,\n\tprevious: ContextRange | undefined,\n): Promise<{ readonly view: ContextView; readonly range: ContextRange | undefined }> {\n\tconst bounds = await session.readOnLine(() => captureContextBounds(storage, conversationId, context, at));\n\tif (bounds === undefined) return { view: emptyView(), range: undefined };\n\tfreezeJson(bounds.head);\n\tlet range: ContextRange;\n\tif (previous === undefined || previous.bounds.head?.id !== bounds.head?.id) {\n\t\trange = deriveRange(bounds, freezeJson(await scanRange(storage, conversationId, rangeQuery(bounds), context)));\n\t} else if (bounds.tail === previous.bounds.tail) {\n\t\trange = previous;\n\t} else if (bounds.tail < previous.bounds.tail) {\n\t\trange = deriveRange(\n\t\t\tbounds,\n\t\t\tprevious.entries.filter((entry) => entry.id <= bounds.tail),\n\t\t);\n\t} else {\n\t\tconst minEntryId = idFromNumber<EntryId>(previous.bounds.tail + 1);\n\t\tconst added = await scanRange(storage, conversationId, { minEntryId, maxEntryId: bounds.tail }, context);\n\t\trange = extendRange(previous, bounds, freezeJson(added));\n\t}\n\tconst { view } = range;\n\treturn {\n\t\tview: {\n\t\t\t...view,\n\t\t\tentries: [...view.entries],\n\t\t\tcontributions: [...view.contributions],\n\t\t\tmessages: [...view.messages],\n\t\t},\n\t\trange,\n\t};\n}\n\n/** Committed context of one conversation: bounds captured on the Session line, entries derived off it. */\nexport async function readContext(\n\tsession: SessionImpl,\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat?: EntryId,\n): Promise<ContextView> {\n\tconst bounds = await session.readOnLine(() => captureContextBounds(storage, conversationId, context, at));\n\treturn deriveContext(storage, conversationId, bounds, context);\n}\n\n/**\n * Derive the active transcript and model context of one conversation within captured bounds.\n *\n * H = newest visible head marker; the range runs from `H.head` (or transcript start) through the tail. Per target,\n * the newest edit in the range wins. Context entries are H followed by the range's non-head entries.\n */\nexport async function deriveContext(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tbounds: ContextBounds | undefined,\n\tcontext: Context,\n): Promise<ContextView> {\n\tif (bounds === undefined) return emptyView();\n\treturn deriveRange(bounds, await scanRange(storage, conversationId, rangeQuery(bounds), context)).view;\n}\n\nfunction emptyView(): ContextView {\n\treturn { head: undefined, entries: [], contributions: [], messages: [] };\n}\n\n/** Derive the context view from the entries scanned within `bounds`. */\nfunction deriveRange(bounds: ContextBounds, entries: readonly EntryRecord[]): ContextRange {\n\tconst edits = new Map<EntryId, ContextEdit>();\n\t// Edits of every entry in the range count, including older head markers that `selectActive()` drops.\n\tfor (const entry of entries) for (const edit of entry.edits ?? []) edits.set(edit.target, edit);\n\tconst active = selectActive(bounds.head, entries);\n\tconst contributions = active.map((entry) => contribute(entry, edits.get(entry.id)));\n\tconst { settled, open } = settle([], contributions.flat());\n\tconst baseline = findBaseline(bounds.head, active);\n\tconst messages = leadWithSystem([...settled, ...orderToolResults(open)], baseline);\n\tconst view = { head: bounds.head, entries: active, contributions, messages };\n\treturn { bounds, entries, view, edited: new Set(edits.keys()), settled, open, baseline };\n}\n\n/**\n * `previous` extended by the entries `added` after its tail under the same head marker: only their contributions and\n * the open messages are derived. An added edit can change an earlier entry, so it derives the whole range again.\n */\nfunction extendRange(previous: ContextRange, bounds: ContextBounds, added: readonly EntryRecord[]): ContextRange {\n\tconst entries = [...previous.entries, ...added];\n\tif (added.some((entry) => entry.edits !== undefined || entry.head !== undefined || previous.edited.has(entry.id))) {\n\t\treturn deriveRange(bounds, entries);\n\t}\n\tconst contributions = added.map((entry) => contribute(entry, undefined));\n\tconst { settled, open } = settle(previous.settled, [...previous.open, ...contributions.flat()]);\n\tconst baseline = previous.baseline !== undefined ? previous.baseline : findBaseline(bounds.head, added);\n\tconst view = {\n\t\thead: bounds.head,\n\t\tentries: [...previous.view.entries, ...added],\n\t\tcontributions: [...previous.view.contributions, ...contributions],\n\t\tmessages: leadWithSystem([...settled, ...orderToolResults(open)], baseline),\n\t};\n\treturn { bounds, entries, view, edited: previous.edited, settled, open, baseline };\n}\n\n/**\n * The baseline among `entries`, oldest first: the system message of the first `pi.system` entry after the head\n * marker, or of the transcript without one, when generation wrote it with every section and tool in effect. After a\n * head marker it always does, and omits the retained `pi.system` entries. Without one it does when it saw no system\n * message before, which holds unless an earlier entry carries one, even if an edit omits it later. An edit that targets\n * the baseline contributes other messages, or none, so none leads; a later entry does not take its place.\n */\nfunction findBaseline(head: ContextBounds[\"head\"], entries: readonly EntryRecord[]): Baseline {\n\tfor (const entry of entries) {\n\t\tif (head !== undefined && entry.id <= head.id) continue;\n\t\tif (SystemEntry.is(entry)) return entry.model?.find((message) => message.role === \"system\") ?? null;\n\t\tif (head === undefined && carriesSystemMessage(entry)) return null;\n\t}\n\treturn undefined;\n}\n\n/** Whether an entry's own messages, or the replacement messages of its edits, include a system message. */\nfunction carriesSystemMessage(entry: EntryRecord): boolean {\n\tconst isSystem = (message: Message) => message.role === \"system\";\n\treturn (\n\t\t(entry.model?.some(isSystem) ?? false) ||\n\t\t(entry.edits?.some((edit) => edit.action === \"replace\" && edit.messages.some(isSystem)) ?? false)\n\t);\n}\n\n/**\n * Move the first system message to the front when only user messages precede it, or when it is the baseline's. A run's\n * input is committed before generation renders the system prompt, so a transcript starts with the input followed by\n * the baseline, and the range after a compaction with the kept history, assistant messages included. Providers treat\n * only a leading system message as the initial prompt and tool set; without it, a later tool change rewrites the\n * request's tool list and invalidates the whole prompt cache. Only the first system message moves, so replaying\n * system messages in order gives the same sections and tools. Any other system message, before an assistant message,\n * stays where it was committed: moving it would change what it means to the model.\n */\nfunction leadWithSystem(messages: Message[], baseline: Baseline): Message[] {\n\tconst index = messages.findIndex((message) => message.role === \"system\");\n\tif (index <= 0) return messages;\n\tconst system = messages[index]!;\n\tif (system !== baseline && messages.slice(0, index).some((message) => message.role !== \"user\")) {\n\t\treturn messages;\n\t}\n\treturn [system, ...messages.slice(0, index), ...messages.slice(index + 1)];\n}\n\n/** One active entry's model messages after its edit and excluded stop reasons, before tool result ordering. */\nfunction contribute(entry: EntryRecord, edit: ContextEdit | undefined): readonly Message[] {\n\tif (edit?.action === \"omit\") return Object.freeze([]);\n\tconst contributed = edit?.action === \"replace\" ? edit.messages : (entry.model ?? []);\n\treturn Object.freeze(\n\t\tcontributed.filter((message) => message.role !== \"assistant\" || !EXCLUDED_STOP_REASONS.has(message.stopReason)),\n\t);\n}\n\n/** Objects `freezeJson()` froze with everything in them. `Object.isFrozen()` holds for shallow freezes too. */\nconst deeplyFrozen = new WeakSet<object>();\n\n/** Freeze a JSON value and everything in it. */\nfunction freezeJson<T>(value: T): T {\n\tif (value === null || typeof value !== \"object\" || deeplyFrozen.has(value)) return value;\n\tfor (const child of Object.values(value)) freezeJson(child);\n\tObject.freeze(value);\n\tdeeplyFrozen.add(value);\n\treturn value;\n}\n\n/**\n * Move the ordered messages before the last assistant message of `open` to `settled`. `orderToolResults()` of a\n * sequence equals the concatenation over its parts when each later part starts with an assistant message.\n */\nfunction settle(\n\tsettled: readonly Message[],\n\topen: readonly Message[],\n): { readonly settled: readonly Message[]; readonly open: readonly Message[] } {\n\tconst last = open.findLastIndex((message) => message.role === \"assistant\");\n\tif (last <= 0) return { settled, open };\n\treturn { settled: [...settled, ...orderToolResults(open.slice(0, last))], open: open.slice(last) };\n}\n\n/** The raw active entries within captured bounds, without deriving model context. */\nexport async function activeEntries(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tbounds: ContextBounds | undefined,\n\tcontext: Context,\n): Promise<readonly EntryRecord[]> {\n\tif (bounds === undefined) return [];\n\treturn selectActive(bounds.head, await scanRange(storage, conversationId, rangeQuery(bounds), context));\n}\n\n/** The visible range of `bounds`: from the head marker's head, or transcript start, through the tail. */\nfunction rangeQuery(bounds: ContextBounds): Omit<EntryQuery, \"conversationId\"> {\n\tconst head = bounds.head;\n\treturn head === undefined ? { maxEntryId: bounds.tail } : { minEntryId: head.head, maxEntryId: bounds.tail };\n}\n\n/** Visible entries within `range`, oldest first. */\nasync function scanRange(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\trange: Omit<EntryQuery, \"conversationId\">,\n\tcontext: Context,\n): Promise<EntryRecord[]> {\n\tconst entries: EntryRecord[] = [];\n\tlet cursor: Cursor | undefined;\n\tdo {\n\t\tconst page = await storage.scanEntries({ conversationId, ...range }, SCAN_PAGE_SIZE, cursor, context);\n\t\tentries.push(...page.items);\n\t\tcursor = page.next;\n\t} while (cursor !== undefined);\n\treturn entries.reverse();\n}\n\n/** The head marker followed by the range's non-head entries, or the whole range without a marker. */\nfunction selectActive(head: ContextBounds[\"head\"], range: readonly EntryRecord[]): readonly EntryRecord[] {\n\treturn head === undefined ? range : [head, ...range.filter((entry) => entry.head === undefined)];\n}\n\n/**\n * Place each assistant's tool results directly after it in call order. Results are taken from the messages before\n * the next assistant; a missing result is synthesized and unmatched results are dropped.\n */\nexport function orderToolResults(messages: readonly Message[]): Message[] {\n\tconst ordered: Message[] = [];\n\tfor (let index = 0; index < messages.length; index++) {\n\t\tconst message = messages[index]!;\n\t\tif (message.role === \"toolResult\") continue;\n\t\tordered.push(message);\n\t\tif (message.role !== \"assistant\") continue;\n\t\tconst calls = message.content.filter((content): content is ToolCall => content.type === \"toolCall\");\n\t\tif (calls.length === 0) continue;\n\t\tconst results = new Map<string, number>();\n\t\tfor (let next = index + 1; next < messages.length && messages[next]!.role !== \"assistant\"; next++) {\n\t\t\tconst candidate = messages[next]!;\n\t\t\tif (candidate.role === \"toolResult\" && !results.has(candidate.toolCallId)) {\n\t\t\t\tresults.set(candidate.toolCallId, next);\n\t\t\t}\n\t\t}\n\t\tfor (const call of calls) {\n\t\t\tconst resultIndex = results.get(call.id);\n\t\t\tordered.push(resultIndex === undefined ? missingResult(call, message.timestamp) : messages[resultIndex]!);\n\t\t}\n\t}\n\treturn ordered;\n}\n\nfunction missingResult(call: ToolCall, timestamp: number): ToolResultMessage {\n\treturn freezeJson({\n\t\trole: \"toolResult\",\n\t\ttoolCallId: call.id,\n\t\ttoolName: call.name,\n\t\tcontent: [{ type: \"text\", text: MISSING_RESULT_TEXT }],\n\t\tisError: true,\n\t\tdetails: { reason: \"missing_result\" },\n\t\ttimestamp,\n\t});\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/harness/tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,kBAAkB,EAAkB,MAAM,uBAAuB,CAAC;AAGpH,OAAO,KAAK,EAA6B,QAAQ,EAAqB,MAAM,uBAAuB,CAAC;AAQpG,OAAO,KAAK,EACX,cAAc,EACd,OAAO,EACP,UAAU,EACV,IAAI,EACJ,MAAM,EAIN,EAAE,EACF,UAAU,EACV,MAAM,aAAa,CAAC;AAarB,OAAO,KAAK,EACX,yBAAyB,EACzB,WAAW,EACX,cAAc,EAEd,mBAAmB,EAEnB,SAAS,EAET,MAAM,YAAY,CAAC;AAGpB;;;GAGG;AACH,MAAM,MAAM,aAAa,GACtB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrD;IACA,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,QAAQ,CAAC;IACf,0GAA0G;IAC1G,QAAQ,CAAC,EAAE,KAAK,CAAC;CAChB,CAAC;AAEL,MAAM,MAAM,kBAAkB,GAC3B;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE;AACnB,2FAA2F;GACzF;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAA;CAAE,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,WAAW,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAQ7G,kFAAkF;AAClF,MAAM,MAAM,eAAe,GAAG;IAC7B,mGAAmG;IACnG,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;IAChC,oHAAoH;IACpH,MAAM,CAAC,EAAE,kBAAkB,CAAC,yBAAyB,CAAC,CAAC;CACvD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,iEAMxB,CAAC;AAKH;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,oEAuFnB,CAAC;AAukBH,oGAAoG;AACpG,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAE/E;AAwCD;;;GAGG;AACH,wBAAsB,gBAAgB,CACrC,EAAE,EAAE,EAAE,EACN,cAAc,EAAE,cAAc,EAC9B,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,mBAAmB,EAC3B,IAAI,EAAE;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAChE,OAAO,CAAC,UAAU,CAAC;IAAE,WAAW,EAAE,cAAc,EAAE,CAAA;CAAE,CAAC,CAAC,CAkBxD"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/harness/tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,kBAAkB,EAAkB,MAAM,uBAAuB,CAAC;AAUpH,OAAO,KAAK,EAA6B,QAAQ,EAAqB,MAAM,uBAAuB,CAAC;AAQpG,OAAO,KAAK,EACX,cAAc,EACd,OAAO,EACP,UAAU,EACV,IAAI,EACJ,MAAM,EAIN,EAAE,EACF,UAAU,EACV,MAAM,aAAa,CAAC;AAYrB,OAAO,KAAK,EACX,yBAAyB,EACzB,WAAW,EACX,cAAc,EAEd,mBAAmB,EAEnB,SAAS,EAET,MAAM,YAAY,CAAC;AAGpB;;;GAGG;AACH,MAAM,MAAM,aAAa,GACtB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrD;IACA,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,QAAQ,CAAC;IACf,0GAA0G;IAC1G,QAAQ,CAAC,EAAE,KAAK,CAAC;CAChB,CAAC;AAEL,MAAM,MAAM,kBAAkB,GAC3B;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE;AACnB,2FAA2F;GACzF;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAA;CAAE,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,WAAW,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAQ7G,kFAAkF;AAClF,MAAM,MAAM,eAAe,GAAG;IAC7B,mGAAmG;IACnG,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;IAChC,oHAAoH;IACpH,MAAM,CAAC,EAAE,kBAAkB,CAAC,yBAAyB,CAAC,CAAC;CACvD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,iEAMxB,CAAC;AAKH;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,oEAuFnB,CAAC;AAukBH,oGAAoG;AACpG,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAE/E;AAwCD;;;GAGG;AACH,wBAAsB,gBAAgB,CACrC,EAAE,EAAE,EAAE,EACN,cAAc,EAAE,cAAc,EAC9B,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,mBAAmB,EAC3B,IAAI,EAAE;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAChE,OAAO,CAAC,UAAU,CAAC;IAAE,WAAW,EAAE,cAAc,EAAE,CAAA;CAAE,CAAC,CAAC,CAkBxD"}
@@ -1,6 +1,7 @@
1
1
  import { copyJson } from "@earendil-works/chord";
2
2
  import { awaitWithContext } from "@earendil-works/chord/context";
3
3
  import { overlap } from "@earendil-works/chord/delta";
4
+ import { boundOutput, OutputBuffer, PROGRESS_BYTES_PER_SECOND, Progress, } from "@earendil-works/chord/output";
4
5
  import { validateToolArguments } from "@earendil-works/pi-ai/utils/validation";
5
6
  import { Compile } from "typebox/compile";
6
7
  import { defineDocFamily } from "../documents.js";
@@ -10,7 +11,6 @@ import { defineTask } from "../tasks.js";
10
11
  import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, utf8ByteLength } from "../truncate.js";
11
12
  import { assignJson } from "./json.js";
12
13
  import { clearProgress, finishSlot, LiveDoc, removeNestedSlots, toolSlot, } from "./live.js";
13
- import { boundOutput, OutputBuffer, PROGRESS_BYTES_PER_SECOND, Progress } from "./output.js";
14
14
  import { recordUsage } from "./usage.js";
15
15
  /**
16
16
  * Memo of a tool task that made a nested call, in any attempt, committed before the first admission of each attempt: