@a-dray/aglib 0.1.1 → 0.2.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/dist/agent.d.ts +12 -0
- package/dist/agent.js.map +1 -1
- package/dist/harness/adapters/native/compaction.d.ts +15 -0
- package/dist/harness/adapters/native/compaction.js +23 -8
- package/dist/harness/adapters/native/compaction.js.map +1 -1
- package/dist/harness/adapters/native/loop.d.ts +18 -1
- package/dist/harness/adapters/native/loop.js.map +1 -1
- package/dist/run.js +1 -0
- package/dist/run.js.map +1 -1
- package/dist/session/entry.d.ts +3 -2
- package/dist/session/entry.js.map +1 -1
- package/dist/session/messages.d.ts +9 -0
- package/dist/session/messages.js +5 -5
- package/dist/session/messages.js.map +1 -1
- package/package.json +1 -1
package/dist/agent.d.ts
CHANGED
|
@@ -15,6 +15,18 @@ export interface Agent {
|
|
|
15
15
|
tools?: readonly Tool[];
|
|
16
16
|
/** Per-call policy over parsed arguments. Absent means every call executes. */
|
|
17
17
|
decide?: Decide;
|
|
18
|
+
/**
|
|
19
|
+
* Name each arrival's sender in the turn the model reads, as `[from kind id]`.
|
|
20
|
+
*
|
|
21
|
+
* Off by default, because two separate things were one: `from` is provenance
|
|
22
|
+
* the log holds whatever this says, and this is text in a prompt. An
|
|
23
|
+
* application that renders its own attribution into the input it delivers —
|
|
24
|
+
* most do, since only they can name a sender in words their agent knows —
|
|
25
|
+
* would otherwise have the model read two names for one sender, one of them a
|
|
26
|
+
* session id that means nothing to it. Turn it on where nothing else says who
|
|
27
|
+
* wrote.
|
|
28
|
+
*/
|
|
29
|
+
attribution?: boolean;
|
|
18
30
|
/** Runs once as an activation ends, whatever ended it; its deliveries commit with the final entries. */
|
|
19
31
|
finished?(run: {
|
|
20
32
|
sessionId: string;
|
package/dist/agent.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"","sourcesContent":["import type { Content } from \"./content.js\";\nimport type { Harness } from \"./harness/harness.js\";\nimport type { Decide, Tool } from \"./tools/tool.js\";\nimport type { Delivery, Runnable, Store } from \"./store/store.js\";\nimport type { Update } from \"./harness/harness.js\";\nimport type { Failure } from \"./result.js\";\nimport type { From, Usage } from \"./session/entry.js\";\n\n/** An inert declaration. Nothing here opens a connection or holds state. */\nexport interface Agent {\n id: string;\n /** Bump when instructions, tools or configuration stop being compatible with an existing session. */\n version: string;\n instructions: Content;\n harness: Harness;\n tools?: readonly Tool[];\n /** Per-call policy over parsed arguments. Absent means every call executes. */\n decide?: Decide;\n /** Runs once as an activation ends, whatever ended it; its deliveries commit with the final entries. */\n finished?(run: {\n sessionId: string; runId: string;\n outcome: \"completed\" | \"failed\" | \"cancelled\";\n output: Content;\n }): readonly Delivery[] | Promise<readonly Delivery[]>;\n /**\n * Ceilings on one activation, over the facts this library holds: turns and\n * tool calls are on the log, and a deadline is the clock.\n *\n * There is no ceiling on money, and the asymmetry is the point. A spend limit\n * would have to read a rate table the library does not have and should not\n * carry, so it would take a function from the caller and then need defending\n * against it — a code for \"you declared a ceiling nothing can hold\", a rule\n * for a generation the function could not price, a check for a price that is\n * negative or not a number. All of that is the cost of enforcing something\n * from a fact we do not own, and enforcing it is policy besides: the two\n * applications that wanted one wanted it to stop at different moments.\n *\n * `RunResult.usage` is what a caller needs from here, and it is solid. What\n * that costs, and what to do about it, is priced by whoever holds the rates.\n */\n limits?: { maxTurns?: number; maxToolCalls?: number; deadline?: string };\n}\n\n/**\n * How an activation ended, and what it consumed on the way.\n *\n * `usage` is on every outcome, not only a completed one: a run that burned four\n * dollars of tokens and then failed burned them, and a caller that has to ask\n * the log to find that out has been handed a result missing the expensive half\n * of what happened.\n *\n * Summed from the `assistant` entries this activation committed, which is why a\n * harness does not report it — the log already holds every generation, and a\n * second total is a second answer. Money is not here: the counts are the fact,\n * and the rates that turn them into money belong to the deployment.\n */\nexport type RunResult =\n | { status: \"completed\"; output: Content; usage: Usage; seq: number }\n | { status: \"cancelled\"; usage: Usage; seq: number }\n | { status: \"failed\"; error: Failure; usage: Usage; seq: number };\n\nexport interface AgentRun extends AsyncIterable<Update> {\n readonly result: Promise<RunResult>;\n cancel(): void;\n}\n\n/**\n * One piece of input for an activation, and where it came from.\n *\n * Always the object form, because `Content` may itself be an array of parts and\n * a bare array therefore cannot say whether it is one multi-part message or\n * several messages. `RunAgentOptions.input` still takes plain content for the\n * common case; only the *many* form is required to name each arrival.\n */\nexport interface Arrival { input: Content; from?: From }\n\ninterface RunAgentBase {\n agent: Agent;\n /** Opaque application index key, used only when this run creates the session. */\n key?: string;\n context?: { run?: string; turn?: string };\n signal?: AbortSignal;\n}\n\n/**\n * An activation is working one of two things, and never both.\n *\n * A caller is **sending** — a person typed, a webhook fired, a test asked a\n * question. Or a worker is running **what the store handed it**, which carries\n * the session, the position to write from, and the deliveries to consume, all\n * as one value that arrived together and cannot be recombined wrongly.\n *\n * That second form replaced three fields a worker had to line up by hand:\n * `sessionId`, the position, and how many deliveries to take. Every one of them\n * was only ever a field of the claim, so each was a value another field already\n * determined — and the one that mattered, taking the deliveries, silently\n * left the input queued for ever when it was forgotten. Both applications\n * written on this wrote the same eight lines to get it right.\n */\nexport type RunAgentOptions = RunAgentBase & (\n | {\n /** One message as plain content, one named arrival, or several named arrivals. */\n input: Content | Arrival | readonly Arrival[];\n /**\n * Which session it goes to, as a UUID. A new one is opened when this is\n * omitted. An application's own naming for a conversation goes in `key`,\n * which is opaque to the library; a store is entitled to keep a session\n * id in a `uuid` column and refuse anything else.\n */\n sessionId?: string;\n /** Omit for an ephemeral run: the log lives in memory and nothing is persisted. */\n store?: Store;\n claim?: never;\n }\n | {\n /**\n * What `store.next()` or `store.interrupted()` handed this worker.\n *\n * Its deliveries become this activation's input and are consumed by the\n * write that commits them, in the same compare-and-swap that checks the\n * position — so a worker that loses the race loses its turn and never the\n * messages. A claim with an empty queue is a **resumption**: nothing opens\n * it, the loop continues the committed log, and the effect that already\n * happened is read there rather than asked for again. Only a harness\n * declaring `recovery: \"history\"` is given one.\n */\n claim: Runnable;\n /**\n * Required here, unlike a caller sending. A claim is a position in a log\n * and a queue to consume, and both of those are the store's — without one\n * the deliveries would be replayed into memory and left queued for ever.\n */\n store: Store;\n /**\n * Something the worker is adding of its own, ahead of what was waiting.\n *\n * One real use: orientation for a harness whose protocol has no system\n * prompt, which has nowhere else to go and must lead. Adding nothing is\n * the ordinary case.\n */\n input?: Content | Arrival | readonly Arrival[];\n sessionId?: never;\n }\n);\n"]}
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"","sourcesContent":["import type { Content } from \"./content.js\";\nimport type { Harness } from \"./harness/harness.js\";\nimport type { Decide, Tool } from \"./tools/tool.js\";\nimport type { Delivery, Runnable, Store } from \"./store/store.js\";\nimport type { Update } from \"./harness/harness.js\";\nimport type { Failure } from \"./result.js\";\nimport type { From, Usage } from \"./session/entry.js\";\n\n/** An inert declaration. Nothing here opens a connection or holds state. */\nexport interface Agent {\n id: string;\n /** Bump when instructions, tools or configuration stop being compatible with an existing session. */\n version: string;\n instructions: Content;\n harness: Harness;\n tools?: readonly Tool[];\n /** Per-call policy over parsed arguments. Absent means every call executes. */\n decide?: Decide;\n /**\n * Name each arrival's sender in the turn the model reads, as `[from kind id]`.\n *\n * Off by default, because two separate things were one: `from` is provenance\n * the log holds whatever this says, and this is text in a prompt. An\n * application that renders its own attribution into the input it delivers —\n * most do, since only they can name a sender in words their agent knows —\n * would otherwise have the model read two names for one sender, one of them a\n * session id that means nothing to it. Turn it on where nothing else says who\n * wrote.\n */\n attribution?: boolean;\n /** Runs once as an activation ends, whatever ended it; its deliveries commit with the final entries. */\n finished?(run: {\n sessionId: string; runId: string;\n outcome: \"completed\" | \"failed\" | \"cancelled\";\n output: Content;\n }): readonly Delivery[] | Promise<readonly Delivery[]>;\n /**\n * Ceilings on one activation, over the facts this library holds: turns and\n * tool calls are on the log, and a deadline is the clock.\n *\n * There is no ceiling on money, and the asymmetry is the point. A spend limit\n * would have to read a rate table the library does not have and should not\n * carry, so it would take a function from the caller and then need defending\n * against it — a code for \"you declared a ceiling nothing can hold\", a rule\n * for a generation the function could not price, a check for a price that is\n * negative or not a number. All of that is the cost of enforcing something\n * from a fact we do not own, and enforcing it is policy besides: the two\n * applications that wanted one wanted it to stop at different moments.\n *\n * `RunResult.usage` is what a caller needs from here, and it is solid. What\n * that costs, and what to do about it, is priced by whoever holds the rates.\n */\n limits?: { maxTurns?: number; maxToolCalls?: number; deadline?: string };\n}\n\n/**\n * How an activation ended, and what it consumed on the way.\n *\n * `usage` is on every outcome, not only a completed one: a run that burned four\n * dollars of tokens and then failed burned them, and a caller that has to ask\n * the log to find that out has been handed a result missing the expensive half\n * of what happened.\n *\n * Summed from the `assistant` entries this activation committed, which is why a\n * harness does not report it — the log already holds every generation, and a\n * second total is a second answer. Money is not here: the counts are the fact,\n * and the rates that turn them into money belong to the deployment.\n */\nexport type RunResult =\n | { status: \"completed\"; output: Content; usage: Usage; seq: number }\n | { status: \"cancelled\"; usage: Usage; seq: number }\n | { status: \"failed\"; error: Failure; usage: Usage; seq: number };\n\nexport interface AgentRun extends AsyncIterable<Update> {\n readonly result: Promise<RunResult>;\n cancel(): void;\n}\n\n/**\n * One piece of input for an activation, and where it came from.\n *\n * Always the object form, because `Content` may itself be an array of parts and\n * a bare array therefore cannot say whether it is one multi-part message or\n * several messages. `RunAgentOptions.input` still takes plain content for the\n * common case; only the *many* form is required to name each arrival.\n */\nexport interface Arrival { input: Content; from?: From }\n\ninterface RunAgentBase {\n agent: Agent;\n /** Opaque application index key, used only when this run creates the session. */\n key?: string;\n context?: { run?: string; turn?: string };\n signal?: AbortSignal;\n}\n\n/**\n * An activation is working one of two things, and never both.\n *\n * A caller is **sending** — a person typed, a webhook fired, a test asked a\n * question. Or a worker is running **what the store handed it**, which carries\n * the session, the position to write from, and the deliveries to consume, all\n * as one value that arrived together and cannot be recombined wrongly.\n *\n * That second form replaced three fields a worker had to line up by hand:\n * `sessionId`, the position, and how many deliveries to take. Every one of them\n * was only ever a field of the claim, so each was a value another field already\n * determined — and the one that mattered, taking the deliveries, silently\n * left the input queued for ever when it was forgotten. Both applications\n * written on this wrote the same eight lines to get it right.\n */\nexport type RunAgentOptions = RunAgentBase & (\n | {\n /** One message as plain content, one named arrival, or several named arrivals. */\n input: Content | Arrival | readonly Arrival[];\n /**\n * Which session it goes to, as a UUID. A new one is opened when this is\n * omitted. An application's own naming for a conversation goes in `key`,\n * which is opaque to the library; a store is entitled to keep a session\n * id in a `uuid` column and refuse anything else.\n */\n sessionId?: string;\n /** Omit for an ephemeral run: the log lives in memory and nothing is persisted. */\n store?: Store;\n claim?: never;\n }\n | {\n /**\n * What `store.next()` or `store.interrupted()` handed this worker.\n *\n * Its deliveries become this activation's input and are consumed by the\n * write that commits them, in the same compare-and-swap that checks the\n * position — so a worker that loses the race loses its turn and never the\n * messages. A claim with an empty queue is a **resumption**: nothing opens\n * it, the loop continues the committed log, and the effect that already\n * happened is read there rather than asked for again. Only a harness\n * declaring `recovery: \"history\"` is given one.\n */\n claim: Runnable;\n /**\n * Required here, unlike a caller sending. A claim is a position in a log\n * and a queue to consume, and both of those are the store's — without one\n * the deliveries would be replayed into memory and left queued for ever.\n */\n store: Store;\n /**\n * Something the worker is adding of its own, ahead of what was waiting.\n *\n * One real use: orientation for a harness whose protocol has no system\n * prompt, which has nowhere else to go and must lead. Adding nothing is\n * the ordinary case.\n */\n input?: Content | Arrival | readonly Arrival[];\n sessionId?: never;\n }\n);\n"]}
|
|
@@ -7,6 +7,21 @@ import type { Stored } from "../../../session/entry.js";
|
|
|
7
7
|
* costs one early or late compaction, not correctness.
|
|
8
8
|
*/
|
|
9
9
|
export declare function estimateTokens(messages: readonly Message[]): number;
|
|
10
|
+
/**
|
|
11
|
+
* The latest position that can be folded without separating a tool call from
|
|
12
|
+
* its results.
|
|
13
|
+
*
|
|
14
|
+
* A position is safe when the log is **drained** there: every call an assistant
|
|
15
|
+
* turn asked for has its `tool.finished`. The gap between two batches of calls
|
|
16
|
+
* is such a point, as are a turn that asked for nothing and the end of a run.
|
|
17
|
+
* Cutting anywhere else leaves one call of a batch summarized and its sibling
|
|
18
|
+
* live, which shows the model a result for a call it can no longer see.
|
|
19
|
+
*
|
|
20
|
+
* A finished run and an empty turn alone were not enough, and the run that
|
|
21
|
+
* needed compaction was exactly the run that had neither: inside one activation
|
|
22
|
+
* every assistant turn holds calls until the one that ends it, so a single long
|
|
23
|
+
* activation never folded and grew until the provider refused it.
|
|
24
|
+
*/
|
|
10
25
|
export declare function compactionCut(entries: readonly Stored[]): number | undefined;
|
|
11
26
|
export declare function summarize(input: {
|
|
12
27
|
model: Model;
|
|
@@ -9,24 +9,39 @@ import { textOf } from "../../../content.js";
|
|
|
9
9
|
export function estimateTokens(messages) {
|
|
10
10
|
return messages.reduce((total, message) => total + JSON.stringify(message).length, 0) / 4;
|
|
11
11
|
}
|
|
12
|
+
/** How much of the tail is kept verbatim. A constant until a caller disagrees. */
|
|
13
|
+
const KEEP_FRACTION = 0.4;
|
|
12
14
|
/**
|
|
13
15
|
* The latest position that can be folded without separating a tool call from
|
|
14
16
|
* its results.
|
|
15
17
|
*
|
|
16
|
-
*
|
|
17
|
-
* turn
|
|
18
|
-
*
|
|
19
|
-
* call
|
|
18
|
+
* A position is safe when the log is **drained** there: every call an assistant
|
|
19
|
+
* turn asked for has its `tool.finished`. The gap between two batches of calls
|
|
20
|
+
* is such a point, as are a turn that asked for nothing and the end of a run.
|
|
21
|
+
* Cutting anywhere else leaves one call of a batch summarized and its sibling
|
|
22
|
+
* live, which shows the model a result for a call it can no longer see.
|
|
23
|
+
*
|
|
24
|
+
* A finished run and an empty turn alone were not enough, and the run that
|
|
25
|
+
* needed compaction was exactly the run that had neither: inside one activation
|
|
26
|
+
* every assistant turn holds calls until the one that ends it, so a single long
|
|
27
|
+
* activation never folded and grew until the provider refused it.
|
|
20
28
|
*/
|
|
21
|
-
/** How much of the tail is kept verbatim. A constant until a caller disagrees. */
|
|
22
|
-
const KEEP_FRACTION = 0.4;
|
|
23
29
|
export function compactionCut(entries) {
|
|
24
30
|
const boundary = Math.floor(entries.length * (1 - KEEP_FRACTION));
|
|
31
|
+
const awaiting = new Set();
|
|
25
32
|
let cut;
|
|
26
33
|
for (const entry of entries.slice(0, boundary)) {
|
|
34
|
+
if (entry.type === "assistant")
|
|
35
|
+
for (const call of entry.calls ?? [])
|
|
36
|
+
awaiting.add(call.callId);
|
|
37
|
+
if (entry.type === "tool.finished")
|
|
38
|
+
awaiting.delete(entry.callId);
|
|
39
|
+
// A run that ended takes its unanswered calls with it. The projection closes
|
|
40
|
+
// each one beside the turn that asked for it, so both fall on the same side
|
|
41
|
+
// of any later cut.
|
|
27
42
|
if (entry.type === "run.finished")
|
|
28
|
-
|
|
29
|
-
if (
|
|
43
|
+
awaiting.clear();
|
|
44
|
+
if (!awaiting.size)
|
|
30
45
|
cut = entry.seq;
|
|
31
46
|
}
|
|
32
47
|
return cut;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compaction.js","sourceRoot":"","sources":["../../../../src/harness/adapters/native/compaction.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,QAA4B;IACzD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;AAC5F,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"compaction.js","sourceRoot":"","sources":["../../../../src/harness/adapters/native/compaction.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,QAA4B;IACzD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;AAC5F,CAAC;AAED,kFAAkF;AAClF,MAAM,aAAa,GAAG,GAAG,CAAC;AAE1B;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,aAAa,CAAC,OAA0B;IACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,IAAI,GAAuB,CAAC;IAC5B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC;QAC/C,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW;YAAE,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,IAAI,EAAE;gBAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAChG,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe;YAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAClE,6EAA6E;QAC7E,4EAA4E;QAC5E,oBAAoB;QACpB,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc;YAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;QACpD,IAAI,CAAC,QAAQ,CAAC,IAAI;YAAE,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;IACtC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,4FAA4F;AAC5F,SAAS,aAAa,CAAC,QAA4B;IACjD,OAAO;QACL,6EAA6E;QAC7E,oEAAoE;QACpE,sDAAsD;QACtD,0CAA0C;QAC1C,wFAAwF;QACxF,wDAAwD;QACxD,EAAE;QACF,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;KACtF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAK/B;IACC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC;QACjD,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,MAAM,IAAI,aAAa,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QACtF,eAAe,EAAE,KAAK;QACtB,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClD,CAAC,CAAC,CAAC;IACJ,OAAO,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACxE,CAAC","sourcesContent":["import type { Model, Message } from \"../../../model/model.js\";\nimport type { Stored } from \"../../../session/entry.js\";\nimport { collect } from \"../../../model/model.js\";\nimport { textOf } from \"../../../content.js\";\n\n/**\n * Crude and deliberate: four characters per token, over the serialized request.\n * A real count needs the provider's tokenizer, which would mean shipping one\n * per provider to decide a threshold that is itself a guess. Being wrong here\n * costs one early or late compaction, not correctness.\n */\nexport function estimateTokens(messages: readonly Message[]): number {\n return messages.reduce((total, message) => total + JSON.stringify(message).length, 0) / 4;\n}\n\n/** How much of the tail is kept verbatim. A constant until a caller disagrees. */\nconst KEEP_FRACTION = 0.4;\n\n/**\n * The latest position that can be folded without separating a tool call from\n * its results.\n *\n * A position is safe when the log is **drained** there: every call an assistant\n * turn asked for has its `tool.finished`. The gap between two batches of calls\n * is such a point, as are a turn that asked for nothing and the end of a run.\n * Cutting anywhere else leaves one call of a batch summarized and its sibling\n * live, which shows the model a result for a call it can no longer see.\n *\n * A finished run and an empty turn alone were not enough, and the run that\n * needed compaction was exactly the run that had neither: inside one activation\n * every assistant turn holds calls until the one that ends it, so a single long\n * activation never folded and grew until the provider refused it.\n */\nexport function compactionCut(entries: readonly Stored[]): number | undefined {\n const boundary = Math.floor(entries.length * (1 - KEEP_FRACTION));\n const awaiting = new Set<string>();\n let cut: number | undefined;\n for (const entry of entries.slice(0, boundary)) {\n if (entry.type === \"assistant\") for (const call of entry.calls ?? []) awaiting.add(call.callId);\n if (entry.type === \"tool.finished\") awaiting.delete(entry.callId);\n // A run that ended takes its unanswered calls with it. The projection closes\n // each one beside the turn that asked for it, so both fall on the same side\n // of any later cut.\n if (entry.type === \"run.finished\") awaiting.clear();\n if (!awaiting.size) cut = entry.seq;\n }\n return cut;\n}\n\n/** What the summary must preserve. Overridable, because what matters is domain-specific. */\nfunction summaryPrompt(messages: readonly Message[]): string {\n return [\n \"You are compacting an agent conversation that continues after this summary.\",\n \"Summarize the transcript below faithfully and concisely, covering:\",\n \"- Intent: the goal and the current state of the task\",\n \"- Decisions: choices made so far and why\",\n \"- Artifacts: files, outputs and results worth remembering (exact names, paths, values)\",\n \"- Pending: unfinished work, next steps, open questions\",\n \"\",\n messages.map((message) => `${message.role}: ${textOf(message.content)}`).join(\"\\n\\n\"),\n ].join(\"\\n\");\n}\n\nexport async function summarize(input: {\n model: Model;\n messages: readonly Message[];\n prompt?: (messages: readonly Message[]) => string;\n signal?: AbortSignal;\n}): Promise<string | undefined> {\n const outcome = await collect(input.model.generate({\n messages: [{ role: \"user\", content: (input.prompt ?? summaryPrompt)(input.messages) }],\n maxOutputTokens: 2_000,\n ...(input.signal ? { signal: input.signal } : {}),\n }));\n return outcome.ok ? textOf(outcome.value.message.content) : undefined;\n}\n"]}
|
|
@@ -4,8 +4,25 @@ export interface NativeHarnessOptions {
|
|
|
4
4
|
model: Model;
|
|
5
5
|
/** Fold older turns into a summary once a request passes this size. */
|
|
6
6
|
compaction?: {
|
|
7
|
+
/**
|
|
8
|
+
* The estimated request size a fold happens above.
|
|
9
|
+
*
|
|
10
|
+
* A budget, not a provider fact: it belongs below the input window of the
|
|
11
|
+
* model that answers, with room for the turn that follows the fold. Folding
|
|
12
|
+
* early spends a model call and hands the run a paraphrase of work the log
|
|
13
|
+
* still holds in full; folding late risks the request a provider refuses.
|
|
14
|
+
*/
|
|
7
15
|
maxInputTokens: number;
|
|
8
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Who writes the summary. Defaults to the run's own model, so an
|
|
18
|
+
* application that says nothing meets neither a second provider nor a
|
|
19
|
+
* second credential.
|
|
20
|
+
*
|
|
21
|
+
* A cheaper one is right where the span being folded is long and reading it
|
|
22
|
+
* back is all the work — this is not the reasoning the main model is being
|
|
23
|
+
* paid for. It still has to take the whole span in one request, and to be
|
|
24
|
+
* good enough that what it drops is what did not matter.
|
|
25
|
+
*/
|
|
9
26
|
model?: Model;
|
|
10
27
|
prompt?: (messages: readonly Message[]) => string;
|
|
11
28
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop.js","sourceRoot":"","sources":["../../../../src/harness/adapters/native/loop.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Harness, HarnessContext, HarnessResult } from \"../../harness.js\";\nimport type { Entry } from \"../../../session/entry.js\";\nimport type { Message, Model } from \"../../../model/model.js\";\nimport { foldedThrough, toMessages } from \"../../../session/messages.js\";\nimport { compactionCut, estimateTokens, summarize } from \"./compaction.js\";\n\nexport interface NativeHarnessOptions {\n model: Model;\n /** Fold older turns into a summary once a request passes this size. */\n compaction?: {\n maxInputTokens: number;\n /** Defaults to the main model. A cheaper one is usually the right call. */\n model?: Model;\n prompt?: (messages: readonly Message[]) => string;\n };\n maxOutputTokens?: number;\n temperature?: number;\n /** How hard the model should think, where the provider supports it. */\n effort?: \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\";\n}\n\n/**\n * Ask the model, run what it asks for, repeat.\n *\n * The loop holds no transcript. Every turn it re-reads `context.history()`,\n * which projects the committed log, and every result it produces goes back\n * through `context.commit` before the next turn is built. There is exactly one\n * representation of the conversation and it is the log.\n */\nexport function createNativeHarness(options: NativeHarnessOptions): Harness {\n return {\n id: \"native\",\n recovery: \"history\",\n\n async run(context: HarnessContext): Promise<HarnessResult> {\n const { runId } = context;\n let compacting = true;\n\n for (;;) {\n if (context.signal.aborted) return { status: \"cancelled\" };\n\n let history = context.history();\n\n if (compacting && options.compaction && estimateTokens(history) > options.compaction.maxInputTokens) {\n const entries = context.entries();\n const folded = foldedThrough(entries);\n const cut = compactionCut(entries);\n\n // Only ever fold forward. A cut at or behind the last summary removes\n // nothing, so committing one would buy a model call and another entry\n // and leave the request exactly as large — every turn, without end.\n if (cut !== undefined && cut > folded) {\n const summary = await summarize({\n model: options.compaction.model ?? options.model,\n // Exactly the span being replaced. Summarizing the whole history\n // put everything after the cut into the summary as well, so it\n // stayed in the request twice: once verbatim, once paraphrased.\n messages: toMessages({\n instructions: context.instructions,\n entries: entries.filter((entry) => entry.seq <= cut),\n }),\n ...(options.compaction.prompt ? { prompt: options.compaction.prompt } : {}),\n signal: context.signal,\n });\n if (!summary) {\n // Paid for and produced nothing. Attempted once per activation\n // rather than once per turn: retrying bought another model call\n // and another failure every turn, and the request stayed exactly\n // as large either way.\n compacting = false;\n }\n if (summary) {\n // The folded entries stay in the log. Compaction changes what the\n // model is shown on the next turn, never what happened.\n await context.commit([{ type: \"summary\", runId, content: summary, replaces: cut }]);\n history = context.history();\n }\n }\n }\n\n // One cache mark, at the end of the system prefix: instructions plus\n // run-scoped context, which are fixed for the life of the run.\n const cacheAfter = history.findIndex((message) => message.role !== \"system\");\n\n const startedAt = new Date().toISOString();\n const generation = options.model.generate({\n messages: history,\n ...(context.tools ? { tools: context.tools.list() } : {}),\n ...(options.maxOutputTokens !== undefined ? { maxOutputTokens: options.maxOutputTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n ...(options.effort ? { effort: options.effort } : {}),\n ...(cacheAfter > 0 ? { cacheAfter } : {}),\n signal: context.signal,\n });\n\n let step = await generation.next();\n while (!step.done) {\n const delta = step.value;\n if (delta.type === \"text.delta\") context.emit({ type: \"text.delta\", text: delta.text });\n if (delta.type === \"reasoning.delta\") context.emit({ type: \"reasoning.delta\", text: delta.text });\n if (delta.type === \"tool-call.delta\") {\n context.emit({ type: \"tool-call.delta\", callId: delta.callId, arguments: delta.arguments });\n }\n step = await generation.next();\n }\n if (!step.value.ok) {\n return step.value.error.code === \"cancelled\"\n ? { status: \"cancelled\" }\n : { status: \"failed\", error: step.value.error };\n }\n\n const endedAt = new Date().toISOString();\n const response = step.value.value;\n const calls = response.message.calls ?? [];\n\n await context.commit([{\n type: \"assistant\", runId, content: response.message.content,\n ...(calls.length ? { calls } : {}),\n usage: response.usage,\n generation: { ...(response.model ? { model: response.model } : {}), startedAt, endedAt },\n }]);\n\n if (!calls.length) {\n // The reason the model stopped decides the outcome, and it decides it\n // before the absence of tool calls does. A turn cut off by the output\n // ceiling or declined by a safety classifier is not an answer, and\n // reporting either as `completed` hands the caller a truncated or\n // refused response dressed as a finished one.\n if (response.finishReason === \"length\") {\n return { status: \"failed\", error: {\n code: \"output-truncated\",\n message: \"The response stopped at the output limit before it was finished.\",\n retryable: false,\n } };\n }\n if (response.finishReason === \"refusal\") {\n return { status: \"failed\", error: {\n code: \"refused\",\n message: \"The provider declined to answer.\",\n retryable: false,\n } };\n }\n return { status: \"completed\", output: response.message.content };\n }\n\n if (response.finishReason === \"length\") {\n // A truncated response can carry half-parsed arguments that happen to\n // be valid JSON. None of the batch runs; the model is told why and\n // can reissue.\n await context.commit(calls.map((call): Entry => ({\n type: \"tool.finished\", runId, callId: call.callId,\n result: { content: \"Not executed: the response was truncated. Reissue the call if it is still needed.\", isError: true },\n })));\n continue;\n }\n\n if (!context.tools) {\n await context.commit(calls.map((call): Entry => ({\n type: \"tool.finished\", runId, callId: call.callId,\n result: { content: `No tool named '${call.name}' is available.`, isError: true },\n })));\n continue;\n }\n\n // Recorded before anything runs. On restart an unmatched `tool.started`\n // is how we know the process died mid-effect — and why nothing is\n // re-executed automatically.\n await context.commit(calls.map((call): Entry => ({ type: \"tool.started\", runId, callId: call.callId })));\n\n const outcome = await context.tools.execute({ calls, signal: context.signal });\n await context.commit(outcome.results.map(({ callId, result }): Entry => ({\n type: \"tool.finished\", runId, callId, result,\n })));\n\n // Nothing is half-done here: every call asked for has been answered and\n // committed. Anything waiting is folded in before the next request is\n // built, so a message delivered to a busy session is read without the\n // activation having to be ended and its work thrown away.\n await context.drain?.();\n }\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"loop.js","sourceRoot":"","sources":["../../../../src/harness/adapters/native/loop.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAkC3E;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAA6B;IAC/D,OAAO;QACL,EAAE,EAAE,QAAQ;QACZ,QAAQ,EAAE,SAAS;QAEnB,KAAK,CAAC,GAAG,CAAC,OAAuB;YAC/B,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;YAC1B,IAAI,UAAU,GAAG,IAAI,CAAC;YAEtB,SAAS,CAAC;gBACR,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO;oBAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;gBAE3D,IAAI,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;gBAEhC,IAAI,UAAU,IAAI,OAAO,CAAC,UAAU,IAAI,cAAc,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,cAAc,EAAE,CAAC;oBACpG,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;oBAClC,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;oBACtC,MAAM,GAAG,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;oBAEnC,sEAAsE;oBACtE,sEAAsE;oBACtE,oEAAoE;oBACpE,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,GAAG,MAAM,EAAE,CAAC;wBACtC,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC;4BAC9B,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK;4BAChD,iEAAiE;4BACjE,+DAA+D;4BAC/D,gEAAgE;4BAChE,QAAQ,EAAE,UAAU,CAAC;gCACnB,YAAY,EAAE,OAAO,CAAC,YAAY;gCAClC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,CAAC;6BACrD,CAAC;4BACF,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;4BAC3E,MAAM,EAAE,OAAO,CAAC,MAAM;yBACvB,CAAC,CAAC;wBACH,IAAI,CAAC,OAAO,EAAE,CAAC;4BACb,+DAA+D;4BAC/D,gEAAgE;4BAChE,iEAAiE;4BACjE,uBAAuB;4BACvB,UAAU,GAAG,KAAK,CAAC;wBACrB,CAAC;wBACD,IAAI,OAAO,EAAE,CAAC;4BACZ,kEAAkE;4BAClE,wDAAwD;4BACxD,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;4BACpF,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;wBAC9B,CAAC;oBACH,CAAC;gBACH,CAAC;gBAED,qEAAqE;gBACrE,+DAA+D;gBAC/D,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;gBAE7E,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;gBAC3C,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC;oBACxC,QAAQ,EAAE,OAAO;oBACjB,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACzD,GAAG,CAAC,OAAO,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC9F,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAClF,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACrD,GAAG,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACzC,MAAM,EAAE,OAAO,CAAC,MAAM;iBACvB,CAAC,CAAC;gBAEH,IAAI,IAAI,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,CAAC;gBACnC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;oBAClB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;oBACzB,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY;wBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;oBACxF,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB;wBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;oBAClG,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;wBACrC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;oBAC9F,CAAC;oBACD,IAAI,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,CAAC;gBACjC,CAAC;gBACD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;oBACnB,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW;wBAC1C,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE;wBACzB,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;gBACpD,CAAC;gBAED,MAAM,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;gBACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;gBAClC,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;gBAE3C,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;wBACpB,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO;wBAC3D,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;wBAClC,KAAK,EAAE,QAAQ,CAAC,KAAK;wBACrB,UAAU,EAAE,EAAE,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE;qBACzF,CAAC,CAAC,CAAC;gBAEJ,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;oBAClB,sEAAsE;oBACtE,sEAAsE;oBACtE,mEAAmE;oBACnE,kEAAkE;oBAClE,8CAA8C;oBAC9C,IAAI,QAAQ,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;wBACvC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;gCAChC,IAAI,EAAE,kBAAkB;gCACxB,OAAO,EAAE,kEAAkE;gCAC3E,SAAS,EAAE,KAAK;6BACjB,EAAE,CAAC;oBACN,CAAC;oBACD,IAAI,QAAQ,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;wBACxC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;gCAChC,IAAI,EAAE,SAAS;gCACf,OAAO,EAAE,kCAAkC;gCAC3C,SAAS,EAAE,KAAK;6BACjB,EAAE,CAAC;oBACN,CAAC;oBACD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;gBACnE,CAAC;gBAED,IAAI,QAAQ,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;oBACvC,sEAAsE;oBACtE,mEAAmE;oBACnE,eAAe;oBACf,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAS,EAAE,CAAC,CAAC;wBAC/C,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM;wBACjD,MAAM,EAAE,EAAE,OAAO,EAAE,mFAAmF,EAAE,OAAO,EAAE,IAAI,EAAE;qBACxH,CAAC,CAAC,CAAC,CAAC;oBACL,SAAS;gBACX,CAAC;gBAED,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;oBACnB,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAS,EAAE,CAAC,CAAC;wBAC/C,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM;wBACjD,MAAM,EAAE,EAAE,OAAO,EAAE,kBAAkB,IAAI,CAAC,IAAI,iBAAiB,EAAE,OAAO,EAAE,IAAI,EAAE;qBACjF,CAAC,CAAC,CAAC,CAAC;oBACL,SAAS;gBACX,CAAC;gBAED,wEAAwE;gBACxE,kEAAkE;gBAClE,6BAA6B;gBAC7B,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAS,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;gBAEzG,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC/E,MAAM,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAS,EAAE,CAAC,CAAC;oBACvE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM;iBAC7C,CAAC,CAAC,CAAC,CAAC;gBAEL,wEAAwE;gBACxE,sEAAsE;gBACtE,sEAAsE;gBACtE,0DAA0D;gBAC1D,MAAM,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;YAC1B,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { Harness, HarnessContext, HarnessResult } from \"../../harness.js\";\nimport type { Entry } from \"../../../session/entry.js\";\nimport type { Message, Model } from \"../../../model/model.js\";\nimport { foldedThrough, toMessages } from \"../../../session/messages.js\";\nimport { compactionCut, estimateTokens, summarize } from \"./compaction.js\";\n\nexport interface NativeHarnessOptions {\n model: Model;\n /** Fold older turns into a summary once a request passes this size. */\n compaction?: {\n /**\n * The estimated request size a fold happens above.\n *\n * A budget, not a provider fact: it belongs below the input window of the\n * model that answers, with room for the turn that follows the fold. Folding\n * early spends a model call and hands the run a paraphrase of work the log\n * still holds in full; folding late risks the request a provider refuses.\n */\n maxInputTokens: number;\n /**\n * Who writes the summary. Defaults to the run's own model, so an\n * application that says nothing meets neither a second provider nor a\n * second credential.\n *\n * A cheaper one is right where the span being folded is long and reading it\n * back is all the work — this is not the reasoning the main model is being\n * paid for. It still has to take the whole span in one request, and to be\n * good enough that what it drops is what did not matter.\n */\n model?: Model;\n prompt?: (messages: readonly Message[]) => string;\n };\n maxOutputTokens?: number;\n temperature?: number;\n /** How hard the model should think, where the provider supports it. */\n effort?: \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\";\n}\n\n/**\n * Ask the model, run what it asks for, repeat.\n *\n * The loop holds no transcript. Every turn it re-reads `context.history()`,\n * which projects the committed log, and every result it produces goes back\n * through `context.commit` before the next turn is built. There is exactly one\n * representation of the conversation and it is the log.\n */\nexport function createNativeHarness(options: NativeHarnessOptions): Harness {\n return {\n id: \"native\",\n recovery: \"history\",\n\n async run(context: HarnessContext): Promise<HarnessResult> {\n const { runId } = context;\n let compacting = true;\n\n for (;;) {\n if (context.signal.aborted) return { status: \"cancelled\" };\n\n let history = context.history();\n\n if (compacting && options.compaction && estimateTokens(history) > options.compaction.maxInputTokens) {\n const entries = context.entries();\n const folded = foldedThrough(entries);\n const cut = compactionCut(entries);\n\n // Only ever fold forward. A cut at or behind the last summary removes\n // nothing, so committing one would buy a model call and another entry\n // and leave the request exactly as large — every turn, without end.\n if (cut !== undefined && cut > folded) {\n const summary = await summarize({\n model: options.compaction.model ?? options.model,\n // Exactly the span being replaced. Summarizing the whole history\n // put everything after the cut into the summary as well, so it\n // stayed in the request twice: once verbatim, once paraphrased.\n messages: toMessages({\n instructions: context.instructions,\n entries: entries.filter((entry) => entry.seq <= cut),\n }),\n ...(options.compaction.prompt ? { prompt: options.compaction.prompt } : {}),\n signal: context.signal,\n });\n if (!summary) {\n // Paid for and produced nothing. Attempted once per activation\n // rather than once per turn: retrying bought another model call\n // and another failure every turn, and the request stayed exactly\n // as large either way.\n compacting = false;\n }\n if (summary) {\n // The folded entries stay in the log. Compaction changes what the\n // model is shown on the next turn, never what happened.\n await context.commit([{ type: \"summary\", runId, content: summary, replaces: cut }]);\n history = context.history();\n }\n }\n }\n\n // One cache mark, at the end of the system prefix: instructions plus\n // run-scoped context, which are fixed for the life of the run.\n const cacheAfter = history.findIndex((message) => message.role !== \"system\");\n\n const startedAt = new Date().toISOString();\n const generation = options.model.generate({\n messages: history,\n ...(context.tools ? { tools: context.tools.list() } : {}),\n ...(options.maxOutputTokens !== undefined ? { maxOutputTokens: options.maxOutputTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n ...(options.effort ? { effort: options.effort } : {}),\n ...(cacheAfter > 0 ? { cacheAfter } : {}),\n signal: context.signal,\n });\n\n let step = await generation.next();\n while (!step.done) {\n const delta = step.value;\n if (delta.type === \"text.delta\") context.emit({ type: \"text.delta\", text: delta.text });\n if (delta.type === \"reasoning.delta\") context.emit({ type: \"reasoning.delta\", text: delta.text });\n if (delta.type === \"tool-call.delta\") {\n context.emit({ type: \"tool-call.delta\", callId: delta.callId, arguments: delta.arguments });\n }\n step = await generation.next();\n }\n if (!step.value.ok) {\n return step.value.error.code === \"cancelled\"\n ? { status: \"cancelled\" }\n : { status: \"failed\", error: step.value.error };\n }\n\n const endedAt = new Date().toISOString();\n const response = step.value.value;\n const calls = response.message.calls ?? [];\n\n await context.commit([{\n type: \"assistant\", runId, content: response.message.content,\n ...(calls.length ? { calls } : {}),\n usage: response.usage,\n generation: { ...(response.model ? { model: response.model } : {}), startedAt, endedAt },\n }]);\n\n if (!calls.length) {\n // The reason the model stopped decides the outcome, and it decides it\n // before the absence of tool calls does. A turn cut off by the output\n // ceiling or declined by a safety classifier is not an answer, and\n // reporting either as `completed` hands the caller a truncated or\n // refused response dressed as a finished one.\n if (response.finishReason === \"length\") {\n return { status: \"failed\", error: {\n code: \"output-truncated\",\n message: \"The response stopped at the output limit before it was finished.\",\n retryable: false,\n } };\n }\n if (response.finishReason === \"refusal\") {\n return { status: \"failed\", error: {\n code: \"refused\",\n message: \"The provider declined to answer.\",\n retryable: false,\n } };\n }\n return { status: \"completed\", output: response.message.content };\n }\n\n if (response.finishReason === \"length\") {\n // A truncated response can carry half-parsed arguments that happen to\n // be valid JSON. None of the batch runs; the model is told why and\n // can reissue.\n await context.commit(calls.map((call): Entry => ({\n type: \"tool.finished\", runId, callId: call.callId,\n result: { content: \"Not executed: the response was truncated. Reissue the call if it is still needed.\", isError: true },\n })));\n continue;\n }\n\n if (!context.tools) {\n await context.commit(calls.map((call): Entry => ({\n type: \"tool.finished\", runId, callId: call.callId,\n result: { content: `No tool named '${call.name}' is available.`, isError: true },\n })));\n continue;\n }\n\n // Recorded before anything runs. On restart an unmatched `tool.started`\n // is how we know the process died mid-effect — and why nothing is\n // re-executed automatically.\n await context.commit(calls.map((call): Entry => ({ type: \"tool.started\", runId, callId: call.callId })));\n\n const outcome = await context.tools.execute({ calls, signal: context.signal });\n await context.commit(outcome.results.map(({ callId, result }): Entry => ({\n type: \"tool.finished\", runId, callId, result,\n })));\n\n // Nothing is half-done here: every call asked for has been answered and\n // committed. Anything waiting is folded in before the next request is\n // built, so a message delivered to a busy session is read without the\n // activation having to be ended and its work thrown away.\n await context.drain?.();\n }\n },\n };\n}\n"]}
|
package/dist/run.js
CHANGED
|
@@ -276,6 +276,7 @@ export function runAgent(options) {
|
|
|
276
276
|
history: () => toMessages({
|
|
277
277
|
instructions: agent.instructions,
|
|
278
278
|
entries: log.entries,
|
|
279
|
+
...(agent.attribution ? { attribution: true } : {}),
|
|
279
280
|
...(options.context ? { context: options.context } : {}),
|
|
280
281
|
}),
|
|
281
282
|
entries: () => log.entries,
|
package/dist/run.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Agent, AgentRun, Arrival, RunAgentOptions, RunResult } from \"./agent.js\";\nimport type { Failure } from \"./result.js\";\nimport type { Update } from \"./harness/harness.js\";\nimport type { Delivery, Store, StoreConflict, StoreError } from \"./store/store.js\";\nimport type { Content } from \"./content.js\";\nimport type { Entry, Stored, Usage } from \"./session/entry.js\";\nimport { err } from \"./result.js\";\nimport { createLog } from \"./session/log.js\";\nimport { toMessages } from \"./session/messages.js\";\nimport { createExecutor } from \"./tools/execute.js\";\n\n/**\n * Start an agent on a session.\n *\n * Without a store the log lives in memory and the session is ephemeral; with\n * one, every commit is durable before the next turn is built. The agent\n * definition is identical either way — durability is a composition choice, not\n * a different program.\n */\nexport function runAgent(options: RunAgentOptions): AgentRun {\n // A claim names its own session; a caller sending into a new one names none.\n const claim = options.claim;\n const sessionId = claim?.sessionId ?? options.sessionId ?? crypto.randomUUID();\n const controller = new AbortController();\n // Composed rather than forwarded. `addEventListener(\"abort\", …)` never fires\n // on a signal that has already aborted, so a caller who gave up before\n // calling got a full run anyway; and a listener added per run to a signal\n // that outlives it is a leak the run cannot clean up.\n const signal = options.signal\n ? AbortSignal.any([options.signal, controller.signal])\n : controller.signal;\n\n const updates = createChannel<Update>();\n const result = execute();\n\n return {\n result,\n cancel: () => controller.abort(),\n [Symbol.asyncIterator]: () => updates.iterator(),\n };\n\n async function execute(): Promise<RunResult> {\n // What this activation consumed. Summed from the `assistant` entries it\n // commits rather than reported by the harness, so one answer serves a loop\n // we own and a loop we do not — and so a run that failed still says what it\n // burned on the way there.\n const usage: Usage = {};\n\n try {\n const { agent, store } = options;\n const restored = store ? await open(store, sessionId, agent, options.key) : undefined;\n if (restored && !restored.ok) {\n return {\n status: \"failed\", error: restored.error,\n seq: positionOf(restored.error, 0), usage,\n };\n }\n // The store's own position, not the last entry's. They agree only while\n // nothing has ever been folded away, and the store is the one that knows.\n const log = createLog(restored?.value.entries ?? [], restored?.value.seq ?? 0);\n const stopping = (error: Failure): RunResult => ({ status: \"failed\", error, seq: log.seq, usage });\n\n // A claim was read at a position. If the session has moved since, this\n // worker lost, and the deliveries it was carrying are still where it\n // found them.\n if (claim && claim.seq !== log.seq) {\n return stopping({ code: \"conflict\", message: `Session is at ${log.seq}, not ${claim.seq}`, retryable: true });\n }\n\n // A ceiling on money needs something that can count it. Refused here\n // rather than silently ignored, because a limit that is declared and not\n // enforced is the most expensive kind of nothing.\n\n // The activation still open on this log, if one is. It decides three\n // things below: whether there is anything to resume, which run a\n // resumption continues, and which run a fresh activation supersedes.\n const interrupted = openRunId(log.entries);\n\n // What this run is adding, then what was waiting. A claim's deliveries\n // are named so the log records who each came from; anything the worker\n // supplies itself leads, because the only thing that ever does is\n // orientation for a harness with nowhere else to put it.\n const arrivals: readonly Arrival[] = [\n ...arrivalsOf(options.input),\n ...(claim?.pending ?? []).map((delivery): Arrival => ({\n input: delivery.input,\n ...(delivery.from ? { from: delivery.from } : {}),\n })),\n ];\n\n // A resumption continues the run it was handed, under that run's id. A\n // fresh id would leave the log holding a `run.started` nothing closes and\n // a `run.finished` nothing opened — a pair that only the store's\n // type-based rule saves, and that anything grouping entries by run reads\n // as broken. A resumption without an interrupted run is refused below.\n const runId = arrivals.length || !interrupted ? crypto.randomUUID() : interrupted;\n\n // Deliveries ride the next commit, which is the one that records the work\n // that produced them. They used to be held back to the run's final entry\n // so that a failed run handed nothing to anyone — a stronger promise than\n // any caller needs, and it was being paid for with a lie: a `send` tool\n // returned \"Delivered\" and interrupted the recipient many turns before\n // the delivery existed, and never at all if a later turn failed.\n const deliveries: Delivery[] = [];\n\n // The ceiling has one owner, and it is here rather than in a harness.\n // Counting committed entries is the only way to bound a harness that owns\n // its own loop — which is most of them — so a limit declared on the agent\n // holds for every one instead of only for ours.\n const limits = agent.limits;\n let stopped: Failure | undefined;\n let turns = 0;\n let toolCalls = 0;\n\n function exceeded(): Failure | undefined {\n if (!limits) return undefined;\n if (limits.maxTurns !== undefined && turns >= limits.maxTurns) {\n return { code: \"turn-limit\", message: `Stopped after ${turns} turns.`, retryable: false };\n }\n if (limits.maxToolCalls !== undefined && toolCalls >= limits.maxToolCalls) {\n return { code: \"tool-call-limit\", message: `Stopped after ${toolCalls} tool calls.`, retryable: false };\n }\n // Checked where work is recorded rather than on a timer: a deadline\n // stops the next piece of work, it does not interrupt one mid-flight.\n if (limits.deadline !== undefined && Date.now() >= Date.parse(limits.deadline)) {\n return { code: \"deadline\", message: `Stopped at the deadline of ${limits.deadline}.`, retryable: false };\n }\n return undefined;\n }\n\n // How many deliveries the next write takes off this session's own queue:\n // what the claim was carrying, and afterwards whatever `drain` folded in.\n // Cleared by the write that lands, because the input is gone once the\n // entries carrying it are durable, and not before.\n let take = claim?.pending.length ?? 0;\n\n async function commit(entries: readonly Entry[], enqueue?: readonly Delivery[]): Promise<void> {\n const outgoing = [...deliveries, ...(enqueue ?? [])];\n deliveries.length = 0;\n if (!entries.length && !outgoing.length) return;\n // Counted before the write, not after: the tokens were consumed whether\n // or not this entry wins the compare-and-swap, and a run that reports\n // nothing after paying for a generation is the opposite of what\n // `RunResult.usage` promises.\n for (const entry of entries) {\n if (entry.type === \"assistant\" && entry.usage) accumulate(usage, entry.usage);\n }\n\n if (store) {\n const written = await store.append({\n sessionId, expectedSeq: log.seq, entries,\n ...(outgoing.length ? { enqueue: outgoing } : {}),\n ...(take ? { takePending: take } : {}),\n });\n if (!written.ok) throw new CommitFailed(written.error, positionOf(written.error, log.seq));\n take = 0;\n }\n for (const stored of log.append(entries)) updates.push({ type: \"entry\", entry: stored });\n\n if (!stopped) {\n for (const entry of entries) {\n if (entry.type === \"assistant\") turns += 1;\n // `tool.finished`, not `tool.started`: the start is committed before\n // the batch executes, so counting it there cancelled the very batch\n // that reached the ceiling — `maxToolCalls: 1` ran no tools at all,\n // and said it had stopped after one.\n if (entry.type === \"tool.finished\") toolCalls += 1;\n }\n stopped = exceeded();\n if (stopped) controller.abort();\n }\n }\n\n /**\n * What the agent wants delivered as this run ends.\n *\n * A callback that throws must not cost the log its terminal entry: the\n * run would stay open, and an open run nothing closes is claimed by\n * `interrupted` every window for ever — the trap the no-recovery path above\n * exists to close. So the throw is held, the run is ended without the\n * deliveries, and it is re-thrown once the log is safe.\n */\n let reportFailure: unknown;\n async function reporting(run: {\n runId: string; outcome: \"completed\" | \"failed\" | \"cancelled\"; output: Content;\n }): Promise<readonly Delivery[]> {\n if (!agent.finished) return [];\n try {\n return await agent.finished({ sessionId, ...run });\n } catch (error) {\n reportFailure = error;\n return [];\n }\n }\n\n // Input that arrived while this activation was running, taken from the\n // front of the queue and committed as part of it. `takePending` removes\n // exactly what was committed, in the same write, so losing the position\n // loses the turn and never the messages.\n async function drain(): Promise<number> {\n if (!store) return 0;\n // Nothing is folded into an activation that is over. Draining here\n // committed the waiting deliveries and took them off the queue, and the\n // loop then ended — leaving a peer's message answered by nobody and\n // invisible to `next` (nothing pending) and to `interrupted` (no open run),\n // which is the trap this whole path exists to close.\n if (signal.aborted) return 0;\n const current = await store.read({ sessionId, afterSeq: log.seq });\n if (!current.ok || !current.value.pending.length) return 0;\n const waiting = current.value.pending;\n take = waiting.length;\n await commit(waiting.map((delivery): Entry => ({\n type: \"run.started\", runId, input: delivery.input,\n ...(delivery.from ? { from: delivery.from } : {}),\n })));\n return waiting.length;\n }\n\n const executor = agent.tools?.length\n ? createExecutor({\n tools: agent.tools,\n ...(agent.decide ? { decide: agent.decide } : {}),\n sessionId, runId,\n enqueue: (delivery) => deliveries.push(delivery),\n report: (callId, data) => updates.push({ type: \"tool.progress\", callId, data }),\n })\n : undefined;\n\n if (!arrivals.length) {\n // An open run is what a resumption continues. Without one there is\n // nothing half-done here: a closed log would otherwise take a second\n // `run.finished` with no beginning to match it.\n if (!interrupted) {\n return stopping({\n code: \"nothing-to-resume\",\n message: \"An activation with no input continues an interrupted run, and this session has none open.\",\n retryable: false,\n });\n }\n if (agent.harness.recovery !== \"history\") {\n // Asked to continue a log this harness cannot read. The interrupted\n // run is ended rather than left open, because nothing else will\n // continue it either — the harness is a property of the session — and\n // a run that stays open is handed back by `interrupted` every window,\n // for ever. A worker that provisions anything before it runs pays for\n // that each time round.\n const failure: Failure = {\n code: \"no-recovery\",\n message: `The ${agent.harness.id} harness cannot restart from history, so this activation cannot be continued.`,\n retryable: false,\n };\n // Whoever was waiting on it is told, in the write that ends it. A\n // parent expecting a child that can now never report is the same dead\n // end one layer up.\n const told = await reporting({ runId: interrupted, outcome: \"failed\", output: \"\" });\n await commit([{ type: \"run.finished\", runId: interrupted, outcome: \"failed\", error: failure }], told);\n return stopping(failure);\n }\n }\n\n // An interrupted predecessor is ended before a new activation begins over\n // it, so one session never holds two open runs. Only on a claim: the\n // store established there that nothing else is running, where a bare\n // `sessionId` establishes nothing and the run may still be live.\n if (arrivals.length && interrupted && claim) {\n await commit([{\n type: \"run.finished\", runId: interrupted, outcome: \"cancelled\",\n error: {\n code: \"superseded\",\n message: \"This activation was interrupted, and a later one began over it.\",\n retryable: false,\n },\n }]);\n }\n\n // Nothing opens a resumption. A `run.started` recording input nobody sent\n // would be a second beginning for one conversation; the loop reads the\n // history it was handed instead, sees the call that already ran, and\n // carries on rather than asking for it again.\n await commit(arrivals.map((arrival): Entry => ({\n type: \"run.started\", runId, input: arrival.input,\n ...(arrival.from ? { from: arrival.from } : {}),\n })));\n\n // Every ceiling is checked where work is recorded, and a resumption\n // records nothing before its first turn — so a deadline that had already\n // passed was never seen. Checked once here too, before the harness is\n // handed anything.\n stopped ??= exceeded();\n\n const outcome = stopped\n ? { status: \"failed\" as const, error: stopped }\n : await agent.harness.run({\n sessionId, runId,\n instructions: agent.instructions,\n history: () => toMessages({\n instructions: agent.instructions,\n entries: log.entries,\n ...(options.context ? { context: options.context } : {}),\n }),\n entries: () => log.entries,\n ...(options.context ? { context: options.context } : {}),\n ...(executor ? { tools: executor } : {}),\n commit: (entries) => commit(entries),\n ...(store ? { drain } : {}),\n emit: (update) => updates.push(update),\n signal,\n });\n\n // A harness stopped by the ceiling reports a cancellation, because that is\n // all it saw. The run knows why it was cancelled and says so instead.\n const settled = stopped && outcome.status === \"cancelled\"\n ? { status: \"failed\" as const, error: stopped }\n : outcome;\n\n const output = settled.status === \"completed\" ? settled.output : \"\";\n const finished = await reporting({ runId, outcome: settled.status, output });\n\n await commit(\n [{\n type: \"run.finished\", runId,\n outcome: settled.status === \"completed\" ? \"completed\" : settled.status === \"cancelled\" ? \"cancelled\" : \"failed\",\n ...(settled.status === \"failed\" ? { error: settled.error } : {}),\n }],\n finished,\n );\n\n if (settled.status === \"completed\") {\n return { status: \"completed\", output: settled.output, seq: log.seq, usage };\n }\n if (settled.status === \"cancelled\") return { status: \"cancelled\", seq: log.seq, usage };\n return stopping(settled.error);\n } catch (error) {\n if (error instanceof CommitFailed) {\n return { status: \"failed\", error: error.failure, seq: error.seq, usage };\n }\n throw error;\n } finally {\n updates.close();\n }\n }\n}\n\nclass CommitFailed extends Error {\n constructor(readonly failure: Failure, readonly seq: number) {\n super(failure.message);\n }\n}\n\n/**\n * Where the session actually is after a refused write. A conflict knows; every\n * other failure leaves the caller with the position it already had. Reporting\n * zero told a caller at forty that it was at the beginning.\n *\n * The `typeof` is not defensive noise: a store may report a conflict, and only\n * a `StoreConflict` carries the position. Casting on the code alone produced a\n * `RunResult.seq` of `undefined` under a type that says `number`.\n */\nfunction positionOf(error: StoreError, fallback: number): number {\n const actual = (error as StoreConflict).actualSeq;\n return error.code === \"conflict\" && typeof actual === \"number\" ? actual : fallback;\n}\n\nconst isArrival = (value: unknown): value is Arrival =>\n typeof value === \"object\" && value !== null && !Array.isArray(value) && \"input\" in value;\n\n/**\n * One arrival, or several.\n *\n * The elements decide it, because the outer shape cannot: an arrival names its\n * `input`, a content part names its `type`, and nothing is both. Reading every\n * array as a batch is what turned one image-and-text message into one entry per\n * part, each holding a part where whole content belongs.\n */\nfunction arrivalsOf(input: RunAgentOptions[\"input\"]): readonly Arrival[] {\n if (input === undefined) return [];\n // Nothing sent is nothing sent. Falling through committed a `run.started`\n // whose input was an empty array of content parts.\n if (Array.isArray(input) && input.length === 0) return [];\n if (Array.isArray(input) && input.length > 0 && input.every(isArrival)) return input;\n if (isArrival(input)) return [input];\n return [{ input: input as Content }];\n}\n\n/**\n * The activation still open on this log, if one is.\n *\n * `run.started` opens and `run.finished` closes, which is the same rule the\n * store maintains its claim from — read here from the entries rather than asked\n * of the store, because the log is the only thing that knows.\n */\nfunction openRunId(entries: readonly Stored[]): string | undefined {\n let open: string | undefined;\n for (const entry of entries) {\n if (entry.type === \"run.started\") open = entry.runId;\n if (entry.type === \"run.finished\") open = undefined;\n }\n return open;\n}\n\n/** Token counts add; unknown stays unknown rather than becoming a zero. */\nfunction accumulate(total: Usage, turn: Usage): void {\n for (const key of [\"inputTokens\", \"outputTokens\", \"cacheReadTokens\", \"cacheWriteTokens\"] as const) {\n if (turn[key] !== undefined) total[key] = (total[key] ?? 0) + turn[key]!;\n }\n}\n\nasync function open(store: Store, sessionId: string, agent: Agent, key?: string) {\n const existing = await store.read({ sessionId });\n if (existing.ok) {\n // `version` is documented as the boundary past which an agent is no longer\n // compatible with an existing session. Nothing was comparing it, so the\n // boundary held for exactly as long as nobody crossed it.\n const stored = existing.value.agent;\n if (stored.id !== agent.id || stored.version !== agent.version) {\n return err<StoreError>({\n code: \"conflict\",\n message: `Session ${sessionId} belongs to ${stored.id}@${stored.version}, not ${agent.id}@${agent.version}`,\n retryable: false,\n });\n }\n return existing;\n }\n if (existing.error.code !== \"not-found\") return existing;\n const created = await store.create({\n sessionId, agent: { id: agent.id, version: agent.version }, ...(key ? { key } : {}),\n });\n if (!created.ok) return created;\n return store.read({ sessionId });\n}\n\n/**\n * A bounded fan-out queue for ephemeral updates.\n *\n * One buffer of recent updates, which every listener starts from and which\n * never grows past its limit. That single rule gives three things:\n *\n * **A run nobody drains costs a fixed amount.** A headless worker used to\n * accumulate every delta of an entire activation, because the queue only ever\n * grew. Dropping the oldest is safe precisely because updates are not recovery\n * state and the committed log is.\n *\n * **Arriving late loses nothing that matters.** The first entry commits before\n * `runAgent` returns, so a caller that iterates immediately would otherwise\n * never see its own run start. A UI reconnecting mid-run gets recent history\n * for the same reason, without asking the store for it.\n *\n * **Everyone sees everything.** Each listener drains its own copy, so an\n * application can watch a run for telemetry and for a UI at once instead of the\n * two stealing events from each other.\n */\nfunction createChannel<T>(limit = 1_024) {\n interface Listener { buffer: T[]; wake?: () => void }\n const listeners = new Set<Listener>();\n const replay: T[] = [];\n let closed = false;\n\n const bounded = (buffer: T[], value: T) => {\n if (buffer.length >= limit) buffer.shift();\n buffer.push(value);\n };\n\n return {\n push(value: T) {\n bounded(replay, value);\n for (const listener of listeners) {\n bounded(listener.buffer, value);\n listener.wake?.();\n }\n },\n close() {\n closed = true;\n for (const listener of listeners) listener.wake?.();\n },\n iterator(): AsyncIterator<T> {\n const listener: Listener = { buffer: [...replay] };\n listeners.add(listener);\n const done = (): IteratorResult<T> => {\n listeners.delete(listener);\n return { value: undefined, done: true };\n };\n return {\n async next(): Promise<IteratorResult<T>> {\n for (;;) {\n const value = listener.buffer.shift();\n if (value !== undefined) return { value, done: false };\n if (closed) return done();\n await new Promise<void>((resolve) => { listener.wake = resolve; });\n }\n },\n async return(): Promise<IteratorResult<T>> { return done(); },\n };\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Agent, AgentRun, Arrival, RunAgentOptions, RunResult } from \"./agent.js\";\nimport type { Failure } from \"./result.js\";\nimport type { Update } from \"./harness/harness.js\";\nimport type { Delivery, Store, StoreConflict, StoreError } from \"./store/store.js\";\nimport type { Content } from \"./content.js\";\nimport type { Entry, Stored, Usage } from \"./session/entry.js\";\nimport { err } from \"./result.js\";\nimport { createLog } from \"./session/log.js\";\nimport { toMessages } from \"./session/messages.js\";\nimport { createExecutor } from \"./tools/execute.js\";\n\n/**\n * Start an agent on a session.\n *\n * Without a store the log lives in memory and the session is ephemeral; with\n * one, every commit is durable before the next turn is built. The agent\n * definition is identical either way — durability is a composition choice, not\n * a different program.\n */\nexport function runAgent(options: RunAgentOptions): AgentRun {\n // A claim names its own session; a caller sending into a new one names none.\n const claim = options.claim;\n const sessionId = claim?.sessionId ?? options.sessionId ?? crypto.randomUUID();\n const controller = new AbortController();\n // Composed rather than forwarded. `addEventListener(\"abort\", …)` never fires\n // on a signal that has already aborted, so a caller who gave up before\n // calling got a full run anyway; and a listener added per run to a signal\n // that outlives it is a leak the run cannot clean up.\n const signal = options.signal\n ? AbortSignal.any([options.signal, controller.signal])\n : controller.signal;\n\n const updates = createChannel<Update>();\n const result = execute();\n\n return {\n result,\n cancel: () => controller.abort(),\n [Symbol.asyncIterator]: () => updates.iterator(),\n };\n\n async function execute(): Promise<RunResult> {\n // What this activation consumed. Summed from the `assistant` entries it\n // commits rather than reported by the harness, so one answer serves a loop\n // we own and a loop we do not — and so a run that failed still says what it\n // burned on the way there.\n const usage: Usage = {};\n\n try {\n const { agent, store } = options;\n const restored = store ? await open(store, sessionId, agent, options.key) : undefined;\n if (restored && !restored.ok) {\n return {\n status: \"failed\", error: restored.error,\n seq: positionOf(restored.error, 0), usage,\n };\n }\n // The store's own position, not the last entry's. They agree only while\n // nothing has ever been folded away, and the store is the one that knows.\n const log = createLog(restored?.value.entries ?? [], restored?.value.seq ?? 0);\n const stopping = (error: Failure): RunResult => ({ status: \"failed\", error, seq: log.seq, usage });\n\n // A claim was read at a position. If the session has moved since, this\n // worker lost, and the deliveries it was carrying are still where it\n // found them.\n if (claim && claim.seq !== log.seq) {\n return stopping({ code: \"conflict\", message: `Session is at ${log.seq}, not ${claim.seq}`, retryable: true });\n }\n\n // A ceiling on money needs something that can count it. Refused here\n // rather than silently ignored, because a limit that is declared and not\n // enforced is the most expensive kind of nothing.\n\n // The activation still open on this log, if one is. It decides three\n // things below: whether there is anything to resume, which run a\n // resumption continues, and which run a fresh activation supersedes.\n const interrupted = openRunId(log.entries);\n\n // What this run is adding, then what was waiting. A claim's deliveries\n // are named so the log records who each came from; anything the worker\n // supplies itself leads, because the only thing that ever does is\n // orientation for a harness with nowhere else to put it.\n const arrivals: readonly Arrival[] = [\n ...arrivalsOf(options.input),\n ...(claim?.pending ?? []).map((delivery): Arrival => ({\n input: delivery.input,\n ...(delivery.from ? { from: delivery.from } : {}),\n })),\n ];\n\n // A resumption continues the run it was handed, under that run's id. A\n // fresh id would leave the log holding a `run.started` nothing closes and\n // a `run.finished` nothing opened — a pair that only the store's\n // type-based rule saves, and that anything grouping entries by run reads\n // as broken. A resumption without an interrupted run is refused below.\n const runId = arrivals.length || !interrupted ? crypto.randomUUID() : interrupted;\n\n // Deliveries ride the next commit, which is the one that records the work\n // that produced them. They used to be held back to the run's final entry\n // so that a failed run handed nothing to anyone — a stronger promise than\n // any caller needs, and it was being paid for with a lie: a `send` tool\n // returned \"Delivered\" and interrupted the recipient many turns before\n // the delivery existed, and never at all if a later turn failed.\n const deliveries: Delivery[] = [];\n\n // The ceiling has one owner, and it is here rather than in a harness.\n // Counting committed entries is the only way to bound a harness that owns\n // its own loop — which is most of them — so a limit declared on the agent\n // holds for every one instead of only for ours.\n const limits = agent.limits;\n let stopped: Failure | undefined;\n let turns = 0;\n let toolCalls = 0;\n\n function exceeded(): Failure | undefined {\n if (!limits) return undefined;\n if (limits.maxTurns !== undefined && turns >= limits.maxTurns) {\n return { code: \"turn-limit\", message: `Stopped after ${turns} turns.`, retryable: false };\n }\n if (limits.maxToolCalls !== undefined && toolCalls >= limits.maxToolCalls) {\n return { code: \"tool-call-limit\", message: `Stopped after ${toolCalls} tool calls.`, retryable: false };\n }\n // Checked where work is recorded rather than on a timer: a deadline\n // stops the next piece of work, it does not interrupt one mid-flight.\n if (limits.deadline !== undefined && Date.now() >= Date.parse(limits.deadline)) {\n return { code: \"deadline\", message: `Stopped at the deadline of ${limits.deadline}.`, retryable: false };\n }\n return undefined;\n }\n\n // How many deliveries the next write takes off this session's own queue:\n // what the claim was carrying, and afterwards whatever `drain` folded in.\n // Cleared by the write that lands, because the input is gone once the\n // entries carrying it are durable, and not before.\n let take = claim?.pending.length ?? 0;\n\n async function commit(entries: readonly Entry[], enqueue?: readonly Delivery[]): Promise<void> {\n const outgoing = [...deliveries, ...(enqueue ?? [])];\n deliveries.length = 0;\n if (!entries.length && !outgoing.length) return;\n // Counted before the write, not after: the tokens were consumed whether\n // or not this entry wins the compare-and-swap, and a run that reports\n // nothing after paying for a generation is the opposite of what\n // `RunResult.usage` promises.\n for (const entry of entries) {\n if (entry.type === \"assistant\" && entry.usage) accumulate(usage, entry.usage);\n }\n\n if (store) {\n const written = await store.append({\n sessionId, expectedSeq: log.seq, entries,\n ...(outgoing.length ? { enqueue: outgoing } : {}),\n ...(take ? { takePending: take } : {}),\n });\n if (!written.ok) throw new CommitFailed(written.error, positionOf(written.error, log.seq));\n take = 0;\n }\n for (const stored of log.append(entries)) updates.push({ type: \"entry\", entry: stored });\n\n if (!stopped) {\n for (const entry of entries) {\n if (entry.type === \"assistant\") turns += 1;\n // `tool.finished`, not `tool.started`: the start is committed before\n // the batch executes, so counting it there cancelled the very batch\n // that reached the ceiling — `maxToolCalls: 1` ran no tools at all,\n // and said it had stopped after one.\n if (entry.type === \"tool.finished\") toolCalls += 1;\n }\n stopped = exceeded();\n if (stopped) controller.abort();\n }\n }\n\n /**\n * What the agent wants delivered as this run ends.\n *\n * A callback that throws must not cost the log its terminal entry: the\n * run would stay open, and an open run nothing closes is claimed by\n * `interrupted` every window for ever — the trap the no-recovery path above\n * exists to close. So the throw is held, the run is ended without the\n * deliveries, and it is re-thrown once the log is safe.\n */\n let reportFailure: unknown;\n async function reporting(run: {\n runId: string; outcome: \"completed\" | \"failed\" | \"cancelled\"; output: Content;\n }): Promise<readonly Delivery[]> {\n if (!agent.finished) return [];\n try {\n return await agent.finished({ sessionId, ...run });\n } catch (error) {\n reportFailure = error;\n return [];\n }\n }\n\n // Input that arrived while this activation was running, taken from the\n // front of the queue and committed as part of it. `takePending` removes\n // exactly what was committed, in the same write, so losing the position\n // loses the turn and never the messages.\n async function drain(): Promise<number> {\n if (!store) return 0;\n // Nothing is folded into an activation that is over. Draining here\n // committed the waiting deliveries and took them off the queue, and the\n // loop then ended — leaving a peer's message answered by nobody and\n // invisible to `next` (nothing pending) and to `interrupted` (no open run),\n // which is the trap this whole path exists to close.\n if (signal.aborted) return 0;\n const current = await store.read({ sessionId, afterSeq: log.seq });\n if (!current.ok || !current.value.pending.length) return 0;\n const waiting = current.value.pending;\n take = waiting.length;\n await commit(waiting.map((delivery): Entry => ({\n type: \"run.started\", runId, input: delivery.input,\n ...(delivery.from ? { from: delivery.from } : {}),\n })));\n return waiting.length;\n }\n\n const executor = agent.tools?.length\n ? createExecutor({\n tools: agent.tools,\n ...(agent.decide ? { decide: agent.decide } : {}),\n sessionId, runId,\n enqueue: (delivery) => deliveries.push(delivery),\n report: (callId, data) => updates.push({ type: \"tool.progress\", callId, data }),\n })\n : undefined;\n\n if (!arrivals.length) {\n // An open run is what a resumption continues. Without one there is\n // nothing half-done here: a closed log would otherwise take a second\n // `run.finished` with no beginning to match it.\n if (!interrupted) {\n return stopping({\n code: \"nothing-to-resume\",\n message: \"An activation with no input continues an interrupted run, and this session has none open.\",\n retryable: false,\n });\n }\n if (agent.harness.recovery !== \"history\") {\n // Asked to continue a log this harness cannot read. The interrupted\n // run is ended rather than left open, because nothing else will\n // continue it either — the harness is a property of the session — and\n // a run that stays open is handed back by `interrupted` every window,\n // for ever. A worker that provisions anything before it runs pays for\n // that each time round.\n const failure: Failure = {\n code: \"no-recovery\",\n message: `The ${agent.harness.id} harness cannot restart from history, so this activation cannot be continued.`,\n retryable: false,\n };\n // Whoever was waiting on it is told, in the write that ends it. A\n // parent expecting a child that can now never report is the same dead\n // end one layer up.\n const told = await reporting({ runId: interrupted, outcome: \"failed\", output: \"\" });\n await commit([{ type: \"run.finished\", runId: interrupted, outcome: \"failed\", error: failure }], told);\n return stopping(failure);\n }\n }\n\n // An interrupted predecessor is ended before a new activation begins over\n // it, so one session never holds two open runs. Only on a claim: the\n // store established there that nothing else is running, where a bare\n // `sessionId` establishes nothing and the run may still be live.\n if (arrivals.length && interrupted && claim) {\n await commit([{\n type: \"run.finished\", runId: interrupted, outcome: \"cancelled\",\n error: {\n code: \"superseded\",\n message: \"This activation was interrupted, and a later one began over it.\",\n retryable: false,\n },\n }]);\n }\n\n // Nothing opens a resumption. A `run.started` recording input nobody sent\n // would be a second beginning for one conversation; the loop reads the\n // history it was handed instead, sees the call that already ran, and\n // carries on rather than asking for it again.\n await commit(arrivals.map((arrival): Entry => ({\n type: \"run.started\", runId, input: arrival.input,\n ...(arrival.from ? { from: arrival.from } : {}),\n })));\n\n // Every ceiling is checked where work is recorded, and a resumption\n // records nothing before its first turn — so a deadline that had already\n // passed was never seen. Checked once here too, before the harness is\n // handed anything.\n stopped ??= exceeded();\n\n const outcome = stopped\n ? { status: \"failed\" as const, error: stopped }\n : await agent.harness.run({\n sessionId, runId,\n instructions: agent.instructions,\n history: () => toMessages({\n instructions: agent.instructions,\n entries: log.entries,\n ...(agent.attribution ? { attribution: true } : {}),\n ...(options.context ? { context: options.context } : {}),\n }),\n entries: () => log.entries,\n ...(options.context ? { context: options.context } : {}),\n ...(executor ? { tools: executor } : {}),\n commit: (entries) => commit(entries),\n ...(store ? { drain } : {}),\n emit: (update) => updates.push(update),\n signal,\n });\n\n // A harness stopped by the ceiling reports a cancellation, because that is\n // all it saw. The run knows why it was cancelled and says so instead.\n const settled = stopped && outcome.status === \"cancelled\"\n ? { status: \"failed\" as const, error: stopped }\n : outcome;\n\n const output = settled.status === \"completed\" ? settled.output : \"\";\n const finished = await reporting({ runId, outcome: settled.status, output });\n\n await commit(\n [{\n type: \"run.finished\", runId,\n outcome: settled.status === \"completed\" ? \"completed\" : settled.status === \"cancelled\" ? \"cancelled\" : \"failed\",\n ...(settled.status === \"failed\" ? { error: settled.error } : {}),\n }],\n finished,\n );\n\n if (settled.status === \"completed\") {\n return { status: \"completed\", output: settled.output, seq: log.seq, usage };\n }\n if (settled.status === \"cancelled\") return { status: \"cancelled\", seq: log.seq, usage };\n return stopping(settled.error);\n } catch (error) {\n if (error instanceof CommitFailed) {\n return { status: \"failed\", error: error.failure, seq: error.seq, usage };\n }\n throw error;\n } finally {\n updates.close();\n }\n }\n}\n\nclass CommitFailed extends Error {\n constructor(readonly failure: Failure, readonly seq: number) {\n super(failure.message);\n }\n}\n\n/**\n * Where the session actually is after a refused write. A conflict knows; every\n * other failure leaves the caller with the position it already had. Reporting\n * zero told a caller at forty that it was at the beginning.\n *\n * The `typeof` is not defensive noise: a store may report a conflict, and only\n * a `StoreConflict` carries the position. Casting on the code alone produced a\n * `RunResult.seq` of `undefined` under a type that says `number`.\n */\nfunction positionOf(error: StoreError, fallback: number): number {\n const actual = (error as StoreConflict).actualSeq;\n return error.code === \"conflict\" && typeof actual === \"number\" ? actual : fallback;\n}\n\nconst isArrival = (value: unknown): value is Arrival =>\n typeof value === \"object\" && value !== null && !Array.isArray(value) && \"input\" in value;\n\n/**\n * One arrival, or several.\n *\n * The elements decide it, because the outer shape cannot: an arrival names its\n * `input`, a content part names its `type`, and nothing is both. Reading every\n * array as a batch is what turned one image-and-text message into one entry per\n * part, each holding a part where whole content belongs.\n */\nfunction arrivalsOf(input: RunAgentOptions[\"input\"]): readonly Arrival[] {\n if (input === undefined) return [];\n // Nothing sent is nothing sent. Falling through committed a `run.started`\n // whose input was an empty array of content parts.\n if (Array.isArray(input) && input.length === 0) return [];\n if (Array.isArray(input) && input.length > 0 && input.every(isArrival)) return input;\n if (isArrival(input)) return [input];\n return [{ input: input as Content }];\n}\n\n/**\n * The activation still open on this log, if one is.\n *\n * `run.started` opens and `run.finished` closes, which is the same rule the\n * store maintains its claim from — read here from the entries rather than asked\n * of the store, because the log is the only thing that knows.\n */\nfunction openRunId(entries: readonly Stored[]): string | undefined {\n let open: string | undefined;\n for (const entry of entries) {\n if (entry.type === \"run.started\") open = entry.runId;\n if (entry.type === \"run.finished\") open = undefined;\n }\n return open;\n}\n\n/** Token counts add; unknown stays unknown rather than becoming a zero. */\nfunction accumulate(total: Usage, turn: Usage): void {\n for (const key of [\"inputTokens\", \"outputTokens\", \"cacheReadTokens\", \"cacheWriteTokens\"] as const) {\n if (turn[key] !== undefined) total[key] = (total[key] ?? 0) + turn[key]!;\n }\n}\n\nasync function open(store: Store, sessionId: string, agent: Agent, key?: string) {\n const existing = await store.read({ sessionId });\n if (existing.ok) {\n // `version` is documented as the boundary past which an agent is no longer\n // compatible with an existing session. Nothing was comparing it, so the\n // boundary held for exactly as long as nobody crossed it.\n const stored = existing.value.agent;\n if (stored.id !== agent.id || stored.version !== agent.version) {\n return err<StoreError>({\n code: \"conflict\",\n message: `Session ${sessionId} belongs to ${stored.id}@${stored.version}, not ${agent.id}@${agent.version}`,\n retryable: false,\n });\n }\n return existing;\n }\n if (existing.error.code !== \"not-found\") return existing;\n const created = await store.create({\n sessionId, agent: { id: agent.id, version: agent.version }, ...(key ? { key } : {}),\n });\n if (!created.ok) return created;\n return store.read({ sessionId });\n}\n\n/**\n * A bounded fan-out queue for ephemeral updates.\n *\n * One buffer of recent updates, which every listener starts from and which\n * never grows past its limit. That single rule gives three things:\n *\n * **A run nobody drains costs a fixed amount.** A headless worker used to\n * accumulate every delta of an entire activation, because the queue only ever\n * grew. Dropping the oldest is safe precisely because updates are not recovery\n * state and the committed log is.\n *\n * **Arriving late loses nothing that matters.** The first entry commits before\n * `runAgent` returns, so a caller that iterates immediately would otherwise\n * never see its own run start. A UI reconnecting mid-run gets recent history\n * for the same reason, without asking the store for it.\n *\n * **Everyone sees everything.** Each listener drains its own copy, so an\n * application can watch a run for telemetry and for a UI at once instead of the\n * two stealing events from each other.\n */\nfunction createChannel<T>(limit = 1_024) {\n interface Listener { buffer: T[]; wake?: () => void }\n const listeners = new Set<Listener>();\n const replay: T[] = [];\n let closed = false;\n\n const bounded = (buffer: T[], value: T) => {\n if (buffer.length >= limit) buffer.shift();\n buffer.push(value);\n };\n\n return {\n push(value: T) {\n bounded(replay, value);\n for (const listener of listeners) {\n bounded(listener.buffer, value);\n listener.wake?.();\n }\n },\n close() {\n closed = true;\n for (const listener of listeners) listener.wake?.();\n },\n iterator(): AsyncIterator<T> {\n const listener: Listener = { buffer: [...replay] };\n listeners.add(listener);\n const done = (): IteratorResult<T> => {\n listeners.delete(listener);\n return { value: undefined, done: true };\n };\n return {\n async next(): Promise<IteratorResult<T>> {\n for (;;) {\n const value = listener.buffer.shift();\n if (value !== undefined) return { value, done: false };\n if (closed) return done();\n await new Promise<void>((resolve) => { listener.wake = resolve; });\n }\n },\n async return(): Promise<IteratorResult<T>> { return done(); },\n };\n },\n };\n}\n"]}
|
package/dist/session/entry.d.ts
CHANGED
|
@@ -152,9 +152,10 @@ export interface Delivery {
|
|
|
152
152
|
input: Content;
|
|
153
153
|
/**
|
|
154
154
|
* Who sent it. Absent means the application did not say — not that nobody
|
|
155
|
-
* did. Recorded on the receiving `run.started`, so
|
|
155
|
+
* did. Recorded on the receiving `run.started`, so an application can tell a
|
|
156
156
|
* peer's message from its user's, and can see that it already answered one
|
|
157
|
-
* that arrives twice.
|
|
157
|
+
* that arrives twice. It becomes text the model reads only where the
|
|
158
|
+
* recipient's agent asks, with `attribution`.
|
|
158
159
|
*/
|
|
159
160
|
from?: From;
|
|
160
161
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../../src/session/entry.ts"],"names":[],"mappings":"","sourcesContent":["import type { Content } from \"../content.js\";\nimport type { Failure } from \"../result.js\";\nimport type { JsonValue } from \"../json.js\";\n\n/** A model's request to invoke a tool. `arguments` is unparsed JSON as the provider sent it. */\nexport interface ToolCall {\n callId: string;\n name: string;\n arguments: string;\n}\n\nexport interface ToolResult {\n content: Content;\n /** Structured channel for the application and the UI. Never reaches the model. */\n details?: JsonValue;\n isError?: boolean;\n}\n\n/**\n * What a generation consumed.\n *\n * Absent stays absent — a count nobody reported is not a zero. This package\n * holds no rates and computes no money from tokens. Where an adapter does\n * arithmetic it is reconciling its wire's encoding to the meanings below:\n * normalization, which is an adapter's job, and never a new fact.\n *\n * That is why `costUsd` belongs here and a rate table does not. A cost the\n * provider *states* is an observation like the counts beside it, and dropping\n * it was the same mistake as dropping the model that served the request: an\n * application was left reconstructing, from a table it maintains by hand, a\n * number the wire had already given it. For a router that is not even possible\n * — it picks an upstream provider per request and adds its own margin, so no\n * static table can say what was charged.\n */\nexport interface Usage {\n /**\n * Input tokens that were neither read from nor written to a cache.\n *\n * **The three input counts are disjoint.** The prompt's total size is\n * `inputTokens + cacheReadTokens + cacheWriteTokens`; no field here is a\n * subset of another, so a caller sums and never subtracts.\n *\n * The wires disagree about this and one of them has to be converted. What\n * Anthropic calls `input_tokens` is already this — \"tokens which were not\n * read from or used to create a cache\". What OpenAI calls `prompt_tokens` is\n * the whole prompt, with `cached_tokens` counted inside it, so that adapter\n * subtracts once and callers stop having to know which wire answered.\n *\n * Disjoint is the choice that survives absence, which is the rule the rest of\n * this type runs on. Summing the fields you were given is right whichever\n * ones are missing; subtracting a field you were not given is not. Under the\n * other convention a heavily cached Anthropic turn priced its fresh tokens at\n * zero — quietly, because the subtraction was clamped at zero rather than\n * being allowed to go negative where somebody would have seen it.\n */\n inputTokens?: number;\n outputTokens?: number;\n /** Input tokens served from a cache. Disjoint from `inputTokens`. */\n cacheReadTokens?: number;\n /** Input tokens written to a cache. Disjoint from `inputTokens`. */\n cacheWriteTokens?: number;\n /**\n * What the provider said this generation cost, in US dollars. Reported,\n * never derived: no adapter computes this from a rate, and one whose wire\n * does not carry a cost leaves it absent rather than estimating.\n */\n costUsd?: number;\n}\n\n/**\n * Who sent an arrival: what sort of sender, and which one.\n *\n * aglib mints exactly one kind — `\"session\"`, a delivery from another session —\n * and interprets no other. Every other kind is the application's word for one of\n * its own senders: a person, a channel, a schedule, a webhook. The vocabulary\n * belongs to whoever can close it, and that is never this package.\n *\n * Two fields because absence was carrying four facts. This used to be the\n * sending session's id alone, so an operator, a channel and a timer all said the\n * same thing by saying nothing, and a recipient could not tell a person typing\n * from a routine firing. Packing both into one string instead — `\"routine:x\"` —\n * would put the vocabulary in a convention nothing closes, which is how such a\n * field ends up with values that exist only by grep and a different reading on\n * every surface that renders it.\n */\nexport interface From {\n kind: string;\n id: string;\n}\n\n/**\n * The canonical vocabulary. This is the session's state, not a record of it:\n * the loop reads its context by projecting these and appends back to them, so\n * there is no second transcript that could disagree.\n *\n * `tool.started` carries only the call id — the assistant entry above it\n * already holds the call, and duplicating name and arguments would be a second\n * field for a value another field determines.\n */\nexport type Entry =\n | { type: \"run.started\"; runId: string; input: Content; from?: From }\n | {\n type: \"assistant\"; runId: string; content: Content;\n calls?: readonly ToolCall[]; usage?: Usage;\n /**\n * What produced this turn, when a model did: which model answered and the\n * span it took. `Stored.at` is when the entry was committed, which is a\n * different fact and not a substitute — anything projecting a generation\n * needs both ends of the call, and the commit is neither of them.\n */\n generation?: { model?: string; startedAt: string; endedAt: string };\n }\n | { type: \"tool.started\"; runId: string; callId: string }\n | { type: \"tool.finished\"; runId: string; callId: string; result: ToolResult }\n /** Compaction output. `replaces` is the seq up to which entries are folded; nothing is deleted. */\n | { type: \"summary\"; runId: string; content: string; replaces: number }\n | { type: \"run.finished\"; runId: string; outcome: \"completed\" | \"failed\" | \"cancelled\"; error?: Failure };\n\nexport type EntryType = Entry[\"type\"];\n\n/** An entry once the log has given it a position. */\nexport type Stored<T extends Entry = Entry> = T & { seq: number; at: string };\n\n/**\n * Input delivered to a session, committed with the sender's own entries.\n * Spawning a child, replying to a parent and messaging a peer are all this.\n */\nexport interface Delivery {\n /** The session it is delivered to. */\n sessionId: string;\n input: Content;\n /**\n * Who sent it. Absent means the application did not say — not that nobody\n * did. Recorded on the receiving `run.started`, so
|
|
1
|
+
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../../src/session/entry.ts"],"names":[],"mappings":"","sourcesContent":["import type { Content } from \"../content.js\";\nimport type { Failure } from \"../result.js\";\nimport type { JsonValue } from \"../json.js\";\n\n/** A model's request to invoke a tool. `arguments` is unparsed JSON as the provider sent it. */\nexport interface ToolCall {\n callId: string;\n name: string;\n arguments: string;\n}\n\nexport interface ToolResult {\n content: Content;\n /** Structured channel for the application and the UI. Never reaches the model. */\n details?: JsonValue;\n isError?: boolean;\n}\n\n/**\n * What a generation consumed.\n *\n * Absent stays absent — a count nobody reported is not a zero. This package\n * holds no rates and computes no money from tokens. Where an adapter does\n * arithmetic it is reconciling its wire's encoding to the meanings below:\n * normalization, which is an adapter's job, and never a new fact.\n *\n * That is why `costUsd` belongs here and a rate table does not. A cost the\n * provider *states* is an observation like the counts beside it, and dropping\n * it was the same mistake as dropping the model that served the request: an\n * application was left reconstructing, from a table it maintains by hand, a\n * number the wire had already given it. For a router that is not even possible\n * — it picks an upstream provider per request and adds its own margin, so no\n * static table can say what was charged.\n */\nexport interface Usage {\n /**\n * Input tokens that were neither read from nor written to a cache.\n *\n * **The three input counts are disjoint.** The prompt's total size is\n * `inputTokens + cacheReadTokens + cacheWriteTokens`; no field here is a\n * subset of another, so a caller sums and never subtracts.\n *\n * The wires disagree about this and one of them has to be converted. What\n * Anthropic calls `input_tokens` is already this — \"tokens which were not\n * read from or used to create a cache\". What OpenAI calls `prompt_tokens` is\n * the whole prompt, with `cached_tokens` counted inside it, so that adapter\n * subtracts once and callers stop having to know which wire answered.\n *\n * Disjoint is the choice that survives absence, which is the rule the rest of\n * this type runs on. Summing the fields you were given is right whichever\n * ones are missing; subtracting a field you were not given is not. Under the\n * other convention a heavily cached Anthropic turn priced its fresh tokens at\n * zero — quietly, because the subtraction was clamped at zero rather than\n * being allowed to go negative where somebody would have seen it.\n */\n inputTokens?: number;\n outputTokens?: number;\n /** Input tokens served from a cache. Disjoint from `inputTokens`. */\n cacheReadTokens?: number;\n /** Input tokens written to a cache. Disjoint from `inputTokens`. */\n cacheWriteTokens?: number;\n /**\n * What the provider said this generation cost, in US dollars. Reported,\n * never derived: no adapter computes this from a rate, and one whose wire\n * does not carry a cost leaves it absent rather than estimating.\n */\n costUsd?: number;\n}\n\n/**\n * Who sent an arrival: what sort of sender, and which one.\n *\n * aglib mints exactly one kind — `\"session\"`, a delivery from another session —\n * and interprets no other. Every other kind is the application's word for one of\n * its own senders: a person, a channel, a schedule, a webhook. The vocabulary\n * belongs to whoever can close it, and that is never this package.\n *\n * Two fields because absence was carrying four facts. This used to be the\n * sending session's id alone, so an operator, a channel and a timer all said the\n * same thing by saying nothing, and a recipient could not tell a person typing\n * from a routine firing. Packing both into one string instead — `\"routine:x\"` —\n * would put the vocabulary in a convention nothing closes, which is how such a\n * field ends up with values that exist only by grep and a different reading on\n * every surface that renders it.\n */\nexport interface From {\n kind: string;\n id: string;\n}\n\n/**\n * The canonical vocabulary. This is the session's state, not a record of it:\n * the loop reads its context by projecting these and appends back to them, so\n * there is no second transcript that could disagree.\n *\n * `tool.started` carries only the call id — the assistant entry above it\n * already holds the call, and duplicating name and arguments would be a second\n * field for a value another field determines.\n */\nexport type Entry =\n | { type: \"run.started\"; runId: string; input: Content; from?: From }\n | {\n type: \"assistant\"; runId: string; content: Content;\n calls?: readonly ToolCall[]; usage?: Usage;\n /**\n * What produced this turn, when a model did: which model answered and the\n * span it took. `Stored.at` is when the entry was committed, which is a\n * different fact and not a substitute — anything projecting a generation\n * needs both ends of the call, and the commit is neither of them.\n */\n generation?: { model?: string; startedAt: string; endedAt: string };\n }\n | { type: \"tool.started\"; runId: string; callId: string }\n | { type: \"tool.finished\"; runId: string; callId: string; result: ToolResult }\n /** Compaction output. `replaces` is the seq up to which entries are folded; nothing is deleted. */\n | { type: \"summary\"; runId: string; content: string; replaces: number }\n | { type: \"run.finished\"; runId: string; outcome: \"completed\" | \"failed\" | \"cancelled\"; error?: Failure };\n\nexport type EntryType = Entry[\"type\"];\n\n/** An entry once the log has given it a position. */\nexport type Stored<T extends Entry = Entry> = T & { seq: number; at: string };\n\n/**\n * Input delivered to a session, committed with the sender's own entries.\n * Spawning a child, replying to a parent and messaging a peer are all this.\n */\nexport interface Delivery {\n /** The session it is delivered to. */\n sessionId: string;\n input: Content;\n /**\n * Who sent it. Absent means the application did not say — not that nobody\n * did. Recorded on the receiving `run.started`, so an application can tell a\n * peer's message from its user's, and can see that it already answered one\n * that arrives twice. It becomes text the model reads only where the\n * recipient's agent asks, with `attribution`.\n */\n from?: From;\n /**\n * Where in the recipient's loop this lands. Defaults to \"next\".\n *\n * Three places, and each is an outcome a sender can reason about rather than\n * a level of urgency a harness interprets:\n *\n * \"interrupt\" — the running activation ends, so the next one begins with\n * this. Its uncommitted work is lost; everything it had\n * already committed stays.\n * \"turn\" — folded into the activation already running, before its next\n * model call. Nothing in flight is lost. Immediately, if\n * nothing is running.\n * \"next\" — at the start of the recipient's next activation.\n *\n * This used to be `now | next | later`, and `now` meant either of the first\n * two depending on the harness — fold if it had a safe point, end the\n * activation if it did not. One word for \"your message arrives and the work\n * continues\" and \"your message arrives and a turn's work is destroyed\". The\n * doctrine was that a priority names the requirement and not the mechanism,\n * but those are not two mechanisms for one requirement: they are two\n * different things happening to somebody's work, and a runtime answer saying\n * which one you got was invented to paper over it. `later` named a fourth\n * thing that nothing implemented.\n *\n * **A harness that cannot honour one falls back to a later place, never an\n * earlier one.** A loop with no safe point cannot do \"turn\", so it does\n * \"next\" — the message waits. It does not end the activation instead, which\n * is what falling back through an ordered scale used to do: ask for the\n * gentlest useful thing and get the most destructive one.\n */\n priority?: \"interrupt\" | \"turn\" | \"next\";\n /**\n * The sender's key for this delivery. Two deliveries with the same id are the\n * same delivery, so a retried send does not arrive twice.\n */\n id?: string;\n}\n"]}
|
|
@@ -39,4 +39,13 @@ export declare function toMessages(input: {
|
|
|
39
39
|
run?: string;
|
|
40
40
|
turn?: string;
|
|
41
41
|
};
|
|
42
|
+
/**
|
|
43
|
+
* Name each arrival's sender in the turn text, as `[from kind id]`.
|
|
44
|
+
*
|
|
45
|
+
* Off by default. `from` is provenance the log keeps whatever this says; an
|
|
46
|
+
* application that renders its own attribution into the input it delivers
|
|
47
|
+
* would otherwise hand the model two names for one sender, one of them a
|
|
48
|
+
* session id that means nothing to it.
|
|
49
|
+
*/
|
|
50
|
+
attribution?: boolean;
|
|
42
51
|
}): readonly Message[];
|
package/dist/session/messages.js
CHANGED
|
@@ -9,7 +9,7 @@ export function foldedThrough(entries) {
|
|
|
9
9
|
return entries.reduce((at, entry) => entry.type === "summary" ? Math.max(at, entry.replaces) : at, 0);
|
|
10
10
|
}
|
|
11
11
|
/** Names the sender in the turn itself, since only text reaches the model. */
|
|
12
|
-
function
|
|
12
|
+
function labelled(input, from) {
|
|
13
13
|
if (!from)
|
|
14
14
|
return input;
|
|
15
15
|
const label = `[from ${from.kind} ${from.id}]`;
|
|
@@ -49,10 +49,10 @@ export function toMessages(input) {
|
|
|
49
49
|
continue;
|
|
50
50
|
switch (entry.type) {
|
|
51
51
|
case "run.started":
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
messages.push({
|
|
53
|
+
role: "user",
|
|
54
|
+
content: input.attribution ? labelled(entry.input, entry.from) : entry.input,
|
|
55
|
+
});
|
|
56
56
|
break;
|
|
57
57
|
case "assistant": {
|
|
58
58
|
messages.push({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/session/messages.ts"],"names":[],"mappings":"AAUA;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,OAA0B;IACtD,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AACxG,CAAC;AAED,8EAA8E;AAC9E,SAAS,
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/session/messages.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Content } from \"../content.js\";\nimport type { From, Stored, ToolCall } from \"./entry.js\";\n\n/** A message as a provider takes it: built per turn from the log, never held. */\nexport type Message =\n | { role: \"system\"; content: Content }\n | { role: \"user\"; content: Content }\n | { role: \"assistant\"; content: Content; calls?: readonly ToolCall[] }\n | { role: \"tool\"; callId: string; content: Content; isError?: boolean };\n\n/**\n * The position everything up to has been folded into a summary.\n *\n * One answer, because two callers need it: the projection skips what is folded,\n * and the loop refuses to cut at or behind it. They were the same reduce written\n * out twice.\n */\nexport function foldedThrough(entries: readonly Stored[]): number {\n return entries.reduce((at, entry) => entry.type === \"summary\" ? Math.max(at, entry.replaces) : at, 0);\n}\n\n/** Names the sender in the turn itself, since only text reaches the model. */\nfunction labelled(input: Content, from: From | undefined): Content {\n if (!from) return input;\n const label = `[from ${from.kind} ${from.id}]`;\n return typeof input === \"string\"\n ? `${label}\\n${input}`\n : [{ type: \"text\" as const, text: label }, ...input];\n}\n\n/**\n * The log projected into what a provider takes.\n *\n * Built fresh for every request and never held. That is the whole reason there\n * is no second transcript to drift from the log, and why nothing needs a test\n * proving two representations agree.\n */\nexport function toMessages(input: {\n instructions: Content;\n entries: readonly Stored[];\n context?: { run?: string; turn?: string };\n /**\n * Name each arrival's sender in the turn text, as `[from kind id]`.\n *\n * Off by default. `from` is provenance the log keeps whatever this says; an\n * application that renders its own attribution into the input it delivers\n * would otherwise hand the model two names for one sender, one of them a\n * session id that means nothing to it.\n */\n attribution?: boolean;\n}): readonly Message[] {\n const messages: Message[] = [{ role: \"system\", content: input.instructions }];\n\n // Run-scoped context sits immediately after the instructions, inside the\n // cacheable prefix, because it does not change for the life of the run.\n if (input.context?.run) messages.push({ role: \"system\", content: input.context.run });\n\n // A summary folds everything up to `replaces`. The source entries stay in the\n // log — compaction changes what the model sees, never what happened.\n const cut = foldedThrough(input.entries);\n\n // A call whose result never committed. It happens when an activation ends\n // between asking and answering — cancelled, interrupted, or beaten to the\n // commit by another worker. The projection has to close it: a provider\n // rejects an assistant turn holding a call with no result, so leaving the gap\n // would make the session permanently unusable. What is said is what is known\n // — the call did not report back — and never an invented result.\n const answered = new Set(\n input.entries.filter((entry) => entry.type === \"tool.finished\").map((entry) => entry.callId),\n );\n\n for (const entry of input.entries) {\n // A summary folds like anything else it covers. Exempting the whole type\n // kept every summary ever written, so a long session carried a chain of\n // them whose content was already inside the newest — duplicated, and\n // rewriting the cached prefix each time one was added.\n if (entry.seq <= cut) continue;\n switch (entry.type) {\n case \"run.started\":\n messages.push({\n role: \"user\",\n content: input.attribution ? labelled(entry.input, entry.from) : entry.input,\n });\n break;\n case \"assistant\": {\n messages.push({\n role: \"assistant\",\n content: entry.content,\n ...(entry.calls?.length ? { calls: entry.calls } : {}),\n });\n for (const call of entry.calls ?? []) {\n if (answered.has(call.callId)) continue;\n messages.push({\n role: \"tool\", callId: call.callId, isError: true,\n content: \"This call did not report back: the activation ended before its result was committed.\",\n });\n }\n break;\n }\n case \"tool.finished\":\n messages.push({\n role: \"tool\",\n callId: entry.callId,\n content: entry.result.content,\n ...(entry.result.isError ? { isError: true } : {}),\n });\n break;\n case \"summary\":\n // Delimited user context rather than an assistant turn: attributing a\n // summary to the assistant puts words in the model's mouth it never said.\n messages.push({ role: \"user\", content: `<summary>\\n${entry.content}\\n</summary>` });\n break;\n // Not model-visible: tool.started is bookkeeping and run.finished is a\n // boundary.\n case \"tool.started\":\n case \"run.finished\":\n break;\n }\n }\n\n // Turn-scoped context sits last, after the cache boundary, because it is for\n // this request only and must not be written into the cached prefix.\n if (input.context?.turn) messages.push({ role: \"system\", content: input.context.turn });\n return messages;\n}\n\n"]}
|