@chopin-ai/harness-pi 0.0.0-stage → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE +202 -0
  2. package/dist/acp.d.ts +35 -0
  3. package/dist/acp.js +119 -0
  4. package/dist/acp.js.map +1 -0
  5. package/dist/auth.d.ts +17 -0
  6. package/dist/auth.js +36 -0
  7. package/dist/auth.js.map +1 -0
  8. package/dist/backoff.d.ts +5 -0
  9. package/dist/backoff.js +13 -0
  10. package/dist/backoff.js.map +1 -0
  11. package/dist/bridge.d.ts +11 -0
  12. package/dist/bridge.js +259 -0
  13. package/dist/bridge.js.map +1 -0
  14. package/dist/client-context.d.ts +7 -0
  15. package/dist/client-context.js +20 -0
  16. package/dist/client-context.js.map +1 -0
  17. package/dist/continuation.d.ts +34 -0
  18. package/dist/continuation.js +171 -0
  19. package/dist/continuation.js.map +1 -0
  20. package/dist/http.d.ts +7 -0
  21. package/dist/http.js +14 -0
  22. package/dist/http.js.map +1 -0
  23. package/dist/idle-compaction.d.ts +25 -0
  24. package/dist/idle-compaction.js +173 -0
  25. package/dist/idle-compaction.js.map +1 -0
  26. package/dist/index.d.ts +21 -0
  27. package/dist/index.js +13 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/layer.d.ts +19 -0
  30. package/dist/layer.js +139 -0
  31. package/dist/layer.js.map +1 -0
  32. package/dist/mcp.d.ts +38 -0
  33. package/dist/mcp.js +199 -0
  34. package/dist/mcp.js.map +1 -0
  35. package/dist/mode.d.ts +11 -0
  36. package/dist/mode.js +35 -0
  37. package/dist/mode.js.map +1 -0
  38. package/dist/pages.d.ts +8 -0
  39. package/dist/pages.js +12 -0
  40. package/dist/pages.js.map +1 -0
  41. package/dist/project-context.d.ts +18 -0
  42. package/dist/project-context.js +77 -0
  43. package/dist/project-context.js.map +1 -0
  44. package/dist/protocol.d.ts +539 -0
  45. package/dist/protocol.js +500 -0
  46. package/dist/protocol.js.map +1 -0
  47. package/dist/server.d.ts +27 -0
  48. package/dist/server.js +482 -0
  49. package/dist/server.js.map +1 -0
  50. package/dist/service.d.ts +136 -0
  51. package/dist/service.js +635 -0
  52. package/dist/service.js.map +1 -0
  53. package/dist/shell.d.ts +31 -0
  54. package/dist/shell.js +57 -0
  55. package/dist/shell.js.map +1 -0
  56. package/dist/submissions.d.ts +22 -0
  57. package/dist/submissions.js +34 -0
  58. package/dist/submissions.js.map +1 -0
  59. package/package.json +49 -3
  60. package/README.md +0 -3
package/dist/bridge.js ADDED
@@ -0,0 +1,259 @@
1
+ import { AgentSideConnection, PROTOCOL_VERSION, RequestError } from "@agentclientprotocol/sdk";
2
+ import { setTimeout as sleep } from "node:timers/promises";
3
+ import { entryUpdates, runInputs, stopReason } from "./acp.js";
4
+ import { call, CALL_TIMEOUT_MS, CallTimeout, heldList, randomId, startConversation, unansweredText, within, } from "./protocol.js";
5
+ import { lost as lostOutcome } from "./submissions.js";
6
+ const AWAY = "the service stopped answering";
7
+ const RESEND_WAIT_MS = 250;
8
+ /** A request the service could not answer, however that came to be known. */
9
+ function lostService(detail = AWAY) {
10
+ return RequestError.internalError({ reason: "lost", detail }, `the agent did not answer: ${unansweredText({ reason: "lost", detail })}`);
11
+ }
12
+ /**
13
+ * Speaks ACP on `stream` for the agent behind `client`, a host connected to the agent service, so any ACP client
14
+ * can drive it. One ACP session is one conversation.
15
+ */
16
+ export function serveAcp(stream, client, options = {}) {
17
+ let bridge;
18
+ const lostAfterMs = options.lostAfterMs ?? CALL_TIMEOUT_MS;
19
+ const connection = new AgentSideConnection((opened) => (bridge = new Bridge(opened, client, lostAfterMs)), stream);
20
+ void connection.closed.finally(() => bridge?.release());
21
+ return connection;
22
+ }
23
+ class Bridge {
24
+ #connection;
25
+ #client;
26
+ #lostAfterMs;
27
+ #sessions = new Map();
28
+ /** Aborted once the client is gone. */
29
+ #gone = new AbortController();
30
+ #list;
31
+ constructor(connection, client, lostAfterMs) {
32
+ this.#connection = connection;
33
+ this.#client = client;
34
+ this.#lostAfterMs = lostAfterMs;
35
+ this.#list = heldList(client, { timeoutMs: lostAfterMs, signal: this.#gone.signal });
36
+ }
37
+ /** Lets go of every session's conversation, and ends the prompts still waiting, once the client is gone. */
38
+ async release() {
39
+ this.#gone.abort();
40
+ this.#list.release();
41
+ const sessions = [...this.#sessions.values()];
42
+ this.#sessions.clear();
43
+ await Promise.all(sessions.map(async (session) => (await session.catch(() => undefined))?.release()));
44
+ }
45
+ async initialize() {
46
+ return { protocolVersion: PROTOCOL_VERSION, agentCapabilities: {}, authMethods: [] };
47
+ }
48
+ async authenticate() {
49
+ return {};
50
+ }
51
+ async newSession() {
52
+ const { id, conversation } = await this.#reach((options) => startConversation(this.#client, this.#list.get, options));
53
+ if (this.#gone.signal.aborted) {
54
+ conversation.release();
55
+ throw RequestError.internalError(undefined, "the client has gone");
56
+ }
57
+ this.#sessions.set(String(id), Promise.resolve(conversation));
58
+ return { sessionId: String(id) };
59
+ }
60
+ async prompt({ sessionId, prompt }) {
61
+ const conversation = await this.#session(sessionId);
62
+ // Text and resource links are what every ACP agent takes; a link reaches the model as its name and its address.
63
+ const text = prompt.flatMap((block) => {
64
+ if (block.type === "text")
65
+ return [block.text];
66
+ if (block.type === "resource_link")
67
+ return [`${block.name} <${block.uri}>`];
68
+ return [];
69
+ }).join("\n");
70
+ if (text.trim() === "")
71
+ throw RequestError.invalidParams(undefined, "the prompt has no text or link");
72
+ const sent = new Set(view(conversation).entries.map((entry) => entry.id));
73
+ const answers = [];
74
+ let forwarding = Promise.resolve();
75
+ let undelivered;
76
+ // Committed entries go out in order, each once; the client already has its own prompt.
77
+ const forward = () => {
78
+ forwarding = forwarding.then(async () => {
79
+ if (conversation.status !== "live")
80
+ return;
81
+ for (const entry of view(conversation).entries.filter((e) => !sent.has(e.id))) {
82
+ sent.add(entry.id);
83
+ answers.push(...(entry.model ?? []).filter((m) => m.role === "assistant"));
84
+ for (const update of entryUpdates(entry)) {
85
+ if (update.sessionUpdate !== "user_message_chunk")
86
+ await this.#connection.sessionUpdate({ sessionId, update });
87
+ }
88
+ }
89
+ }).catch((error) => {
90
+ undelivered ??= error;
91
+ });
92
+ };
93
+ const submission = await this.#send(sessionId, conversation, text);
94
+ const outcome = await this.#follow(sessionId, conversation, submission, forward);
95
+ if (outcome.status === "gone")
96
+ return { stopReason: "cancelled" };
97
+ if (outcome.status === "ended")
98
+ throw RequestError.resourceNotFound(sessionId);
99
+ forward();
100
+ await forwarding;
101
+ if (undelivered !== undefined)
102
+ throw undelivered;
103
+ if (outcome.status === "unanswered") {
104
+ if (outcome.reason === "aborted")
105
+ return { stopReason: "cancelled" };
106
+ if (outcome.reason === "lost")
107
+ throw lostService(outcome.detail);
108
+ const why = unansweredText({ reason: outcome.reason ?? "unknown", detail: outcome.detail });
109
+ throw RequestError.internalError({ reason: outcome.reason, detail: outcome.detail }, `the agent did not answer: ${why}`);
110
+ }
111
+ return { stopReason: stopReason(answers.at(-1)) };
112
+ }
113
+ /**
114
+ * Submits `text` under a request of its own, so it can be sent again when the link fails mid-call: the service
115
+ * admits it once, whether or not the first try reached it.
116
+ */
117
+ async #send(sessionId, conversation, text) {
118
+ const request = randomId();
119
+ // One bound for every try, so a send waits no longer than any other request.
120
+ return this.#reach((options) => within(options, async (signal) => {
121
+ for (;;) {
122
+ // Only a call whose link wavered while it ran is sent again; one on a steady link failed for what it is.
123
+ let wavered = false;
124
+ const stop = conversation.onStatus(() => void (wavered = true));
125
+ try {
126
+ return await this.#client.root((ctx) => conversation.submit({ text, request }, ctx), { signal });
127
+ }
128
+ catch (error) {
129
+ // The link's own news may trail the call's failure by a turn of the event loop.
130
+ await sleep(0);
131
+ if (conversation.status === "gone" || conversation.status === "released") {
132
+ this.#forget(sessionId, conversation);
133
+ throw RequestError.resourceNotFound(sessionId);
134
+ }
135
+ if (!wavered && conversation.status === "live")
136
+ throw error;
137
+ await sleep(RESEND_WAIT_MS, undefined, { signal });
138
+ }
139
+ finally {
140
+ stop();
141
+ }
142
+ }
143
+ }));
144
+ }
145
+ async cancel({ sessionId }) {
146
+ const conversation = await this.#session(sessionId);
147
+ await this.#call((ctx) => conversation.abort({}, ctx));
148
+ }
149
+ /**
150
+ * How `submission` ends: once it has settled and the view no longer shows it running, so the last answer is in
151
+ * the view. A reconnect brings the object's state back as the store has it; a service that stays away, or a
152
+ * conversation that ended, takes the session with it.
153
+ */
154
+ #follow(sessionId, conversation, submission, changed) {
155
+ const stops = [];
156
+ let ended = false;
157
+ return new Promise((resolve) => {
158
+ const settle = (outcome) => {
159
+ if (ended)
160
+ return;
161
+ ended = true;
162
+ resolve(outcome);
163
+ };
164
+ const lost = (detail) => settle(lostOutcome(detail));
165
+ const check = () => {
166
+ if (ended || conversation.status !== "live")
167
+ return;
168
+ changed();
169
+ const current = conversation.submissions.value[String(submission)];
170
+ if (current !== undefined && current.status !== "pending" && !runInputs(view(conversation)).includes(submission))
171
+ settle(current);
172
+ };
173
+ let away;
174
+ const followed = (status) => {
175
+ if (ended)
176
+ return;
177
+ clearTimeout(away);
178
+ if (status === "live") {
179
+ check();
180
+ }
181
+ else if (status === "loading") {
182
+ away = setTimeout(() => {
183
+ lost(AWAY);
184
+ this.#forget(sessionId, conversation);
185
+ }, this.#lostAfterMs);
186
+ }
187
+ else {
188
+ settle({ status: "ended" });
189
+ this.#forget(sessionId, conversation);
190
+ }
191
+ };
192
+ // The agent keeps working for a client that has gone, but nobody waits for it here any longer.
193
+ const gone = () => settle({ status: "gone" });
194
+ this.#gone.signal.addEventListener("abort", gone, { once: true });
195
+ stops.push(() => clearTimeout(away), () => this.#gone.signal.removeEventListener("abort", gone));
196
+ // Subscribing delivers the current value at once, so the outcome may already be known here.
197
+ stops.push(conversation.view.subscribe(check), conversation.submissions.subscribe(check), conversation.onStatus(followed));
198
+ // Status listeners hear only of changes; one that already happened is taken as it stands.
199
+ if (conversation.status !== "live")
200
+ followed(conversation.status);
201
+ if (this.#gone.signal.aborted)
202
+ gone();
203
+ }).finally(() => stops.forEach((stop) => stop()));
204
+ }
205
+ /** A held conversation, once it is live: waiting out a reconnect, but no longer than the service may stay away. */
206
+ async #session(sessionId) {
207
+ const session = this.#sessions.get(sessionId);
208
+ if (session === undefined)
209
+ throw RequestError.resourceNotFound(sessionId);
210
+ const conversation = await session;
211
+ await this.#reach((options) => within(options, (signal) => this.#live(sessionId, conversation, signal)));
212
+ return conversation;
213
+ }
214
+ /** Resolves once the session's conversation is live; one that ended takes the session with it. */
215
+ #live(sessionId, conversation, signal) {
216
+ return new Promise((resolve, reject) => {
217
+ const settled = (status) => {
218
+ if (status === "loading")
219
+ return;
220
+ stop();
221
+ signal.removeEventListener("abort", aborted);
222
+ if (status === "live")
223
+ return resolve();
224
+ this.#forget(sessionId, conversation);
225
+ reject(RequestError.resourceNotFound(sessionId));
226
+ };
227
+ const aborted = () => {
228
+ stop();
229
+ reject(signal.reason);
230
+ };
231
+ const stop = conversation.onStatus(settled);
232
+ if (signal.aborted)
233
+ return aborted();
234
+ signal.addEventListener("abort", aborted, { once: true });
235
+ settled(conversation.status);
236
+ });
237
+ }
238
+ #forget(sessionId, conversation) {
239
+ this.#sessions.delete(sessionId);
240
+ conversation.release();
241
+ }
242
+ /** A method call on an object the bridge holds, through any reconnect, as long as the service may stay away. */
243
+ #call(work) {
244
+ return this.#reach((options) => call(this.#client, work, options));
245
+ }
246
+ /** Any wait on the service, for as long as it may stay away and the client is here. */
247
+ async #reach(work) {
248
+ try {
249
+ return await work({ timeoutMs: this.#lostAfterMs, signal: this.#gone.signal });
250
+ }
251
+ catch (error) {
252
+ throw error instanceof CallTimeout || this.#gone.signal.aborted ? lostService() : error;
253
+ }
254
+ }
255
+ }
256
+ function view(conversation) {
257
+ return conversation.view.value;
258
+ }
259
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { type Extension } from "@earendil-works/pi-durable";
2
+ /** What a client shows its user beside a conversation, in the client's own words, such as the page they are on. */
3
+ export declare const ClientContextDoc: import("@earendil-works/pi-durable").ConversationDocToken<{
4
+ text: string;
5
+ }>;
6
+ /** Gives the model the client's context as it stands at each request; nothing while the client has set none. */
7
+ export declare const clientContext: Extension;
@@ -0,0 +1,20 @@
1
+ import { defineDoc, defineExtension, section } from "@earendil-works/pi-durable";
2
+ /** What a client shows its user beside a conversation, in the client's own words, such as the page they are on. */
3
+ export const ClientContextDoc = defineDoc({
4
+ kind: "chopin.client-context",
5
+ version: 1,
6
+ scope: "conversation",
7
+ history: "latest",
8
+ fork: "current",
9
+ initial: () => ({ text: "" }),
10
+ checkpointWhen: () => false,
11
+ });
12
+ /** Gives the model the client's context as it stands at each request; nothing while the client has set none. */
13
+ export const clientContext = defineExtension({
14
+ name: "chopin-client-context",
15
+ sections: [section("client_context", async ({ read, conversationId }, context) => {
16
+ const text = (await read.snapshot(ClientContextDoc, conversationId, context))?.text ?? "";
17
+ return text === "" ? undefined : text;
18
+ })],
19
+ });
20
+ //# sourceMappingURL=client-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-context.js","sourceRoot":"","sources":["../src/client-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,OAAO,EAAkB,MAAM,4BAA4B,CAAC;AAEjG,mHAAmH;AACnH,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAmB;IAC1D,IAAI,EAAE,uBAAuB;IAC7B,OAAO,EAAE,CAAC;IACV,KAAK,EAAE,cAAc;IACrB,OAAO,EAAE,QAAQ;IACjB,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IAC7B,cAAc,EAAE,GAAG,EAAE,CAAC,KAAK;CAC5B,CAAC,CAAC;AAEH,gHAAgH;AAChH,MAAM,CAAC,MAAM,aAAa,GAAc,eAAe,CAAC;IACtD,IAAI,EAAE,uBAAuB;IAC7B,QAAQ,EAAE,CAAC,OAAO,CAAC,gBAAgB,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,OAAO,EAAE,EAAE;YAC/E,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,gBAAgB,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC;YAC1F,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;QACxC,CAAC,CAAC,CAAC;CACJ,CAAC,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { type TranscriptEntry } from "@chopin-ai/core";
2
+ import type { Api, AssistantMessage, Message, Model, Models } from "@earendil-works/pi-ai";
3
+ import type { Extension } from "@earendil-works/pi-durable";
4
+ /** A decision point to continue from: what came before it, and what the request carries. */
5
+ export interface ContinuationInput {
6
+ /** The session before the decision point, as model messages. */
7
+ messages: readonly Message[];
8
+ /** The extensions whose sections and tools the request carries, the layer's among them, in install order. */
9
+ extensions: readonly Extension[];
10
+ model: Model<Api>;
11
+ models: Models;
12
+ signal?: AbortSignal;
13
+ }
14
+ /**
15
+ * The agent's next step at a decision point, from one request: the system prompt rendered from `extensions` as a
16
+ * session's would be, the tools they offer, and the session so far. Nothing it asks for is run. Two versions of a
17
+ * layer continued this way differ only in the layer.
18
+ */
19
+ export declare function continueAt(input: ContinuationInput): Promise<AssistantMessage>;
20
+ /**
21
+ * What a provider would refuse in a conversation continued at a step: it must open with the user, give every call its
22
+ * result right after it and no result without its call, and end with the user's or a tool's last word.
23
+ */
24
+ export declare function conversationProblems(messages: readonly Message[]): string[];
25
+ /** The step as a judge reads it: what it said and the calls it asked for. */
26
+ export declare function stepText(step: AssistantMessage): string;
27
+ /**
28
+ * The model messages of an ACP transcript: the user's text, the agent's text and calls, and the calls' results.
29
+ * Thoughts are left out, as no provider takes back another's. Assistant turns carry `model`'s identity, so the
30
+ * provider reads them as its own. A provider takes a result only right after its call, so a result whose call is not
31
+ * here is left out, as is a call that never got one, and what the user said while calls were running follows their
32
+ * results.
33
+ */
34
+ export declare function transcriptMessages(entries: readonly TranscriptEntry[], model: Model<Api>): Message[];
@@ -0,0 +1,171 @@
1
+ import { LoopFault } from "@chopin-ai/core";
2
+ import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
3
+ /**
4
+ * The agent's next step at a decision point, from one request: the system prompt rendered from `extensions` as a
5
+ * session's would be, the tools they offer, and the session so far. Nothing it asks for is run. Two versions of a
6
+ * layer continued this way differ only in the layer.
7
+ */
8
+ export async function continueAt(input) {
9
+ const systemPrompt = await renderSections(input);
10
+ const tools = input.extensions.flatMap((extension) => (extension.tools ?? []).map((tool) => ({ name: tool.name, description: tool.description, parameters: tool.parameters })));
11
+ // The step is asked for after the last word of the user or a tool; text the agent had begun it with is part of it.
12
+ const messages = [...input.messages];
13
+ while (messages.at(-1)?.role === "assistant")
14
+ messages.pop();
15
+ const problems = conversationProblems(messages);
16
+ if (problems.length > 0)
17
+ throw new LoopFault(`a provider would refuse this continuation: ${problems.join("; ")}`);
18
+ return input.models.completeSimple(input.model, { systemPrompt, messages, tools }, {
19
+ ...(input.signal && { signal: input.signal }),
20
+ });
21
+ }
22
+ /**
23
+ * What a provider would refuse in a conversation continued at a step: it must open with the user, give every call its
24
+ * result right after it and no result without its call, and end with the user's or a tool's last word.
25
+ */
26
+ export function conversationProblems(messages) {
27
+ const problems = [];
28
+ if (messages[0]?.role !== "user")
29
+ problems.push("it does not open with the user");
30
+ const last = messages.at(-1)?.role;
31
+ if (last !== "user" && last !== "toolResult")
32
+ problems.push("it ends with the agent");
33
+ messages.forEach((message, at) => {
34
+ if (message.role === "toolResult")
35
+ return;
36
+ const calls = message.role === "assistant" ? message.content.flatMap((block) => (block.type === "toolCall" ? [block.id] : [])) : [];
37
+ const results = [];
38
+ for (let next = at + 1; messages[next]?.role === "toolResult"; next += 1)
39
+ results.push(messages[next].toolCallId);
40
+ if (calls.some((id) => !results.includes(id)))
41
+ problems.push(`a call in message ${at} has no result right after it`);
42
+ if (results.some((id) => !calls.includes(id)))
43
+ problems.push(`a result after message ${at} answers no call in it`);
44
+ });
45
+ return problems;
46
+ }
47
+ /** The step as a judge reads it: what it said and the calls it asked for. */
48
+ export function stepText(step) {
49
+ return JSON.stringify(step.content.flatMap((block) => {
50
+ if (block.type === "text")
51
+ return [{ text: block.text }];
52
+ if (block.type === "toolCall")
53
+ return [{ call: block.name, arguments: block.arguments }];
54
+ return [];
55
+ }));
56
+ }
57
+ /**
58
+ * The model messages of an ACP transcript: the user's text, the agent's text and calls, and the calls' results.
59
+ * Thoughts are left out, as no provider takes back another's. Assistant turns carry `model`'s identity, so the
60
+ * provider reads them as its own. A provider takes a result only right after its call, so a result whose call is not
61
+ * here is left out, as is a call that never got one, and what the user said while calls were running follows their
62
+ * results.
63
+ */
64
+ export function transcriptMessages(entries, model) {
65
+ const messages = [];
66
+ const names = new Map();
67
+ const waiting = new Set();
68
+ let held = [];
69
+ const assistant = () => {
70
+ const last = messages.at(-1);
71
+ if (last?.role === "assistant")
72
+ return last;
73
+ const made = {
74
+ role: "assistant", content: [], api: model.api, provider: model.provider, model: model.id, stopReason: "stop",
75
+ usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } },
76
+ timestamp: 0,
77
+ };
78
+ messages.push(made);
79
+ return made;
80
+ };
81
+ const say = (text) => {
82
+ const last = messages.at(-1);
83
+ if (last?.role === "user" && typeof last.content === "string")
84
+ last.content = `${last.content}\n${text}`;
85
+ else
86
+ messages.push({ role: "user", content: text, timestamp: 0 });
87
+ };
88
+ const flush = () => {
89
+ for (const text of held)
90
+ say(text);
91
+ held = [];
92
+ };
93
+ // Calls still waiting when the agent moves on never got a result.
94
+ const settle = () => {
95
+ if (waiting.size > 0) {
96
+ for (let at = messages.length - 1; at >= 0; at -= 1) {
97
+ const message = messages[at];
98
+ if (message.role !== "assistant")
99
+ continue;
100
+ message.content = message.content.filter((block) => block.type !== "toolCall" || !waiting.has(block.id));
101
+ if (message.content.length === 0)
102
+ messages.splice(at, 1);
103
+ else if (!message.content.some((block) => block.type === "toolCall"))
104
+ message.stopReason = "stop";
105
+ }
106
+ waiting.clear();
107
+ }
108
+ flush();
109
+ };
110
+ for (const entry of entries) {
111
+ if (!("notification" in entry))
112
+ continue;
113
+ const update = entry.notification.update;
114
+ if (update.sessionUpdate === "user_message_chunk" && update.content.type === "text") {
115
+ if (waiting.size > 0)
116
+ held.push(update.content.text);
117
+ else
118
+ say(update.content.text);
119
+ }
120
+ else if (update.sessionUpdate === "agent_message_chunk" && update.content.type === "text") {
121
+ settle();
122
+ assistant().content.push({ type: "text", text: update.content.text });
123
+ }
124
+ else if (update.sessionUpdate === "tool_call") {
125
+ if (messages.at(-1)?.role !== "assistant")
126
+ settle();
127
+ names.set(update.toolCallId, update.title);
128
+ const call = { type: "toolCall", id: update.toolCallId, name: update.title, arguments: (update.rawInput ?? {}) };
129
+ const turn = assistant();
130
+ turn.content.push(call);
131
+ turn.stopReason = "toolUse";
132
+ waiting.add(update.toolCallId);
133
+ }
134
+ else if (update.sessionUpdate === "tool_call_update" && (update.status === "completed" || update.status === "failed")) {
135
+ if (!waiting.delete(update.toolCallId))
136
+ continue;
137
+ const text = (update.content ?? []).flatMap((item) => (item.type === "content" && item.content.type === "text" ? [item.content.text] : [])).join("\n");
138
+ messages.push({
139
+ role: "toolResult", toolCallId: update.toolCallId, toolName: names.get(update.toolCallId) ?? update.title ?? "",
140
+ content: [{ type: "text", text }], isError: update.status === "failed", timestamp: 0,
141
+ });
142
+ if (waiting.size === 0)
143
+ flush();
144
+ }
145
+ }
146
+ settle();
147
+ return messages;
148
+ }
149
+ const NO_DOCUMENTS = { snapshot: async () => undefined, snapshotAsOf: async () => undefined };
150
+ async function renderSections(input) {
151
+ const sections = input.extensions.flatMap((extension) => extension.sections ?? []);
152
+ const prompt = {
153
+ conversationId: 0,
154
+ agent: {
155
+ model: { provider: input.model.provider, modelId: input.model.id }, thinkingLevel: "off", extensions: input.extensions,
156
+ tools: input.extensions.flatMap((extension) => extension.tools ?? []), callable: [], deferred: [], sections,
157
+ },
158
+ env: undefined,
159
+ shown: {},
160
+ read: NO_DOCUMENTS,
161
+ };
162
+ const rendered = [];
163
+ for (const section of sections) {
164
+ const text = await section.render(prompt, BACKGROUND_CONTEXT);
165
+ if (text === undefined || text === "")
166
+ continue;
167
+ rendered.push(section.tag === false ? text : `<${section.key}>\n${text}\n</${section.key}>`);
168
+ }
169
+ return rendered.join("\n\n");
170
+ }
171
+ //# sourceMappingURL=continuation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"continuation.js","sourceRoot":"","sources":["../src/continuation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAwB,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAenE;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAwB;IACvD,MAAM,YAAY,GAAG,MAAM,cAAc,CAAC,KAAK,CAAC,CAAC;IACjD,MAAM,KAAK,GAAW,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACjG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;IACtF,mHAAmH;IACnH,MAAM,QAAQ,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;IACrC,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,WAAW;QAAE,QAAQ,CAAC,GAAG,EAAE,CAAC;IAC7D,MAAM,QAAQ,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAChD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,8CAA8C,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClH,OAAO,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;QACjF,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;KAC9C,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAA4B;IAC/D,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,MAAM;QAAE,QAAQ,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;IAClF,MAAM,IAAI,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;IACnC,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,YAAY;QAAE,QAAQ,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IACtF,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE;QAC/B,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY;YAAE,OAAO;QAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACpI,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,KAAK,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,KAAK,YAAY,EAAE,IAAI,IAAI,CAAC;YAAE,OAAO,CAAC,IAAI,CAAE,QAAQ,CAAC,IAAI,CAAuB,CAAC,UAAU,CAAC,CAAC;QACzI,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,qBAAqB,EAAE,+BAA+B,CAAC,CAAC;QACrH,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,0BAA0B,EAAE,wBAAwB,CAAC,CAAC;IACrH,CAAC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,QAAQ,CAAC,IAAsB;IAC7C,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAY,EAAE;QAC7D,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACzD,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU;YAAE,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;QACzF,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAmC,EAAE,KAAiB;IACvF,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,IAAI,IAAI,GAAa,EAAE,CAAC;IACxB,MAAM,SAAS,GAAG,GAAqB,EAAE;QACvC,MAAM,IAAI,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,IAAI,EAAE,IAAI,KAAK,WAAW;YAAE,OAAO,IAAI,CAAC;QAC5C,MAAM,IAAI,GAAqB;YAC7B,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,EAAE,MAAM;YAC7G,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YACjJ,SAAS,EAAE,CAAC;SACb,CAAC;QACF,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IACF,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,EAAE;QAC3B,MAAM,IAAI,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,IAAI,EAAE,IAAI,KAAK,MAAM,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ;YAAE,IAAI,CAAC,OAAO,GAAG,GAAG,IAAI,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;;YACpG,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;IACpE,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,KAAK,MAAM,IAAI,IAAI,IAAI;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,GAAG,EAAE,CAAC;IACZ,CAAC,CAAC;IACF,kEAAkE;IAClE,MAAM,MAAM,GAAG,GAAG,EAAE;QAClB,IAAI,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YACrB,KAAK,IAAI,EAAE,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC;gBACpD,MAAM,OAAO,GAAG,QAAQ,CAAC,EAAE,CAAE,CAAC;gBAC9B,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW;oBAAE,SAAS;gBAC3C,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;gBACzG,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;oBAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;qBACpD,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU,CAAC;oBAAE,OAAO,CAAC,UAAU,GAAG,MAAM,CAAC;YACpG,CAAC;YACD,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,CAAC;QACD,KAAK,EAAE,CAAC;IACV,CAAC,CAAC;IACF,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,CAAC,cAAc,IAAI,KAAK,CAAC;YAAE,SAAS;QACzC,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC;QACzC,IAAI,MAAM,CAAC,aAAa,KAAK,oBAAoB,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACpF,IAAI,OAAO,CAAC,IAAI,GAAG,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;;gBAChD,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC;aAAM,IAAI,MAAM,CAAC,aAAa,KAAK,qBAAqB,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC5F,MAAM,EAAE,CAAC;YACT,SAAS,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QACxE,CAAC;aAAM,IAAI,MAAM,CAAC,aAAa,KAAK,WAAW,EAAE,CAAC;YAChD,IAAI,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,WAAW;gBAAE,MAAM,EAAE,CAAC;YACpD,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;YAC3C,MAAM,IAAI,GAAa,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAA0B,EAAE,CAAC;YACpJ,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC;YACzB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACjC,CAAC;aAAM,IAAI,MAAM,CAAC,aAAa,KAAK,kBAAkB,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,EAAE,CAAC;YACxH,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC;gBAAE,SAAS;YACjD,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvJ,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,KAAK,IAAI,EAAE;gBAC/G,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,SAAS,EAAE,CAAC;aACrF,CAAC,CAAC;YACH,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC;gBAAE,KAAK,EAAE,CAAC;QAClC,CAAC;IACH,CAAC;IACD,MAAM,EAAE,CAAC;IACT,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,YAAY,GAAG,EAAE,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC,SAAS,EAAE,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC,SAAS,EAA+B,CAAC;AAE3H,KAAK,UAAU,cAAc,CAAC,KAAwB;IACpD,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IACnF,MAAM,MAAM,GAAgB;QAC1B,cAAc,EAAE,CAAmB;QACnC,KAAK,EAAE;YACL,KAAK,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU;YACtH,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ;SAC5G;QACD,GAAG,EAAE,SAAS;QACd,KAAK,EAAE,EAAE;QACT,IAAI,EAAE,YAAY;KACnB,CAAC;IACF,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;QAC9D,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,EAAE;YAAE,SAAS;QAChD,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,GAAG,MAAM,IAAI,OAAO,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;IAC/F,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import { type Authenticate } from "./auth.js";
3
+ /**
4
+ * Whether the service has work in hand, as `GET /status` tells whoever runs it, so a workspace is reclaimed only once
5
+ * it has none. Asking is not using: it keeps nothing awake.
6
+ */
7
+ export declare function statusHandler(authenticate: Authenticate, busy: () => Promise<boolean>): (request: IncomingMessage, response: ServerResponse) => Promise<void>;
package/dist/http.js ADDED
@@ -0,0 +1,14 @@
1
+ import { credentialOf } from "./auth.js";
2
+ /**
3
+ * Whether the service has work in hand, as `GET /status` tells whoever runs it, so a workspace is reclaimed only once
4
+ * it has none. Asking is not using: it keeps nothing awake.
5
+ */
6
+ export function statusHandler(authenticate, busy) {
7
+ return async (request, response) => {
8
+ const credential = credentialOf(request);
9
+ if (credential === undefined || (await authenticate(credential)) === undefined)
10
+ return void response.writeHead(401).end("unauthorized");
11
+ response.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ busy: await busy() }));
12
+ };
13
+ }
14
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAqB,MAAM,WAAW,CAAC;AAE5D;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,YAA0B,EAAE,IAA4B;IACpF,OAAO,KAAK,EAAE,OAAwB,EAAE,QAAwB,EAAiB,EAAE;QACjF,MAAM,UAAU,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI,UAAU,KAAK,SAAS,IAAI,CAAC,MAAM,YAAY,CAAC,UAAU,CAAC,CAAC,KAAK,SAAS;YAAE,OAAO,KAAK,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACxI,QAAQ,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;IAC9G,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { Models } from "@earendil-works/pi-ai";
2
+ import { type ConversationId, type Extension, type Harness } from "@earendil-works/pi-durable";
3
+ /** Where a conversation was compacted for being idle; the model never reads it. */
4
+ export declare const IdleCompactionEntry: import("@earendil-works/pi-durable").Entry<never>;
5
+ export interface IdleCompactionOptions {
6
+ /** The models the harness asks, with its credentials. */
7
+ models: Models;
8
+ /** Whether people come back to `conversation`; no other is compacted for being idle. */
9
+ eligible(conversation: ConversationId): Promise<boolean>;
10
+ report?: (error: unknown) => void;
11
+ }
12
+ /**
13
+ * Compacts a conversation left idle just before its prompt cache expires. The summary is asked for in a request that
14
+ * repeats the conversation's own and adds one message, so the provider reads all but that from its cache.
15
+ */
16
+ export declare class IdleCompaction {
17
+ #private;
18
+ readonly extension: Extension;
19
+ constructor(options: IdleCompactionOptions);
20
+ /** Starts on `harness`, which runs this extension. */
21
+ attach(harness: Harness): void;
22
+ /** Drops what is scheduled for `conversation`, and stops its compaction if one is asking for a summary. */
23
+ cancel(conversation: ConversationId): void;
24
+ close(): void;
25
+ }