@earendil-works/pi-durable 0.99.1 → 1.0.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.
- package/CHANGELOG.md +7 -0
- package/README.md +557 -46
- package/dist/entries.d.ts +20 -2
- package/dist/entries.d.ts.map +1 -1
- package/dist/entries.js +14 -1
- package/dist/entries.js.map +1 -1
- package/dist/env/index.d.ts +18 -38
- package/dist/env/index.d.ts.map +1 -1
- package/dist/env/index.js +2 -0
- package/dist/env/index.js.map +1 -1
- package/dist/env/node.d.ts +2 -0
- package/dist/env/node.d.ts.map +1 -1
- package/dist/env/node.js +53 -58
- package/dist/env/node.js.map +1 -1
- package/dist/harness/agent.d.ts +31 -0
- package/dist/harness/agent.d.ts.map +1 -0
- package/dist/harness/agent.js +209 -0
- package/dist/harness/agent.js.map +1 -0
- package/dist/harness/compaction.d.ts +56 -0
- package/dist/harness/compaction.d.ts.map +1 -0
- package/dist/harness/compaction.js +382 -0
- package/dist/harness/compaction.js.map +1 -0
- package/dist/harness/context.d.ts +8 -0
- package/dist/harness/context.d.ts.map +1 -1
- package/dist/harness/context.js +32 -21
- package/dist/harness/context.js.map +1 -1
- package/dist/harness/define.d.ts +18 -0
- package/dist/harness/define.d.ts.map +1 -0
- package/dist/harness/define.js +25 -0
- package/dist/harness/define.js.map +1 -0
- package/dist/harness/events.d.ts +166 -0
- package/dist/harness/events.d.ts.map +1 -0
- package/dist/harness/events.js +319 -0
- package/dist/harness/events.js.map +1 -0
- package/dist/harness/generation.d.ts +36 -7
- package/dist/harness/generation.d.ts.map +1 -1
- package/dist/harness/generation.js +356 -82
- package/dist/harness/generation.js.map +1 -1
- package/dist/harness/harness.d.ts.map +1 -1
- package/dist/harness/harness.js +141 -106
- package/dist/harness/harness.js.map +1 -1
- package/dist/harness/inbox.d.ts +59 -0
- package/dist/harness/inbox.d.ts.map +1 -0
- package/dist/harness/inbox.js +97 -0
- package/dist/harness/inbox.js.map +1 -0
- package/dist/harness/json.d.ts +10 -0
- package/dist/harness/json.d.ts.map +1 -0
- package/dist/harness/json.js +33 -0
- package/dist/harness/json.js.map +1 -0
- package/dist/harness/live.d.ts +66 -7
- package/dist/harness/live.d.ts.map +1 -1
- package/dist/harness/live.js +71 -7
- package/dist/harness/live.js.map +1 -1
- package/dist/harness/output.d.ts +62 -0
- package/dist/harness/output.d.ts.map +1 -0
- package/dist/harness/output.js +261 -0
- package/dist/harness/output.js.map +1 -0
- package/dist/harness/prompt.d.ts +9 -6
- package/dist/harness/prompt.d.ts.map +1 -1
- package/dist/harness/prompt.js +57 -14
- package/dist/harness/prompt.js.map +1 -1
- package/dist/harness/registry.d.ts +2 -3
- package/dist/harness/registry.d.ts.map +1 -1
- package/dist/harness/registry.js +69 -277
- package/dist/harness/registry.js.map +1 -1
- package/dist/harness/scheduler.d.ts +42 -12
- package/dist/harness/scheduler.d.ts.map +1 -1
- package/dist/harness/scheduler.js +649 -75
- package/dist/harness/scheduler.js.map +1 -1
- package/dist/harness/submissions.d.ts +14 -9
- package/dist/harness/submissions.d.ts.map +1 -1
- package/dist/harness/submissions.js +76 -34
- package/dist/harness/submissions.js.map +1 -1
- package/dist/harness/task-graph.d.ts +49 -0
- package/dist/harness/task-graph.d.ts.map +1 -0
- package/dist/harness/task-graph.js +163 -0
- package/dist/harness/task-graph.js.map +1 -0
- package/dist/harness/tool.d.ts +36 -0
- package/dist/harness/tool.d.ts.map +1 -0
- package/dist/harness/tool.js +415 -0
- package/dist/harness/tool.js.map +1 -0
- package/dist/harness/types.d.ts +330 -125
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/harness/usage.d.ts +18 -0
- package/dist/harness/usage.d.ts.map +1 -0
- package/dist/harness/usage.js +57 -0
- package/dist/harness/usage.js.map +1 -0
- package/dist/harness/view.d.ts +43 -0
- package/dist/harness/view.d.ts.map +1 -0
- package/dist/harness/view.js +189 -0
- package/dist/harness/view.js.map +1 -0
- package/dist/index.d.ts +13 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/session/observation.d.ts +18 -11
- package/dist/session/observation.d.ts.map +1 -1
- package/dist/session/observation.js +24 -13
- package/dist/session/observation.js.map +1 -1
- package/dist/session/session.d.ts +10 -1
- package/dist/session/session.d.ts.map +1 -1
- package/dist/session/session.js +23 -5
- package/dist/session/session.js.map +1 -1
- package/dist/session/transaction.d.ts +12 -3
- package/dist/session/transaction.d.ts.map +1 -1
- package/dist/session/transaction.js +90 -31
- package/dist/session/transaction.js.map +1 -1
- package/dist/storage/jsonl/storage.d.ts +22 -2
- package/dist/storage/jsonl/storage.d.ts.map +1 -1
- package/dist/storage/jsonl/storage.js +1 -1
- package/dist/storage/jsonl/storage.js.map +1 -1
- package/dist/storage/memory.js +1 -1
- package/dist/storage/memory.js.map +1 -1
- package/dist/storage/sqlite/database.d.ts +26 -19
- package/dist/storage/sqlite/database.d.ts.map +1 -1
- package/dist/storage/sqlite/database.js.map +1 -1
- package/dist/storage/sqlite/index.d.ts +1 -1
- package/dist/storage/sqlite/index.d.ts.map +1 -1
- package/dist/storage/sqlite/index.js.map +1 -1
- package/dist/storage/sqlite/migrations.js +7 -7
- package/dist/storage/sqlite/migrations.js.map +1 -1
- package/dist/storage/sqlite/node.d.ts +23 -7
- package/dist/storage/sqlite/node.d.ts.map +1 -1
- package/dist/storage/sqlite/node.js +129 -47
- package/dist/storage/sqlite/node.js.map +1 -1
- package/dist/storage/sqlite/storage.d.ts +12 -0
- package/dist/storage/sqlite/storage.d.ts.map +1 -1
- package/dist/storage/sqlite/storage.js +121 -129
- package/dist/storage/sqlite/storage.js.map +1 -1
- package/dist/testing/storage-benchmark.d.ts.map +1 -1
- package/dist/testing/storage-benchmark.js +0 -1
- package/dist/testing/storage-benchmark.js.map +1 -1
- package/dist/testing/storage-conformance.d.ts.map +1 -1
- package/dist/testing/storage-conformance.js +34 -2
- package/dist/testing/storage-conformance.js.map +1 -1
- package/dist/tools/bash.d.ts +28 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +70 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/edit-diff.d.ts +89 -0
- package/dist/tools/edit-diff.d.ts.map +1 -0
- package/dist/tools/edit-diff.js +386 -0
- package/dist/tools/edit-diff.js.map +1 -0
- package/dist/tools/edit.d.ts +18 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +117 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/env.d.ts +5 -0
- package/dist/tools/env.d.ts.map +1 -0
- package/dist/tools/env.js +7 -0
- package/dist/tools/env.js.map +1 -0
- package/dist/tools/file-mutation-queue.d.ts +9 -0
- package/dist/tools/file-mutation-queue.d.ts.map +1 -0
- package/dist/tools/file-mutation-queue.js +52 -0
- package/dist/tools/file-mutation-queue.js.map +1 -0
- package/dist/tools/image.d.ts +2 -0
- package/dist/tools/image.d.ts.map +1 -0
- package/dist/tools/image.js +92 -0
- package/dist/tools/image.js.map +1 -0
- package/dist/tools/index.d.ts +23 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +15 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/path-utils.d.ts +5 -0
- package/dist/tools/path-utils.d.ts.map +1 -0
- package/dist/tools/path-utils.js +26 -0
- package/dist/tools/path-utils.js.map +1 -0
- package/dist/tools/read.d.ts +17 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +101 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/write.d.ts +10 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +31 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/{env/utils/truncate.d.ts → truncate.d.ts} +1 -17
- package/dist/truncate.d.ts.map +1 -0
- package/dist/truncate.js +146 -0
- package/dist/truncate.js.map +1 -0
- package/dist/types.d.ts +97 -19
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +32 -14
- package/dist/env/utils/adaptive-publisher.d.ts +0 -24
- package/dist/env/utils/adaptive-publisher.d.ts.map +0 -1
- package/dist/env/utils/adaptive-publisher.js +0 -79
- package/dist/env/utils/adaptive-publisher.js.map +0 -1
- package/dist/env/utils/output-capture.d.ts +0 -31
- package/dist/env/utils/output-capture.d.ts.map +0 -1
- package/dist/env/utils/output-capture.js +0 -238
- package/dist/env/utils/output-capture.js.map +0 -1
- package/dist/env/utils/truncate.d.ts.map +0 -1
- package/dist/env/utils/truncate.js +0 -294
- package/dist/env/utils/truncate.js.map +0 -1
- package/dist/harness/config.d.ts +0 -20
- package/dist/harness/config.d.ts.map +0 -1
- package/dist/harness/config.js +0 -18
- package/dist/harness/config.js.map +0 -1
- package/docs/chord-delta-findings.md +0 -358
- package/docs/pico-v5-chord-usage.md +0 -373
- package/docs/pico-v5-handoff.md +0 -581
- package/docs/pico-v5.md +0 -3084
package/dist/harness/live.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live.js","sourceRoot":"","sources":["../../src/harness/live.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAqB5C,MAAM,CAAC,MAAM,OAAO,GAAG,SAAS,CAAY;IAC3C,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,CAAC;IACV,KAAK,EAAE,cAAc;IACrB,OAAO,EAAE,QAAQ;IACjB,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC;IACnB,gHAAgH;IAChH,iDAAiD;IACjD,cAAc,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,KAAK,SAAS;CACzD,CAAC,CAAC;AAEH,sDAAsD;AACtD,MAAM,cAAc,GAAwB,IAAI,GAAG,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC;AAEvE;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAC,EAAM,EAAE,IAAsB,EAAE,MAAc,EAAE,UAAgC;IACtG,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;QACjC,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM;YAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC,GAAG,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,CAAC,UAAU,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC3C,EAAe,EACf,MAAmD,EACnD,OAAyB;IAEzB,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;QAAE,OAAO;IAC7C,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;IAC1D,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,EAAE;QAAE,OAAO;IAC3C,MAAM,CACL,EAAE,EACF,IAAI,EACJ,MAAM,CAAC,EAAE,EACT,OAAO,CAAC,MAAM,KAAK,SAAS;QAC3B,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE;QAC5E,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CACnD,CAAC;AACH,CAAC","sourcesContent":["import type { Draft, JsonRepresentation, JsonValue } from \"@earendil-works/chord\";\nimport type { AssistantMessage } from \"@earendil-works/pi-ai\";\nimport { defineDoc } from \"../documents.ts\";\nimport type { Transaction } from \"../session/transaction.ts\";\nimport type { SubmissionId, SubmissionSettlement, TaskId, TaskRecord, Tx } from \"../types.ts\";\nimport type { SchedulerOutcome } from \"./scheduler.ts\";\n\n/** Built-in live conversation state: run control and presentation of the current generation. */\nexport type LiveState = {\n\t/** Run control: the task responsible for the run and its placed inputs; present exactly while busy. */\n\trun?: { taskId: TaskId; inputs: SubmissionId[] };\n\t/** Presentation of the current generation attempt. */\n\tgeneration?: {\n\t\tattempt: number;\n\t\t/** Committed throttled partial of the in-flight response. */\n\t\tmessage?: JsonRepresentation<AssistantMessage>;\n\t\t/** Durable backoff before the next attempt. */\n\t\tretry?: { at: number; error: string };\n\t\t/** Provider-side deferred response being polled. */\n\t\tdeferred?: { pollAt: number };\n\t};\n};\n\nexport const LiveDoc = defineDoc<LiveState>({\n\tkind: \"pi.live\",\n\tversion: 1,\n\tscope: \"conversation\",\n\thistory: \"latest\",\n\tfork: \"initial\",\n\tinitial: () => ({}),\n\t// A complete base whenever nothing is in flight, so the delta chain spans at most one generation, including its\n\t// retries and deferred polls, or one tool round.\n\tcheckpointWhen: (value) => value.generation === undefined,\n});\n\n/** Built-in task kinds that can own `pi.live.run`. */\nconst RUN_TASK_KINDS: ReadonlySet<string> = new Set([\"pi.generation\"]);\n\n/**\n * End the run owned by `taskId`: settle each of its inputs and remove `run`. Always removes `generation`, whose\n * presentation belongs to the ending task.\n */\nexport function endRun(tx: Tx, live: Draft<LiveState>, taskId: TaskId, settlement: SubmissionSettlement): void {\n\tif (live.run?.taskId === taskId) {\n\t\tfor (const id of live.run.inputs) tx.settleSubmission(id, settlement);\n\t\tdelete live.run;\n\t}\n\tdelete live.generation;\n}\n\n/**\n * Harness cleanup for a terminal outcome the scheduler writes itself (`faulted` or `orphaned`). Ignores non-run task\n * kinds so it never creates `pi.live` elsewhere. Committed partials are discarded without a transcript entry.\n */\nexport async function settleSchedulerOutcome(\n\ttx: Transaction,\n\trecord: TaskRecord<JsonValue, JsonValue, JsonValue>,\n\toutcome: SchedulerOutcome,\n): Promise<void> {\n\tif (!RUN_TASK_KINDS.has(record.kind)) return;\n\tconst live = await tx.doc(LiveDoc, record.conversationId);\n\tif (live.run?.taskId !== record.id) return;\n\tendRun(\n\t\ttx,\n\t\tlive,\n\t\trecord.id,\n\t\toutcome.status === \"faulted\"\n\t\t\t? { status: \"unanswered\", reason: \"faulted\", detail: outcome.error.message }\n\t\t\t: { status: \"unanswered\", reason: outcome.reason },\n\t);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"live.js","sourceRoot":"","sources":["../../src/harness/live.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAyDjD,MAAM,CAAC,MAAM,OAAO,GAAG,SAAS,CAAY;IAC3C,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,CAAC;IACV,KAAK,EAAE,cAAc;IACrB,OAAO,EAAE,QAAQ;IACjB,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC;IACnB,kHAAkH;IAClH,+GAA+G;IAC/G,iHAAiH;IACjH,+GAA+G;IAC/G,cAAc,EAAE,CAAC,KAAK,EAAE,EAAE,CACzB,KAAK,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC;CACjG,CAAC,CAAC;AAEH,sDAAsD;AACtD,MAAM,cAAc,GAAwB,IAAI,GAAG,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC;AACvE,MAAM,cAAc,GAAG,SAAS,CAAC;AACjC,MAAM,oBAAoB,GAAG,eAAe,CAAC;AAE7C;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAC,EAAM,EAAE,IAAsB,EAAE,MAAc,EAAE,UAAgC;IACtG,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;QACjC,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM;YAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC,GAAG,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,CAAC,UAAU,CAAC;IACvB,OAAO,IAAI,CAAC,KAAK,CAAC;AACnB,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,mBAAmB,CAAC,IAAsB,EAAE,MAAwB;IACnF,IAAI,CAAC,WAAW,KAAK,EAAE,CAAC;IACxB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,gBAAgB,CAAC,IAAsB,EAAE,MAAc;IACtE,OAAO,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;AACrE,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,sBAAsB,CAAC,IAAsB,EAAE,MAAc;IAC5E,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC;IAClC,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO;IACnC,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IACvE,IAAI,KAAK,IAAI,CAAC;QAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC1C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,WAAW,CAAC;AACpD,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,QAAQ,CAAC,IAAsB,EAAE,MAAc;IAC9D,OAAO,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;AAC3D,CAAC;AAED,4GAA4G;AAC5G,MAAM,UAAU,UAAU,CAAC,IAAqB,EAAE,KAA0B;IAC3E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACrB,IAAI,KAAK,KAAK,SAAS;QAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IAC5C,aAAa,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,aAAa,CAAC,IAAqB;IAClD,OAAO,IAAI,CAAC,MAAM,CAAC;IACnB,OAAO,IAAI,CAAC,YAAY,CAAC;IACzB,OAAO,IAAI,CAAC,YAAY,CAAC;IACzB,OAAO,IAAI,CAAC,OAAO,CAAC;IACpB,OAAO,IAAI,CAAC,WAAW,CAAC;AACzB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC3C,EAAe,EACf,MAAmD,EACnD,OAAyB;IAEzB,IAAI,MAAM,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QAC/E,IAAI,IAAI,KAAK,SAAS;YAAE,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACpD,OAAO;IACR,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,oBAAoB,EAAE,CAAC;QAC1C,sBAAsB,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QAChF,OAAO;IACR,CAAC;IACD,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;QAAE,OAAO;IAC7C,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;IAC1D,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,EAAE;QAAE,OAAO;IAC3C,MAAM,cAAc,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;IACtD,MAAM,CACL,EAAE,EACF,IAAI,EACJ,MAAM,CAAC,EAAE,EACT,OAAO,CAAC,MAAM,KAAK,SAAS;QAC3B,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE;QAC5E,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CACnD,CAAC;AACH,CAAC","sourcesContent":["import type { Draft, JsonRepresentation, JsonValue } from \"@earendil-works/chord\";\nimport type { AssistantMessage } from \"@earendil-works/pi-ai\";\nimport { defineDoc } from \"../documents.ts\";\nimport type { Transaction } from \"../session/transaction.ts\";\nimport type { EntryId, SubmissionId, SubmissionSettlement, TaskId, TaskRecord, Tx } from \"../types.ts\";\nimport { convertPartial } from \"./generation.ts\";\nimport type { SchedulerOutcome } from \"./scheduler.ts\";\nimport type { CompactionReason, CompactionResult, ToolDiagnostic } from \"./types.ts\";\n\n/** Presentation of one tool call of the current round. */\nexport type ToolSlot = {\n\tcallId: string;\n\tname: string;\n\t/**\n\t * Absent for a call not started yet (sequential round) and for a call its request did not offer, which starts `done`\n\t * with the `entry` generation wrote.\n\t */\n\ttaskId?: TaskId;\n\tstatus: \"pending\" | \"running\" | \"done\";\n\t/** Retained running output and what the bounds dropped. */\n\toutput?: string;\n\tdroppedBytes?: number;\n\tdroppedLines?: number;\n\t/** Last `details()` value. */\n\tdetails?: JsonValue;\n\t/** Diagnostics recorded through `api.diagnostic()`. */\n\tdiagnostics?: ToolDiagnostic[];\n\t/** Result entry once done; absent when the tool task faulted or was orphaned. */\n\tentry?: EntryId;\n};\n\n/** Presentation of one live compaction task (spec §8.7). */\nexport type CompactionStatus = {\n\ttaskId: TaskId<CompactionResult>;\n\treason: CompactionReason;\n\t/** Whether a generation waits for it: a compaction the generation owns. */\n\tblocking: boolean;\n\tattempt: number;\n\t/** Durable backoff before the next summarization attempt. */\n\tretry?: { at: number; error: string };\n};\n\n/** Built-in live conversation state: run control and presentation of the current generation and tool round. */\nexport type LiveState = {\n\t/** Run control: the task that settles the run's inputs, and those inputs; present exactly while busy. */\n\trun?: { taskId: TaskId; inputs: SubmissionId[] };\n\t/** Presentation of the current generation attempt. */\n\tgeneration?: {\n\t\tattempt: number;\n\t\t/** Committed throttled partial of the in-flight response. */\n\t\tmessage?: JsonRepresentation<AssistantMessage>;\n\t\t/** Durable backoff before the next attempt. */\n\t\tretry?: { at: number; error: string };\n\t\t/** Provider-side deferred response being polled. */\n\t\tdeferred?: { pollAt: number };\n\t};\n\t/** The current tool round in call order, from the tool-calling answer until the generation's `tools` phase ends it. */\n\ttools?: ToolSlot[];\n\t/** Live compaction tasks in task ID order; absent when none. */\n\tcompactions?: CompactionStatus[];\n};\n\nexport const LiveDoc = defineDoc<LiveState>({\n\tkind: \"pi.live\",\n\tversion: 1,\n\tscope: \"conversation\",\n\thistory: \"latest\",\n\tfork: \"initial\",\n\tinitial: () => ({}),\n\t// REMINDER: a complete base whenever nothing runs (spec §8.2): no generation and no running tool slot. That holds\n\t// while idle, in the commit handing a generation over to its tool round, and between tools, so the delta chain\n\t// spans at most one generation or the overlapping execution of one round's tools. A slot holds output only while\n\t// running, so every base is small. Do not add a delta-count bound; the tool output benchmark checks this rule.\n\tcheckpointWhen: (value) =>\n\t\tvalue.generation === undefined && !(value.tools ?? []).some((slot) => slot.status === \"running\"),\n});\n\n/** Built-in task kinds that can own `pi.live.run`. */\nconst RUN_TASK_KINDS: ReadonlySet<string> = new Set([\"pi.generation\"]);\nconst TOOL_TASK_KIND = \"pi.tool\";\nconst COMPACTION_TASK_KIND = \"pi.compaction\";\n\n/**\n * End the run owned by `taskId`: settle each of its inputs and remove `run`. Always removes `generation` and `tools`,\n * whose presentation belongs to the ending run.\n */\nexport function endRun(tx: Tx, live: Draft<LiveState>, taskId: TaskId, settlement: SubmissionSettlement): void {\n\tif (live.run?.taskId === taskId) {\n\t\tfor (const id of live.run.inputs) tx.settleSubmission(id, settlement);\n\t\tdelete live.run;\n\t}\n\tdelete live.generation;\n\tdelete live.tools;\n}\n\n/** Add the status of a compaction task created in this commit; statuses stay in task ID order. */\nexport function addCompactionStatus(live: Draft<LiveState>, status: CompactionStatus): void {\n\tlive.compactions ??= [];\n\tlive.compactions.push(status);\n}\n\n/** The status of compaction task `taskId`, if listed. */\nexport function compactionStatus(live: Draft<LiveState>, taskId: TaskId): Draft<CompactionStatus> | undefined {\n\treturn live.compactions?.find((status) => status.taskId === taskId);\n}\n\n/** Remove the status of compaction task `taskId`, and the list once empty. */\nexport function removeCompactionStatus(live: Draft<LiveState>, taskId: TaskId): void {\n\tconst statuses = live.compactions;\n\tif (statuses === undefined) return;\n\tconst index = statuses.findIndex((status) => status.taskId === taskId);\n\tif (index >= 0) statuses.splice(index, 1);\n\tif (statuses.length === 0) delete live.compactions;\n}\n\n/** The slot of tool task `taskId` in the current round, if the round still lists it. */\nexport function toolSlot(live: Draft<LiveState>, taskId: TaskId): Draft<ToolSlot> | undefined {\n\treturn live.tools?.find((slot) => slot.taskId === taskId);\n}\n\n/** Mark a slot done: the result entry, if any, now carries its running output, details, and diagnostics. */\nexport function finishSlot(slot: Draft<ToolSlot>, entry: EntryId | undefined): void {\n\tslot.status = \"done\";\n\tif (entry !== undefined) slot.entry = entry;\n\tclearProgress(slot);\n}\n\n/** Remove what a tool published while running; its result entry or a rerun replaces it. */\nexport function clearProgress(slot: Draft<ToolSlot>): void {\n\tdelete slot.output;\n\tdelete slot.droppedBytes;\n\tdelete slot.droppedLines;\n\tdelete slot.details;\n\tdelete slot.diagnostics;\n}\n\n/**\n * Harness cleanup for a terminal outcome the scheduler writes itself (`faulted` or `orphaned`). A run task ends its\n * run; a tool task's slot is marked done without an entry, and context derivation synthesizes the missing result; a\n * compaction task's status is removed.\n * Ignores other kinds so it never creates `pi.live` elsewhere. The scheduler calls this without knowing task kinds;\n * the Harness passes it in (spec §5.4).\n * REMINDER: a committed generation partial becomes an aborted assistant entry here, exactly as in the generation abort\n * handler, so the transcript keeps what the model produced and `pi.usage` counts its spend. The scheduler's commit has\n * no task scope, so that entry has no `byTaskId`. Faults come from task bugs\n * or malformed provider data (a non-JSON value in a response), or a commit the Storage rejected without effect; an\n * uncertain storage failure poisons the Session instead and writes no outcome.\n */\nexport async function settleSchedulerOutcome(\n\ttx: Transaction,\n\trecord: TaskRecord<JsonValue, JsonValue, JsonValue>,\n\toutcome: SchedulerOutcome,\n): Promise<void> {\n\tif (record.kind === TOOL_TASK_KIND) {\n\t\tconst slot = toolSlot(await tx.doc(LiveDoc, record.conversationId), record.id);\n\t\tif (slot !== undefined) finishSlot(slot, undefined);\n\t\treturn;\n\t}\n\tif (record.kind === COMPACTION_TASK_KIND) {\n\t\tremoveCompactionStatus(await tx.doc(LiveDoc, record.conversationId), record.id);\n\t\treturn;\n\t}\n\tif (!RUN_TASK_KINDS.has(record.kind)) return;\n\tconst live = await tx.doc(LiveDoc, record.conversationId);\n\tif (live.run?.taskId !== record.id) return;\n\tawait convertPartial(tx, live, record.conversationId);\n\tendRun(\n\t\ttx,\n\t\tlive,\n\t\trecord.id,\n\t\toutcome.status === \"faulted\"\n\t\t\t? { status: \"unanswered\", reason: \"faulted\", detail: outcome.error.message }\n\t\t\t: { status: \"unanswered\", reason: outcome.reason },\n\t);\n}\n"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** Retention limits of one tool's output. */
|
|
2
|
+
export type OutputLimits = {
|
|
3
|
+
readonly maxBytes: number;
|
|
4
|
+
readonly maxLines: number;
|
|
5
|
+
readonly retain: "head" | "tail";
|
|
6
|
+
};
|
|
7
|
+
/** Retained output and what the limits dropped. */
|
|
8
|
+
export type BoundedOutput = {
|
|
9
|
+
readonly text: string;
|
|
10
|
+
readonly droppedBytes: number;
|
|
11
|
+
readonly droppedLines: number;
|
|
12
|
+
};
|
|
13
|
+
/** An exact slice of the input within the limits, and what it left out. */
|
|
14
|
+
export type OutputSlice = {
|
|
15
|
+
readonly text: string;
|
|
16
|
+
readonly bytes: number;
|
|
17
|
+
readonly droppedBytes: number;
|
|
18
|
+
readonly droppedLines: number;
|
|
19
|
+
};
|
|
20
|
+
/** Remove control characters that break display and transcripts; tabs and newlines stay. */
|
|
21
|
+
export declare function sanitizeOutput(text: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Bound `text` to whole lines within the limits: the first lines for `head`, the last lines for `tail`. The result is an
|
|
24
|
+
* exact slice, trailing newline included. A single line longer than `maxBytes` is cut at the byte limit on a character
|
|
25
|
+
* boundary.
|
|
26
|
+
*/
|
|
27
|
+
export declare function boundOutput(text: string, limits: OutputLimits): OutputSlice;
|
|
28
|
+
/** The last character boundary at or before `index`. */
|
|
29
|
+
export declare function characterEnd(bytes: Uint8Array, index: number): number;
|
|
30
|
+
/**
|
|
31
|
+
* Bounded running output of one tool call. Accepting a chunk costs time proportional to the chunk: head retention stops
|
|
32
|
+
* storing once the window is full, and tail retention drops stored text the window no longer needs when it snapshots.
|
|
33
|
+
* Counts of the whole stream are kept so the dropped totals stay exact.
|
|
34
|
+
*/
|
|
35
|
+
export declare class OutputBuffer {
|
|
36
|
+
#private;
|
|
37
|
+
constructor(limits: OutputLimits);
|
|
38
|
+
/** Bytes currently held; bounded by the limits plus one chunk. */
|
|
39
|
+
get storedBytes(): number;
|
|
40
|
+
/** Accept a chunk; returns whether anything was accepted. */
|
|
41
|
+
push(chunk: string | Uint8Array): boolean;
|
|
42
|
+
/** Flush an incomplete trailing character as a replacement character; call when the stream ends. */
|
|
43
|
+
end(): void;
|
|
44
|
+
/** Retained, sanitized output and what the limits dropped from the whole stream. */
|
|
45
|
+
snapshot(): BoundedOutput;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Adaptive progress commits, like the environment's shell output capture: the first change after an idle period
|
|
49
|
+
* commits at once; each commit then delays the next by at least 100 ms and by its written size at 100 KiB/s. At most
|
|
50
|
+
* one commit is in flight; changes made meanwhile coalesce into the next one.
|
|
51
|
+
*/
|
|
52
|
+
export declare class Progress {
|
|
53
|
+
#private;
|
|
54
|
+
constructor(write: () => Promise<number>, onError: (error: unknown) => void);
|
|
55
|
+
/** Schedule a commit. */
|
|
56
|
+
mark(): void;
|
|
57
|
+
/** Schedule a commit; the promise settles with the commit that includes this change. */
|
|
58
|
+
markAndWait(): Promise<void>;
|
|
59
|
+
/** Stop committing and wait for the commit in flight; returns the waiters the final commit must settle. */
|
|
60
|
+
stop(): Promise<PromiseWithResolvers<void>[]>;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/harness/output.ts"],"names":[],"mappings":"AAEA,6CAA6C;AAC7C,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAEtH,mDAAmD;AACnD,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpH,2EAA2E;AAC3E,MAAM,MAAM,WAAW,GAAG;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC9B,CAAC;AAOF,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,WAAW,CAU3E;AA0CD,wDAAwD;AACxD,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAIrE;AAgBD;;;;GAIG;AACH,qBAAa,YAAY;;IAYxB,YAAY,MAAM,EAAE,YAAY,EAE/B;IAED,kEAAkE;IAClE,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,6DAA6D;IAC7D,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAKxC;IAED,oGAAoG;IACpG,GAAG,IAAI,IAAI,CAEV;IAgCD,oFAAoF;IACpF,QAAQ,IAAI,aAAa,CAmBxB;CACD;AAiBD;;;;GAIG;AACH,qBAAa,QAAQ;;IAUpB,YAAY,KAAK,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,EAG1E;IAED,yBAAyB;IACzB,IAAI,IAAI,IAAI,CAGX;IAED,wFAAwF;IACxF,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAK3B;IAED,2GAA2G;IACrG,IAAI,IAAI,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC,CAMlD;CAmCD"}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { utf8ByteLength } from "../truncate.js";
|
|
2
|
+
const NEWLINE = 0x0a;
|
|
3
|
+
const INVALID_OUTPUT = /[\x00-\x08\x0b-\x1f\ufff9-\ufffb]/g;
|
|
4
|
+
const encoder = new TextEncoder();
|
|
5
|
+
const decoder = new TextDecoder();
|
|
6
|
+
/** Remove control characters that break display and transcripts; tabs and newlines stay. */
|
|
7
|
+
export function sanitizeOutput(text) {
|
|
8
|
+
return text.replace(INVALID_OUTPUT, "");
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Bound `text` to whole lines within the limits: the first lines for `head`, the last lines for `tail`. The result is an
|
|
12
|
+
* exact slice, trailing newline included. A single line longer than `maxBytes` is cut at the byte limit on a character
|
|
13
|
+
* boundary.
|
|
14
|
+
*/
|
|
15
|
+
export function boundOutput(text, limits) {
|
|
16
|
+
const bytes = encoder.encode(text);
|
|
17
|
+
const [from, to] = limits.retain === "head" ? headRange(bytes, limits) : tailRange(bytes, limits);
|
|
18
|
+
const kept = bytes.subarray(from, to);
|
|
19
|
+
return {
|
|
20
|
+
text: kept.length === bytes.length ? text : decoder.decode(kept),
|
|
21
|
+
bytes: kept.length,
|
|
22
|
+
droppedBytes: bytes.length - kept.length,
|
|
23
|
+
droppedLines: lineCount(bytes) - lineCount(kept),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function headRange(bytes, limits) {
|
|
27
|
+
if (limits.maxLines === 0 || limits.maxBytes === 0)
|
|
28
|
+
return [0, 0];
|
|
29
|
+
let end = bytes.length;
|
|
30
|
+
let lines = 0;
|
|
31
|
+
for (let index = bytes.indexOf(NEWLINE); index !== -1; index = bytes.indexOf(NEWLINE, index + 1)) {
|
|
32
|
+
if (++lines === limits.maxLines) {
|
|
33
|
+
end = index + 1;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (end > limits.maxBytes) {
|
|
38
|
+
const newline = bytes.lastIndexOf(NEWLINE, limits.maxBytes - 1);
|
|
39
|
+
end = newline === -1 ? characterEnd(bytes, limits.maxBytes) : newline + 1;
|
|
40
|
+
}
|
|
41
|
+
return [0, end];
|
|
42
|
+
}
|
|
43
|
+
function tailRange(bytes, limits) {
|
|
44
|
+
if (limits.maxLines === 0 || limits.maxBytes === 0)
|
|
45
|
+
return [bytes.length, bytes.length];
|
|
46
|
+
// A trailing newline ends the last line rather than starting another.
|
|
47
|
+
const last = bytes[bytes.length - 1] === NEWLINE ? bytes.length - 2 : bytes.length - 1;
|
|
48
|
+
let start = 0;
|
|
49
|
+
let lines = 1;
|
|
50
|
+
for (let index = last < 0 ? -1 : bytes.lastIndexOf(NEWLINE, last); index !== -1;) {
|
|
51
|
+
if (lines === limits.maxLines) {
|
|
52
|
+
start = index + 1;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
lines++;
|
|
56
|
+
index = index === 0 ? -1 : bytes.lastIndexOf(NEWLINE, index - 1);
|
|
57
|
+
}
|
|
58
|
+
if (bytes.length - start > limits.maxBytes) {
|
|
59
|
+
const from = bytes.length - limits.maxBytes;
|
|
60
|
+
const newline = bytes.indexOf(NEWLINE, from - 1);
|
|
61
|
+
// The first line starting inside the byte window, or a cut of the last line when it alone is too long.
|
|
62
|
+
start = newline !== -1 && newline + 1 < bytes.length ? newline + 1 : characterStart(bytes, from);
|
|
63
|
+
}
|
|
64
|
+
return [start, bytes.length];
|
|
65
|
+
}
|
|
66
|
+
/** The last character boundary at or before `index`. */
|
|
67
|
+
export function characterEnd(bytes, index) {
|
|
68
|
+
let end = index;
|
|
69
|
+
while (end > 0 && ((bytes[end] ?? 0) & 0xc0) === 0x80)
|
|
70
|
+
end--;
|
|
71
|
+
return end;
|
|
72
|
+
}
|
|
73
|
+
/** The first character boundary at or after `index`. */
|
|
74
|
+
function characterStart(bytes, index) {
|
|
75
|
+
let start = index;
|
|
76
|
+
while (start < bytes.length && ((bytes[start] ?? 0) & 0xc0) === 0x80)
|
|
77
|
+
start++;
|
|
78
|
+
return start;
|
|
79
|
+
}
|
|
80
|
+
function lineCount(bytes) {
|
|
81
|
+
if (bytes.length === 0)
|
|
82
|
+
return 0;
|
|
83
|
+
let newlines = 0;
|
|
84
|
+
for (let index = bytes.indexOf(NEWLINE); index !== -1; index = bytes.indexOf(NEWLINE, index + 1))
|
|
85
|
+
newlines++;
|
|
86
|
+
return newlines + (bytes[bytes.length - 1] === NEWLINE ? 0 : 1);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Bounded running output of one tool call. Accepting a chunk costs time proportional to the chunk: head retention stops
|
|
90
|
+
* storing once the window is full, and tail retention drops stored text the window no longer needs when it snapshots.
|
|
91
|
+
* Counts of the whole stream are kept so the dropped totals stay exact.
|
|
92
|
+
*/
|
|
93
|
+
export class OutputBuffer {
|
|
94
|
+
#limits;
|
|
95
|
+
#decoder = new TextDecoder();
|
|
96
|
+
/** Stored chunks: for head the start of the stream, for tail a suffix that still contains the next window. */
|
|
97
|
+
#chunks = [];
|
|
98
|
+
#storedBytes = 0;
|
|
99
|
+
#storedNewlines = 0;
|
|
100
|
+
#full = false;
|
|
101
|
+
#totalBytes = 0;
|
|
102
|
+
#totalNewlines = 0;
|
|
103
|
+
#endsWithNewline = true;
|
|
104
|
+
constructor(limits) {
|
|
105
|
+
this.#limits = limits;
|
|
106
|
+
}
|
|
107
|
+
/** Bytes currently held; bounded by the limits plus one chunk. */
|
|
108
|
+
get storedBytes() {
|
|
109
|
+
return this.#storedBytes;
|
|
110
|
+
}
|
|
111
|
+
/** Accept a chunk; returns whether anything was accepted. */
|
|
112
|
+
push(chunk) {
|
|
113
|
+
// Bytes of an incomplete character from an earlier byte chunk come first.
|
|
114
|
+
const text = typeof chunk === "string" ? this.#decoder.decode() + chunk : this.#decoder.decode(chunk, { stream: true });
|
|
115
|
+
return this.#accept(text);
|
|
116
|
+
}
|
|
117
|
+
/** Flush an incomplete trailing character as a replacement character; call when the stream ends. */
|
|
118
|
+
end() {
|
|
119
|
+
this.#accept(this.#decoder.decode());
|
|
120
|
+
}
|
|
121
|
+
#accept(text) {
|
|
122
|
+
if (text.length === 0)
|
|
123
|
+
return false;
|
|
124
|
+
const bytes = utf8ByteLength(text);
|
|
125
|
+
const newlines = countNewlines(text);
|
|
126
|
+
this.#totalBytes += bytes;
|
|
127
|
+
this.#totalNewlines += newlines;
|
|
128
|
+
this.#endsWithNewline = text.endsWith("\n");
|
|
129
|
+
if (this.#full)
|
|
130
|
+
return true;
|
|
131
|
+
this.#chunks.push({ text, bytes, newlines });
|
|
132
|
+
this.#storedBytes += bytes;
|
|
133
|
+
this.#storedNewlines += newlines;
|
|
134
|
+
if (this.#limits.retain === "head") {
|
|
135
|
+
// Nothing past a full window is ever needed.
|
|
136
|
+
this.#full = this.#storedBytes > this.#limits.maxBytes || this.#storedNewlines >= this.#limits.maxLines;
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
// Drop leading chunks while the rest still holds more than a window: more than `maxBytes` bytes or `maxLines`
|
|
140
|
+
// newlines, plus one, so the window's line start can still be found. Each chunk is dropped once.
|
|
141
|
+
while (this.#chunks.length > 1) {
|
|
142
|
+
const first = this.#chunks[0];
|
|
143
|
+
const bytesAfter = this.#storedBytes - first.bytes;
|
|
144
|
+
const newlinesAfter = this.#storedNewlines - first.newlines;
|
|
145
|
+
if (bytesAfter <= this.#limits.maxBytes + 1 && newlinesAfter <= this.#limits.maxLines + 1)
|
|
146
|
+
break;
|
|
147
|
+
this.#chunks.shift();
|
|
148
|
+
this.#storedBytes = bytesAfter;
|
|
149
|
+
this.#storedNewlines = newlinesAfter;
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
/** Retained, sanitized output and what the limits dropped from the whole stream. */
|
|
154
|
+
snapshot() {
|
|
155
|
+
const stored = this.#chunks.length === 1 ? this.#chunks[0].text : this.#chunks.map((chunk) => chunk.text).join("");
|
|
156
|
+
const kept = boundOutput(stored, this.#limits);
|
|
157
|
+
const storedLines = lines(this.#storedNewlines, stored === "" || stored.endsWith("\n"));
|
|
158
|
+
const keptLines = storedLines - kept.droppedLines;
|
|
159
|
+
// Tail windows never reach back before this one, so only the kept slice needs storing.
|
|
160
|
+
if (this.#limits.retain === "tail" || this.#chunks.length > 1) {
|
|
161
|
+
const text = this.#limits.retain === "tail" ? kept.text : stored;
|
|
162
|
+
const bytes = this.#limits.retain === "tail" ? kept.bytes : this.#storedBytes;
|
|
163
|
+
this.#chunks = text === "" ? [] : [{ text, bytes, newlines: countNewlines(text) }];
|
|
164
|
+
this.#storedBytes = bytes;
|
|
165
|
+
this.#storedNewlines = this.#chunks[0]?.newlines ?? 0;
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
text: sanitizeOutput(kept.text),
|
|
169
|
+
droppedBytes: this.#totalBytes - kept.bytes,
|
|
170
|
+
droppedLines: lines(this.#totalNewlines, this.#endsWithNewline) - keptLines,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/** Lines of text with `newlines` newlines; a final unterminated line counts. */
|
|
175
|
+
function lines(newlines, terminated) {
|
|
176
|
+
return newlines + (terminated ? 0 : 1);
|
|
177
|
+
}
|
|
178
|
+
function countNewlines(text) {
|
|
179
|
+
let count = 0;
|
|
180
|
+
for (let index = text.indexOf("\n"); index !== -1; index = text.indexOf("\n", index + 1))
|
|
181
|
+
count++;
|
|
182
|
+
return count;
|
|
183
|
+
}
|
|
184
|
+
/** Minimum pause between progress commits; each commit also buys a pause proportional to what it wrote. */
|
|
185
|
+
const MIN_PROGRESS_INTERVAL_MS = 100;
|
|
186
|
+
const PROGRESS_BYTES_PER_SECOND = 100 * 1024;
|
|
187
|
+
/**
|
|
188
|
+
* Adaptive progress commits, like the environment's shell output capture: the first change after an idle period
|
|
189
|
+
* commits at once; each commit then delays the next by at least 100 ms and by its written size at 100 KiB/s. At most
|
|
190
|
+
* one commit is in flight; changes made meanwhile coalesce into the next one.
|
|
191
|
+
*/
|
|
192
|
+
export class Progress {
|
|
193
|
+
#write;
|
|
194
|
+
#onError;
|
|
195
|
+
#waiters = [];
|
|
196
|
+
#timer;
|
|
197
|
+
#inFlight;
|
|
198
|
+
#nextAt = 0;
|
|
199
|
+
#dirty = false;
|
|
200
|
+
#stopped = false;
|
|
201
|
+
constructor(write, onError) {
|
|
202
|
+
this.#write = write;
|
|
203
|
+
this.#onError = onError;
|
|
204
|
+
}
|
|
205
|
+
/** Schedule a commit. */
|
|
206
|
+
mark() {
|
|
207
|
+
this.#dirty = true;
|
|
208
|
+
this.#schedule();
|
|
209
|
+
}
|
|
210
|
+
/** Schedule a commit; the promise settles with the commit that includes this change. */
|
|
211
|
+
markAndWait() {
|
|
212
|
+
const waiter = Promise.withResolvers();
|
|
213
|
+
this.#waiters.push(waiter);
|
|
214
|
+
this.mark();
|
|
215
|
+
return waiter.promise;
|
|
216
|
+
}
|
|
217
|
+
/** Stop committing and wait for the commit in flight; returns the waiters the final commit must settle. */
|
|
218
|
+
async stop() {
|
|
219
|
+
this.#stopped = true;
|
|
220
|
+
clearTimeout(this.#timer);
|
|
221
|
+
this.#timer = undefined;
|
|
222
|
+
await this.#inFlight;
|
|
223
|
+
return this.#waiters.splice(0);
|
|
224
|
+
}
|
|
225
|
+
#schedule() {
|
|
226
|
+
if (this.#stopped || this.#timer !== undefined || this.#inFlight !== undefined)
|
|
227
|
+
return;
|
|
228
|
+
const wait = this.#nextAt - Date.now();
|
|
229
|
+
if (wait <= 0)
|
|
230
|
+
this.#flush();
|
|
231
|
+
else
|
|
232
|
+
this.#timer = setTimeout(() => {
|
|
233
|
+
this.#timer = undefined;
|
|
234
|
+
this.#flush();
|
|
235
|
+
}, wait);
|
|
236
|
+
}
|
|
237
|
+
#flush() {
|
|
238
|
+
if (this.#stopped || !this.#dirty)
|
|
239
|
+
return;
|
|
240
|
+
this.#dirty = false;
|
|
241
|
+
const waiters = this.#waiters.splice(0);
|
|
242
|
+
const started = Date.now();
|
|
243
|
+
this.#inFlight = this.#write()
|
|
244
|
+
.then((bytes) => {
|
|
245
|
+
this.#nextAt = started + Math.max(MIN_PROGRESS_INTERVAL_MS, (bytes * 1000) / PROGRESS_BYTES_PER_SECOND);
|
|
246
|
+
for (const waiter of waiters)
|
|
247
|
+
waiter.resolve();
|
|
248
|
+
}, (error) => {
|
|
249
|
+
this.#nextAt = started + MIN_PROGRESS_INTERVAL_MS;
|
|
250
|
+
for (const waiter of waiters)
|
|
251
|
+
waiter.reject(error);
|
|
252
|
+
this.#onError(error);
|
|
253
|
+
})
|
|
254
|
+
.finally(() => {
|
|
255
|
+
this.#inFlight = undefined;
|
|
256
|
+
if (this.#dirty)
|
|
257
|
+
this.#schedule();
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/harness/output.ts"],"names":[],"mappings":"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","sourcesContent":["import { utf8ByteLength } from \"../truncate.ts\";\n\n/** Retention limits of one tool's output. */\nexport type OutputLimits = { readonly maxBytes: number; readonly maxLines: number; readonly retain: \"head\" | \"tail\" };\n\n/** Retained output and what the limits dropped. */\nexport type BoundedOutput = { readonly text: string; readonly droppedBytes: number; readonly droppedLines: number };\n\n/** An exact slice of the input within the limits, and what it left out. */\nexport type OutputSlice = {\n\treadonly text: string;\n\treadonly bytes: number;\n\treadonly droppedBytes: number;\n\treadonly droppedLines: number;\n};\n\nconst NEWLINE = 0x0a;\nconst INVALID_OUTPUT = /[\\x00-\\x08\\x0b-\\x1f\\ufff9-\\ufffb]/g;\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/** Remove control characters that break display and transcripts; tabs and newlines stay. */\nexport function sanitizeOutput(text: string): string {\n\treturn text.replace(INVALID_OUTPUT, \"\");\n}\n\n/**\n * Bound `text` to whole lines within the limits: the first lines for `head`, the last lines for `tail`. The result is an\n * exact slice, trailing newline included. A single line longer than `maxBytes` is cut at the byte limit on a character\n * boundary.\n */\nexport function boundOutput(text: string, limits: OutputLimits): OutputSlice {\n\tconst bytes = encoder.encode(text);\n\tconst [from, to] = limits.retain === \"head\" ? headRange(bytes, limits) : tailRange(bytes, limits);\n\tconst kept = bytes.subarray(from, to);\n\treturn {\n\t\ttext: kept.length === bytes.length ? text : decoder.decode(kept),\n\t\tbytes: kept.length,\n\t\tdroppedBytes: bytes.length - kept.length,\n\t\tdroppedLines: lineCount(bytes) - lineCount(kept),\n\t};\n}\n\nfunction headRange(bytes: Uint8Array, limits: OutputLimits): [number, number] {\n\tif (limits.maxLines === 0 || limits.maxBytes === 0) return [0, 0];\n\tlet end = bytes.length;\n\tlet lines = 0;\n\tfor (let index = bytes.indexOf(NEWLINE); index !== -1; index = bytes.indexOf(NEWLINE, index + 1)) {\n\t\tif (++lines === limits.maxLines) {\n\t\t\tend = index + 1;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (end > limits.maxBytes) {\n\t\tconst newline = bytes.lastIndexOf(NEWLINE, limits.maxBytes - 1);\n\t\tend = newline === -1 ? characterEnd(bytes, limits.maxBytes) : newline + 1;\n\t}\n\treturn [0, end];\n}\n\nfunction tailRange(bytes: Uint8Array, limits: OutputLimits): [number, number] {\n\tif (limits.maxLines === 0 || limits.maxBytes === 0) return [bytes.length, bytes.length];\n\t// A trailing newline ends the last line rather than starting another.\n\tconst last = bytes[bytes.length - 1] === NEWLINE ? bytes.length - 2 : bytes.length - 1;\n\tlet start = 0;\n\tlet lines = 1;\n\tfor (let index = last < 0 ? -1 : bytes.lastIndexOf(NEWLINE, last); index !== -1; ) {\n\t\tif (lines === limits.maxLines) {\n\t\t\tstart = index + 1;\n\t\t\tbreak;\n\t\t}\n\t\tlines++;\n\t\tindex = index === 0 ? -1 : bytes.lastIndexOf(NEWLINE, index - 1);\n\t}\n\tif (bytes.length - start > limits.maxBytes) {\n\t\tconst from = bytes.length - limits.maxBytes;\n\t\tconst newline = bytes.indexOf(NEWLINE, from - 1);\n\t\t// The first line starting inside the byte window, or a cut of the last line when it alone is too long.\n\t\tstart = newline !== -1 && newline + 1 < bytes.length ? newline + 1 : characterStart(bytes, from);\n\t}\n\treturn [start, bytes.length];\n}\n\n/** The last character boundary at or before `index`. */\nexport function characterEnd(bytes: Uint8Array, index: number): number {\n\tlet end = index;\n\twhile (end > 0 && ((bytes[end] ?? 0) & 0xc0) === 0x80) end--;\n\treturn end;\n}\n\n/** The first character boundary at or after `index`. */\nfunction characterStart(bytes: Uint8Array, index: number): number {\n\tlet start = index;\n\twhile (start < bytes.length && ((bytes[start] ?? 0) & 0xc0) === 0x80) start++;\n\treturn start;\n}\n\nfunction lineCount(bytes: Uint8Array): number {\n\tif (bytes.length === 0) return 0;\n\tlet newlines = 0;\n\tfor (let index = bytes.indexOf(NEWLINE); index !== -1; index = bytes.indexOf(NEWLINE, index + 1)) newlines++;\n\treturn newlines + (bytes[bytes.length - 1] === NEWLINE ? 0 : 1);\n}\n\n/**\n * Bounded running output of one tool call. Accepting a chunk costs time proportional to the chunk: head retention stops\n * storing once the window is full, and tail retention drops stored text the window no longer needs when it snapshots.\n * Counts of the whole stream are kept so the dropped totals stay exact.\n */\nexport class OutputBuffer {\n\treadonly #limits: OutputLimits;\n\treadonly #decoder = new TextDecoder();\n\t/** Stored chunks: for head the start of the stream, for tail a suffix that still contains the next window. */\n\t#chunks: { readonly text: string; readonly bytes: number; readonly newlines: number }[] = [];\n\t#storedBytes = 0;\n\t#storedNewlines = 0;\n\t#full = false;\n\t#totalBytes = 0;\n\t#totalNewlines = 0;\n\t#endsWithNewline = true;\n\n\tconstructor(limits: OutputLimits) {\n\t\tthis.#limits = limits;\n\t}\n\n\t/** Bytes currently held; bounded by the limits plus one chunk. */\n\tget storedBytes(): number {\n\t\treturn this.#storedBytes;\n\t}\n\n\t/** Accept a chunk; returns whether anything was accepted. */\n\tpush(chunk: string | Uint8Array): boolean {\n\t\t// Bytes of an incomplete character from an earlier byte chunk come first.\n\t\tconst text =\n\t\t\ttypeof chunk === \"string\" ? this.#decoder.decode() + chunk : this.#decoder.decode(chunk, { stream: true });\n\t\treturn this.#accept(text);\n\t}\n\n\t/** Flush an incomplete trailing character as a replacement character; call when the stream ends. */\n\tend(): void {\n\t\tthis.#accept(this.#decoder.decode());\n\t}\n\n\t#accept(text: string): boolean {\n\t\tif (text.length === 0) return false;\n\t\tconst bytes = utf8ByteLength(text);\n\t\tconst newlines = countNewlines(text);\n\t\tthis.#totalBytes += bytes;\n\t\tthis.#totalNewlines += newlines;\n\t\tthis.#endsWithNewline = text.endsWith(\"\\n\");\n\t\tif (this.#full) return true;\n\t\tthis.#chunks.push({ text, bytes, newlines });\n\t\tthis.#storedBytes += bytes;\n\t\tthis.#storedNewlines += newlines;\n\t\tif (this.#limits.retain === \"head\") {\n\t\t\t// Nothing past a full window is ever needed.\n\t\t\tthis.#full = this.#storedBytes > this.#limits.maxBytes || this.#storedNewlines >= this.#limits.maxLines;\n\t\t\treturn true;\n\t\t}\n\t\t// Drop leading chunks while the rest still holds more than a window: more than `maxBytes` bytes or `maxLines`\n\t\t// newlines, plus one, so the window's line start can still be found. Each chunk is dropped once.\n\t\twhile (this.#chunks.length > 1) {\n\t\t\tconst first = this.#chunks[0]!;\n\t\t\tconst bytesAfter = this.#storedBytes - first.bytes;\n\t\t\tconst newlinesAfter = this.#storedNewlines - first.newlines;\n\t\t\tif (bytesAfter <= this.#limits.maxBytes + 1 && newlinesAfter <= this.#limits.maxLines + 1) break;\n\t\t\tthis.#chunks.shift();\n\t\t\tthis.#storedBytes = bytesAfter;\n\t\t\tthis.#storedNewlines = newlinesAfter;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/** Retained, sanitized output and what the limits dropped from the whole stream. */\n\tsnapshot(): BoundedOutput {\n\t\tconst stored =\n\t\t\tthis.#chunks.length === 1 ? this.#chunks[0]!.text : this.#chunks.map((chunk) => chunk.text).join(\"\");\n\t\tconst kept = boundOutput(stored, this.#limits);\n\t\tconst storedLines = lines(this.#storedNewlines, stored === \"\" || stored.endsWith(\"\\n\"));\n\t\tconst keptLines = storedLines - kept.droppedLines;\n\t\t// Tail windows never reach back before this one, so only the kept slice needs storing.\n\t\tif (this.#limits.retain === \"tail\" || this.#chunks.length > 1) {\n\t\t\tconst text = this.#limits.retain === \"tail\" ? kept.text : stored;\n\t\t\tconst bytes = this.#limits.retain === \"tail\" ? kept.bytes : this.#storedBytes;\n\t\t\tthis.#chunks = text === \"\" ? [] : [{ text, bytes, newlines: countNewlines(text) }];\n\t\t\tthis.#storedBytes = bytes;\n\t\t\tthis.#storedNewlines = this.#chunks[0]?.newlines ?? 0;\n\t\t}\n\t\treturn {\n\t\t\ttext: sanitizeOutput(kept.text),\n\t\t\tdroppedBytes: this.#totalBytes - kept.bytes,\n\t\t\tdroppedLines: lines(this.#totalNewlines, this.#endsWithNewline) - keptLines,\n\t\t};\n\t}\n}\n\n/** Lines of text with `newlines` newlines; a final unterminated line counts. */\nfunction lines(newlines: number, terminated: boolean): number {\n\treturn newlines + (terminated ? 0 : 1);\n}\n\nfunction countNewlines(text: string): number {\n\tlet count = 0;\n\tfor (let index = text.indexOf(\"\\n\"); index !== -1; index = text.indexOf(\"\\n\", index + 1)) count++;\n\treturn count;\n}\n\n/** Minimum pause between progress commits; each commit also buys a pause proportional to what it wrote. */\nconst MIN_PROGRESS_INTERVAL_MS = 100;\nconst PROGRESS_BYTES_PER_SECOND = 100 * 1024;\n\n/**\n * Adaptive progress commits, like the environment's shell output capture: the first change after an idle period\n * commits at once; each commit then delays the next by at least 100 ms and by its written size at 100 KiB/s. At most\n * one commit is in flight; changes made meanwhile coalesce into the next one.\n */\nexport class Progress {\n\treadonly #write: () => Promise<number>;\n\treadonly #onError: (error: unknown) => void;\n\t#waiters: PromiseWithResolvers<void>[] = [];\n\t#timer: ReturnType<typeof setTimeout> | undefined;\n\t#inFlight: Promise<void> | undefined;\n\t#nextAt = 0;\n\t#dirty = false;\n\t#stopped = false;\n\n\tconstructor(write: () => Promise<number>, onError: (error: unknown) => void) {\n\t\tthis.#write = write;\n\t\tthis.#onError = onError;\n\t}\n\n\t/** Schedule a commit. */\n\tmark(): void {\n\t\tthis.#dirty = true;\n\t\tthis.#schedule();\n\t}\n\n\t/** Schedule a commit; the promise settles with the commit that includes this change. */\n\tmarkAndWait(): Promise<void> {\n\t\tconst waiter = Promise.withResolvers<void>();\n\t\tthis.#waiters.push(waiter);\n\t\tthis.mark();\n\t\treturn waiter.promise;\n\t}\n\n\t/** Stop committing and wait for the commit in flight; returns the waiters the final commit must settle. */\n\tasync stop(): Promise<PromiseWithResolvers<void>[]> {\n\t\tthis.#stopped = true;\n\t\tclearTimeout(this.#timer);\n\t\tthis.#timer = undefined;\n\t\tawait this.#inFlight;\n\t\treturn this.#waiters.splice(0);\n\t}\n\n\t#schedule(): void {\n\t\tif (this.#stopped || this.#timer !== undefined || this.#inFlight !== undefined) return;\n\t\tconst wait = this.#nextAt - Date.now();\n\t\tif (wait <= 0) this.#flush();\n\t\telse\n\t\t\tthis.#timer = setTimeout(() => {\n\t\t\t\tthis.#timer = undefined;\n\t\t\t\tthis.#flush();\n\t\t\t}, wait);\n\t}\n\n\t#flush(): void {\n\t\tif (this.#stopped || !this.#dirty) return;\n\t\tthis.#dirty = false;\n\t\tconst waiters = this.#waiters.splice(0);\n\t\tconst started = Date.now();\n\t\tthis.#inFlight = this.#write()\n\t\t\t.then(\n\t\t\t\t(bytes) => {\n\t\t\t\t\tthis.#nextAt = started + Math.max(MIN_PROGRESS_INTERVAL_MS, (bytes * 1000) / PROGRESS_BYTES_PER_SECOND);\n\t\t\t\t\tfor (const waiter of waiters) waiter.resolve();\n\t\t\t\t},\n\t\t\t\t(error: unknown) => {\n\t\t\t\t\tthis.#nextAt = started + MIN_PROGRESS_INTERVAL_MS;\n\t\t\t\t\tfor (const waiter of waiters) waiter.reject(error);\n\t\t\t\t\tthis.#onError(error);\n\t\t\t\t},\n\t\t\t)\n\t\t\t.finally(() => {\n\t\t\t\tthis.#inFlight = undefined;\n\t\t\t\tif (this.#dirty) this.#schedule();\n\t\t\t});\n\t}\n}\n"]}
|
package/dist/harness/prompt.d.ts
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
import type { Context } from "@earendil-works/chord";
|
|
2
|
-
import type { Message } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { Message, Tool } from "@earendil-works/pi-ai";
|
|
3
3
|
import type { TypedEntryDraft } from "../types.ts";
|
|
4
4
|
import type { ContextView, PromptInput, PromptSection, ToolRegistration } from "./types.ts";
|
|
5
5
|
/** Sections in effect after replaying system messages in order: set in place, `null` deletes, re-adding appends. */
|
|
6
6
|
export declare function replaySections(messages: readonly Message[]): Map<string, string>;
|
|
7
7
|
/**
|
|
8
|
-
* Render sections in
|
|
8
|
+
* Render the agent's sections in order. `undefined` omits a section; tagged text is wrapped as `<key>\n...\n</key>`. A
|
|
9
9
|
* section that throws keeps its shown text, if any, and is reported; errors after `context` is aborted propagate.
|
|
10
10
|
*/
|
|
11
11
|
export declare function renderSections<Tool extends ToolRegistration>(sections: readonly PromptSection<Tool>[], input: PromptInput<Tool>, shown: ReadonlyMap<string, string>, report: (error: unknown) => void, context: Context): Promise<Map<string, string>>;
|
|
12
12
|
type SystemDraft = TypedEntryDraft<never>;
|
|
13
13
|
/**
|
|
14
|
-
* Plan the `pi.system` entries that make the replayed sections of `view` equal `desired` in values
|
|
14
|
+
* Plan the `pi.system` entries that make the replayed sections and tools of `view` equal `desired` and `tools` in values
|
|
15
|
+
* and order.
|
|
15
16
|
*
|
|
16
17
|
* - A head marker with no later `pi.system` entry in context: one complete baseline that omits every retained earlier
|
|
17
18
|
* `pi.system` entry, written even when it restates the replayed values.
|
|
18
|
-
* - Otherwise, when a minimal patch would leave a different order: remove every shown section, then re-add
|
|
19
|
-
* desired section in order.
|
|
19
|
+
* - Otherwise, when a minimal section patch would leave a different order: remove every shown section, then re-add
|
|
20
|
+
* every desired section in order.
|
|
20
21
|
* - Otherwise the minimal patch of changed values and `null` removals, or nothing.
|
|
22
|
+
*
|
|
23
|
+
* Tool changes ride on the last planned entry, or on one entry of their own.
|
|
21
24
|
*/
|
|
22
|
-
export declare function planSystemEntries(view: ContextView, desired: ReadonlyMap<string, string>, timestamp: number): SystemDraft[];
|
|
25
|
+
export declare function planSystemEntries(view: ContextView, desired: ReadonlyMap<string, string>, tools: readonly Tool[], timestamp: number): SystemDraft[];
|
|
23
26
|
export {};
|
|
24
27
|
//# sourceMappingURL=prompt.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/harness/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,OAAO,EAAiB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/harness/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,OAAO,EAAiB,IAAI,EAAiB,MAAM,uBAAuB,CAAC;AAGzF,OAAO,KAAK,EAAe,eAAe,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE5F,oHAAoH;AACpH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAUhF;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,IAAI,SAAS,gBAAgB,EACjE,QAAQ,EAAE,SAAS,aAAa,CAAC,IAAI,CAAC,EAAE,EACxC,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,EACxB,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,EAChC,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAiB9B;AAED,KAAK,WAAW,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;AAI1C;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAChC,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,KAAK,EAAE,SAAS,IAAI,EAAE,EACtB,SAAS,EAAE,MAAM,GACf,WAAW,EAAE,CAsBf"}
|
package/dist/harness/prompt.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { declarationsEqual, getCurrentTools, toToolDeclaration } from "@earendil-works/pi-ai/utils/transcript";
|
|
1
2
|
import { SystemEntry } from "../entries.js";
|
|
2
3
|
/** Sections in effect after replaying system messages in order: set in place, `null` deletes, re-adding appends. */
|
|
3
4
|
export function replaySections(messages) {
|
|
@@ -15,7 +16,7 @@ export function replaySections(messages) {
|
|
|
15
16
|
return shown;
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
18
|
-
* Render sections in
|
|
19
|
+
* Render the agent's sections in order. `undefined` omits a section; tagged text is wrapped as `<key>\n...\n</key>`. A
|
|
19
20
|
* section that throws keeps its shown text, if any, and is reported; errors after `context` is aborted propagate.
|
|
20
21
|
*/
|
|
21
22
|
export async function renderSections(sections, input, shown, report, context) {
|
|
@@ -41,34 +42,69 @@ export async function renderSections(sections, input, shown, report, context) {
|
|
|
41
42
|
return desired;
|
|
42
43
|
}
|
|
43
44
|
/**
|
|
44
|
-
* Plan the `pi.system` entries that make the replayed sections of `view` equal `desired` in values
|
|
45
|
+
* Plan the `pi.system` entries that make the replayed sections and tools of `view` equal `desired` and `tools` in values
|
|
46
|
+
* and order.
|
|
45
47
|
*
|
|
46
48
|
* - A head marker with no later `pi.system` entry in context: one complete baseline that omits every retained earlier
|
|
47
49
|
* `pi.system` entry, written even when it restates the replayed values.
|
|
48
|
-
* - Otherwise, when a minimal patch would leave a different order: remove every shown section, then re-add
|
|
49
|
-
* desired section in order.
|
|
50
|
+
* - Otherwise, when a minimal section patch would leave a different order: remove every shown section, then re-add
|
|
51
|
+
* every desired section in order.
|
|
50
52
|
* - Otherwise the minimal patch of changed values and `null` removals, or nothing.
|
|
53
|
+
*
|
|
54
|
+
* Tool changes ride on the last planned entry, or on one entry of their own.
|
|
51
55
|
*/
|
|
52
|
-
export function planSystemEntries(view, desired, timestamp) {
|
|
56
|
+
export function planSystemEntries(view, desired, tools, timestamp) {
|
|
53
57
|
const head = view.head;
|
|
54
58
|
if (head !== undefined && !view.entries.some((entry) => SystemEntry.is(entry) && entry.id > head.id)) {
|
|
55
59
|
const edits = view.entries
|
|
56
60
|
.filter((entry) => SystemEntry.is(entry))
|
|
57
61
|
.map((entry) => ({ target: entry.id, action: "omit" }));
|
|
58
|
-
const baseline = systemEntry(Object.fromEntries(desired), timestamp);
|
|
62
|
+
const baseline = systemEntry(Object.fromEntries(desired), { toolsRemoved: [], toolsAdded: tools.map(toToolDeclaration) }, timestamp);
|
|
59
63
|
return [edits.length === 0 ? baseline : { ...baseline, edits }];
|
|
60
64
|
}
|
|
61
|
-
const
|
|
65
|
+
const sections = planSections(replaySections(view.messages), desired);
|
|
66
|
+
const changes = planTools(getCurrentTools(view.messages), tools);
|
|
67
|
+
if (changes.toolsRemoved.length === 0 && changes.toolsAdded.length === 0) {
|
|
68
|
+
return sections.map((patch) => systemEntry(patch, undefined, timestamp));
|
|
69
|
+
}
|
|
70
|
+
if (sections.length === 0)
|
|
71
|
+
return [systemEntry(undefined, changes, timestamp)];
|
|
72
|
+
return sections.map((patch, index) => systemEntry(patch, index === sections.length - 1 ? changes : undefined, timestamp));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Tool changes from `offered` to `desired`. A changed declaration is removed and re-added. Replay keeps retained tools
|
|
76
|
+
* in place and appends additions; when that would not yield the desired order, every offered tool is removed and
|
|
77
|
+
* every desired tool re-added in order.
|
|
78
|
+
*/
|
|
79
|
+
function planTools(offered, desired) {
|
|
80
|
+
const wanted = new Map(desired.map((tool) => [tool.name, tool]));
|
|
81
|
+
const kept = offered.filter((tool) => {
|
|
82
|
+
const next = wanted.get(tool.name);
|
|
83
|
+
return next !== undefined && declarationsEqual(tool, next);
|
|
84
|
+
});
|
|
85
|
+
const keptNames = new Set(kept.map((tool) => tool.name));
|
|
86
|
+
const added = desired.filter((tool) => !keptNames.has(tool.name));
|
|
87
|
+
const replayed = [...kept, ...added];
|
|
88
|
+
if (replayed.some((tool, index) => tool.name !== desired[index].name)) {
|
|
89
|
+
return {
|
|
90
|
+
toolsRemoved: offered.map((tool) => ({ name: tool.name })),
|
|
91
|
+
toolsAdded: desired.map(toToolDeclaration),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
toolsRemoved: offered.filter((tool) => !keptNames.has(tool.name)).map((tool) => ({ name: tool.name })),
|
|
96
|
+
toolsAdded: added.map(toToolDeclaration),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/** Section patches: none, the minimal patch, or a remove-all/re-add-all pair when the order would differ. */
|
|
100
|
+
function planSections(shown, desired) {
|
|
62
101
|
const patchedOrder = [
|
|
63
102
|
...[...shown.keys()].filter((key) => desired.has(key)),
|
|
64
103
|
...[...desired.keys()].filter((key) => !shown.has(key)),
|
|
65
104
|
];
|
|
66
105
|
const desiredOrder = [...desired.keys()];
|
|
67
106
|
if (patchedOrder.some((key, index) => key !== desiredOrder[index])) {
|
|
68
|
-
return [
|
|
69
|
-
systemEntry(Object.fromEntries([...shown.keys()].map((key) => [key, null])), timestamp),
|
|
70
|
-
systemEntry(Object.fromEntries(desired), timestamp),
|
|
71
|
-
];
|
|
107
|
+
return [Object.fromEntries([...shown.keys()].map((key) => [key, null])), Object.fromEntries(desired)];
|
|
72
108
|
}
|
|
73
109
|
const patch = {};
|
|
74
110
|
for (const [key, value] of shown) {
|
|
@@ -79,10 +115,17 @@ export function planSystemEntries(view, desired, timestamp) {
|
|
|
79
115
|
for (const [key, value] of desired)
|
|
80
116
|
if (!shown.has(key))
|
|
81
117
|
patch[key] = value;
|
|
82
|
-
return Object.keys(patch).length === 0 ? [] : [
|
|
118
|
+
return Object.keys(patch).length === 0 ? [] : [patch];
|
|
83
119
|
}
|
|
84
|
-
function systemEntry(sections, timestamp) {
|
|
85
|
-
const message = {
|
|
120
|
+
function systemEntry(sections, tools, timestamp) {
|
|
121
|
+
const message = {
|
|
122
|
+
role: "system",
|
|
123
|
+
content: "",
|
|
124
|
+
...(sections === undefined ? {} : { sections }),
|
|
125
|
+
...(tools === undefined || tools.toolsRemoved.length === 0 ? {} : { toolsRemoved: tools.toolsRemoved }),
|
|
126
|
+
...(tools === undefined || tools.toolsAdded.length === 0 ? {} : { toolsAdded: tools.toolsAdded }),
|
|
127
|
+
timestamp,
|
|
128
|
+
};
|
|
86
129
|
return { model: [message] };
|
|
87
130
|
}
|
|
88
131
|
//# sourceMappingURL=prompt.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/harness/prompt.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport type { Message, SystemMessage } from \"@earendil-works/pi-ai\";\nimport { SystemEntry } from \"../entries.ts\";\nimport type { ContextEdit, TypedEntryDraft } from \"../types.ts\";\nimport type { ContextView, PromptInput, PromptSection, ToolRegistration } from \"./types.ts\";\n\n/** Sections in effect after replaying system messages in order: set in place, `null` deletes, re-adding appends. */\nexport function replaySections(messages: readonly Message[]): Map<string, string> {\n\tconst shown = new Map<string, string>();\n\tfor (const message of messages) {\n\t\tif (message.role !== \"system\" || message.sections === undefined) continue;\n\t\tfor (const [key, value] of Object.entries(message.sections)) {\n\t\t\tif (value === null) shown.delete(key);\n\t\t\telse shown.set(key, value);\n\t\t}\n\t}\n\treturn shown;\n}\n\n/**\n * Render sections in registry order. `undefined` omits a section; tagged text is wrapped as `<key>\\n...\\n</key>`. A\n * section that throws keeps its shown text, if any, and is reported; errors after `context` is aborted propagate.\n */\nexport async function renderSections<Tool extends ToolRegistration>(\n\tsections: readonly PromptSection<Tool>[],\n\tinput: PromptInput<Tool>,\n\tshown: ReadonlyMap<string, string>,\n\treport: (error: unknown) => void,\n\tcontext: Context,\n): Promise<Map<string, string>> {\n\tconst desired = new Map<string, string>();\n\tfor (const section of sections) {\n\t\tlet text: string | undefined;\n\t\ttry {\n\t\t\ttext = await section.render(input, context);\n\t\t} catch (error) {\n\t\t\tif (context.abortSignal?.aborted) throw error;\n\t\t\treport(error);\n\t\t\tconst kept = shown.get(section.key);\n\t\t\tif (kept !== undefined) desired.set(section.key, kept);\n\t\t\tcontinue;\n\t\t}\n\t\tif (text === undefined) continue;\n\t\tdesired.set(section.key, section.tag === false ? text : `<${section.key}>\\n${text}\\n</${section.key}>`);\n\t}\n\treturn desired;\n}\n\ntype SystemDraft = TypedEntryDraft<never>;\n\n/**\n * Plan the `pi.system` entries that make the replayed sections of `view` equal `desired` in values and order.\n *\n * - A head marker with no later `pi.system` entry in context: one complete baseline that omits every retained earlier\n * `pi.system` entry, written even when it restates the replayed values.\n * - Otherwise, when a minimal patch would leave a different order: remove every shown section, then re-add every\n * desired section in order.\n * - Otherwise the minimal patch of changed values and `null` removals, or nothing.\n */\nexport function planSystemEntries(\n\tview: ContextView,\n\tdesired: ReadonlyMap<string, string>,\n\ttimestamp: number,\n): SystemDraft[] {\n\tconst head = view.head;\n\tif (head !== undefined && !view.entries.some((entry) => SystemEntry.is(entry) && entry.id > head.id)) {\n\t\tconst edits: ContextEdit[] = view.entries\n\t\t\t.filter((entry) => SystemEntry.is(entry))\n\t\t\t.map((entry) => ({ target: entry.id, action: \"omit\" }));\n\t\tconst baseline = systemEntry(Object.fromEntries(desired), timestamp);\n\t\treturn [edits.length === 0 ? baseline : { ...baseline, edits }];\n\t}\n\n\tconst shown = replaySections(view.messages);\n\tconst patchedOrder = [\n\t\t...[...shown.keys()].filter((key) => desired.has(key)),\n\t\t...[...desired.keys()].filter((key) => !shown.has(key)),\n\t];\n\tconst desiredOrder = [...desired.keys()];\n\tif (patchedOrder.some((key, index) => key !== desiredOrder[index])) {\n\t\treturn [\n\t\t\tsystemEntry(Object.fromEntries([...shown.keys()].map((key) => [key, null])), timestamp),\n\t\t\tsystemEntry(Object.fromEntries(desired), timestamp),\n\t\t];\n\t}\n\n\tconst patch: Record<string, string | null> = {};\n\tfor (const [key, value] of shown) {\n\t\tconst next = desired.get(key);\n\t\tif (next !== value) patch[key] = next ?? null;\n\t}\n\tfor (const [key, value] of desired) if (!shown.has(key)) patch[key] = value;\n\treturn Object.keys(patch).length === 0 ? [] : [systemEntry(patch, timestamp)];\n}\n\nfunction systemEntry(sections: Record<string, string | null>, timestamp: number): SystemDraft {\n\tconst message: SystemMessage = { role: \"system\", content: \"\", sections, timestamp };\n\treturn { model: [message] };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/harness/prompt.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport type { Message, SystemMessage, Tool, ToolReference } from \"@earendil-works/pi-ai\";\nimport { declarationsEqual, getCurrentTools, toToolDeclaration } from \"@earendil-works/pi-ai/utils/transcript\";\nimport { SystemEntry } from \"../entries.ts\";\nimport type { ContextEdit, TypedEntryDraft } from \"../types.ts\";\nimport type { ContextView, PromptInput, PromptSection, ToolRegistration } from \"./types.ts\";\n\n/** Sections in effect after replaying system messages in order: set in place, `null` deletes, re-adding appends. */\nexport function replaySections(messages: readonly Message[]): Map<string, string> {\n\tconst shown = new Map<string, string>();\n\tfor (const message of messages) {\n\t\tif (message.role !== \"system\" || message.sections === undefined) continue;\n\t\tfor (const [key, value] of Object.entries(message.sections)) {\n\t\t\tif (value === null) shown.delete(key);\n\t\t\telse shown.set(key, value);\n\t\t}\n\t}\n\treturn shown;\n}\n\n/**\n * Render the agent's sections in order. `undefined` omits a section; tagged text is wrapped as `<key>\\n...\\n</key>`. A\n * section that throws keeps its shown text, if any, and is reported; errors after `context` is aborted propagate.\n */\nexport async function renderSections<Tool extends ToolRegistration>(\n\tsections: readonly PromptSection<Tool>[],\n\tinput: PromptInput<Tool>,\n\tshown: ReadonlyMap<string, string>,\n\treport: (error: unknown) => void,\n\tcontext: Context,\n): Promise<Map<string, string>> {\n\tconst desired = new Map<string, string>();\n\tfor (const section of sections) {\n\t\tlet text: string | undefined;\n\t\ttry {\n\t\t\ttext = await section.render(input, context);\n\t\t} catch (error) {\n\t\t\tif (context.abortSignal?.aborted) throw error;\n\t\t\treport(error);\n\t\t\tconst kept = shown.get(section.key);\n\t\t\tif (kept !== undefined) desired.set(section.key, kept);\n\t\t\tcontinue;\n\t\t}\n\t\tif (text === undefined) continue;\n\t\tdesired.set(section.key, section.tag === false ? text : `<${section.key}>\\n${text}\\n</${section.key}>`);\n\t}\n\treturn desired;\n}\n\ntype SystemDraft = TypedEntryDraft<never>;\n\ntype ToolChanges = { readonly toolsRemoved: ToolReference[]; readonly toolsAdded: Tool[] };\n\n/**\n * Plan the `pi.system` entries that make the replayed sections and tools of `view` equal `desired` and `tools` in values\n * and order.\n *\n * - A head marker with no later `pi.system` entry in context: one complete baseline that omits every retained earlier\n * `pi.system` entry, written even when it restates the replayed values.\n * - Otherwise, when a minimal section patch would leave a different order: remove every shown section, then re-add\n * every desired section in order.\n * - Otherwise the minimal patch of changed values and `null` removals, or nothing.\n *\n * Tool changes ride on the last planned entry, or on one entry of their own.\n */\nexport function planSystemEntries(\n\tview: ContextView,\n\tdesired: ReadonlyMap<string, string>,\n\ttools: readonly Tool[],\n\ttimestamp: number,\n): SystemDraft[] {\n\tconst head = view.head;\n\tif (head !== undefined && !view.entries.some((entry) => SystemEntry.is(entry) && entry.id > head.id)) {\n\t\tconst edits: ContextEdit[] = view.entries\n\t\t\t.filter((entry) => SystemEntry.is(entry))\n\t\t\t.map((entry) => ({ target: entry.id, action: \"omit\" }));\n\t\tconst baseline = systemEntry(\n\t\t\tObject.fromEntries(desired),\n\t\t\t{ toolsRemoved: [], toolsAdded: tools.map(toToolDeclaration) },\n\t\t\ttimestamp,\n\t\t);\n\t\treturn [edits.length === 0 ? baseline : { ...baseline, edits }];\n\t}\n\tconst sections = planSections(replaySections(view.messages), desired);\n\tconst changes = planTools(getCurrentTools(view.messages), tools);\n\tif (changes.toolsRemoved.length === 0 && changes.toolsAdded.length === 0) {\n\t\treturn sections.map((patch) => systemEntry(patch, undefined, timestamp));\n\t}\n\tif (sections.length === 0) return [systemEntry(undefined, changes, timestamp)];\n\treturn sections.map((patch, index) =>\n\t\tsystemEntry(patch, index === sections.length - 1 ? changes : undefined, timestamp),\n\t);\n}\n\n/**\n * Tool changes from `offered` to `desired`. A changed declaration is removed and re-added. Replay keeps retained tools\n * in place and appends additions; when that would not yield the desired order, every offered tool is removed and\n * every desired tool re-added in order.\n */\nfunction planTools(offered: readonly Tool[], desired: readonly Tool[]): ToolChanges {\n\tconst wanted = new Map(desired.map((tool) => [tool.name, tool]));\n\tconst kept = offered.filter((tool) => {\n\t\tconst next = wanted.get(tool.name);\n\t\treturn next !== undefined && declarationsEqual(tool, next);\n\t});\n\tconst keptNames = new Set(kept.map((tool) => tool.name));\n\tconst added = desired.filter((tool) => !keptNames.has(tool.name));\n\tconst replayed = [...kept, ...added];\n\tif (replayed.some((tool, index) => tool.name !== desired[index]!.name)) {\n\t\treturn {\n\t\t\ttoolsRemoved: offered.map((tool) => ({ name: tool.name })),\n\t\t\ttoolsAdded: desired.map(toToolDeclaration),\n\t\t};\n\t}\n\treturn {\n\t\ttoolsRemoved: offered.filter((tool) => !keptNames.has(tool.name)).map((tool) => ({ name: tool.name })),\n\t\ttoolsAdded: added.map(toToolDeclaration),\n\t};\n}\n\n/** Section patches: none, the minimal patch, or a remove-all/re-add-all pair when the order would differ. */\nfunction planSections(\n\tshown: ReadonlyMap<string, string>,\n\tdesired: ReadonlyMap<string, string>,\n): Record<string, string | null>[] {\n\tconst patchedOrder = [\n\t\t...[...shown.keys()].filter((key) => desired.has(key)),\n\t\t...[...desired.keys()].filter((key) => !shown.has(key)),\n\t];\n\tconst desiredOrder = [...desired.keys()];\n\tif (patchedOrder.some((key, index) => key !== desiredOrder[index])) {\n\t\treturn [Object.fromEntries([...shown.keys()].map((key) => [key, null])), Object.fromEntries(desired)];\n\t}\n\n\tconst patch: Record<string, string | null> = {};\n\tfor (const [key, value] of shown) {\n\t\tconst next = desired.get(key);\n\t\tif (next !== value) patch[key] = next ?? null;\n\t}\n\tfor (const [key, value] of desired) if (!shown.has(key)) patch[key] = value;\n\treturn Object.keys(patch).length === 0 ? [] : [patch];\n}\n\nfunction systemEntry(\n\tsections: Record<string, string | null> | undefined,\n\ttools: ToolChanges | undefined,\n\ttimestamp: number,\n): SystemDraft {\n\tconst message: SystemMessage = {\n\t\trole: \"system\",\n\t\tcontent: \"\",\n\t\t...(sections === undefined ? {} : { sections }),\n\t\t...(tools === undefined || tools.toolsRemoved.length === 0 ? {} : { toolsRemoved: tools.toolsRemoved }),\n\t\t...(tools === undefined || tools.toolsAdded.length === 0 ? {} : { toolsAdded: tools.toolsAdded }),\n\t\ttimestamp,\n\t};\n\treturn { model: [message] };\n}\n"]}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { AnyTask, Registry, ToolRegistration } from "./types.ts";
|
|
2
|
-
/** Built-in task definitions every registry
|
|
2
|
+
/** Built-in task definitions every registry holds; they are not an extension and cannot be removed or replaced. */
|
|
3
3
|
export declare const BUILTIN_TASKS: readonly AnyTask[];
|
|
4
|
-
|
|
5
|
-
/** Create an application-owned registry holding only the built-ins. */
|
|
4
|
+
/** Create an application-owned registry holding only the built-in tasks. */
|
|
6
5
|
export declare function createRegistry<Tool extends ToolRegistration = ToolRegistration>(): Registry<Tool>;
|
|
7
6
|
//# sourceMappingURL=registry.d.ts.map
|