@earendil-works/pi-durable 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +2 -0
  3. package/dist/harness/agent.d.ts.map +1 -1
  4. package/dist/harness/agent.js +3 -0
  5. package/dist/harness/agent.js.map +1 -1
  6. package/dist/harness/compaction.js +1 -1
  7. package/dist/harness/compaction.js.map +1 -1
  8. package/dist/harness/context.d.ts +29 -1
  9. package/dist/harness/context.d.ts.map +1 -1
  10. package/dist/harness/context.js +125 -24
  11. package/dist/harness/context.js.map +1 -1
  12. package/dist/harness/generation.d.ts.map +1 -1
  13. package/dist/harness/generation.js +3 -3
  14. package/dist/harness/generation.js.map +1 -1
  15. package/dist/harness/harness.d.ts.map +1 -1
  16. package/dist/harness/harness.js +4 -3
  17. package/dist/harness/harness.js.map +1 -1
  18. package/dist/harness/scheduler.d.ts.map +1 -1
  19. package/dist/harness/scheduler.js +98 -5
  20. package/dist/harness/scheduler.js.map +1 -1
  21. package/dist/harness/tool.d.ts +1 -1
  22. package/dist/harness/tool.d.ts.map +1 -1
  23. package/dist/harness/tool.js +15 -5
  24. package/dist/harness/tool.js.map +1 -1
  25. package/dist/harness/types.d.ts +18 -2
  26. package/dist/harness/types.d.ts.map +1 -1
  27. package/dist/harness/types.js.map +1 -1
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js.map +1 -1
  31. package/dist/session/session.d.ts +5 -3
  32. package/dist/session/session.d.ts.map +1 -1
  33. package/dist/session/session.js +5 -4
  34. package/dist/session/session.js.map +1 -1
  35. package/dist/session/transaction.d.ts +2 -0
  36. package/dist/session/transaction.d.ts.map +1 -1
  37. package/dist/session/transaction.js +18 -1
  38. package/dist/session/transaction.js.map +1 -1
  39. package/dist/storage/jsonl/storage.d.ts +4 -0
  40. package/dist/storage/jsonl/storage.d.ts.map +1 -1
  41. package/dist/storage/memory.d.ts +2 -0
  42. package/dist/storage/memory.d.ts.map +1 -1
  43. package/dist/storage/memory.js +72 -18
  44. package/dist/storage/memory.js.map +1 -1
  45. package/dist/storage/scan.d.ts +18 -0
  46. package/dist/storage/scan.d.ts.map +1 -0
  47. package/dist/storage/scan.js +28 -0
  48. package/dist/storage/scan.js.map +1 -0
  49. package/dist/storage/sqlite/cloudflare.d.ts +46 -0
  50. package/dist/storage/sqlite/cloudflare.d.ts.map +1 -0
  51. package/dist/storage/sqlite/cloudflare.js +106 -0
  52. package/dist/storage/sqlite/cloudflare.js.map +1 -0
  53. package/dist/storage/sqlite/storage.d.ts +2 -0
  54. package/dist/storage/sqlite/storage.d.ts.map +1 -1
  55. package/dist/storage/sqlite/storage.js +68 -16
  56. package/dist/storage/sqlite/storage.js.map +1 -1
  57. package/dist/testing/storage-conformance.d.ts.map +1 -1
  58. package/dist/testing/storage-conformance.js +58 -0
  59. package/dist/testing/storage-conformance.js.map +1 -1
  60. package/dist/types.d.ts +30 -7
  61. package/dist/types.d.ts.map +1 -1
  62. package/dist/types.js.map +1 -1
  63. package/package.json +10 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.1.0] - 2026-10-07
4
+
5
+ ### Breaking Changes
6
+
7
+ - `Storage` implementations must honor the new `order` field of `ConversationQuery`, `EntryQuery`, `TaskQuery`, and `SubmissionQuery`, and continue a cursor in the order it was returned with. A storage that ignores it returns pages in the wrong direction; the conformance suite covers both orders ([#10546](https://github.com/earendil-works/pi/issues/10546)).
8
+ - `TaskRuntime.context()` takes the cutoff as an options object, matching `Conversation.context()`: `runtime.context(conversationId, context, { at })` replaces `runtime.context(conversationId, context, at)`.
9
+
10
+ ### Added
11
+
12
+ - `ToolExecutionApi.models` and `HookApi.models`: the Harness's `models`, so tools and hooks can resolve models and make requests with the same catalog, credentials, and request transforms as generation ([#10395](https://github.com/earendil-works/pi/issues/10395)).
13
+ - `Conversation.context(context, { at })` returns the model context as of a visible earlier entry, the same view `fork(at)` would start with, without creating a conversation ([#10512](https://github.com/earendil-works/pi/issues/10512)).
14
+ - `ScanOrder` (`ascending` or `descending`) as `order` on conversation, entry, task, and submission scans, including `Tx.scanTasks()` and `Conversation.entries()`, so hosts can page the newest tasks first. Defaults are unchanged: entries newest first, everything else oldest first. A cursor continues in its scan's order, so the query may omit `order`; asking for the other order with it throws ([#10546](https://github.com/earendil-works/pi/issues/10546)).
15
+ - Task records carry `startedAt` and `endedAt`, wall-clock times the Session stamps at the first change to `running` and at the change to `terminal`, with `HarnessOptions.now` or the new `createSession(storage, { now })` option. `startedAt` survives waits and reopen. `inspect()` shows them on each live task's record. Records written by earlier versions have neither ([#10549](https://github.com/earendil-works/pi/issues/10549)).
16
+ - `pi.tool-result` messages carry `durationMs`: how long `execute()` took in that attempt, measured with a monotonic clock. Calls that did not execute, and interrupted or aborted calls, have none ([#10549](https://github.com/earendil-works/pi/issues/10549)).
17
+ - Assistant messages carry the `durationMs` pi-ai measures for each response ([#10549](https://github.com/earendil-works/pi/issues/10549)).
18
+ - `openDurableObjectSqliteStorage(ctx.storage)` in `@earendil-works/pi-durable/storage/sqlite/cloudflare`: SQLite storage on a SQLite-backed Cloudflare Durable Object.
19
+ - `settings.contextRetentionMs` (default ten minutes): how long an idle conversation keeps its last context read in memory; `0` drops it once idle.
20
+
21
+ ### Changed
22
+
23
+ - `runtime.context()` keeps each conversation's last read range and derived view in memory: a later read by any of its tasks with the same head marker scans and derives only the entries committed since. A generation no longer rereads and re-derives the whole transcript for each model request. Busy conversations keep it; idle ones for `settings.contextRetentionMs`.
24
+
25
+ ### Fixed
26
+
27
+ - A conversation's first system message, and the baseline after a compaction or reset, now leads the model context instead of following the input that started the run. Providers with native mid-conversation tool changes, such as Anthropic, keep the prompt cache across later tool changes instead of rewriting it in full ([#10542](https://github.com/earendil-works/pi/issues/10542)).
28
+
3
29
  ## [1.0.4] - 2026-10-05
4
30
 
5
31
  ### Breaking Changes
package/README.md CHANGED
@@ -224,6 +224,7 @@ const harness = await Harness.open(storage, {
224
224
  compaction: { reserveTokens: 16384 },
225
225
  progress: { partialIntervalMs: 100, outputIntervalMs: 100 },
226
226
  toolExecution: "parallel",
227
+ contextRetentionMs: 600_000, // idle conversations keep their context in memory this long (checked lazily where timers cannot be unreferenced)
227
228
  get followUpMode() {
228
229
  return userSettings.followUpMode;
229
230
  },
@@ -539,6 +540,7 @@ const usage = await harness.usage(context); // { models: { "openai/gpt-6-sol": U
539
540
  | Memory | `MemoryStorage` from the package root | Nothing is persisted. |
540
541
  | SQLite | `openNodeSqliteStorage(file)` from `@earendil-works/pi-durable/storage/sqlite/node` | One database file. WAL mode with `synchronous = NORMAL`: commits survive process crashes; the newest may be lost on power or host failure. |
541
542
  | 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. |
543
+ | 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. |
542
544
 
543
545
  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`.
544
546
 
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/harness/agent.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,KAAK,EACX,KAAK,EACL,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,uBAAuB,EAEvB,eAAe,EACf,cAAc,EAEd,gBAAgB,EAChB,QAAQ,EACR,gBAAgB,EAChB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,oBAAoB,EAAE,uBAKlC,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,gBAKvC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,cAGrC,CAAC;AAEF,8DAA8D;AAC9D,eAAO,MAAM,gBAAgB,iBAAiB,CAAC;AAE/C,6FAA6F;AAC7F,eAAO,MAAM,QAAQ,kEAQnB,CAAC;AAEH,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,GAAG,SAAS,GAAG,QAAQ,CAgB/E;AAED,4HAA4H;AAC5H,wBAAsB,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAG1G;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAY9G;AAuCD;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAMzF;AAED,sFAAsF;AACtF,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAMnE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,SAAS,gBAAgB,EACzD,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,SAAS,EACvC,QAAQ,EAAE,gBAAgB,CAAC,IAAI,CAAC,EAChC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAC9B,KAAK,CAAC,IAAI,CAAC,CA0Db"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/harness/agent.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,KAAK,EACX,KAAK,EACL,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,uBAAuB,EAEvB,eAAe,EACf,cAAc,EAEd,gBAAgB,EAChB,QAAQ,EACR,gBAAgB,EAChB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,oBAAoB,EAAE,uBAKlC,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,gBAKvC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,cAGrC,CAAC;AAEF,8DAA8D;AAC9D,eAAO,MAAM,gBAAgB,iBAAiB,CAAC;AAE/C,6FAA6F;AAC7F,eAAO,MAAM,QAAQ,kEAQnB,CAAC;AAKH,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,GAAG,SAAS,GAAG,QAAQ,CAiB/E;AAED,4HAA4H;AAC5H,wBAAsB,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAG1G;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAY9G;AAuCD;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAMzF;AAED,sFAAsF;AACtF,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAMnE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,SAAS,gBAAgB,EACzD,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,SAAS,EACvC,QAAQ,EAAE,gBAAgB,CAAC,IAAI,CAAC,EAChC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAC9B,KAAK,CAAC,IAAI,CAAC,CA0Db"}
@@ -28,6 +28,8 @@ export const AgentDoc = defineDoc({
28
28
  initial: () => ({}),
29
29
  checkpointWhen: () => true,
30
30
  });
31
+ /** Default `settings.contextRetentionMs`: ten minutes. */
32
+ const DEFAULT_CONTEXT_RETENTION_MS = 600_000;
31
33
  /** Resolve the host settings: every field over its built-in default, object fields merged. */
32
34
  export function resolveSettings(settings) {
33
35
  const extensions = settings?.extensions;
@@ -44,6 +46,7 @@ export function resolveSettings(settings) {
44
46
  toolExecution: settings?.toolExecution ?? "parallel",
45
47
  steeringMode: settings?.steeringMode ?? "one-at-a-time",
46
48
  followUpMode: settings?.followUpMode ?? "one-at-a-time",
49
+ contextRetentionMs: settings?.contextRetentionMs ?? DEFAULT_CONTEXT_RETENTION_MS,
47
50
  };
48
51
  }
49
52
  /** Apply one change to `pi.agent`: a given field replaces the stored one, `null` clears it, `undefined` changes nothing. */
@@ -1 +1 @@
1
- {"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/harness/agent.ts"],"names":[],"mappings":"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","sourcesContent":["import { copyJson, type Draft } from \"@earendil-works/chord\";\nimport { defineDoc } from \"../documents.ts\";\nimport type { ConversationId, ConversationRecord, Tx } from \"../types.ts\";\nimport type {\n\tAgent,\n\tAgentChange,\n\tAgentState,\n\tCompactionPolicy,\n\tConversationRetryPolicy,\n\tExtension,\n\tHarnessSettings,\n\tProgressPolicy,\n\tPromptSection,\n\tRegistrySnapshot,\n\tSettings,\n\tToolRegistration,\n} from \"./types.ts\";\n\nexport const DEFAULT_RETRY_POLICY: ConversationRetryPolicy = {\n\tenabled: true,\n\tmaxRetries: 3,\n\tbaseDelayMs: 2000,\n\tmaxAgentDelayMs: 60000,\n};\n\nexport const DEFAULT_COMPACTION_POLICY: CompactionPolicy = {\n\tenabled: true,\n\treserveTokens: 16384,\n\tkeepRecentTokens: 20000,\n\tbackgroundTokens: 32768,\n};\n\nexport const DEFAULT_PROGRESS_POLICY: ProgressPolicy = {\n\tpartialIntervalMs: 100,\n\toutputIntervalMs: 100,\n};\n\n/** The reserved section key of the agent's `instructions`. */\nexport const INSTRUCTIONS_KEY = \"instructions\";\n\n/** Built-in agent document; rewindable so forks start from the agent at their fork entry. */\nexport const AgentDoc = defineDoc<AgentState>({\n\tkind: \"pi.agent\",\n\tversion: 1,\n\tscope: \"conversation\",\n\thistory: \"rewindable\",\n\tfork: \"asOf\",\n\tinitial: () => ({}),\n\tcheckpointWhen: () => true,\n});\n\n/** Resolve the host settings: every field over its built-in default, object fields merged. */\nexport function resolveSettings(settings: HarnessSettings | undefined): Settings {\n\tconst extensions = settings?.extensions;\n\treturn {\n\t\t...(extensions === undefined ? {} : { extensions }),\n\t\tstream: { ...settings?.stream },\n\t\tretry: { ...DEFAULT_RETRY_POLICY, ...settings?.retry },\n\t\tcompaction: { ...DEFAULT_COMPACTION_POLICY, ...settings?.compaction },\n\t\t// Field by field, so an explicitly undefined interval keeps its default.\n\t\tprogress: {\n\t\t\tpartialIntervalMs: settings?.progress?.partialIntervalMs ?? DEFAULT_PROGRESS_POLICY.partialIntervalMs,\n\t\t\toutputIntervalMs: settings?.progress?.outputIntervalMs ?? DEFAULT_PROGRESS_POLICY.outputIntervalMs,\n\t\t},\n\t\ttoolExecution: settings?.toolExecution ?? \"parallel\",\n\t\tsteeringMode: settings?.steeringMode ?? \"one-at-a-time\",\n\t\tfollowUpMode: settings?.followUpMode ?? \"one-at-a-time\",\n\t};\n}\n\n/** Apply one change to `pi.agent`: a given field replaces the stored one, `null` clears it, `undefined` changes nothing. */\nexport async function configure(tx: Tx, conversationId: ConversationId, change: AgentChange): Promise<void> {\n\tconst state = await tx.doc(AgentDoc, conversationId);\n\tapplyChange(state, change);\n}\n\n/**\n * `addTools` of a tool round: an array gets each name it lacks appended, `{ remove }` loses the names, and unset tools\n * already offer every tool, so nothing is written.\n */\nexport async function addTools(tx: Tx, conversationId: ConversationId, added: readonly string[]): Promise<void> {\n\tconst state = await tx.doc(AgentDoc, conversationId);\n\tconst tools = state.tools;\n\tif (tools === undefined) return;\n\tif (Array.isArray(tools)) {\n\t\tfor (const name of added) if (!tools.includes(name)) tools.push(name);\n\t} else {\n\t\tconst remove = (tools as { remove: string[] }).remove;\n\t\tif (remove.some((name) => added.includes(name))) {\n\t\t\tstate.tools = { remove: remove.filter((name) => !added.includes(name)) };\n\t\t}\n\t}\n}\n\nfunction applyChange(state: Draft<AgentState>, change: AgentChange): void {\n\tconst set = <K extends keyof AgentState>(key: K, value: AgentState[K] | null | undefined) => {\n\t\tif (value === undefined) return;\n\t\tif (value === null) delete state[key];\n\t\telse state[key] = value as Draft<AgentState>[K];\n\t};\n\tset(\"model\", change.model === undefined || change.model === null ? change.model : { ...change.model });\n\tset(\"thinkingLevel\", change.thinkingLevel);\n\tconst extensions = change.extensions;\n\tset(\n\t\t\"extensions\",\n\t\textensions === undefined || extensions === null\n\t\t\t? extensions\n\t\t\t: isList(extensions)\n\t\t\t\t? names(extensions)\n\t\t\t\t: {\n\t\t\t\t\t\t...(extensions.add === undefined ? {} : { add: names(extensions.add) }),\n\t\t\t\t\t\t...(extensions.remove === undefined ? {} : { remove: names(extensions.remove) }),\n\t\t\t\t\t},\n\t);\n\tconst tools = change.tools;\n\tset(\n\t\t\"tools\",\n\t\ttools === undefined || tools === null ? tools : isList(tools) ? names(tools) : { remove: names(tools.remove) },\n\t);\n\tset(\"instructions\", change.instructions);\n\tset(\"cwd\", change.cwd);\n}\n\nfunction isList<T>(value: readonly T[] | object): value is readonly T[] {\n\treturn Array.isArray(value);\n}\n\nfunction names(items: readonly { readonly name: string }[]): string[] {\n\treturn items.map((item) => item.name);\n}\n\n/**\n * Built-in part of every Harness commit that creates or forks a conversation, for `pi.agent`: a fork keeps its `asOf`\n * copy; a new task-owned conversation copies the stored agent of its owner task's conversation; a new ownerless one\n * starts empty.\n */\nexport async function createAgent(tx: Tx, conversation: ConversationRecord): Promise<void> {\n\tif (conversation.parent !== undefined) return;\n\tconst agent = await tx.doc(AgentDoc, conversation.id);\n\tif (conversation.owner === undefined) return;\n\tconst owner = await tx.doc(AgentDoc, conversation.owner.conversationId);\n\tObject.assign(agent, copyJson(owner) as AgentState);\n}\n\n/** Handlers of the selected extensions' hooks for a task name, in extension order. */\nexport function agentHooks(agent: Agent, taskName: string): object[] {\n\tconst handlers: object[] = [];\n\tfor (const extension of agent.extensions) {\n\t\tfor (const hook of extension.hooks ?? []) if (hook.task === taskName) handlers.push(hook.handlers);\n\t}\n\treturn handlers;\n}\n\n/**\n * Resolve an agent from its stored state (absent: every field unset), a registry snapshot, and resolved settings. A\n * wrapper that throws or renames drops its target and is reported; a wrapper without a target does nothing.\n */\nexport function resolveAgent<Tool extends ToolRegistration>(\n\tstate: Readonly<AgentState> | undefined,\n\tsnapshot: RegistrySnapshot<Tool>,\n\tsettings: Settings,\n\treport: (error: unknown) => void,\n): Agent<Tool> {\n\tconst extensions = selectExtensions(state?.extensions, snapshot, settings);\n\n\tconst composed = new Map<string, Tool>();\n\tfor (const extension of extensions) for (const tool of extension.tools ?? []) composed.set(tool.name, tool);\n\tconst sections = new Map<string, PromptSection<Tool>>();\n\tfor (const extension of extensions) {\n\t\tfor (const section of extension.sections ?? []) sections.set(section.key, section);\n\t}\n\tfor (const extension of extensions) {\n\t\tfor (const wrap of extension.wraps ?? []) {\n\t\t\tif (\"tool\" in wrap)\n\t\t\t\tapplyWrap(\n\t\t\t\t\tcomposed,\n\t\t\t\t\twrap.tool,\n\t\t\t\t\t(tool) => wrap.wrap(tool),\n\t\t\t\t\t(tool) => tool.name,\n\t\t\t\t\treport,\n\t\t\t\t);\n\t\t\telse\n\t\t\t\tapplyWrap(\n\t\t\t\t\tsections,\n\t\t\t\t\twrap.section,\n\t\t\t\t\t(section) => wrap.wrap(section),\n\t\t\t\t\t(section) => section.key,\n\t\t\t\t\treport,\n\t\t\t\t);\n\t\t}\n\t}\n\n\tconst filter = state?.tools;\n\tlet tools: Tool[];\n\tif (filter === undefined) tools = [...composed.values()];\n\telse if (Array.isArray(filter)) {\n\t\ttools = [];\n\t\tfor (const name of new Set(filter)) {\n\t\t\tconst tool = composed.get(name);\n\t\t\tif (tool !== undefined) tools.push(tool);\n\t\t}\n\t} else {\n\t\tconst removed = new Set((filter as { remove: string[] }).remove);\n\t\ttools = [...composed.values()].filter((tool) => !removed.has(tool.name));\n\t}\n\n\tconst instructions = state?.instructions;\n\tconst agentSections = [...sections.values()];\n\tif (instructions !== undefined) agentSections.push({ key: INSTRUCTIONS_KEY, render: () => instructions });\n\n\tconst agent: Agent<Tool> = {\n\t\t...(state?.model === undefined ? {} : { model: state.model }),\n\t\tthinkingLevel: state?.thinkingLevel ?? \"off\",\n\t\textensions,\n\t\ttools,\n\t\tsections: agentSections,\n\t\t...(instructions === undefined ? {} : { instructions }),\n\t\t...(state?.cwd === undefined ? {} : { cwd: state.cwd }),\n\t};\n\treturn agent;\n}\n\n/** Selected installed extensions: the stored array, or the default selection edited by `{ add, remove }`. */\nfunction selectExtensions<Tool extends ToolRegistration>(\n\tstored: AgentState[\"extensions\"],\n\tsnapshot: RegistrySnapshot<Tool>,\n\tsettings: Settings,\n): Extension<Tool>[] {\n\tlet selected: string[];\n\tif (Array.isArray(stored)) selected = stored;\n\telse {\n\t\tconst base = settings.extensions?.map((extension) => extension.name) ?? snapshot.installed().map((e) => e.name);\n\t\tconst edit = stored as { add?: string[]; remove?: string[] } | undefined;\n\t\tconst removed = new Set(edit?.remove ?? []);\n\t\tselected = [...base, ...(edit?.add ?? [])].filter((name) => !removed.has(name));\n\t}\n\tconst extensions: Extension<Tool>[] = [];\n\tfor (const name of new Set(selected)) {\n\t\tconst extension = snapshot.extension(name);\n\t\tif (extension !== undefined) extensions.push(extension);\n\t}\n\treturn extensions;\n}\n\nfunction applyWrap<T>(\n\titems: Map<string, T>,\n\ttarget: string,\n\twrap: (item: T) => T,\n\tnameOf: (item: T) => string,\n\treport: (error: unknown) => void,\n): void {\n\tconst item = items.get(target);\n\tif (item === undefined) return;\n\ttry {\n\t\tconst wrapped = wrap(item);\n\t\tif (nameOf(wrapped) !== target) throw new Error(`Wrapper renamed ${target} to ${nameOf(wrapped)}`);\n\t\titems.set(target, wrapped);\n\t} catch (error) {\n\t\titems.delete(target);\n\t\treport(error);\n\t}\n}\n"]}
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/harness/agent.ts"],"names":[],"mappings":"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","sourcesContent":["import { copyJson, type Draft } from \"@earendil-works/chord\";\nimport { defineDoc } from \"../documents.ts\";\nimport type { ConversationId, ConversationRecord, Tx } from \"../types.ts\";\nimport type {\n\tAgent,\n\tAgentChange,\n\tAgentState,\n\tCompactionPolicy,\n\tConversationRetryPolicy,\n\tExtension,\n\tHarnessSettings,\n\tProgressPolicy,\n\tPromptSection,\n\tRegistrySnapshot,\n\tSettings,\n\tToolRegistration,\n} from \"./types.ts\";\n\nexport const DEFAULT_RETRY_POLICY: ConversationRetryPolicy = {\n\tenabled: true,\n\tmaxRetries: 3,\n\tbaseDelayMs: 2000,\n\tmaxAgentDelayMs: 60000,\n};\n\nexport const DEFAULT_COMPACTION_POLICY: CompactionPolicy = {\n\tenabled: true,\n\treserveTokens: 16384,\n\tkeepRecentTokens: 20000,\n\tbackgroundTokens: 32768,\n};\n\nexport const DEFAULT_PROGRESS_POLICY: ProgressPolicy = {\n\tpartialIntervalMs: 100,\n\toutputIntervalMs: 100,\n};\n\n/** The reserved section key of the agent's `instructions`. */\nexport const INSTRUCTIONS_KEY = \"instructions\";\n\n/** Built-in agent document; rewindable so forks start from the agent at their fork entry. */\nexport const AgentDoc = defineDoc<AgentState>({\n\tkind: \"pi.agent\",\n\tversion: 1,\n\tscope: \"conversation\",\n\thistory: \"rewindable\",\n\tfork: \"asOf\",\n\tinitial: () => ({}),\n\tcheckpointWhen: () => true,\n});\n\n/** Default `settings.contextRetentionMs`: ten minutes. */\nconst DEFAULT_CONTEXT_RETENTION_MS = 600_000;\n\n/** Resolve the host settings: every field over its built-in default, object fields merged. */\nexport function resolveSettings(settings: HarnessSettings | undefined): Settings {\n\tconst extensions = settings?.extensions;\n\treturn {\n\t\t...(extensions === undefined ? {} : { extensions }),\n\t\tstream: { ...settings?.stream },\n\t\tretry: { ...DEFAULT_RETRY_POLICY, ...settings?.retry },\n\t\tcompaction: { ...DEFAULT_COMPACTION_POLICY, ...settings?.compaction },\n\t\t// Field by field, so an explicitly undefined interval keeps its default.\n\t\tprogress: {\n\t\t\tpartialIntervalMs: settings?.progress?.partialIntervalMs ?? DEFAULT_PROGRESS_POLICY.partialIntervalMs,\n\t\t\toutputIntervalMs: settings?.progress?.outputIntervalMs ?? DEFAULT_PROGRESS_POLICY.outputIntervalMs,\n\t\t},\n\t\ttoolExecution: settings?.toolExecution ?? \"parallel\",\n\t\tsteeringMode: settings?.steeringMode ?? \"one-at-a-time\",\n\t\tfollowUpMode: settings?.followUpMode ?? \"one-at-a-time\",\n\t\tcontextRetentionMs: settings?.contextRetentionMs ?? DEFAULT_CONTEXT_RETENTION_MS,\n\t};\n}\n\n/** Apply one change to `pi.agent`: a given field replaces the stored one, `null` clears it, `undefined` changes nothing. */\nexport async function configure(tx: Tx, conversationId: ConversationId, change: AgentChange): Promise<void> {\n\tconst state = await tx.doc(AgentDoc, conversationId);\n\tapplyChange(state, change);\n}\n\n/**\n * `addTools` of a tool round: an array gets each name it lacks appended, `{ remove }` loses the names, and unset tools\n * already offer every tool, so nothing is written.\n */\nexport async function addTools(tx: Tx, conversationId: ConversationId, added: readonly string[]): Promise<void> {\n\tconst state = await tx.doc(AgentDoc, conversationId);\n\tconst tools = state.tools;\n\tif (tools === undefined) return;\n\tif (Array.isArray(tools)) {\n\t\tfor (const name of added) if (!tools.includes(name)) tools.push(name);\n\t} else {\n\t\tconst remove = (tools as { remove: string[] }).remove;\n\t\tif (remove.some((name) => added.includes(name))) {\n\t\t\tstate.tools = { remove: remove.filter((name) => !added.includes(name)) };\n\t\t}\n\t}\n}\n\nfunction applyChange(state: Draft<AgentState>, change: AgentChange): void {\n\tconst set = <K extends keyof AgentState>(key: K, value: AgentState[K] | null | undefined) => {\n\t\tif (value === undefined) return;\n\t\tif (value === null) delete state[key];\n\t\telse state[key] = value as Draft<AgentState>[K];\n\t};\n\tset(\"model\", change.model === undefined || change.model === null ? change.model : { ...change.model });\n\tset(\"thinkingLevel\", change.thinkingLevel);\n\tconst extensions = change.extensions;\n\tset(\n\t\t\"extensions\",\n\t\textensions === undefined || extensions === null\n\t\t\t? extensions\n\t\t\t: isList(extensions)\n\t\t\t\t? names(extensions)\n\t\t\t\t: {\n\t\t\t\t\t\t...(extensions.add === undefined ? {} : { add: names(extensions.add) }),\n\t\t\t\t\t\t...(extensions.remove === undefined ? {} : { remove: names(extensions.remove) }),\n\t\t\t\t\t},\n\t);\n\tconst tools = change.tools;\n\tset(\n\t\t\"tools\",\n\t\ttools === undefined || tools === null ? tools : isList(tools) ? names(tools) : { remove: names(tools.remove) },\n\t);\n\tset(\"instructions\", change.instructions);\n\tset(\"cwd\", change.cwd);\n}\n\nfunction isList<T>(value: readonly T[] | object): value is readonly T[] {\n\treturn Array.isArray(value);\n}\n\nfunction names(items: readonly { readonly name: string }[]): string[] {\n\treturn items.map((item) => item.name);\n}\n\n/**\n * Built-in part of every Harness commit that creates or forks a conversation, for `pi.agent`: a fork keeps its `asOf`\n * copy; a new task-owned conversation copies the stored agent of its owner task's conversation; a new ownerless one\n * starts empty.\n */\nexport async function createAgent(tx: Tx, conversation: ConversationRecord): Promise<void> {\n\tif (conversation.parent !== undefined) return;\n\tconst agent = await tx.doc(AgentDoc, conversation.id);\n\tif (conversation.owner === undefined) return;\n\tconst owner = await tx.doc(AgentDoc, conversation.owner.conversationId);\n\tObject.assign(agent, copyJson(owner) as AgentState);\n}\n\n/** Handlers of the selected extensions' hooks for a task name, in extension order. */\nexport function agentHooks(agent: Agent, taskName: string): object[] {\n\tconst handlers: object[] = [];\n\tfor (const extension of agent.extensions) {\n\t\tfor (const hook of extension.hooks ?? []) if (hook.task === taskName) handlers.push(hook.handlers);\n\t}\n\treturn handlers;\n}\n\n/**\n * Resolve an agent from its stored state (absent: every field unset), a registry snapshot, and resolved settings. A\n * wrapper that throws or renames drops its target and is reported; a wrapper without a target does nothing.\n */\nexport function resolveAgent<Tool extends ToolRegistration>(\n\tstate: Readonly<AgentState> | undefined,\n\tsnapshot: RegistrySnapshot<Tool>,\n\tsettings: Settings,\n\treport: (error: unknown) => void,\n): Agent<Tool> {\n\tconst extensions = selectExtensions(state?.extensions, snapshot, settings);\n\n\tconst composed = new Map<string, Tool>();\n\tfor (const extension of extensions) for (const tool of extension.tools ?? []) composed.set(tool.name, tool);\n\tconst sections = new Map<string, PromptSection<Tool>>();\n\tfor (const extension of extensions) {\n\t\tfor (const section of extension.sections ?? []) sections.set(section.key, section);\n\t}\n\tfor (const extension of extensions) {\n\t\tfor (const wrap of extension.wraps ?? []) {\n\t\t\tif (\"tool\" in wrap)\n\t\t\t\tapplyWrap(\n\t\t\t\t\tcomposed,\n\t\t\t\t\twrap.tool,\n\t\t\t\t\t(tool) => wrap.wrap(tool),\n\t\t\t\t\t(tool) => tool.name,\n\t\t\t\t\treport,\n\t\t\t\t);\n\t\t\telse\n\t\t\t\tapplyWrap(\n\t\t\t\t\tsections,\n\t\t\t\t\twrap.section,\n\t\t\t\t\t(section) => wrap.wrap(section),\n\t\t\t\t\t(section) => section.key,\n\t\t\t\t\treport,\n\t\t\t\t);\n\t\t}\n\t}\n\n\tconst filter = state?.tools;\n\tlet tools: Tool[];\n\tif (filter === undefined) tools = [...composed.values()];\n\telse if (Array.isArray(filter)) {\n\t\ttools = [];\n\t\tfor (const name of new Set(filter)) {\n\t\t\tconst tool = composed.get(name);\n\t\t\tif (tool !== undefined) tools.push(tool);\n\t\t}\n\t} else {\n\t\tconst removed = new Set((filter as { remove: string[] }).remove);\n\t\ttools = [...composed.values()].filter((tool) => !removed.has(tool.name));\n\t}\n\n\tconst instructions = state?.instructions;\n\tconst agentSections = [...sections.values()];\n\tif (instructions !== undefined) agentSections.push({ key: INSTRUCTIONS_KEY, render: () => instructions });\n\n\tconst agent: Agent<Tool> = {\n\t\t...(state?.model === undefined ? {} : { model: state.model }),\n\t\tthinkingLevel: state?.thinkingLevel ?? \"off\",\n\t\textensions,\n\t\ttools,\n\t\tsections: agentSections,\n\t\t...(instructions === undefined ? {} : { instructions }),\n\t\t...(state?.cwd === undefined ? {} : { cwd: state.cwd }),\n\t};\n\treturn agent;\n}\n\n/** Selected installed extensions: the stored array, or the default selection edited by `{ add, remove }`. */\nfunction selectExtensions<Tool extends ToolRegistration>(\n\tstored: AgentState[\"extensions\"],\n\tsnapshot: RegistrySnapshot<Tool>,\n\tsettings: Settings,\n): Extension<Tool>[] {\n\tlet selected: string[];\n\tif (Array.isArray(stored)) selected = stored;\n\telse {\n\t\tconst base = settings.extensions?.map((extension) => extension.name) ?? snapshot.installed().map((e) => e.name);\n\t\tconst edit = stored as { add?: string[]; remove?: string[] } | undefined;\n\t\tconst removed = new Set(edit?.remove ?? []);\n\t\tselected = [...base, ...(edit?.add ?? [])].filter((name) => !removed.has(name));\n\t}\n\tconst extensions: Extension<Tool>[] = [];\n\tfor (const name of new Set(selected)) {\n\t\tconst extension = snapshot.extension(name);\n\t\tif (extension !== undefined) extensions.push(extension);\n\t}\n\treturn extensions;\n}\n\nfunction applyWrap<T>(\n\titems: Map<string, T>,\n\ttarget: string,\n\twrap: (item: T) => T,\n\tnameOf: (item: T) => string,\n\treport: (error: unknown) => void,\n): void {\n\tconst item = items.get(target);\n\tif (item === undefined) return;\n\ttry {\n\t\tconst wrapped = wrap(item);\n\t\tif (nameOf(wrapped) !== target) throw new Error(`Wrapper renamed ${target} to ${nameOf(wrapped)}`);\n\t\titems.set(target, wrapped);\n\t} catch (error) {\n\t\titems.delete(target);\n\t\treport(error);\n\t}\n}\n"]}
@@ -105,7 +105,7 @@ export const CompactionTask = defineTask({
105
105
  if (model === undefined)
106
106
  return failNoModel(runtime, ref, context);
107
107
  // The context at `tail` is immutable, so this is the range `select` chose.
108
- const view = await runtime.context(runtime.conversationId, context, tail);
108
+ const view = await runtime.context(runtime.conversationId, context, { at: tail });
109
109
  const cut = view.entries.findIndex((entry) => entry.id === firstKept);
110
110
  const now = runtime.now();
111
111
  const messages = [
@@ -1 +1 @@
1
- {"version":3,"file":"compaction.js","sourceRoot":"","sources":["../../src/harness/compaction.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context, Draft } from \"@earendil-works/chord\";\nimport type { AssistantMessage, Message, ModelThinkingLevel, SimpleStreamOptions } from \"@earendil-works/pi-ai\";\nimport { calculateContextTokens, estimateMessageTokens } from \"@earendil-works/pi-ai/utils/estimate\";\nimport { isRetryableAssistantError, retryDelayMs } from \"@earendil-works/pi-ai/utils/retry\";\nimport { CompactionEntry } from \"../entries.ts\";\nimport { defineTask } from \"../tasks.ts\";\nimport type {\n\tConversationId,\n\tEntryDraft,\n\tEntryId,\n\tNextTaskState,\n\tRunningTask,\n\tTaskId,\n\tTaskRuntime,\n\tTx,\n} from \"../types.ts\";\nimport { orderToolResults } from \"./context.ts\";\nimport { addCompactionStatus, compactionStatus, LiveDoc, type LiveState, removeCompactionStatus } from \"./live.ts\";\nimport { ensureProviderSessionId } from \"./provider.ts\";\nimport { admitSubmission } from \"./submissions.ts\";\nimport type {\n\tCompactionHooks,\n\tCompactionReason,\n\tCompactionResult,\n\tContextView,\n\tConversationStreamOptions,\n\tModelRef,\n} from \"./types.ts\";\nimport { recordUsage } from \"./usage.ts\";\n\nexport type CompactionInput = { reason: CompactionReason; instructions?: string };\n\n/** The pinned summarization request. */\nexport type SummaryRequest = {\n\tattempt: number;\n\tmodel: ModelRef;\n\tthinkingLevel: ModelThinkingLevel;\n\tstreamOptions: ConversationStreamOptions;\n\tmaxTokens: number;\n\t/** Newest entry of the context the range was selected from. */\n\ttail: EntryId;\n\t/** First entry kept verbatim; the summary's `head`. */\n\tfirstKept: EntryId;\n};\n\nexport type CompactionCheckpoint =\n\t| { phase: \"select\" }\n\t| ({ phase: \"summarize\" } & SummaryRequest)\n\t| ({ phase: \"retry\"; until: number } & SummaryRequest);\n\ntype Runtime = TaskRuntime<CompactionInput, CompactionCheckpoint, CompactionResult, CompactionHooks>;\ntype Next = NextTaskState<CompactionCheckpoint, CompactionResult>;\n\n/** Longest tool result text a serialized summary source keeps. */\nconst TOOL_RESULT_MAX_CHARS = 2000;\n\nconst SUMMARY_PREFIX =\n\t\"The conversation history before this point was compacted into the following summary:\\n\\n<summary>\\n\";\nconst SUMMARY_SUFFIX = \"\\n</summary>\";\n\nconst SUMMARIZATION_SYSTEM_PROMPT = `You are a context summarization assistant. Your task is to read a conversation between a user and an AI assistant, then produce a structured summary following the exact format specified.\n\nDo NOT continue the conversation. Do NOT respond to any questions in the conversation. ONLY output the structured summary.`;\n\nconst SUMMARIZATION_PROMPT = `The messages above are a conversation to summarize. Create a structured context checkpoint summary that another LLM will use to continue the work. If the conversation starts with an earlier summary, preserve its information and fold the newer messages into it.\n\nUse this EXACT format:\n\n## Goal\n[What is the user trying to accomplish? Can be multiple items if the session covers different tasks.]\n\n## Constraints & Preferences\n- [Any constraints, preferences, or requirements mentioned by user]\n- [Or \"(none)\" if none were mentioned]\n\n## Progress\n### Done\n- [x] [Completed tasks/changes]\n\n### In Progress\n- [ ] [Current work]\n\n### Blocked\n- [Issues preventing progress, if any]\n\n## Key Decisions\n- **[Decision]**: [Brief rationale]\n\n## Next Steps\n1. [Ordered list of what should happen next]\n\n## Critical Context\n- [Any data, examples, or references needed to continue]\n- [Or \"(none)\" if not applicable]\n\nKeep each section concise. Preserve exact file paths, function names, and error messages.`;\n\n/**\n * Built-in compaction task (spec §8.7): select an old prefix of the model context, summarize it, and place a summary\n * entry whose `head` is the first kept entry. A compaction the generation owns blocks it and appends directly; a\n * conversation-owned one places its summary through a write submission.\n */\nexport const CompactionTask = defineTask<CompactionInput, CompactionCheckpoint, CompactionResult, CompactionHooks>({\n\tname: \"pi.compaction\",\n\tversion: 1,\n\tinitial: () => ({ phase: \"select\" }),\n\tphases: {\n\t\tselect: async (task, runtime, context) => {\n\t\t\tconst { conversationId } = runtime;\n\t\t\tconst agent = await runtime.agent(context);\n\t\t\tconst settings = runtime.settings;\n\t\t\tconst ref = agent.model;\n\t\t\tconst model = ref === undefined ? undefined : runtime.models.getModel(ref.provider, ref.modelId);\n\t\t\tif (ref === undefined || model === undefined) return failNoModel(runtime, ref, context);\n\t\t\tconst policy = settings.compaction;\n\t\t\tconst view = await runtime.context(conversationId, context);\n\t\t\tconst cut = selectCut(view, policy.keepRecentTokens);\n\t\t\tif (cut === undefined) return complete(runtime, context);\n\t\t\tconst firstKept = view.entries[cut]!.id;\n\t\t\tconst { reason, instructions } = task.input;\n\t\t\tconst compaction = {\n\t\t\t\treason,\n\t\t\t\tentries: view.entries.slice(0, cut),\n\t\t\t\tmessages: summarizedMessages(view, cut),\n\t\t\t\tfirstKept,\n\t\t\t\t...(instructions === undefined ? {} : { instructions }),\n\t\t\t};\n\t\t\tlet decision: { readonly decline: true } | { readonly summary: string } | undefined;\n\t\t\tawait runtime.hooks.each(\"beforeCompact\", async (hook) => {\n\t\t\t\tif (decision === undefined) decision = await hook(compaction, runtime, context);\n\t\t\t});\n\t\t\tif (decision !== undefined && \"decline\" in decision) return complete(runtime, context);\n\t\t\tif (decision !== undefined) return place(runtime, firstKept, decision.summary, context);\n\t\t\tconst request: SummaryRequest = {\n\t\t\t\tattempt: 1,\n\t\t\t\tmodel: ref,\n\t\t\t\tthinkingLevel: agent.thinkingLevel,\n\t\t\t\tstreamOptions: settings.stream,\n\t\t\t\tmaxTokens: Math.min(\n\t\t\t\t\tMath.floor(0.8 * policy.reserveTokens),\n\t\t\t\t\tmodel.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY,\n\t\t\t\t),\n\t\t\t\ttail: view.entries.reduce((tail, entry) => (entry.id > tail ? entry.id : tail), firstKept),\n\t\t\t\tfirstKept,\n\t\t\t};\n\t\t\tawait runtime.commit(() => ({ status: \"running\", checkpoint: { phase: \"summarize\", ...request } }), context);\n\t\t},\n\t\tsummarize: async (task, runtime, context) => {\n\t\t\tconst { phase: _, ...request } = task.state.checkpoint;\n\t\t\tconst { model: ref, thinkingLevel, streamOptions, maxTokens, tail, firstKept, attempt } = request;\n\t\t\tconst model = runtime.models.getModel(ref.provider, ref.modelId);\n\t\t\tif (model === undefined) return failNoModel(runtime, ref, context);\n\t\t\t// The context at `tail` is immutable, so this is the range `select` chose.\n\t\t\tconst view = await runtime.context(runtime.conversationId, context, tail);\n\t\t\tconst cut = view.entries.findIndex((entry) => entry.id === firstKept);\n\t\t\tconst now = runtime.now();\n\t\t\tconst messages: Message[] = [\n\t\t\t\t{ role: \"system\", content: SUMMARIZATION_SYSTEM_PROMPT, timestamp: now },\n\t\t\t\t{\n\t\t\t\t\trole: \"user\",\n\t\t\t\t\tcontent: [{ type: \"text\", text: summaryPrompt(summarizedMessages(view, cut), task.input.instructions) }],\n\t\t\t\t\ttimestamp: now,\n\t\t\t\t},\n\t\t\t];\n\t\t\tconst { deferred: _deferred, ...forwarded } = streamOptions;\n\t\t\tconst options: SimpleStreamOptions = {\n\t\t\t\t...forwarded,\n\t\t\t\tcacheRetention: \"none\",\n\t\t\t\tmaxTokens,\n\t\t\t\tsignal: runtime.signal,\n\t\t\t\tsessionId: await ensureProviderSessionId(runtime, context),\n\t\t\t\t...(thinkingLevel === \"off\" ? {} : { reasoning: thinkingLevel }),\n\t\t\t};\n\t\t\tconst message = await runtime.models.completeSimple(model, { messages }, options);\n\t\t\t// An abort mark or close: the abort invocation or the reopened task handles the committed state.\n\t\t\truntime.signal.throwIfAborted();\n\t\t\tconst summary = summaryText(message);\n\t\t\tconst policy = runtime.settings.retry;\n\t\t\tconst retry =\n\t\t\t\tmessage.stopReason === \"error\" &&\n\t\t\t\tisRetryableAssistantError(message) &&\n\t\t\t\tpolicy.enabled &&\n\t\t\t\tattempt <= policy.maxRetries;\n\t\t\tconst until = retry ? runtime.now() + retryDelayMs(policy, attempt) : 0;\n\t\t\tawait runtime.commit(async (tx, current): Promise<Next> => {\n\t\t\t\tawait recordUsage(\n\t\t\t\t\ttx,\n\t\t\t\t\truntime.conversationId,\n\t\t\t\t\t\"models\",\n\t\t\t\t\t`${message.provider}/${message.model}`,\n\t\t\t\t\tmessage.usage,\n\t\t\t\t);\n\t\t\t\tconst live = await tx.doc(LiveDoc, runtime.conversationId);\n\t\t\t\tif (summary !== undefined) return placeSummary(tx, runtime, current, live, firstKept, summary);\n\t\t\t\tif (retry) {\n\t\t\t\t\tconst status = compactionStatus(live, runtime.taskId);\n\t\t\t\t\tif (status !== undefined) status.retry = { at: until, error: message.errorMessage ?? \"\" };\n\t\t\t\t\treturn { status: \"running\", checkpoint: { phase: \"retry\", ...request, until } };\n\t\t\t\t}\n\t\t\t\tremoveCompactionStatus(live, runtime.taskId);\n\t\t\t\tconst text = summaryFailure(message);\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"terminal\",\n\t\t\t\t\toutcome: { status: \"failed\", error: { message: text, detail: { reason: \"model_error\" } } },\n\t\t\t\t};\n\t\t\t}, context);\n\t\t},\n\t\tretry: async (task, runtime, context) => {\n\t\t\tconst { phase: _, until, ...request } = task.state.checkpoint;\n\t\t\tawait runtime.sleep(until, context);\n\t\t\tconst attempt = request.attempt + 1;\n\t\t\tawait runtime.commit(async (tx) => {\n\t\t\t\tconst status = compactionStatus(await tx.doc(LiveDoc, runtime.conversationId), runtime.taskId);\n\t\t\t\tif (status !== undefined) {\n\t\t\t\t\tstatus.attempt = attempt;\n\t\t\t\t\tdelete status.retry;\n\t\t\t\t}\n\t\t\t\treturn { status: \"running\", checkpoint: { phase: \"summarize\", ...request, attempt } };\n\t\t\t}, context);\n\t\t},\n\t},\n\tabort: async (_task, runtime, context) => {\n\t\tawait runtime.commit(async (tx) => {\n\t\t\tremoveCompactionStatus(await tx.doc(LiveDoc, runtime.conversationId), runtime.taskId);\n\t\t\treturn { status: \"terminal\", outcome: { status: \"aborted\" } };\n\t\t}, context);\n\t},\n});\n\n/**\n * Create a compaction task with its status in this commit. `owner` is the generation that waits for it (a blocking\n * compaction); without one it is conversation-owned, and `background` unless it is manual.\n */\nexport async function createCompaction(\n\ttx: Tx,\n\tconversationId: ConversationId,\n\tinput: CompactionInput,\n\towner?: TaskId,\n): Promise<TaskId<CompactionResult>> {\n\tconst ownership =\n\t\towner === undefined ? ({ kind: \"conversation\" } as const) : { kind: \"task\" as const, taskId: owner };\n\tconst background = owner === undefined && input.reason !== \"manual\";\n\tconst taskId = await tx.createTask(CompactionTask, input, { ownership, conversationId, background });\n\tconst status = { taskId, reason: input.reason, blocking: owner !== undefined, attempt: 1 };\n\taddCompactionStatus(await tx.doc(LiveDoc, conversationId), status);\n\treturn taskId;\n}\n\n/**\n * Index in `view.entries` of the first entry a summary keeps, or `undefined` when there is nothing to compact\n * (spec §8.7). Walks back from the tail until `keepRecentTokens` are kept, then cuts at the first candidate at or after\n * that entry: an entry whose contribution starts with a user or assistant message, never a tool result, and never a\n * user entry that a result of the preceding assistant's calls still follows.\n */\nexport function selectCut(view: ContextView, keepRecentTokens: number): number | undefined {\n\tconst { contributions } = view;\n\tconst start = view.head === undefined ? 0 : 1;\n\tconst candidates: number[] = [];\n\tfor (let index = start; index < contributions.length; index++) {\n\t\tif (isCandidate(contributions, index)) candidates.push(index);\n\t}\n\tlet kept = 0;\n\tlet cut: number | undefined;\n\tfor (let index = contributions.length - 1; index >= start; index--) {\n\t\tfor (const message of contributions[index]!) kept += estimateMessageTokens(message);\n\t\tif (kept < keepRecentTokens) continue;\n\t\tcut = candidates.find((candidate) => candidate >= index) ?? candidates.at(-1);\n\t\tbreak;\n\t}\n\tif (cut === undefined) return undefined;\n\tfor (let index = start; index < cut; index++) if (contributions[index]!.length > 0) return cut;\n\treturn undefined;\n}\n\nfunction isCandidate(contributions: ContextView[\"contributions\"], index: number): boolean {\n\tconst first = contributions[index]![0];\n\tif (first?.role === \"assistant\") return true;\n\tif (first?.role !== \"user\") return false;\n\t// A result of the preceding assistant's calls that follows this entry, before the next assistant, belongs before it.\n\tlet calls: ReadonlySet<string> = new Set();\n\tfor (let before = index - 1; before >= 0; before--) {\n\t\tconst assistant = contributions[before]!.findLast((message) => message.role === \"assistant\");\n\t\tif (assistant === undefined) continue;\n\t\tcalls = new Set(assistant.content.flatMap((content) => (content.type === \"toolCall\" ? [content.id] : [])));\n\t\tbreak;\n\t}\n\tif (calls.size === 0) return true;\n\tfor (let after = index; after < contributions.length; after++) {\n\t\tfor (const [position, message] of contributions[after]!.entries()) {\n\t\t\tif (message.role === \"assistant\" && (after > index || position > 0)) return true;\n\t\t\tif (message.role === \"toolResult\" && calls.has(message.toolCallId)) return false;\n\t\t}\n\t}\n\treturn true;\n}\n\n/** Model messages of the entries before `cut`: the head marker first, ordered like model context (spec §2.1). */\nexport function summarizedMessages(view: ContextView, cut: number): Message[] {\n\treturn orderToolResults(view.contributions.slice(0, cut).flat());\n}\n\n/**\n * Size of a request over `view` followed by `extra` (spec §8.3): the usage of the newest assistant appended after the\n * head marker, whose request included the marker, plus estimates of the messages after it; without one, estimates of\n * every message.\n */\nexport function estimateContext(view: ContextView, extra: readonly Message[]): number {\n\tlet measured: AssistantMessage | undefined;\n\tconst after = view.head?.id ?? Number.NEGATIVE_INFINITY;\n\tfor (let index = view.entries.length - 1; index >= 0 && measured === undefined; index--) {\n\t\tif (view.entries[index]!.id <= after) continue;\n\t\tmeasured = view.contributions[index]!.findLast(\n\t\t\t(message): message is AssistantMessage =>\n\t\t\t\tmessage.role === \"assistant\" && calculateContextTokens(message.usage) > 0,\n\t\t);\n\t}\n\tconst from = measured === undefined ? 0 : view.messages.lastIndexOf(measured) + 1;\n\tlet tokens = measured === undefined ? 0 : calculateContextTokens(measured.usage);\n\tfor (const message of view.messages.slice(from)) tokens += estimateMessageTokens(message);\n\tfor (const message of extra) tokens += estimateMessageTokens(message);\n\treturn tokens;\n}\n\n/** The summary of a clean `stop` with text and no tool call; anything else is not a summary. */\nfunction summaryText(message: AssistantMessage): string | undefined {\n\tif (message.stopReason !== \"stop\" || message.content.some((content) => content.type === \"toolCall\"))\n\t\treturn undefined;\n\tconst text = message.content\n\t\t.flatMap((content) => (content.type === \"text\" ? [content.text] : []))\n\t\t.join(\"\\n\")\n\t\t.trim();\n\treturn text.length === 0 ? undefined : text;\n}\n\nfunction summaryFailure(message: AssistantMessage): string {\n\tif (message.stopReason === \"error\" || message.stopReason === \"aborted\") {\n\t\treturn `Summarization failed: ${message.errorMessage ?? message.stopReason}`;\n\t}\n\tif (message.stopReason === \"length\") return \"Summarization hit the token limit; the summary is incomplete\";\n\tif (message.content.some((content) => content.type === \"toolCall\")) return \"Summarization attempted to call a tool\";\n\treturn \"Summarization produced no text\";\n}\n\n/** The summarizer's user message: the serialized conversation, the prompt, and any instructions. */\nfunction summaryPrompt(messages: readonly Message[], instructions: string | undefined): string {\n\tconst focus = instructions === undefined ? \"\" : `\\n\\nAdditional focus: ${instructions}`;\n\treturn `<conversation>\\n${serializeConversation(messages)}\\n</conversation>\\n\\n${SUMMARIZATION_PROMPT}${focus}`;\n}\n\n/** Messages as plain text, so the summarizer reads a transcript instead of continuing it. System messages are omitted. */\nexport function serializeConversation(messages: readonly Message[]): string {\n\tconst parts: string[] = [];\n\tfor (const message of messages) {\n\t\tif (message.role === \"user\") {\n\t\t\tconst text = contentText(message.content);\n\t\t\tif (text.length > 0) parts.push(`[User]: ${text}`);\n\t\t} else if (message.role === \"assistant\") {\n\t\t\tconst thinking = message.content.flatMap((content) => (content.type === \"thinking\" ? [content.thinking] : []));\n\t\t\tconst text = message.content.flatMap((content) => (content.type === \"text\" ? [content.text] : []));\n\t\t\tconst calls = message.content.flatMap((content) =>\n\t\t\t\tcontent.type === \"toolCall\"\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t`${content.name}(${Object.entries(content.arguments)\n\t\t\t\t\t\t\t\t.map(([key, value]) => `${key}=${JSON.stringify(value)}`)\n\t\t\t\t\t\t\t\t.join(\", \")})`,\n\t\t\t\t\t\t]\n\t\t\t\t\t: [],\n\t\t\t);\n\t\t\tif (thinking.length > 0) parts.push(`[Assistant thinking]: ${thinking.join(\"\\n\")}`);\n\t\t\tif (text.length > 0) parts.push(`[Assistant]: ${text.join(\"\\n\")}`);\n\t\t\tif (calls.length > 0) parts.push(`[Assistant tool calls]: ${calls.join(\"; \")}`);\n\t\t} else if (message.role === \"toolResult\") {\n\t\t\tconst text = contentText(message.content);\n\t\t\tif (text.length > 0) parts.push(`[Tool result]: ${truncate(text, TOOL_RESULT_MAX_CHARS)}`);\n\t\t}\n\t}\n\treturn parts.join(\"\\n\\n\");\n}\n\nfunction contentText(content: string | readonly { readonly type: string; readonly text?: string }[]): string {\n\tif (typeof content === \"string\") return content;\n\treturn content\n\t\t.flatMap((block) => (block.type === \"text\" && block.text !== undefined ? [block.text] : []))\n\t\t.join(\"\\n\");\n}\n\nfunction truncate(text: string, maxChars: number): string {\n\tif (text.length <= maxChars) return text;\n\treturn `${text.slice(0, maxChars)}\\n\\n[... ${text.length - maxChars} more characters truncated]`;\n}\n\n/** Place a summary supplied by a hook in its own commit. */\nasync function place(runtime: Runtime, firstKept: EntryId, summary: string, context: Context): Promise<void> {\n\tawait runtime.commit(\n\t\tasync (tx, current) =>\n\t\t\tplaceSummary(tx, runtime, current, await tx.doc(LiveDoc, runtime.conversationId), firstKept, summary),\n\t\tcontext,\n\t);\n}\n\n/**\n * Place the summary entry and complete (spec §8.7). A blocking compaction, owned by its generation, appends it: the\n * generation holds the run and waits. A conversation-owned one admits it as a write submission, placed at once when\n * idle, otherwise at the next boundary, or settled `stale`.\n * REMINDER: nothing else may append to a busy conversation, so every non-blocking summary goes through admission.\n */\nasync function placeSummary(\n\ttx: Tx,\n\truntime: Runtime,\n\tcurrent: RunningTask<CompactionInput, CompactionCheckpoint, CompactionResult>,\n\tlive: Draft<LiveState>,\n\tfirstKept: EntryId,\n\tsummary: string,\n): Promise<Next> {\n\tremoveCompactionStatus(live, runtime.taskId);\n\tconst text = `${SUMMARY_PREFIX}${summary}${SUMMARY_SUFFIX}`;\n\tconst entry = {\n\t\tkind: CompactionEntry.kind,\n\t\thead: firstKept,\n\t\tmodel: [{ role: \"user\", content: [{ type: \"text\", text }], timestamp: runtime.now() }],\n\t\tdata: { reason: current.input.reason },\n\t} satisfies EntryDraft;\n\tconst result: CompactionResult =\n\t\tcurrent.owner === undefined\n\t\t\t? {\n\t\t\t\t\tsubmissionId: await admitSubmission(\n\t\t\t\t\t\ttx,\n\t\t\t\t\t\truntime.conversationId,\n\t\t\t\t\t\t{ type: \"write\", requestId: `compaction:${runtime.taskId}`, entry },\n\t\t\t\t\t\truntime.now(),\n\t\t\t\t\t\truntime.settings,\n\t\t\t\t\t),\n\t\t\t\t}\n\t\t\t: { entryId: (await tx.appendEntry(runtime.conversationId, entry)).id };\n\treturn { status: \"terminal\", outcome: { status: \"completed\", result } };\n}\n\n/** Remove the status and complete without a summary. */\nasync function complete(runtime: Runtime, context: Context): Promise<void> {\n\tawait runtime.commit(async (tx) => {\n\t\tremoveCompactionStatus(await tx.doc(LiveDoc, runtime.conversationId), runtime.taskId);\n\t\treturn { status: \"terminal\", outcome: { status: \"completed\", result: {} } };\n\t}, context);\n}\n\nasync function failNoModel(runtime: Runtime, ref: ModelRef | undefined, context: Context): Promise<void> {\n\tconst message =\n\t\tref === undefined ? \"No model is configured\" : `Model ${ref.provider}/${ref.modelId} is not available`;\n\tawait runtime.commit(async (tx) => {\n\t\tremoveCompactionStatus(await tx.doc(LiveDoc, runtime.conversationId), runtime.taskId);\n\t\treturn { status: \"terminal\", outcome: { status: \"failed\", error: { message, detail: { reason: \"no_model\" } } } };\n\t}, context);\n}\n"]}
1
+ {"version":3,"file":"compaction.js","sourceRoot":"","sources":["../../src/harness/compaction.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context, Draft } from \"@earendil-works/chord\";\nimport type { AssistantMessage, Message, ModelThinkingLevel, SimpleStreamOptions } from \"@earendil-works/pi-ai\";\nimport { calculateContextTokens, estimateMessageTokens } from \"@earendil-works/pi-ai/utils/estimate\";\nimport { isRetryableAssistantError, retryDelayMs } from \"@earendil-works/pi-ai/utils/retry\";\nimport { CompactionEntry } from \"../entries.ts\";\nimport { defineTask } from \"../tasks.ts\";\nimport type {\n\tConversationId,\n\tEntryDraft,\n\tEntryId,\n\tNextTaskState,\n\tRunningTask,\n\tTaskId,\n\tTaskRuntime,\n\tTx,\n} from \"../types.ts\";\nimport { orderToolResults } from \"./context.ts\";\nimport { addCompactionStatus, compactionStatus, LiveDoc, type LiveState, removeCompactionStatus } from \"./live.ts\";\nimport { ensureProviderSessionId } from \"./provider.ts\";\nimport { admitSubmission } from \"./submissions.ts\";\nimport type {\n\tCompactionHooks,\n\tCompactionReason,\n\tCompactionResult,\n\tContextView,\n\tConversationStreamOptions,\n\tModelRef,\n} from \"./types.ts\";\nimport { recordUsage } from \"./usage.ts\";\n\nexport type CompactionInput = { reason: CompactionReason; instructions?: string };\n\n/** The pinned summarization request. */\nexport type SummaryRequest = {\n\tattempt: number;\n\tmodel: ModelRef;\n\tthinkingLevel: ModelThinkingLevel;\n\tstreamOptions: ConversationStreamOptions;\n\tmaxTokens: number;\n\t/** Newest entry of the context the range was selected from. */\n\ttail: EntryId;\n\t/** First entry kept verbatim; the summary's `head`. */\n\tfirstKept: EntryId;\n};\n\nexport type CompactionCheckpoint =\n\t| { phase: \"select\" }\n\t| ({ phase: \"summarize\" } & SummaryRequest)\n\t| ({ phase: \"retry\"; until: number } & SummaryRequest);\n\ntype Runtime = TaskRuntime<CompactionInput, CompactionCheckpoint, CompactionResult, CompactionHooks>;\ntype Next = NextTaskState<CompactionCheckpoint, CompactionResult>;\n\n/** Longest tool result text a serialized summary source keeps. */\nconst TOOL_RESULT_MAX_CHARS = 2000;\n\nconst SUMMARY_PREFIX =\n\t\"The conversation history before this point was compacted into the following summary:\\n\\n<summary>\\n\";\nconst SUMMARY_SUFFIX = \"\\n</summary>\";\n\nconst SUMMARIZATION_SYSTEM_PROMPT = `You are a context summarization assistant. Your task is to read a conversation between a user and an AI assistant, then produce a structured summary following the exact format specified.\n\nDo NOT continue the conversation. Do NOT respond to any questions in the conversation. ONLY output the structured summary.`;\n\nconst SUMMARIZATION_PROMPT = `The messages above are a conversation to summarize. Create a structured context checkpoint summary that another LLM will use to continue the work. If the conversation starts with an earlier summary, preserve its information and fold the newer messages into it.\n\nUse this EXACT format:\n\n## Goal\n[What is the user trying to accomplish? Can be multiple items if the session covers different tasks.]\n\n## Constraints & Preferences\n- [Any constraints, preferences, or requirements mentioned by user]\n- [Or \"(none)\" if none were mentioned]\n\n## Progress\n### Done\n- [x] [Completed tasks/changes]\n\n### In Progress\n- [ ] [Current work]\n\n### Blocked\n- [Issues preventing progress, if any]\n\n## Key Decisions\n- **[Decision]**: [Brief rationale]\n\n## Next Steps\n1. [Ordered list of what should happen next]\n\n## Critical Context\n- [Any data, examples, or references needed to continue]\n- [Or \"(none)\" if not applicable]\n\nKeep each section concise. Preserve exact file paths, function names, and error messages.`;\n\n/**\n * Built-in compaction task (spec §8.7): select an old prefix of the model context, summarize it, and place a summary\n * entry whose `head` is the first kept entry. A compaction the generation owns blocks it and appends directly; a\n * conversation-owned one places its summary through a write submission.\n */\nexport const CompactionTask = defineTask<CompactionInput, CompactionCheckpoint, CompactionResult, CompactionHooks>({\n\tname: \"pi.compaction\",\n\tversion: 1,\n\tinitial: () => ({ phase: \"select\" }),\n\tphases: {\n\t\tselect: async (task, runtime, context) => {\n\t\t\tconst { conversationId } = runtime;\n\t\t\tconst agent = await runtime.agent(context);\n\t\t\tconst settings = runtime.settings;\n\t\t\tconst ref = agent.model;\n\t\t\tconst model = ref === undefined ? undefined : runtime.models.getModel(ref.provider, ref.modelId);\n\t\t\tif (ref === undefined || model === undefined) return failNoModel(runtime, ref, context);\n\t\t\tconst policy = settings.compaction;\n\t\t\tconst view = await runtime.context(conversationId, context);\n\t\t\tconst cut = selectCut(view, policy.keepRecentTokens);\n\t\t\tif (cut === undefined) return complete(runtime, context);\n\t\t\tconst firstKept = view.entries[cut]!.id;\n\t\t\tconst { reason, instructions } = task.input;\n\t\t\tconst compaction = {\n\t\t\t\treason,\n\t\t\t\tentries: view.entries.slice(0, cut),\n\t\t\t\tmessages: summarizedMessages(view, cut),\n\t\t\t\tfirstKept,\n\t\t\t\t...(instructions === undefined ? {} : { instructions }),\n\t\t\t};\n\t\t\tlet decision: { readonly decline: true } | { readonly summary: string } | undefined;\n\t\t\tawait runtime.hooks.each(\"beforeCompact\", async (hook) => {\n\t\t\t\tif (decision === undefined) decision = await hook(compaction, runtime, context);\n\t\t\t});\n\t\t\tif (decision !== undefined && \"decline\" in decision) return complete(runtime, context);\n\t\t\tif (decision !== undefined) return place(runtime, firstKept, decision.summary, context);\n\t\t\tconst request: SummaryRequest = {\n\t\t\t\tattempt: 1,\n\t\t\t\tmodel: ref,\n\t\t\t\tthinkingLevel: agent.thinkingLevel,\n\t\t\t\tstreamOptions: settings.stream,\n\t\t\t\tmaxTokens: Math.min(\n\t\t\t\t\tMath.floor(0.8 * policy.reserveTokens),\n\t\t\t\t\tmodel.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY,\n\t\t\t\t),\n\t\t\t\ttail: view.entries.reduce((tail, entry) => (entry.id > tail ? entry.id : tail), firstKept),\n\t\t\t\tfirstKept,\n\t\t\t};\n\t\t\tawait runtime.commit(() => ({ status: \"running\", checkpoint: { phase: \"summarize\", ...request } }), context);\n\t\t},\n\t\tsummarize: async (task, runtime, context) => {\n\t\t\tconst { phase: _, ...request } = task.state.checkpoint;\n\t\t\tconst { model: ref, thinkingLevel, streamOptions, maxTokens, tail, firstKept, attempt } = request;\n\t\t\tconst model = runtime.models.getModel(ref.provider, ref.modelId);\n\t\t\tif (model === undefined) return failNoModel(runtime, ref, context);\n\t\t\t// The context at `tail` is immutable, so this is the range `select` chose.\n\t\t\tconst view = await runtime.context(runtime.conversationId, context, { at: tail });\n\t\t\tconst cut = view.entries.findIndex((entry) => entry.id === firstKept);\n\t\t\tconst now = runtime.now();\n\t\t\tconst messages: Message[] = [\n\t\t\t\t{ role: \"system\", content: SUMMARIZATION_SYSTEM_PROMPT, timestamp: now },\n\t\t\t\t{\n\t\t\t\t\trole: \"user\",\n\t\t\t\t\tcontent: [{ type: \"text\", text: summaryPrompt(summarizedMessages(view, cut), task.input.instructions) }],\n\t\t\t\t\ttimestamp: now,\n\t\t\t\t},\n\t\t\t];\n\t\t\tconst { deferred: _deferred, ...forwarded } = streamOptions;\n\t\t\tconst options: SimpleStreamOptions = {\n\t\t\t\t...forwarded,\n\t\t\t\tcacheRetention: \"none\",\n\t\t\t\tmaxTokens,\n\t\t\t\tsignal: runtime.signal,\n\t\t\t\tsessionId: await ensureProviderSessionId(runtime, context),\n\t\t\t\t...(thinkingLevel === \"off\" ? {} : { reasoning: thinkingLevel }),\n\t\t\t};\n\t\t\tconst message = await runtime.models.completeSimple(model, { messages }, options);\n\t\t\t// An abort mark or close: the abort invocation or the reopened task handles the committed state.\n\t\t\truntime.signal.throwIfAborted();\n\t\t\tconst summary = summaryText(message);\n\t\t\tconst policy = runtime.settings.retry;\n\t\t\tconst retry =\n\t\t\t\tmessage.stopReason === \"error\" &&\n\t\t\t\tisRetryableAssistantError(message) &&\n\t\t\t\tpolicy.enabled &&\n\t\t\t\tattempt <= policy.maxRetries;\n\t\t\tconst until = retry ? runtime.now() + retryDelayMs(policy, attempt) : 0;\n\t\t\tawait runtime.commit(async (tx, current): Promise<Next> => {\n\t\t\t\tawait recordUsage(\n\t\t\t\t\ttx,\n\t\t\t\t\truntime.conversationId,\n\t\t\t\t\t\"models\",\n\t\t\t\t\t`${message.provider}/${message.model}`,\n\t\t\t\t\tmessage.usage,\n\t\t\t\t);\n\t\t\t\tconst live = await tx.doc(LiveDoc, runtime.conversationId);\n\t\t\t\tif (summary !== undefined) return placeSummary(tx, runtime, current, live, firstKept, summary);\n\t\t\t\tif (retry) {\n\t\t\t\t\tconst status = compactionStatus(live, runtime.taskId);\n\t\t\t\t\tif (status !== undefined) status.retry = { at: until, error: message.errorMessage ?? \"\" };\n\t\t\t\t\treturn { status: \"running\", checkpoint: { phase: \"retry\", ...request, until } };\n\t\t\t\t}\n\t\t\t\tremoveCompactionStatus(live, runtime.taskId);\n\t\t\t\tconst text = summaryFailure(message);\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"terminal\",\n\t\t\t\t\toutcome: { status: \"failed\", error: { message: text, detail: { reason: \"model_error\" } } },\n\t\t\t\t};\n\t\t\t}, context);\n\t\t},\n\t\tretry: async (task, runtime, context) => {\n\t\t\tconst { phase: _, until, ...request } = task.state.checkpoint;\n\t\t\tawait runtime.sleep(until, context);\n\t\t\tconst attempt = request.attempt + 1;\n\t\t\tawait runtime.commit(async (tx) => {\n\t\t\t\tconst status = compactionStatus(await tx.doc(LiveDoc, runtime.conversationId), runtime.taskId);\n\t\t\t\tif (status !== undefined) {\n\t\t\t\t\tstatus.attempt = attempt;\n\t\t\t\t\tdelete status.retry;\n\t\t\t\t}\n\t\t\t\treturn { status: \"running\", checkpoint: { phase: \"summarize\", ...request, attempt } };\n\t\t\t}, context);\n\t\t},\n\t},\n\tabort: async (_task, runtime, context) => {\n\t\tawait runtime.commit(async (tx) => {\n\t\t\tremoveCompactionStatus(await tx.doc(LiveDoc, runtime.conversationId), runtime.taskId);\n\t\t\treturn { status: \"terminal\", outcome: { status: \"aborted\" } };\n\t\t}, context);\n\t},\n});\n\n/**\n * Create a compaction task with its status in this commit. `owner` is the generation that waits for it (a blocking\n * compaction); without one it is conversation-owned, and `background` unless it is manual.\n */\nexport async function createCompaction(\n\ttx: Tx,\n\tconversationId: ConversationId,\n\tinput: CompactionInput,\n\towner?: TaskId,\n): Promise<TaskId<CompactionResult>> {\n\tconst ownership =\n\t\towner === undefined ? ({ kind: \"conversation\" } as const) : { kind: \"task\" as const, taskId: owner };\n\tconst background = owner === undefined && input.reason !== \"manual\";\n\tconst taskId = await tx.createTask(CompactionTask, input, { ownership, conversationId, background });\n\tconst status = { taskId, reason: input.reason, blocking: owner !== undefined, attempt: 1 };\n\taddCompactionStatus(await tx.doc(LiveDoc, conversationId), status);\n\treturn taskId;\n}\n\n/**\n * Index in `view.entries` of the first entry a summary keeps, or `undefined` when there is nothing to compact\n * (spec §8.7). Walks back from the tail until `keepRecentTokens` are kept, then cuts at the first candidate at or after\n * that entry: an entry whose contribution starts with a user or assistant message, never a tool result, and never a\n * user entry that a result of the preceding assistant's calls still follows.\n */\nexport function selectCut(view: ContextView, keepRecentTokens: number): number | undefined {\n\tconst { contributions } = view;\n\tconst start = view.head === undefined ? 0 : 1;\n\tconst candidates: number[] = [];\n\tfor (let index = start; index < contributions.length; index++) {\n\t\tif (isCandidate(contributions, index)) candidates.push(index);\n\t}\n\tlet kept = 0;\n\tlet cut: number | undefined;\n\tfor (let index = contributions.length - 1; index >= start; index--) {\n\t\tfor (const message of contributions[index]!) kept += estimateMessageTokens(message);\n\t\tif (kept < keepRecentTokens) continue;\n\t\tcut = candidates.find((candidate) => candidate >= index) ?? candidates.at(-1);\n\t\tbreak;\n\t}\n\tif (cut === undefined) return undefined;\n\tfor (let index = start; index < cut; index++) if (contributions[index]!.length > 0) return cut;\n\treturn undefined;\n}\n\nfunction isCandidate(contributions: ContextView[\"contributions\"], index: number): boolean {\n\tconst first = contributions[index]![0];\n\tif (first?.role === \"assistant\") return true;\n\tif (first?.role !== \"user\") return false;\n\t// A result of the preceding assistant's calls that follows this entry, before the next assistant, belongs before it.\n\tlet calls: ReadonlySet<string> = new Set();\n\tfor (let before = index - 1; before >= 0; before--) {\n\t\tconst assistant = contributions[before]!.findLast((message) => message.role === \"assistant\");\n\t\tif (assistant === undefined) continue;\n\t\tcalls = new Set(assistant.content.flatMap((content) => (content.type === \"toolCall\" ? [content.id] : [])));\n\t\tbreak;\n\t}\n\tif (calls.size === 0) return true;\n\tfor (let after = index; after < contributions.length; after++) {\n\t\tfor (const [position, message] of contributions[after]!.entries()) {\n\t\t\tif (message.role === \"assistant\" && (after > index || position > 0)) return true;\n\t\t\tif (message.role === \"toolResult\" && calls.has(message.toolCallId)) return false;\n\t\t}\n\t}\n\treturn true;\n}\n\n/** Model messages of the entries before `cut`: the head marker first, ordered like model context (spec §2.1). */\nexport function summarizedMessages(view: ContextView, cut: number): Message[] {\n\treturn orderToolResults(view.contributions.slice(0, cut).flat());\n}\n\n/**\n * Size of a request over `view` followed by `extra` (spec §8.3): the usage of the newest assistant appended after the\n * head marker, whose request included the marker, plus estimates of the messages after it; without one, estimates of\n * every message.\n */\nexport function estimateContext(view: ContextView, extra: readonly Message[]): number {\n\tlet measured: AssistantMessage | undefined;\n\tconst after = view.head?.id ?? Number.NEGATIVE_INFINITY;\n\tfor (let index = view.entries.length - 1; index >= 0 && measured === undefined; index--) {\n\t\tif (view.entries[index]!.id <= after) continue;\n\t\tmeasured = view.contributions[index]!.findLast(\n\t\t\t(message): message is AssistantMessage =>\n\t\t\t\tmessage.role === \"assistant\" && calculateContextTokens(message.usage) > 0,\n\t\t);\n\t}\n\tconst from = measured === undefined ? 0 : view.messages.lastIndexOf(measured) + 1;\n\tlet tokens = measured === undefined ? 0 : calculateContextTokens(measured.usage);\n\tfor (const message of view.messages.slice(from)) tokens += estimateMessageTokens(message);\n\tfor (const message of extra) tokens += estimateMessageTokens(message);\n\treturn tokens;\n}\n\n/** The summary of a clean `stop` with text and no tool call; anything else is not a summary. */\nfunction summaryText(message: AssistantMessage): string | undefined {\n\tif (message.stopReason !== \"stop\" || message.content.some((content) => content.type === \"toolCall\"))\n\t\treturn undefined;\n\tconst text = message.content\n\t\t.flatMap((content) => (content.type === \"text\" ? [content.text] : []))\n\t\t.join(\"\\n\")\n\t\t.trim();\n\treturn text.length === 0 ? undefined : text;\n}\n\nfunction summaryFailure(message: AssistantMessage): string {\n\tif (message.stopReason === \"error\" || message.stopReason === \"aborted\") {\n\t\treturn `Summarization failed: ${message.errorMessage ?? message.stopReason}`;\n\t}\n\tif (message.stopReason === \"length\") return \"Summarization hit the token limit; the summary is incomplete\";\n\tif (message.content.some((content) => content.type === \"toolCall\")) return \"Summarization attempted to call a tool\";\n\treturn \"Summarization produced no text\";\n}\n\n/** The summarizer's user message: the serialized conversation, the prompt, and any instructions. */\nfunction summaryPrompt(messages: readonly Message[], instructions: string | undefined): string {\n\tconst focus = instructions === undefined ? \"\" : `\\n\\nAdditional focus: ${instructions}`;\n\treturn `<conversation>\\n${serializeConversation(messages)}\\n</conversation>\\n\\n${SUMMARIZATION_PROMPT}${focus}`;\n}\n\n/** Messages as plain text, so the summarizer reads a transcript instead of continuing it. System messages are omitted. */\nexport function serializeConversation(messages: readonly Message[]): string {\n\tconst parts: string[] = [];\n\tfor (const message of messages) {\n\t\tif (message.role === \"user\") {\n\t\t\tconst text = contentText(message.content);\n\t\t\tif (text.length > 0) parts.push(`[User]: ${text}`);\n\t\t} else if (message.role === \"assistant\") {\n\t\t\tconst thinking = message.content.flatMap((content) => (content.type === \"thinking\" ? [content.thinking] : []));\n\t\t\tconst text = message.content.flatMap((content) => (content.type === \"text\" ? [content.text] : []));\n\t\t\tconst calls = message.content.flatMap((content) =>\n\t\t\t\tcontent.type === \"toolCall\"\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t`${content.name}(${Object.entries(content.arguments)\n\t\t\t\t\t\t\t\t.map(([key, value]) => `${key}=${JSON.stringify(value)}`)\n\t\t\t\t\t\t\t\t.join(\", \")})`,\n\t\t\t\t\t\t]\n\t\t\t\t\t: [],\n\t\t\t);\n\t\t\tif (thinking.length > 0) parts.push(`[Assistant thinking]: ${thinking.join(\"\\n\")}`);\n\t\t\tif (text.length > 0) parts.push(`[Assistant]: ${text.join(\"\\n\")}`);\n\t\t\tif (calls.length > 0) parts.push(`[Assistant tool calls]: ${calls.join(\"; \")}`);\n\t\t} else if (message.role === \"toolResult\") {\n\t\t\tconst text = contentText(message.content);\n\t\t\tif (text.length > 0) parts.push(`[Tool result]: ${truncate(text, TOOL_RESULT_MAX_CHARS)}`);\n\t\t}\n\t}\n\treturn parts.join(\"\\n\\n\");\n}\n\nfunction contentText(content: string | readonly { readonly type: string; readonly text?: string }[]): string {\n\tif (typeof content === \"string\") return content;\n\treturn content\n\t\t.flatMap((block) => (block.type === \"text\" && block.text !== undefined ? [block.text] : []))\n\t\t.join(\"\\n\");\n}\n\nfunction truncate(text: string, maxChars: number): string {\n\tif (text.length <= maxChars) return text;\n\treturn `${text.slice(0, maxChars)}\\n\\n[... ${text.length - maxChars} more characters truncated]`;\n}\n\n/** Place a summary supplied by a hook in its own commit. */\nasync function place(runtime: Runtime, firstKept: EntryId, summary: string, context: Context): Promise<void> {\n\tawait runtime.commit(\n\t\tasync (tx, current) =>\n\t\t\tplaceSummary(tx, runtime, current, await tx.doc(LiveDoc, runtime.conversationId), firstKept, summary),\n\t\tcontext,\n\t);\n}\n\n/**\n * Place the summary entry and complete (spec §8.7). A blocking compaction, owned by its generation, appends it: the\n * generation holds the run and waits. A conversation-owned one admits it as a write submission, placed at once when\n * idle, otherwise at the next boundary, or settled `stale`.\n * REMINDER: nothing else may append to a busy conversation, so every non-blocking summary goes through admission.\n */\nasync function placeSummary(\n\ttx: Tx,\n\truntime: Runtime,\n\tcurrent: RunningTask<CompactionInput, CompactionCheckpoint, CompactionResult>,\n\tlive: Draft<LiveState>,\n\tfirstKept: EntryId,\n\tsummary: string,\n): Promise<Next> {\n\tremoveCompactionStatus(live, runtime.taskId);\n\tconst text = `${SUMMARY_PREFIX}${summary}${SUMMARY_SUFFIX}`;\n\tconst entry = {\n\t\tkind: CompactionEntry.kind,\n\t\thead: firstKept,\n\t\tmodel: [{ role: \"user\", content: [{ type: \"text\", text }], timestamp: runtime.now() }],\n\t\tdata: { reason: current.input.reason },\n\t} satisfies EntryDraft;\n\tconst result: CompactionResult =\n\t\tcurrent.owner === undefined\n\t\t\t? {\n\t\t\t\t\tsubmissionId: await admitSubmission(\n\t\t\t\t\t\ttx,\n\t\t\t\t\t\truntime.conversationId,\n\t\t\t\t\t\t{ type: \"write\", requestId: `compaction:${runtime.taskId}`, entry },\n\t\t\t\t\t\truntime.now(),\n\t\t\t\t\t\truntime.settings,\n\t\t\t\t\t),\n\t\t\t\t}\n\t\t\t: { entryId: (await tx.appendEntry(runtime.conversationId, entry)).id };\n\treturn { status: \"terminal\", outcome: { status: \"completed\", result } };\n}\n\n/** Remove the status and complete without a summary. */\nasync function complete(runtime: Runtime, context: Context): Promise<void> {\n\tawait runtime.commit(async (tx) => {\n\t\tremoveCompactionStatus(await tx.doc(LiveDoc, runtime.conversationId), runtime.taskId);\n\t\treturn { status: \"terminal\", outcome: { status: \"completed\", result: {} } };\n\t}, context);\n}\n\nasync function failNoModel(runtime: Runtime, ref: ModelRef | undefined, context: Context): Promise<void> {\n\tconst message =\n\t\tref === undefined ? \"No model is configured\" : `Model ${ref.provider}/${ref.modelId} is not available`;\n\tawait runtime.commit(async (tx) => {\n\t\tremoveCompactionStatus(await tx.doc(LiveDoc, runtime.conversationId), runtime.taskId);\n\t\treturn { status: \"terminal\", outcome: { status: \"failed\", error: { message, detail: { reason: \"no_model\" } } } };\n\t}, context);\n}\n"]}
@@ -16,6 +16,34 @@ export type ContextBounds = {
16
16
  * them off the line.
17
17
  */
18
18
  export declare function captureContextBounds(storage: Storage, conversationId: ConversationId, context: Context, at?: EntryId): Promise<ContextBounds | undefined>;
19
+ /**
20
+ * One context read: the visible entries it scanned, from the head marker's head, or transcript start, through
21
+ * `bounds.tail`, oldest first, and the view derived from them. Entries at or below the tail never change, so a later
22
+ * read with the same head marker extends both.
23
+ */
24
+ export type ContextRange = {
25
+ readonly bounds: ContextBounds;
26
+ readonly entries: readonly EntryRecord[];
27
+ readonly view: ContextView;
28
+ /** Targets of the edits in `entries`. */
29
+ readonly edited: ReadonlySet<EntryId>;
30
+ /**
31
+ * `view.messages` from the contributed messages before the last assistant message. Tool results are ordered within
32
+ * the messages up to the next assistant message, so later entries cannot change these.
33
+ */
34
+ readonly settled: readonly Message[];
35
+ /** Contributed messages from the last assistant message on, before tool result ordering. */
36
+ readonly open: readonly Message[];
37
+ };
38
+ /**
39
+ * `readContext()` that reuses `previous`, an earlier range of the same conversation: with the same head marker, only
40
+ * entries after its tail are scanned. Returns the view and the range to pass to the next read. A range outlives the
41
+ * read, so its entries are frozen like `MemoryStorage` records, and the returned view has its own arrays.
42
+ */
43
+ export declare function readContextFrom(session: SessionImpl, storage: Storage, conversationId: ConversationId, context: Context, at: EntryId | undefined, previous: ContextRange | undefined): Promise<{
44
+ readonly view: ContextView;
45
+ readonly range: ContextRange | undefined;
46
+ }>;
19
47
  /** Committed context of one conversation: bounds captured on the Session line, entries derived off it. */
20
48
  export declare function readContext(session: SessionImpl, storage: Storage, conversationId: ConversationId, context: Context, at?: EntryId): Promise<ContextView>;
21
49
  /**
@@ -26,7 +54,7 @@ export declare function readContext(session: SessionImpl, storage: Storage, conv
26
54
  */
27
55
  export declare function deriveContext(storage: Storage, conversationId: ConversationId, bounds: ContextBounds | undefined, context: Context): Promise<ContextView>;
28
56
  /** The raw active entries within captured bounds, without deriving model context. */
29
- export declare function activeEntries(storage: Storage, conversationId: ConversationId, bounds: ContextBounds | undefined, context: Context): Promise<EntryRecord[]>;
57
+ export declare function activeEntries(storage: Storage, conversationId: ConversationId, bounds: ContextBounds | undefined, context: Context): Promise<readonly EntryRecord[]>;
30
58
  /**
31
59
  * Place each assistant's tool results directly after it in call order. Results are taken from the messages before
32
60
  * the next assistant; a missing result is synthesized and unmatched results are dropped.
@@ -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;AACpG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAe,cAAc,EAAU,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtG,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,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,CAkBtB;AAED,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,WAAW,EAAE,CAAC,CAGxB;AAgCD;;;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;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,3 +1,4 @@
1
+ import { idFromNumber } from "../ids.js";
1
2
  const SCAN_PAGE_SIZE = 256;
2
3
  const EXCLUDED_STOP_REASONS = new Set(["aborted", "error", "deferred"]);
3
4
  const MISSING_RESULT_TEXT = "Tool result unavailable: history ends before this call completed.";
@@ -21,6 +22,42 @@ export async function captureContextBounds(storage, conversationId, context, at)
21
22
  }
22
23
  return { head: await storage.findLatestHeadMarker(conversationId, tail, context), tail };
23
24
  }
25
+ /**
26
+ * `readContext()` that reuses `previous`, an earlier range of the same conversation: with the same head marker, only
27
+ * entries after its tail are scanned. Returns the view and the range to pass to the next read. A range outlives the
28
+ * read, so its entries are frozen like `MemoryStorage` records, and the returned view has its own arrays.
29
+ */
30
+ export async function readContextFrom(session, storage, conversationId, context, at, previous) {
31
+ const bounds = await session.readOnLine(() => captureContextBounds(storage, conversationId, context, at));
32
+ if (bounds === undefined)
33
+ return { view: emptyView(), range: undefined };
34
+ freezeJson(bounds.head);
35
+ let range;
36
+ if (previous === undefined || previous.bounds.head?.id !== bounds.head?.id) {
37
+ range = deriveRange(bounds, freezeJson(await scanRange(storage, conversationId, rangeQuery(bounds), context)));
38
+ }
39
+ else if (bounds.tail === previous.bounds.tail) {
40
+ range = previous;
41
+ }
42
+ else if (bounds.tail < previous.bounds.tail) {
43
+ range = deriveRange(bounds, previous.entries.filter((entry) => entry.id <= bounds.tail));
44
+ }
45
+ else {
46
+ const minEntryId = idFromNumber(previous.bounds.tail + 1);
47
+ const added = await scanRange(storage, conversationId, { minEntryId, maxEntryId: bounds.tail }, context);
48
+ range = extendRange(previous, bounds, freezeJson(added));
49
+ }
50
+ const { view } = range;
51
+ return {
52
+ view: {
53
+ ...view,
54
+ entries: [...view.entries],
55
+ contributions: [...view.contributions],
56
+ messages: [...view.messages],
57
+ },
58
+ range,
59
+ };
60
+ }
24
61
  /** Committed context of one conversation: bounds captured on the Session line, entries derived off it. */
25
62
  export async function readContext(session, storage, conversationId, context, at) {
26
63
  const bounds = await session.readOnLine(() => captureContextBounds(storage, conversationId, context, at));
@@ -34,43 +71,107 @@ export async function readContext(session, storage, conversationId, context, at)
34
71
  */
35
72
  export async function deriveContext(storage, conversationId, bounds, context) {
36
73
  if (bounds === undefined)
37
- return { head: undefined, entries: [], contributions: [], messages: [] };
38
- const head = bounds.head;
39
- const range = await scanRange(storage, conversationId, bounds, context);
74
+ return emptyView();
75
+ return deriveRange(bounds, await scanRange(storage, conversationId, rangeQuery(bounds), context)).view;
76
+ }
77
+ function emptyView() {
78
+ return { head: undefined, entries: [], contributions: [], messages: [] };
79
+ }
80
+ /** Derive the context view from the entries scanned within `bounds`. */
81
+ function deriveRange(bounds, entries) {
40
82
  const edits = new Map();
41
83
  // Edits of every entry in the range count, including older head markers that `selectActive()` drops.
42
- for (const entry of range)
84
+ for (const entry of entries)
43
85
  for (const edit of entry.edits ?? [])
44
86
  edits.set(edit.target, edit);
45
- const entries = selectActive(head, range);
46
- const contributions = entries.map((entry) => {
47
- const edit = edits.get(entry.id);
48
- if (edit?.action === "omit")
49
- return [];
50
- const contributed = edit?.action === "replace" ? edit.messages : (entry.model ?? []);
51
- return contributed.filter((message) => message.role !== "assistant" || !EXCLUDED_STOP_REASONS.has(message.stopReason));
52
- });
53
- return { head, entries, contributions, messages: orderToolResults(contributions.flat()) };
87
+ const active = selectActive(bounds.head, entries);
88
+ const contributions = active.map((entry) => contribute(entry, edits.get(entry.id)));
89
+ const { settled, open } = settle([], contributions.flat());
90
+ const messages = leadWithSystem([...settled, ...orderToolResults(open)]);
91
+ const view = { head: bounds.head, entries: active, contributions, messages };
92
+ return { bounds, entries, view, edited: new Set(edits.keys()), settled, open };
93
+ }
94
+ /**
95
+ * `previous` extended by the entries `added` after its tail under the same head marker: only their contributions and
96
+ * the open messages are derived. An added edit can change an earlier entry, so it derives the whole range again.
97
+ */
98
+ function extendRange(previous, bounds, added) {
99
+ const entries = [...previous.entries, ...added];
100
+ if (added.some((entry) => entry.edits !== undefined || entry.head !== undefined || previous.edited.has(entry.id))) {
101
+ return deriveRange(bounds, entries);
102
+ }
103
+ const contributions = added.map((entry) => contribute(entry, undefined));
104
+ const { settled, open } = settle(previous.settled, [...previous.open, ...contributions.flat()]);
105
+ const view = {
106
+ head: bounds.head,
107
+ entries: [...previous.view.entries, ...added],
108
+ contributions: [...previous.view.contributions, ...contributions],
109
+ messages: leadWithSystem([...settled, ...orderToolResults(open)]),
110
+ };
111
+ return { bounds, entries, view, edited: previous.edited, settled, open };
112
+ }
113
+ /**
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.
118
+ */
119
+ function leadWithSystem(messages) {
120
+ const index = messages.findIndex((message) => message.role !== "user");
121
+ if (index <= 0 || messages[index].role !== "system")
122
+ return messages;
123
+ return [messages[index], ...messages.slice(0, index), ...messages.slice(index + 1)];
124
+ }
125
+ /** One active entry's model messages after its edit and excluded stop reasons, before tool result ordering. */
126
+ function contribute(entry, edit) {
127
+ if (edit?.action === "omit")
128
+ return Object.freeze([]);
129
+ const contributed = edit?.action === "replace" ? edit.messages : (entry.model ?? []);
130
+ return Object.freeze(contributed.filter((message) => message.role !== "assistant" || !EXCLUDED_STOP_REASONS.has(message.stopReason)));
131
+ }
132
+ /** Objects `freezeJson()` froze with everything in them. `Object.isFrozen()` holds for shallow freezes too. */
133
+ const deeplyFrozen = new WeakSet();
134
+ /** Freeze a JSON value and everything in it. */
135
+ function freezeJson(value) {
136
+ if (value === null || typeof value !== "object" || deeplyFrozen.has(value))
137
+ return value;
138
+ for (const child of Object.values(value))
139
+ freezeJson(child);
140
+ Object.freeze(value);
141
+ deeplyFrozen.add(value);
142
+ return value;
143
+ }
144
+ /**
145
+ * Move the ordered messages before the last assistant message of `open` to `settled`. `orderToolResults()` of a
146
+ * sequence equals the concatenation over its parts when each later part starts with an assistant message.
147
+ */
148
+ function settle(settled, open) {
149
+ const last = open.findLastIndex((message) => message.role === "assistant");
150
+ if (last <= 0)
151
+ return { settled, open };
152
+ return { settled: [...settled, ...orderToolResults(open.slice(0, last))], open: open.slice(last) };
54
153
  }
55
154
  /** The raw active entries within captured bounds, without deriving model context. */
56
155
  export async function activeEntries(storage, conversationId, bounds, context) {
57
156
  if (bounds === undefined)
58
157
  return [];
59
- return selectActive(bounds.head, await scanRange(storage, conversationId, bounds, context));
158
+ return selectActive(bounds.head, await scanRange(storage, conversationId, rangeQuery(bounds), context));
60
159
  }
61
- /** Visible entries from the head marker's head, or transcript start, through the tail, oldest first. */
62
- async function scanRange(storage, conversationId, bounds, context) {
160
+ /** The visible range of `bounds`: from the head marker's head, or transcript start, through the tail. */
161
+ function rangeQuery(bounds) {
63
162
  const head = bounds.head;
64
- const range = [];
163
+ return head === undefined ? { maxEntryId: bounds.tail } : { minEntryId: head.head, maxEntryId: bounds.tail };
164
+ }
165
+ /** Visible entries within `range`, oldest first. */
166
+ async function scanRange(storage, conversationId, range, context) {
167
+ const entries = [];
65
168
  let cursor;
66
169
  do {
67
- const page = await storage.scanEntries(head === undefined
68
- ? { conversationId, maxEntryId: bounds.tail }
69
- : { conversationId, minEntryId: head.head, maxEntryId: bounds.tail }, SCAN_PAGE_SIZE, cursor, context);
70
- range.push(...page.items);
170
+ const page = await storage.scanEntries({ conversationId, ...range }, SCAN_PAGE_SIZE, cursor, context);
171
+ entries.push(...page.items);
71
172
  cursor = page.next;
72
173
  } while (cursor !== undefined);
73
- return range.reverse();
174
+ return entries.reverse();
74
175
  }
75
176
  /** The head marker followed by the range's non-head entries, or the whole range without a marker. */
76
177
  function selectActive(head, range) {
@@ -107,7 +208,7 @@ export function orderToolResults(messages) {
107
208
  return ordered;
108
209
  }
109
210
  function missingResult(call, timestamp) {
110
- return {
211
+ return freezeJson({
111
212
  role: "toolResult",
112
213
  toolCallId: call.id,
113
214
  toolName: call.name,
@@ -115,6 +216,6 @@ function missingResult(call, timestamp) {
115
216
  isError: true,
116
217
  details: { reason: "missing_result" },
117
218
  timestamp,
118
- };
219
+ });
119
220
  }
120
221
  //# sourceMappingURL=context.js.map