agentfootprint 7.13.0 → 7.14.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 (57) hide show
  1. package/dist/core/Agent.js +79 -2
  2. package/dist/core/Agent.js.map +1 -1
  3. package/dist/esm/core/Agent.d.ts +47 -0
  4. package/dist/esm/core/Agent.js +79 -2
  5. package/dist/esm/core/Agent.js.map +1 -1
  6. package/dist/esm/hosting/envelope.d.ts +33 -0
  7. package/dist/esm/hosting/envelope.js +49 -0
  8. package/dist/esm/hosting/envelope.js.map +1 -0
  9. package/dist/esm/hosting/errors.d.ts +78 -0
  10. package/dist/esm/hosting/errors.js +119 -0
  11. package/dist/esm/hosting/errors.js.map +1 -0
  12. package/dist/esm/hosting/index.d.ts +53 -0
  13. package/dist/esm/hosting/index.js +52 -0
  14. package/dist/esm/hosting/index.js.map +1 -0
  15. package/dist/esm/hosting/memorySessions.d.ts +22 -0
  16. package/dist/esm/hosting/memorySessions.js +31 -0
  17. package/dist/esm/hosting/memorySessions.js.map +1 -0
  18. package/dist/esm/hosting/nodeHost.d.ts +64 -0
  19. package/dist/esm/hosting/nodeHost.js +245 -0
  20. package/dist/esm/hosting/nodeHost.js.map +1 -0
  21. package/dist/esm/hosting/standingAgent.d.ts +62 -0
  22. package/dist/esm/hosting/standingAgent.js +192 -0
  23. package/dist/esm/hosting/standingAgent.js.map +1 -0
  24. package/dist/esm/hosting/types.d.ts +206 -0
  25. package/dist/esm/hosting/types.js +21 -0
  26. package/dist/esm/hosting/types.js.map +1 -0
  27. package/dist/hosting/envelope.js +54 -0
  28. package/dist/hosting/envelope.js.map +1 -0
  29. package/dist/hosting/errors.js +126 -0
  30. package/dist/hosting/errors.js.map +1 -0
  31. package/dist/hosting/index.js +64 -0
  32. package/dist/hosting/index.js.map +1 -0
  33. package/dist/hosting/memorySessions.js +35 -0
  34. package/dist/hosting/memorySessions.js.map +1 -0
  35. package/dist/hosting/nodeHost.js +272 -0
  36. package/dist/hosting/nodeHost.js.map +1 -0
  37. package/dist/hosting/standingAgent.js +196 -0
  38. package/dist/hosting/standingAgent.js.map +1 -0
  39. package/dist/hosting/types.js +22 -0
  40. package/dist/hosting/types.js.map +1 -0
  41. package/dist/types/core/Agent.d.ts +47 -0
  42. package/dist/types/core/Agent.d.ts.map +1 -1
  43. package/dist/types/hosting/envelope.d.ts +34 -0
  44. package/dist/types/hosting/envelope.d.ts.map +1 -0
  45. package/dist/types/hosting/errors.d.ts +79 -0
  46. package/dist/types/hosting/errors.d.ts.map +1 -0
  47. package/dist/types/hosting/index.d.ts +54 -0
  48. package/dist/types/hosting/index.d.ts.map +1 -0
  49. package/dist/types/hosting/memorySessions.d.ts +23 -0
  50. package/dist/types/hosting/memorySessions.d.ts.map +1 -0
  51. package/dist/types/hosting/nodeHost.d.ts +65 -0
  52. package/dist/types/hosting/nodeHost.d.ts.map +1 -0
  53. package/dist/types/hosting/standingAgent.d.ts +63 -0
  54. package/dist/types/hosting/standingAgent.d.ts.map +1 -0
  55. package/dist/types/hosting/types.d.ts +207 -0
  56. package/dist/types/hosting/types.d.ts.map +1 -0
  57. package/package.json +14 -1
@@ -0,0 +1,245 @@
1
+ /**
2
+ * hosting/nodeHost — the plain HTTP adapter, built on `node:http` and nothing
3
+ * else.
4
+ *
5
+ * const host = nodeHost({ port: 8080 });
6
+ * const handle = await host.serve(async (request, reply) => {
7
+ * reply.complete(await answer(request.input));
8
+ * });
9
+ *
10
+ * Two routes: `POST /invoke` takes `{ input, sessionId? }` and answers
11
+ * `{ output }`; `GET /health` answers `{ status: 'ok' }`. Both paths are
12
+ * options, because the paths are the part most likely to be dictated to you by
13
+ * whatever is in front of the process — a load balancer, a container contract,
14
+ * a colleague's convention. A path is a deployment detail, so it is a knob
15
+ * here and absent from the port entirely.
16
+ *
17
+ * **Streaming is the caller's choice, not the server's.** Send
18
+ * `Accept: text/event-stream` and the reply is Server-Sent Events, one `chunk`
19
+ * event per `reply.emit(...)` then a final `complete`. Send anything else and
20
+ * the same handler produces one JSON body — it emits into a buffer that the
21
+ * completion settles. The handler cannot tell the difference and does not need
22
+ * to, which is the property `capabilities` exists to describe.
23
+ *
24
+ * Pattern: Adapter. Everything specific to HTTP — the paths, the JSON body
25
+ * shape, the status codes, the SSE framing — lives in this file. `types.ts`
26
+ * knows none of it, and a future adapter for somewhere else re-decides all of
27
+ * it without touching a port.
28
+ */
29
+ import { encodeSSE } from '../stream.js';
30
+ import { HostClosedError } from './errors.js';
31
+ const HOST_NAME = 'nodeHost';
32
+ const CAPABILITIES = ['streaming'];
33
+ /**
34
+ * Status codes this adapter maps by error code. Anything else is a 500.
35
+ *
36
+ * All three are deliberately NOT 5xx: none of them is the agent breaking. A
37
+ * closed host is shutting down (503), a concurrent turn conflicts with the run
38
+ * already going (409), and a paused run conflicts with the state this reply can
39
+ * carry (409) — the run is unfinished, not failed, and a 500 would say
40
+ * otherwise to every dashboard that ever sees it.
41
+ */
42
+ const STATUS_BY_CODE = {
43
+ ERR_HOST_CLOSED: 503,
44
+ ERR_CONCURRENT_RUN: 409,
45
+ ERR_PAUSE_NOT_CARRIED: 409,
46
+ };
47
+ /**
48
+ * An HTTP host for one handler.
49
+ *
50
+ * @example
51
+ * const handle = await nodeHost({ port: 0 }).serve(handler);
52
+ * await fetch(`${handle.url}/invoke`, {
53
+ * method: 'POST',
54
+ * headers: { 'content-type': 'application/json' },
55
+ * body: JSON.stringify({ input: 'hello', sessionId: 'c-1' }),
56
+ * });
57
+ * await handle.close();
58
+ */
59
+ export function nodeHost(options = {}) {
60
+ const port = options.port ?? 8080;
61
+ const hostname = options.hostname ?? '0.0.0.0';
62
+ const invokePath = options.invokePath ?? '/invoke';
63
+ const healthPath = options.healthPath ?? '/health';
64
+ return {
65
+ name: HOST_NAME,
66
+ capabilities: CAPABILITIES,
67
+ async serve(handler) {
68
+ const { createServer } = await import('node:http');
69
+ const startedAt = Date.now();
70
+ const inFlight = new Set();
71
+ let accepting = true;
72
+ let closing;
73
+ const server = createServer((req, res) => {
74
+ const path = (req.url ?? '').split('?')[0];
75
+ if (req.method === 'GET' && path === healthPath) {
76
+ sendJson(res, 200, { status: 'ok', uptimeMs: Date.now() - startedAt });
77
+ return;
78
+ }
79
+ if (req.method !== 'POST' || path !== invokePath) {
80
+ sendJson(res, 404, { error: `no route for ${req.method ?? '?'} ${path}` });
81
+ return;
82
+ }
83
+ if (!accepting) {
84
+ const refusal = new HostClosedError(HOST_NAME);
85
+ sendJson(res, STATUS_BY_CODE[refusal.code] ?? 500, {
86
+ error: refusal.message,
87
+ code: refusal.code,
88
+ });
89
+ return;
90
+ }
91
+ const served = serveOne(req, res, handler);
92
+ inFlight.add(served);
93
+ void served.finally(() => inFlight.delete(served));
94
+ });
95
+ await new Promise((resolve, reject) => {
96
+ server.once('error', reject);
97
+ server.listen(port, hostname, resolve);
98
+ });
99
+ const address = server.address();
100
+ const boundPort = typeof address === 'object' && address ? address.port : port;
101
+ const displayHost = hostname === '0.0.0.0' || hostname === '::' ? '127.0.0.1' : hostname;
102
+ return {
103
+ url: `http://${displayHost}:${boundPort}`,
104
+ port: boundPort,
105
+ close() {
106
+ // Idempotent: the first call owns the shutdown, later ones await it.
107
+ closing ??= (async () => {
108
+ accepting = false;
109
+ // Drain BEFORE touching sockets — an in-flight request is work the
110
+ // caller is still waiting on, and dropping it would be the exact
111
+ // thing close() promises not to do.
112
+ await Promise.allSettled([...inFlight]);
113
+ server.closeIdleConnections();
114
+ await new Promise((resolve) => server.close(() => resolve()));
115
+ })();
116
+ return closing;
117
+ },
118
+ };
119
+ },
120
+ };
121
+ }
122
+ /** Run one request through the handler and write whatever it decides. */
123
+ async function serveOne(req, res, handler) {
124
+ const wantsStream = (req.headers.accept ?? '').includes('text/event-stream');
125
+ const controller = new AbortController();
126
+ let body;
127
+ try {
128
+ body = await readJson(req);
129
+ }
130
+ catch (err) {
131
+ sendJson(res, 400, { error: `invalid JSON body: ${asError(err).message}` });
132
+ return;
133
+ }
134
+ const input = typeof body.input === 'string' ? body.input : '';
135
+ // sessionId is caller data whichever way it arrives; the body wins so a
136
+ // caller that sets both is not surprised by which one the server preferred.
137
+ const sessionId = typeof body.sessionId === 'string'
138
+ ? body.sessionId
139
+ : typeof req.headers['x-session-id'] === 'string'
140
+ ? req.headers['x-session-id']
141
+ : undefined;
142
+ if (wantsStream) {
143
+ res.writeHead(200, {
144
+ 'content-type': 'text/event-stream',
145
+ 'cache-control': 'no-cache',
146
+ connection: 'keep-alive',
147
+ });
148
+ }
149
+ let settled = false;
150
+ // The caller hung up before we answered — tell the handler so it can stop
151
+ // paying for work nobody is waiting for.
152
+ res.once('close', () => {
153
+ if (!settled)
154
+ controller.abort();
155
+ });
156
+ const reply = {
157
+ emit(chunk) {
158
+ // Not streaming? The chunk is a preview of text `complete` will deliver
159
+ // in full, so there is nothing to send and nothing to keep.
160
+ if (settled || !wantsStream)
161
+ return;
162
+ res.write(encodeSSE('chunk', { text: chunk }));
163
+ },
164
+ complete(output) {
165
+ if (settled)
166
+ return;
167
+ settled = true;
168
+ if (wantsStream) {
169
+ res.write(encodeSSE('complete', { output }));
170
+ res.end();
171
+ }
172
+ else {
173
+ sendJson(res, 200, { output });
174
+ }
175
+ },
176
+ fail(error) {
177
+ if (settled)
178
+ return;
179
+ settled = true;
180
+ const code = error.code;
181
+ const payload = { error: error.message, ...(code !== undefined && { code }) };
182
+ if (wantsStream) {
183
+ res.write(encodeSSE('error', payload));
184
+ res.end();
185
+ }
186
+ else {
187
+ sendJson(res, code !== undefined ? STATUS_BY_CODE[code] ?? 500 : 500, payload);
188
+ }
189
+ },
190
+ };
191
+ try {
192
+ await handler({
193
+ input,
194
+ ...(sessionId !== undefined && { sessionId }),
195
+ headers: lowerCased(req.headers),
196
+ signal: controller.signal,
197
+ }, reply);
198
+ }
199
+ catch (err) {
200
+ // A handler that throws is a failed request, never a hung one.
201
+ reply.fail(asError(err));
202
+ }
203
+ // A handler that returned without answering gets one authored answer rather
204
+ // than a socket the caller waits on until it times out.
205
+ if (!settled) {
206
+ reply.fail(new Error('[hosting] the handler returned without calling complete() or fail().'));
207
+ }
208
+ }
209
+ function readJson(req) {
210
+ return new Promise((resolve, reject) => {
211
+ const chunks = [];
212
+ req.on('data', (c) => chunks.push(c));
213
+ req.on('error', reject);
214
+ req.on('end', () => {
215
+ const raw = Buffer.concat(chunks).toString('utf8');
216
+ if (!raw)
217
+ return resolve({});
218
+ try {
219
+ const parsed = JSON.parse(raw);
220
+ resolve(parsed && typeof parsed === 'object' ? parsed : {});
221
+ }
222
+ catch (err) {
223
+ reject(err);
224
+ }
225
+ });
226
+ });
227
+ }
228
+ function sendJson(res, status, body) {
229
+ res.writeHead(status, { 'content-type': 'application/json' });
230
+ res.end(JSON.stringify(body));
231
+ }
232
+ function lowerCased(headers) {
233
+ const out = {};
234
+ for (const [name, value] of Object.entries(headers)) {
235
+ if (typeof value === 'string')
236
+ out[name.toLowerCase()] = value;
237
+ else if (Array.isArray(value))
238
+ out[name.toLowerCase()] = value.join(', ');
239
+ }
240
+ return out;
241
+ }
242
+ function asError(err) {
243
+ return err instanceof Error ? err : new Error(String(err));
244
+ }
245
+ //# sourceMappingURL=nodeHost.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodeHost.js","sourceRoot":"","sources":["../../../src/hosting/nodeHost.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * hosting/standingAgent — an agent that stays up and remembers.
3
+ *
4
+ * await standingAgent({
5
+ * agent,
6
+ * sessions: memorySessions(),
7
+ * host: nodeHost({ port: 8080 }),
8
+ * });
9
+ *
10
+ * One request at a time it does four things: wake and hydrate the session,
11
+ * resume that conversation or start a fresh one, persist what the run leaves
12
+ * behind, then reply. Everything else is somebody else's job — the host carries
13
+ * bytes, the store keeps them, the agent thinks.
14
+ *
15
+ * ── Resuming is a REPLAY, and that has a cost you must know about ────────────
16
+ * A stored conversation is restored through `agent.resumeOnError(...)`, and
17
+ * this is its caveat, stated here in the words the Agent states it in, because
18
+ * a composition that hides the caveat of the thing it composes is worse than no
19
+ * composition at all:
20
+ *
21
+ * > **Tool re-execution / idempotency**: tool side effects from the FAILED
22
+ * > iteration are not in the checkpoint. The model re-decides from the
23
+ * > restored history and may re-issue those tool calls — they WILL execute
24
+ * > again (there is no built-in toolCallId dedup). Mutating tools (payments,
25
+ * > emails, DB writes) must be idempotent — key on stable call content, not
26
+ * > `ctx.toolCallId` (a re-issued call gets a new id).
27
+ *
28
+ * ── Why one run at a time ───────────────────────────────────────────────────
29
+ * An Agent instance holds per-run state on itself, and this composer shares ONE
30
+ * instance across every session. Two runs overlapping on it do not crash —
31
+ * which is precisely the danger. They both finish, and the state the composer
32
+ * reads afterwards belongs to whichever started last, so one session's envelope
33
+ * can end up holding another session's conversation. Nothing in the recording
34
+ * would say so. Runs are therefore serialized, globally, and that is a
35
+ * correctness requirement rather than a tuning choice.
36
+ *
37
+ * `onConcurrentInvoke` is the separate question of what to do when a second
38
+ * turn of the SAME conversation arrives while the first is running: refuse it
39
+ * (default) or queue it. A request for a DIFFERENT session is never refused —
40
+ * there is nothing wrong with it; it simply waits its turn.
41
+ *
42
+ * Pattern: Composition root. It owns wiring and ordering; it invents no
43
+ * mechanism of its own.
44
+ */
45
+ import type { HostHandle, StandingAgentOptions } from './types.js';
46
+ /**
47
+ * Serve one agent, with per-session conversation memory, on any
48
+ * {@link AgentHost}.
49
+ *
50
+ * Resolves once the host is live. Closing the returned handle closes the host
51
+ * and detaches the listeners this composer added to the agent.
52
+ *
53
+ * @example
54
+ * const handle = await standingAgent({
55
+ * agent,
56
+ * sessions: memorySessions(),
57
+ * host: nodeHost({ port: 0 }),
58
+ * onConcurrentInvoke: 'enqueue',
59
+ * });
60
+ * process.on('SIGTERM', () => void handle.close());
61
+ */
62
+ export declare function standingAgent<TH extends HostHandle>(options: StandingAgentOptions<TH>): Promise<TH>;
@@ -0,0 +1,192 @@
1
+ /**
2
+ * hosting/standingAgent — an agent that stays up and remembers.
3
+ *
4
+ * await standingAgent({
5
+ * agent,
6
+ * sessions: memorySessions(),
7
+ * host: nodeHost({ port: 8080 }),
8
+ * });
9
+ *
10
+ * One request at a time it does four things: wake and hydrate the session,
11
+ * resume that conversation or start a fresh one, persist what the run leaves
12
+ * behind, then reply. Everything else is somebody else's job — the host carries
13
+ * bytes, the store keeps them, the agent thinks.
14
+ *
15
+ * ── Resuming is a REPLAY, and that has a cost you must know about ────────────
16
+ * A stored conversation is restored through `agent.resumeOnError(...)`, and
17
+ * this is its caveat, stated here in the words the Agent states it in, because
18
+ * a composition that hides the caveat of the thing it composes is worse than no
19
+ * composition at all:
20
+ *
21
+ * > **Tool re-execution / idempotency**: tool side effects from the FAILED
22
+ * > iteration are not in the checkpoint. The model re-decides from the
23
+ * > restored history and may re-issue those tool calls — they WILL execute
24
+ * > again (there is no built-in toolCallId dedup). Mutating tools (payments,
25
+ * > emails, DB writes) must be idempotent — key on stable call content, not
26
+ * > `ctx.toolCallId` (a re-issued call gets a new id).
27
+ *
28
+ * ── Why one run at a time ───────────────────────────────────────────────────
29
+ * An Agent instance holds per-run state on itself, and this composer shares ONE
30
+ * instance across every session. Two runs overlapping on it do not crash —
31
+ * which is precisely the danger. They both finish, and the state the composer
32
+ * reads afterwards belongs to whichever started last, so one session's envelope
33
+ * can end up holding another session's conversation. Nothing in the recording
34
+ * would say so. Runs are therefore serialized, globally, and that is a
35
+ * correctness requirement rather than a tuning choice.
36
+ *
37
+ * `onConcurrentInvoke` is the separate question of what to do when a second
38
+ * turn of the SAME conversation arrives while the first is running: refuse it
39
+ * (default) or queue it. A request for a DIFFERENT session is never refused —
40
+ * there is nothing wrong with it; it simply waits its turn.
41
+ *
42
+ * Pattern: Composition root. It owns wiring and ordering; it invents no
43
+ * mechanism of its own.
44
+ */
45
+ import { isPaused } from '../core/pause.js';
46
+ import { toEnvelope, readEnvelope } from './envelope.js';
47
+ import { ConcurrentRunError, PauseNotCarriedError } from './errors.js';
48
+ /**
49
+ * Serve one agent, with per-session conversation memory, on any
50
+ * {@link AgentHost}.
51
+ *
52
+ * Resolves once the host is live. Closing the returned handle closes the host
53
+ * and detaches the listeners this composer added to the agent.
54
+ *
55
+ * @example
56
+ * const handle = await standingAgent({
57
+ * agent,
58
+ * sessions: memorySessions(),
59
+ * host: nodeHost({ port: 0 }),
60
+ * onConcurrentInvoke: 'enqueue',
61
+ * });
62
+ * process.on('SIGTERM', () => void handle.close());
63
+ */
64
+ export async function standingAgent(options) {
65
+ const { agent, sessions, host } = options;
66
+ const policy = options.onConcurrentInvoke ?? 'reject';
67
+ // Runs are serialized, so at any moment there is at most one active reply and
68
+ // at most one active run to name in a refusal.
69
+ let activeReply;
70
+ let activeRunId;
71
+ let activeSession;
72
+ const queued = [];
73
+ let chain = Promise.resolve();
74
+ let anonymous = 0;
75
+ const offTurnStart = agent.on('agentfootprint.agent.turn_start', (event) => {
76
+ activeRunId = event.meta?.runId;
77
+ });
78
+ // Tokens reach the caller only if the host streams; `emit` on a host that
79
+ // does not is a no-op by design, so this needs no capability check.
80
+ const offToken = agent.on('agentfootprint.stream.token', (event) => {
81
+ const content = event.payload?.content;
82
+ if (typeof content === 'string' && content.length > 0)
83
+ activeReply?.emit?.(content);
84
+ });
85
+ /** Queue `work` behind everything already waiting, FIFO. */
86
+ function serialize(sessionKey, work) {
87
+ if (policy === 'reject' && (activeSession === sessionKey || queued.includes(sessionKey))) {
88
+ return Promise.reject(new ConcurrentRunError(sessionKey, activeRunId));
89
+ }
90
+ queued.push(sessionKey);
91
+ const mine = chain.then(async () => {
92
+ queued.splice(queued.indexOf(sessionKey), 1);
93
+ activeSession = sessionKey;
94
+ try {
95
+ await work();
96
+ }
97
+ finally {
98
+ activeSession = undefined;
99
+ activeRunId = undefined;
100
+ }
101
+ });
102
+ // The chain must never reject, or one failed request would wedge every
103
+ // request behind it.
104
+ chain = mine.then(() => undefined, () => undefined);
105
+ return mine;
106
+ }
107
+ const handler = async (request, reply) => {
108
+ const sessionId = request.sessionId;
109
+ // A request with no session has nothing to hydrate and nowhere to persist
110
+ // that the caller could ask for again, so it gets its own key and can never
111
+ // collide with anyone.
112
+ const sessionKey = sessionId ?? `#anonymous-${++anonymous}`;
113
+ try {
114
+ await serialize(sessionKey, () => answerOne(agent, sessions, request, reply, sessionId));
115
+ }
116
+ catch (err) {
117
+ reply.fail(err instanceof Error ? err : new Error(String(err)));
118
+ }
119
+ };
120
+ async function answerOne(runner, store, request, reply, sessionId) {
121
+ activeReply = reply;
122
+ try {
123
+ let prior;
124
+ if (sessionId !== undefined) {
125
+ await store.onWake?.(sessionId, 'invoke');
126
+ const stored = await store.hydrate(sessionId);
127
+ // Throws by name on a format this runtime cannot read — better a loud
128
+ // refusal than an agent answering from half a conversation.
129
+ if (stored !== undefined)
130
+ prior = readEnvelope(stored);
131
+ }
132
+ // The signal reaches tool execution, tool discovery and skill-entry
133
+ // scoring. It does NOT currently reach the LLM call, so a caller who
134
+ // hangs up mid-generation stops the tools, not the token stream.
135
+ const runOptions = request.signal ? { env: { signal: request.signal } } : undefined;
136
+ const output = prior
137
+ ? await runner.resumeOnError(continueConversation(prior, request.input), runOptions)
138
+ : await runner.run({ message: request.input }, runOptions);
139
+ if (isPaused(output)) {
140
+ reply.fail(new PauseNotCarriedError(pausedToolName(runner), sessionId));
141
+ return;
142
+ }
143
+ if (sessionId !== undefined) {
144
+ const conversation = runner.checkpoint();
145
+ // Persist BEFORE answering: the caller learns the answer only once the
146
+ // conversation that produced it is durable, so a queued next turn can
147
+ // never read state older than the answer already given.
148
+ if (conversation)
149
+ await store.persist(sessionId, toEnvelope(conversation));
150
+ }
151
+ reply.complete(typeof output === 'string' ? output : String(output));
152
+ }
153
+ finally {
154
+ activeReply = undefined;
155
+ }
156
+ }
157
+ const handle = await host.serve(handler);
158
+ // Keep whatever the adapter put on its own handle (nodeHost's bound `url`,
159
+ // say) and replace only `close`, which now also detaches this composer's
160
+ // listeners. The cast is the one place TypeScript cannot see that a spread
161
+ // plus one override is still the adapter's handle type.
162
+ return {
163
+ ...handle,
164
+ async close() {
165
+ await handle.close();
166
+ offTurnStart();
167
+ offToken();
168
+ },
169
+ };
170
+ }
171
+ /**
172
+ * The stored conversation plus this turn's message.
173
+ *
174
+ * The new message has to be appended by hand: `resumeOnError` restores the
175
+ * history it is given and the LLM stage reads that history directly, so a
176
+ * message left out here is a message the model never sees.
177
+ */
178
+ function continueConversation(prior, input) {
179
+ return {
180
+ ...prior,
181
+ history: [...prior.history, { role: 'user', content: input }],
182
+ originalInput: { message: input },
183
+ checkpointedAt: Date.now(),
184
+ };
185
+ }
186
+ /** Which tool asked for a human, when the paused run recorded one. */
187
+ function pausedToolName(runner) {
188
+ const state = runner.getLastSnapshot()?.sharedState;
189
+ const name = state?.pausedToolName;
190
+ return typeof name === 'string' && name.length > 0 ? name : undefined;
191
+ }
192
+ //# sourceMappingURL=standingAgent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standingAgent.js","sourceRoot":"","sources":["../../../src/hosting/standingAgent.ts"],"names":[],"mappings":"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"}