@a-dray/aglib 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/dist/agent.d.ts +158 -0
- package/dist/agent.js +2 -0
- package/dist/agent.js.map +1 -0
- package/dist/content.d.ts +45 -0
- package/dist/content.js +11 -0
- package/dist/content.js.map +1 -0
- package/dist/harness/adapters/acp/index.d.ts +92 -0
- package/dist/harness/adapters/acp/index.js +479 -0
- package/dist/harness/adapters/acp/index.js.map +1 -0
- package/dist/harness/adapters/acp/rpc.d.ts +23 -0
- package/dist/harness/adapters/acp/rpc.js +103 -0
- package/dist/harness/adapters/acp/rpc.js.map +1 -0
- package/dist/harness/adapters/native/compaction.d.ts +16 -0
- package/dist/harness/adapters/native/compaction.js +55 -0
- package/dist/harness/adapters/native/compaction.js.map +1 -0
- package/dist/harness/adapters/native/index.d.ts +2 -0
- package/dist/harness/adapters/native/index.js +2 -0
- package/dist/harness/adapters/native/index.js.map +1 -0
- package/dist/harness/adapters/native/loop.d.ts +25 -0
- package/dist/harness/adapters/native/loop.js +153 -0
- package/dist/harness/adapters/native/loop.js.map +1 -0
- package/dist/harness/harness.d.ts +129 -0
- package/dist/harness/harness.js +2 -0
- package/dist/harness/harness.js.map +1 -0
- package/dist/harness/index.d.ts +3 -0
- package/dist/harness/index.js +2 -0
- package/dist/harness/index.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +4 -0
- package/dist/json.js +2 -0
- package/dist/json.js.map +1 -0
- package/dist/model/adapters/anthropic/index.d.ts +34 -0
- package/dist/model/adapters/anthropic/index.js +271 -0
- package/dist/model/adapters/anthropic/index.js.map +1 -0
- package/dist/model/adapters/fake/index.d.ts +27 -0
- package/dist/model/adapters/fake/index.js +37 -0
- package/dist/model/adapters/fake/index.js.map +1 -0
- package/dist/model/adapters/openai-compatible/index.d.ts +29 -0
- package/dist/model/adapters/openai-compatible/index.js +226 -0
- package/dist/model/adapters/openai-compatible/index.js.map +1 -0
- package/dist/model/conformance.d.ts +210 -0
- package/dist/model/conformance.js +427 -0
- package/dist/model/conformance.js.map +1 -0
- package/dist/model/index.d.ts +2 -0
- package/dist/model/index.js +2 -0
- package/dist/model/index.js.map +1 -0
- package/dist/model/model.d.ts +85 -0
- package/dist/model/model.js +8 -0
- package/dist/model/model.js.map +1 -0
- package/dist/result.d.ts +23 -0
- package/dist/result.js +3 -0
- package/dist/result.js.map +1 -0
- package/dist/run.d.ts +10 -0
- package/dist/run.js +477 -0
- package/dist/run.js.map +1 -0
- package/dist/sandbox/adapters/docker.d.ts +22 -0
- package/dist/sandbox/adapters/docker.js +443 -0
- package/dist/sandbox/adapters/docker.js.map +1 -0
- package/dist/sandbox/adapters/local.d.ts +11 -0
- package/dist/sandbox/adapters/local.js +284 -0
- package/dist/sandbox/adapters/local.js.map +1 -0
- package/dist/sandbox/conformance.d.ts +73 -0
- package/dist/sandbox/conformance.js +309 -0
- package/dist/sandbox/conformance.js.map +1 -0
- package/dist/sandbox/index.d.ts +1 -0
- package/dist/sandbox/index.js +2 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/sandbox/sandbox.d.ts +172 -0
- package/dist/sandbox/sandbox.js +2 -0
- package/dist/sandbox/sandbox.js.map +1 -0
- package/dist/session/entry.d.ts +196 -0
- package/dist/session/entry.js +2 -0
- package/dist/session/entry.js.map +1 -0
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +2 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/log.d.ts +20 -0
- package/dist/session/log.js +24 -0
- package/dist/session/log.js.map +1 -0
- package/dist/session/messages.d.ts +42 -0
- package/dist/session/messages.js +99 -0
- package/dist/session/messages.js.map +1 -0
- package/dist/store/adapters/sqlite.d.ts +20 -0
- package/dist/store/adapters/sqlite.js +365 -0
- package/dist/store/adapters/sqlite.js.map +1 -0
- package/dist/store/conformance.d.ts +66 -0
- package/dist/store/conformance.js +580 -0
- package/dist/store/conformance.js.map +1 -0
- package/dist/store/index.d.ts +1 -0
- package/dist/store/index.js +2 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/store.d.ts +244 -0
- package/dist/store/store.js +2 -0
- package/dist/store/store.js.map +1 -0
- package/dist/tools/execute.d.ts +24 -0
- package/dist/tools/execute.js +105 -0
- package/dist/tools/execute.js.map +1 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +2 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/tool.d.ts +106 -0
- package/dist/tools/tool.js +34 -0
- package/dist/tools/tool.js.map +1 -0
- package/package.json +119 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import type { Content } from "../content.js";
|
|
2
|
+
import type { Failure } from "../result.js";
|
|
3
|
+
import type { JsonValue } from "../json.js";
|
|
4
|
+
/** A model's request to invoke a tool. `arguments` is unparsed JSON as the provider sent it. */
|
|
5
|
+
export interface ToolCall {
|
|
6
|
+
callId: string;
|
|
7
|
+
name: string;
|
|
8
|
+
arguments: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ToolResult {
|
|
11
|
+
content: Content;
|
|
12
|
+
/** Structured channel for the application and the UI. Never reaches the model. */
|
|
13
|
+
details?: JsonValue;
|
|
14
|
+
isError?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* What a generation consumed.
|
|
18
|
+
*
|
|
19
|
+
* Absent stays absent — a count nobody reported is not a zero. This package
|
|
20
|
+
* holds no rates and computes no money from tokens. Where an adapter does
|
|
21
|
+
* arithmetic it is reconciling its wire's encoding to the meanings below:
|
|
22
|
+
* normalization, which is an adapter's job, and never a new fact.
|
|
23
|
+
*
|
|
24
|
+
* That is why `costUsd` belongs here and a rate table does not. A cost the
|
|
25
|
+
* provider *states* is an observation like the counts beside it, and dropping
|
|
26
|
+
* it was the same mistake as dropping the model that served the request: an
|
|
27
|
+
* application was left reconstructing, from a table it maintains by hand, a
|
|
28
|
+
* number the wire had already given it. For a router that is not even possible
|
|
29
|
+
* — it picks an upstream provider per request and adds its own margin, so no
|
|
30
|
+
* static table can say what was charged.
|
|
31
|
+
*/
|
|
32
|
+
export interface Usage {
|
|
33
|
+
/**
|
|
34
|
+
* Input tokens that were neither read from nor written to a cache.
|
|
35
|
+
*
|
|
36
|
+
* **The three input counts are disjoint.** The prompt's total size is
|
|
37
|
+
* `inputTokens + cacheReadTokens + cacheWriteTokens`; no field here is a
|
|
38
|
+
* subset of another, so a caller sums and never subtracts.
|
|
39
|
+
*
|
|
40
|
+
* The wires disagree about this and one of them has to be converted. What
|
|
41
|
+
* Anthropic calls `input_tokens` is already this — "tokens which were not
|
|
42
|
+
* read from or used to create a cache". What OpenAI calls `prompt_tokens` is
|
|
43
|
+
* the whole prompt, with `cached_tokens` counted inside it, so that adapter
|
|
44
|
+
* subtracts once and callers stop having to know which wire answered.
|
|
45
|
+
*
|
|
46
|
+
* Disjoint is the choice that survives absence, which is the rule the rest of
|
|
47
|
+
* this type runs on. Summing the fields you were given is right whichever
|
|
48
|
+
* ones are missing; subtracting a field you were not given is not. Under the
|
|
49
|
+
* other convention a heavily cached Anthropic turn priced its fresh tokens at
|
|
50
|
+
* zero — quietly, because the subtraction was clamped at zero rather than
|
|
51
|
+
* being allowed to go negative where somebody would have seen it.
|
|
52
|
+
*/
|
|
53
|
+
inputTokens?: number;
|
|
54
|
+
outputTokens?: number;
|
|
55
|
+
/** Input tokens served from a cache. Disjoint from `inputTokens`. */
|
|
56
|
+
cacheReadTokens?: number;
|
|
57
|
+
/** Input tokens written to a cache. Disjoint from `inputTokens`. */
|
|
58
|
+
cacheWriteTokens?: number;
|
|
59
|
+
/**
|
|
60
|
+
* What the provider said this generation cost, in US dollars. Reported,
|
|
61
|
+
* never derived: no adapter computes this from a rate, and one whose wire
|
|
62
|
+
* does not carry a cost leaves it absent rather than estimating.
|
|
63
|
+
*/
|
|
64
|
+
costUsd?: number;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Who sent an arrival: what sort of sender, and which one.
|
|
68
|
+
*
|
|
69
|
+
* aglib mints exactly one kind — `"session"`, a delivery from another session —
|
|
70
|
+
* and interprets no other. Every other kind is the application's word for one of
|
|
71
|
+
* its own senders: a person, a channel, a schedule, a webhook. The vocabulary
|
|
72
|
+
* belongs to whoever can close it, and that is never this package.
|
|
73
|
+
*
|
|
74
|
+
* Two fields because absence was carrying four facts. This used to be the
|
|
75
|
+
* sending session's id alone, so an operator, a channel and a timer all said the
|
|
76
|
+
* same thing by saying nothing, and a recipient could not tell a person typing
|
|
77
|
+
* from a routine firing. Packing both into one string instead — `"routine:x"` —
|
|
78
|
+
* would put the vocabulary in a convention nothing closes, which is how such a
|
|
79
|
+
* field ends up with values that exist only by grep and a different reading on
|
|
80
|
+
* every surface that renders it.
|
|
81
|
+
*/
|
|
82
|
+
export interface From {
|
|
83
|
+
kind: string;
|
|
84
|
+
id: string;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The canonical vocabulary. This is the session's state, not a record of it:
|
|
88
|
+
* the loop reads its context by projecting these and appends back to them, so
|
|
89
|
+
* there is no second transcript that could disagree.
|
|
90
|
+
*
|
|
91
|
+
* `tool.started` carries only the call id — the assistant entry above it
|
|
92
|
+
* already holds the call, and duplicating name and arguments would be a second
|
|
93
|
+
* field for a value another field determines.
|
|
94
|
+
*/
|
|
95
|
+
export type Entry = {
|
|
96
|
+
type: "run.started";
|
|
97
|
+
runId: string;
|
|
98
|
+
input: Content;
|
|
99
|
+
from?: From;
|
|
100
|
+
} | {
|
|
101
|
+
type: "assistant";
|
|
102
|
+
runId: string;
|
|
103
|
+
content: Content;
|
|
104
|
+
calls?: readonly ToolCall[];
|
|
105
|
+
usage?: Usage;
|
|
106
|
+
/**
|
|
107
|
+
* What produced this turn, when a model did: which model answered and the
|
|
108
|
+
* span it took. `Stored.at` is when the entry was committed, which is a
|
|
109
|
+
* different fact and not a substitute — anything projecting a generation
|
|
110
|
+
* needs both ends of the call, and the commit is neither of them.
|
|
111
|
+
*/
|
|
112
|
+
generation?: {
|
|
113
|
+
model?: string;
|
|
114
|
+
startedAt: string;
|
|
115
|
+
endedAt: string;
|
|
116
|
+
};
|
|
117
|
+
} | {
|
|
118
|
+
type: "tool.started";
|
|
119
|
+
runId: string;
|
|
120
|
+
callId: string;
|
|
121
|
+
} | {
|
|
122
|
+
type: "tool.finished";
|
|
123
|
+
runId: string;
|
|
124
|
+
callId: string;
|
|
125
|
+
result: ToolResult;
|
|
126
|
+
}
|
|
127
|
+
/** Compaction output. `replaces` is the seq up to which entries are folded; nothing is deleted. */
|
|
128
|
+
| {
|
|
129
|
+
type: "summary";
|
|
130
|
+
runId: string;
|
|
131
|
+
content: string;
|
|
132
|
+
replaces: number;
|
|
133
|
+
} | {
|
|
134
|
+
type: "run.finished";
|
|
135
|
+
runId: string;
|
|
136
|
+
outcome: "completed" | "failed" | "cancelled";
|
|
137
|
+
error?: Failure;
|
|
138
|
+
};
|
|
139
|
+
export type EntryType = Entry["type"];
|
|
140
|
+
/** An entry once the log has given it a position. */
|
|
141
|
+
export type Stored<T extends Entry = Entry> = T & {
|
|
142
|
+
seq: number;
|
|
143
|
+
at: string;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Input delivered to a session, committed with the sender's own entries.
|
|
147
|
+
* Spawning a child, replying to a parent and messaging a peer are all this.
|
|
148
|
+
*/
|
|
149
|
+
export interface Delivery {
|
|
150
|
+
/** The session it is delivered to. */
|
|
151
|
+
sessionId: string;
|
|
152
|
+
input: Content;
|
|
153
|
+
/**
|
|
154
|
+
* Who sent it. Absent means the application did not say — not that nobody
|
|
155
|
+
* did. Recorded on the receiving `run.started`, so a recipient can tell a
|
|
156
|
+
* peer's message from its user's, and can see that it already answered one
|
|
157
|
+
* that arrives twice.
|
|
158
|
+
*/
|
|
159
|
+
from?: From;
|
|
160
|
+
/**
|
|
161
|
+
* Where in the recipient's loop this lands. Defaults to "next".
|
|
162
|
+
*
|
|
163
|
+
* Three places, and each is an outcome a sender can reason about rather than
|
|
164
|
+
* a level of urgency a harness interprets:
|
|
165
|
+
*
|
|
166
|
+
* "interrupt" — the running activation ends, so the next one begins with
|
|
167
|
+
* this. Its uncommitted work is lost; everything it had
|
|
168
|
+
* already committed stays.
|
|
169
|
+
* "turn" — folded into the activation already running, before its next
|
|
170
|
+
* model call. Nothing in flight is lost. Immediately, if
|
|
171
|
+
* nothing is running.
|
|
172
|
+
* "next" — at the start of the recipient's next activation.
|
|
173
|
+
*
|
|
174
|
+
* This used to be `now | next | later`, and `now` meant either of the first
|
|
175
|
+
* two depending on the harness — fold if it had a safe point, end the
|
|
176
|
+
* activation if it did not. One word for "your message arrives and the work
|
|
177
|
+
* continues" and "your message arrives and a turn's work is destroyed". The
|
|
178
|
+
* doctrine was that a priority names the requirement and not the mechanism,
|
|
179
|
+
* but those are not two mechanisms for one requirement: they are two
|
|
180
|
+
* different things happening to somebody's work, and a runtime answer saying
|
|
181
|
+
* which one you got was invented to paper over it. `later` named a fourth
|
|
182
|
+
* thing that nothing implemented.
|
|
183
|
+
*
|
|
184
|
+
* **A harness that cannot honour one falls back to a later place, never an
|
|
185
|
+
* earlier one.** A loop with no safe point cannot do "turn", so it does
|
|
186
|
+
* "next" — the message waits. It does not end the activation instead, which
|
|
187
|
+
* is what falling back through an ordered scale used to do: ask for the
|
|
188
|
+
* gentlest useful thing and get the most destructive one.
|
|
189
|
+
*/
|
|
190
|
+
priority?: "interrupt" | "turn" | "next";
|
|
191
|
+
/**
|
|
192
|
+
* The sender's key for this delivery. Two deliveries with the same id are the
|
|
193
|
+
* same delivery, so a retried send does not arrive twice.
|
|
194
|
+
*/
|
|
195
|
+
id?: string;
|
|
196
|
+
}
|
|
@@ -0,0 +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 a recipient can tell a\n * peer's message from its user's, and can see that it already answered one\n * that arrives twice.\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"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/session/index.ts"],"names":[],"mappings":"","sourcesContent":["export type { Delivery, Entry, EntryType, From, Stored, ToolCall, ToolResult, Usage } from \"./entry.js\";\nexport type { Message } from \"./messages.js\";\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Entry, Stored } from "./entry.js";
|
|
2
|
+
/**
|
|
3
|
+
* The session's state: ordered entries and the position of the last one.
|
|
4
|
+
*
|
|
5
|
+
* Not a record of state kept somewhere else — this is what the loop reads to
|
|
6
|
+
* build its next request and what it appends its results to. A store persists
|
|
7
|
+
* it; without one it lives here and the session is ephemeral.
|
|
8
|
+
*/
|
|
9
|
+
export interface Log {
|
|
10
|
+
readonly seq: number;
|
|
11
|
+
readonly entries: readonly Stored[];
|
|
12
|
+
/** Stamps and appends, returning what was added. */
|
|
13
|
+
append(entries: readonly Entry[]): readonly Stored[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* `at` is the session's real position, which is the store's to say. Deriving it
|
|
17
|
+
* from the last entry is a second answer to the same question, and the two
|
|
18
|
+
* agree only while nothing has ever been read from a cursor.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createLog(initial?: readonly Stored[], at?: number): Log;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `at` is the session's real position, which is the store's to say. Deriving it
|
|
3
|
+
* from the last entry is a second answer to the same question, and the two
|
|
4
|
+
* agree only while nothing has ever been read from a cursor.
|
|
5
|
+
*/
|
|
6
|
+
export function createLog(initial = [], at = initial.at(-1)?.seq ?? 0) {
|
|
7
|
+
const entries = [...initial];
|
|
8
|
+
let seq = at;
|
|
9
|
+
return {
|
|
10
|
+
get seq() { return seq; },
|
|
11
|
+
// A copy. `entries()` on a harness context is documented beside `history()`,
|
|
12
|
+
// which is rebuilt every call precisely so a harness cannot hold something
|
|
13
|
+
// that drifts from the log; handing out the live array made one of the pair
|
|
14
|
+
// a lie.
|
|
15
|
+
get entries() { return [...entries]; },
|
|
16
|
+
append(added) {
|
|
17
|
+
const at = new Date().toISOString();
|
|
18
|
+
const stamped = added.map((entry) => ({ ...entry, seq: (seq += 1), at }));
|
|
19
|
+
entries.push(...stamped);
|
|
20
|
+
return stamped;
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/session/log.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,UAA6B,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC;IACtF,MAAM,OAAO,GAAa,CAAC,GAAG,OAAO,CAAC,CAAC;IACvC,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,OAAO;QACL,IAAI,GAAG,KAAK,OAAO,GAAG,CAAC,CAAC,CAAC;QACzB,6EAA6E;QAC7E,2EAA2E;QAC3E,4EAA4E;QAC5E,SAAS;QACT,IAAI,OAAO,KAAwB,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACzD,MAAM,CAAC,KAAK;YACV,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAU,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;YAClF,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC;YACzB,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { Entry, Stored } from \"./entry.js\";\n\n/**\n * The session's state: ordered entries and the position of the last one.\n *\n * Not a record of state kept somewhere else — this is what the loop reads to\n * build its next request and what it appends its results to. A store persists\n * it; without one it lives here and the session is ephemeral.\n */\nexport interface Log {\n readonly seq: number;\n readonly entries: readonly Stored[];\n /** Stamps and appends, returning what was added. */\n append(entries: readonly Entry[]): readonly Stored[];\n}\n\n/**\n * `at` is the session's real position, which is the store's to say. Deriving it\n * from the last entry is a second answer to the same question, and the two\n * agree only while nothing has ever been read from a cursor.\n */\nexport function createLog(initial: readonly Stored[] = [], at = initial.at(-1)?.seq ?? 0): Log {\n const entries: Stored[] = [...initial];\n let seq = at;\n return {\n get seq() { return seq; },\n // A copy. `entries()` on a harness context is documented beside `history()`,\n // which is rebuilt every call precisely so a harness cannot hold something\n // that drifts from the log; handing out the live array made one of the pair\n // a lie.\n get entries(): readonly Stored[] { return [...entries]; },\n append(added) {\n const at = new Date().toISOString();\n const stamped = added.map((entry): Stored => ({ ...entry, seq: (seq += 1), at }));\n entries.push(...stamped);\n return stamped;\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Content } from "../content.js";
|
|
2
|
+
import type { Stored, ToolCall } from "./entry.js";
|
|
3
|
+
/** A message as a provider takes it: built per turn from the log, never held. */
|
|
4
|
+
export type Message = {
|
|
5
|
+
role: "system";
|
|
6
|
+
content: Content;
|
|
7
|
+
} | {
|
|
8
|
+
role: "user";
|
|
9
|
+
content: Content;
|
|
10
|
+
} | {
|
|
11
|
+
role: "assistant";
|
|
12
|
+
content: Content;
|
|
13
|
+
calls?: readonly ToolCall[];
|
|
14
|
+
} | {
|
|
15
|
+
role: "tool";
|
|
16
|
+
callId: string;
|
|
17
|
+
content: Content;
|
|
18
|
+
isError?: boolean;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* The position everything up to has been folded into a summary.
|
|
22
|
+
*
|
|
23
|
+
* One answer, because two callers need it: the projection skips what is folded,
|
|
24
|
+
* and the loop refuses to cut at or behind it. They were the same reduce written
|
|
25
|
+
* out twice.
|
|
26
|
+
*/
|
|
27
|
+
export declare function foldedThrough(entries: readonly Stored[]): number;
|
|
28
|
+
/**
|
|
29
|
+
* The log projected into what a provider takes.
|
|
30
|
+
*
|
|
31
|
+
* Built fresh for every request and never held. That is the whole reason there
|
|
32
|
+
* is no second transcript to drift from the log, and why nothing needs a test
|
|
33
|
+
* proving two representations agree.
|
|
34
|
+
*/
|
|
35
|
+
export declare function toMessages(input: {
|
|
36
|
+
instructions: Content;
|
|
37
|
+
entries: readonly Stored[];
|
|
38
|
+
context?: {
|
|
39
|
+
run?: string;
|
|
40
|
+
turn?: string;
|
|
41
|
+
};
|
|
42
|
+
}): readonly Message[];
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The position everything up to has been folded into a summary.
|
|
3
|
+
*
|
|
4
|
+
* One answer, because two callers need it: the projection skips what is folded,
|
|
5
|
+
* and the loop refuses to cut at or behind it. They were the same reduce written
|
|
6
|
+
* out twice.
|
|
7
|
+
*/
|
|
8
|
+
export function foldedThrough(entries) {
|
|
9
|
+
return entries.reduce((at, entry) => entry.type === "summary" ? Math.max(at, entry.replaces) : at, 0);
|
|
10
|
+
}
|
|
11
|
+
/** Names the sender in the turn itself, since only text reaches the model. */
|
|
12
|
+
function sent(input, from) {
|
|
13
|
+
if (!from)
|
|
14
|
+
return input;
|
|
15
|
+
const label = `[from ${from.kind} ${from.id}]`;
|
|
16
|
+
return typeof input === "string"
|
|
17
|
+
? `${label}\n${input}`
|
|
18
|
+
: [{ type: "text", text: label }, ...input];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The log projected into what a provider takes.
|
|
22
|
+
*
|
|
23
|
+
* Built fresh for every request and never held. That is the whole reason there
|
|
24
|
+
* is no second transcript to drift from the log, and why nothing needs a test
|
|
25
|
+
* proving two representations agree.
|
|
26
|
+
*/
|
|
27
|
+
export function toMessages(input) {
|
|
28
|
+
const messages = [{ role: "system", content: input.instructions }];
|
|
29
|
+
// Run-scoped context sits immediately after the instructions, inside the
|
|
30
|
+
// cacheable prefix, because it does not change for the life of the run.
|
|
31
|
+
if (input.context?.run)
|
|
32
|
+
messages.push({ role: "system", content: input.context.run });
|
|
33
|
+
// A summary folds everything up to `replaces`. The source entries stay in the
|
|
34
|
+
// log — compaction changes what the model sees, never what happened.
|
|
35
|
+
const cut = foldedThrough(input.entries);
|
|
36
|
+
// A call whose result never committed. It happens when an activation ends
|
|
37
|
+
// between asking and answering — cancelled, interrupted, or beaten to the
|
|
38
|
+
// commit by another worker. The projection has to close it: a provider
|
|
39
|
+
// rejects an assistant turn holding a call with no result, so leaving the gap
|
|
40
|
+
// would make the session permanently unusable. What is said is what is known
|
|
41
|
+
// — the call did not report back — and never an invented result.
|
|
42
|
+
const answered = new Set(input.entries.filter((entry) => entry.type === "tool.finished").map((entry) => entry.callId));
|
|
43
|
+
for (const entry of input.entries) {
|
|
44
|
+
// A summary folds like anything else it covers. Exempting the whole type
|
|
45
|
+
// kept every summary ever written, so a long session carried a chain of
|
|
46
|
+
// them whose content was already inside the newest — duplicated, and
|
|
47
|
+
// rewriting the cached prefix each time one was added.
|
|
48
|
+
if (entry.seq <= cut)
|
|
49
|
+
continue;
|
|
50
|
+
switch (entry.type) {
|
|
51
|
+
case "run.started":
|
|
52
|
+
// Provenance the model can see. `from` was recorded and then dropped
|
|
53
|
+
// here, so the documents claimed a recipient could tell a peer's
|
|
54
|
+
// message from its user's while the only actor that had to could not.
|
|
55
|
+
messages.push({ role: "user", content: sent(entry.input, entry.from) });
|
|
56
|
+
break;
|
|
57
|
+
case "assistant": {
|
|
58
|
+
messages.push({
|
|
59
|
+
role: "assistant",
|
|
60
|
+
content: entry.content,
|
|
61
|
+
...(entry.calls?.length ? { calls: entry.calls } : {}),
|
|
62
|
+
});
|
|
63
|
+
for (const call of entry.calls ?? []) {
|
|
64
|
+
if (answered.has(call.callId))
|
|
65
|
+
continue;
|
|
66
|
+
messages.push({
|
|
67
|
+
role: "tool", callId: call.callId, isError: true,
|
|
68
|
+
content: "This call did not report back: the activation ended before its result was committed.",
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case "tool.finished":
|
|
74
|
+
messages.push({
|
|
75
|
+
role: "tool",
|
|
76
|
+
callId: entry.callId,
|
|
77
|
+
content: entry.result.content,
|
|
78
|
+
...(entry.result.isError ? { isError: true } : {}),
|
|
79
|
+
});
|
|
80
|
+
break;
|
|
81
|
+
case "summary":
|
|
82
|
+
// Delimited user context rather than an assistant turn: attributing a
|
|
83
|
+
// summary to the assistant puts words in the model's mouth it never said.
|
|
84
|
+
messages.push({ role: "user", content: `<summary>\n${entry.content}\n</summary>` });
|
|
85
|
+
break;
|
|
86
|
+
// Not model-visible: tool.started is bookkeeping and run.finished is a
|
|
87
|
+
// boundary.
|
|
88
|
+
case "tool.started":
|
|
89
|
+
case "run.finished":
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Turn-scoped context sits last, after the cache boundary, because it is for
|
|
94
|
+
// this request only and must not be written into the cached prefix.
|
|
95
|
+
if (input.context?.turn)
|
|
96
|
+
messages.push({ role: "system", content: input.context.turn });
|
|
97
|
+
return messages;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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 sent(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}): 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 // Provenance the model can see. `from` was recorded and then dropped\n // here, so the documents claimed a recipient could tell a peer's\n // message from its user's while the only actor that had to could not.\n messages.push({ role: \"user\", content: sent(entry.input, entry.from) });\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"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Store } from "../store.js";
|
|
2
|
+
/**
|
|
3
|
+
* The minimum a driver must provide. Declared here rather than importing one so
|
|
4
|
+
* the same file serves `node:sqlite` and `bun:sqlite`, and so an application can
|
|
5
|
+
* hand in a database it already opened — which is what lets its own tables live
|
|
6
|
+
* beside the log and join against it.
|
|
7
|
+
*/
|
|
8
|
+
export interface SqliteDatabase {
|
|
9
|
+
exec(sql: string): void;
|
|
10
|
+
query(sql: string): {
|
|
11
|
+
all(...parameters: unknown[]): unknown[];
|
|
12
|
+
run(...parameters: unknown[]): unknown;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export declare function createSqliteStore(input: {
|
|
16
|
+
database: SqliteDatabase;
|
|
17
|
+
claimMs?: number;
|
|
18
|
+
/** How long a delivery id is remembered. Defaults to seven days. */
|
|
19
|
+
deliveryMemoryMs?: number;
|
|
20
|
+
}): Store;
|