agentfootprint 7.18.0 → 7.19.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 (79) hide show
  1. package/dist/adapters/hosting/agentcore.js +23 -9
  2. package/dist/adapters/hosting/agentcore.js.map +1 -1
  3. package/dist/core/Agent.js +6 -0
  4. package/dist/core/Agent.js.map +1 -1
  5. package/dist/core/agent/stages/toolCalls.js +13 -0
  6. package/dist/core/agent/stages/toolCalls.js.map +1 -1
  7. package/dist/core/durabilityBarrier.js +68 -0
  8. package/dist/core/durabilityBarrier.js.map +1 -0
  9. package/dist/esm/adapters/hosting/agentcore.d.ts +4 -4
  10. package/dist/esm/adapters/hosting/agentcore.js +24 -10
  11. package/dist/esm/adapters/hosting/agentcore.js.map +1 -1
  12. package/dist/esm/core/Agent.js +6 -0
  13. package/dist/esm/core/Agent.js.map +1 -1
  14. package/dist/esm/core/agent/stages/toolCalls.d.ts +21 -0
  15. package/dist/esm/core/agent/stages/toolCalls.js +13 -0
  16. package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
  17. package/dist/esm/core/durabilityBarrier.d.ts +61 -0
  18. package/dist/esm/core/durabilityBarrier.js +63 -0
  19. package/dist/esm/core/durabilityBarrier.js.map +1 -0
  20. package/dist/esm/hosting/durability.d.ts +92 -0
  21. package/dist/esm/hosting/durability.js +174 -0
  22. package/dist/esm/hosting/durability.js.map +1 -0
  23. package/dist/esm/hosting/envelope.d.ts +75 -14
  24. package/dist/esm/hosting/envelope.js +141 -16
  25. package/dist/esm/hosting/envelope.js.map +1 -1
  26. package/dist/esm/hosting/errors.d.ts +60 -11
  27. package/dist/esm/hosting/errors.js +92 -18
  28. package/dist/esm/hosting/errors.js.map +1 -1
  29. package/dist/esm/hosting/httpHost.d.ts +17 -1
  30. package/dist/esm/hosting/httpHost.js +42 -6
  31. package/dist/esm/hosting/httpHost.js.map +1 -1
  32. package/dist/esm/hosting/index.d.ts +7 -4
  33. package/dist/esm/hosting/index.js +6 -3
  34. package/dist/esm/hosting/index.js.map +1 -1
  35. package/dist/esm/hosting/nodeHost.d.ts +4 -2
  36. package/dist/esm/hosting/nodeHost.js +14 -3
  37. package/dist/esm/hosting/nodeHost.js.map +1 -1
  38. package/dist/esm/hosting/standingAgent.d.ts +22 -7
  39. package/dist/esm/hosting/standingAgent.js +144 -32
  40. package/dist/esm/hosting/standingAgent.js.map +1 -1
  41. package/dist/esm/hosting/types.d.ts +193 -19
  42. package/dist/hosting/durability.js +178 -0
  43. package/dist/hosting/durability.js.map +1 -0
  44. package/dist/hosting/envelope.js +146 -18
  45. package/dist/hosting/envelope.js.map +1 -1
  46. package/dist/hosting/errors.js +95 -19
  47. package/dist/hosting/errors.js.map +1 -1
  48. package/dist/hosting/httpHost.js +42 -6
  49. package/dist/hosting/httpHost.js.map +1 -1
  50. package/dist/hosting/index.js +10 -2
  51. package/dist/hosting/index.js.map +1 -1
  52. package/dist/hosting/nodeHost.js +14 -3
  53. package/dist/hosting/nodeHost.js.map +1 -1
  54. package/dist/hosting/standingAgent.js +142 -30
  55. package/dist/hosting/standingAgent.js.map +1 -1
  56. package/dist/types/adapters/hosting/agentcore.d.ts +4 -4
  57. package/dist/types/adapters/hosting/agentcore.d.ts.map +1 -1
  58. package/dist/types/core/Agent.d.ts.map +1 -1
  59. package/dist/types/core/agent/stages/toolCalls.d.ts +21 -0
  60. package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
  61. package/dist/types/core/durabilityBarrier.d.ts +62 -0
  62. package/dist/types/core/durabilityBarrier.d.ts.map +1 -0
  63. package/dist/types/hosting/durability.d.ts +93 -0
  64. package/dist/types/hosting/durability.d.ts.map +1 -0
  65. package/dist/types/hosting/envelope.d.ts +75 -14
  66. package/dist/types/hosting/envelope.d.ts.map +1 -1
  67. package/dist/types/hosting/errors.d.ts +60 -11
  68. package/dist/types/hosting/errors.d.ts.map +1 -1
  69. package/dist/types/hosting/httpHost.d.ts +17 -1
  70. package/dist/types/hosting/httpHost.d.ts.map +1 -1
  71. package/dist/types/hosting/index.d.ts +7 -4
  72. package/dist/types/hosting/index.d.ts.map +1 -1
  73. package/dist/types/hosting/nodeHost.d.ts +4 -2
  74. package/dist/types/hosting/nodeHost.d.ts.map +1 -1
  75. package/dist/types/hosting/standingAgent.d.ts +22 -7
  76. package/dist/types/hosting/standingAgent.d.ts.map +1 -1
  77. package/dist/types/hosting/types.d.ts +193 -19
  78. package/dist/types/hosting/types.d.ts.map +1 -1
  79. package/package.json +1 -1
@@ -5,17 +5,26 @@
5
5
  * agent,
6
6
  * sessions: memorySessions(),
7
7
  * host: nodeHost({ port: 8080 }),
8
+ * durability: 'sync', // optional; 'exit' is the default
8
9
  * });
9
10
  *
10
11
  * 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
+ * continue that session or start a fresh one, persist what the run leaves
12
13
  * behind, then reply. Everything else is somebody else's job — the host carries
13
14
  * bytes, the store keeps them, the agent thinks.
14
15
  *
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
16
+ * ── A run has three ends, and this composer honours all three ────────────────
17
+ * It answered, it asked a person something, or it failed. An answer completes
18
+ * the reply and stores a conversation. A QUESTION stores the paused run as
19
+ * `'flowchart-v1'` and leaves through `reply.awaiting(...)` — its own terminal,
20
+ * never `fail`, because a pause is unfinished work and reporting it as a failure
21
+ * tells every dashboard downstream something untrue. A later request for that
22
+ * session carrying `decision` continues the run from exactly where it stopped.
23
+ *
24
+ * ── Resuming a CONVERSATION is a REPLAY, and that has a cost ─────────────────
25
+ * A stored conversation is restored through `agent.resumeOnError(...)`, and this
26
+ * is its caveat, stated here in the words the Agent states it in, because a
27
+ * composition that hides the caveat of the thing it composes is worse than no
19
28
  * composition at all:
20
29
  *
21
30
  * > **Tool re-execution / idempotency**: tool side effects from the FAILED
@@ -25,6 +34,10 @@
25
34
  * > emails, DB writes) must be idempotent — key on stable call content, not
26
35
  * > `ctx.toolCallId` (a re-issued call gets a new id).
27
36
  *
37
+ * `durability` is the dial that bounds how much of that a crash can cost you.
38
+ * Resuming a PAUSED run is different in kind: it is not a replay at all — the
39
+ * engine continues from its own checkpoint, and no earlier tool call re-runs.
40
+ *
28
41
  * ── Why one run at a time ───────────────────────────────────────────────────
29
42
  * An Agent instance holds per-run state on itself, and this composer shares ONE
30
43
  * instance across every session. Two runs overlapping on it do not crash —
@@ -43,14 +56,16 @@
43
56
  * mechanism of its own.
44
57
  */
45
58
  import { isPaused } from '../core/pause.js';
46
- import { toEnvelope, readEnvelope } from './envelope.js';
47
- import { ConcurrentRunError, PauseNotCarriedError } from './errors.js';
59
+ import { durableWriter } from './durability.js';
60
+ import { readEnvelope, readPausedRun, toEnvelope, toPausedEnvelope } from './envelope.js';
61
+ import { AwaitingDecisionError, ConcurrentRunError, NoPendingAskError, PauseNotCarriedError, } from './errors.js';
48
62
  /**
49
63
  * Serve one agent, with per-session conversation memory, on any
50
64
  * {@link AgentHost}.
51
65
  *
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.
66
+ * Resolves once the host is live. Closing the returned handle closes the host,
67
+ * detaches the listeners this composer added to the agent, and removes its
68
+ * durability wiring.
54
69
  *
55
70
  * @example
56
71
  * const handle = await standingAgent({
@@ -58,17 +73,21 @@ import { ConcurrentRunError, PauseNotCarriedError } from './errors.js';
58
73
  * sessions: memorySessions(),
59
74
  * host: nodeHost({ port: 0 }),
60
75
  * onConcurrentInvoke: 'enqueue',
76
+ * durability: 'sync',
61
77
  * });
62
78
  * process.on('SIGTERM', () => void handle.close());
63
79
  */
64
80
  export async function standingAgent(options) {
65
81
  const { agent, sessions, host } = options;
66
82
  const policy = options.onConcurrentInvoke ?? 'reject';
83
+ const durability = options.durability ?? 'exit';
67
84
  // Runs are serialized, so at any moment there is at most one active reply and
68
85
  // at most one active run to name in a refusal.
69
86
  let activeReply;
70
87
  let activeRunId;
71
88
  let activeSession;
89
+ /** The REAL session id of the active run — never the anonymous placeholder. */
90
+ let activeSessionId;
72
91
  const queued = [];
73
92
  let chain = Promise.resolve();
74
93
  let anonymous = 0;
@@ -82,6 +101,19 @@ export async function standingAgent(options) {
82
101
  if (typeof content === 'string' && content.length > 0)
83
102
  activeReply?.emit?.(content);
84
103
  });
104
+ // Under 'exit' NOTHING is built: no observer on the agent, no barrier, no
105
+ // per-commit work. That is what "the default is byte-identical" means here —
106
+ // not a mode that happens to write once, but wiring that is never installed.
107
+ const writer = durability === 'exit'
108
+ ? undefined
109
+ : durableWriter({
110
+ mode: durability,
111
+ session: () => activeSessionId,
112
+ runId: () => activeRunId,
113
+ write: (sessionId, conversation) => sessions.persist(sessionId, toEnvelope(conversation)),
114
+ });
115
+ const detachWriter = writer ? agent.attach(writer.recorder) : undefined;
116
+ const uninstallBarrier = writer?.install(agent);
85
117
  /** Queue `work` behind everything already waiting, FIFO. */
86
118
  function serialize(sessionKey, work) {
87
119
  if (policy === 'reject' && (activeSession === sessionKey || queued.includes(sessionKey))) {
@@ -119,40 +151,101 @@ export async function standingAgent(options) {
119
151
  };
120
152
  async function answerOne(runner, store, request, reply, sessionId) {
121
153
  activeReply = reply;
154
+ // The writer only ever writes for the run it is inside; an anonymous
155
+ // request has nowhere to write to and gets nothing.
156
+ activeSessionId = sessionId;
122
157
  try {
123
158
  let prior;
159
+ let paused;
124
160
  if (sessionId !== undefined) {
125
- await store.onWake?.(sessionId, 'invoke');
161
+ // A request carrying a decision is waking this session to CONTINUE a
162
+ // run, which is a different thing to ask a store to be ready for.
163
+ await store.onWake?.(sessionId, request.decision !== undefined ? 'resume' : 'invoke');
126
164
  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);
165
+ if (stored !== undefined) {
166
+ // Both readers throw by name on a format this runtime cannot read —
167
+ // better a loud refusal than an agent answering from half a session.
168
+ if (stored.format === 'flowchart-v1')
169
+ paused = readPausedRun(stored);
170
+ else
171
+ prior = readEnvelope(stored);
172
+ }
131
173
  }
132
174
  // The signal reaches tool execution, tool discovery and skill-entry
133
175
  // scoring. It does NOT currently reach the LLM call, so a caller who
134
176
  // hangs up mid-generation stops the tools, not the token stream.
135
177
  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));
178
+ // ── The one discriminant ─────────────────────────────────────────
179
+ // A request carrying `decision` answers a pending question; a request
180
+ // without one is a new message. Never inferred from the text: reading
181
+ // approval out of prose is a guess, and a consent gate may not be built
182
+ // on a guess.
183
+ if (request.decision !== undefined) {
184
+ if (!paused) {
185
+ reply.fail(new NoPendingAskError(sessionId ?? '(anonymous)'));
186
+ return;
187
+ }
188
+ await deliver(await runner.resume(paused.checkpoint, request.decision, runOptions), runner, store, reply, sessionId);
141
189
  return;
142
190
  }
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));
191
+ if (paused && sessionId !== undefined) {
192
+ // The message is not run and the pause is not discarded. Both of the
193
+ // other options lose something a person cared about.
194
+ reply.fail(new AwaitingDecisionError(sessionId, paused.pending));
195
+ return;
150
196
  }
151
- reply.complete(typeof output === 'string' ? output : String(output));
197
+ const output = prior
198
+ ? await runner.resumeOnError(continueConversation(prior, request.input), runOptions)
199
+ : await runner.run({ message: request.input }, runOptions);
200
+ await deliver(output, runner, store, reply, sessionId);
201
+ }
202
+ catch (err) {
203
+ // A run that threw still has to drain. A write left in flight would race
204
+ // the NEXT turn's terminal write and could land after it. The drain's own
205
+ // failure is swallowed here and only here: the caller is already being
206
+ // told about the run's error, and replacing it with a storage error would
207
+ // hide the thing that actually went wrong.
208
+ await writer?.settle().catch(() => undefined);
209
+ throw err;
152
210
  }
153
211
  finally {
154
212
  activeReply = undefined;
213
+ activeSessionId = undefined;
214
+ }
215
+ }
216
+ /** Store what the run left behind, then end the reply with the right terminal. */
217
+ async function deliver(output, runner, store, reply, sessionId) {
218
+ // Mid-run writes settle FIRST. Ordering, not tidiness: an 'async' write
219
+ // still in flight would otherwise land after the terminal envelope and
220
+ // overwrite it — a stored pause quietly demoted back to a conversation,
221
+ // and the question a person was asked gone with it. It also rethrows a
222
+ // store that refused this run's progress, so a broken store fails the
223
+ // request rather than being reported as a clean answer.
224
+ await writer?.settle();
225
+ if (isPaused(output)) {
226
+ const pending = describePause(output, sessionId);
227
+ const conversation = sessionId === undefined ? undefined : runner.checkpoint();
228
+ if (sessionId !== undefined && conversation) {
229
+ await store.persist(sessionId, toPausedEnvelope({ checkpoint: output.checkpoint, conversation, pending }));
230
+ if (reply.awaiting) {
231
+ reply.awaiting(pending);
232
+ return;
233
+ }
234
+ }
235
+ // Either there was nowhere to store it, or the host cannot describe a
236
+ // question. Both are refusals about THIS reply, not about the run.
237
+ reply.fail(new PauseNotCarriedError(pending.tool, sessionId, conversation !== undefined));
238
+ return;
239
+ }
240
+ if (sessionId !== undefined) {
241
+ const conversation = runner.checkpoint();
242
+ // Persist BEFORE answering: the caller learns the answer only once the
243
+ // conversation that produced it is durable, so a queued next turn can
244
+ // never read state older than the answer already given.
245
+ if (conversation)
246
+ await store.persist(sessionId, toEnvelope(conversation));
155
247
  }
248
+ reply.complete(typeof output === 'string' ? output : String(output));
156
249
  }
157
250
  const handle = await host.serve(handler);
158
251
  // Keep whatever the adapter put on its own handle (nodeHost's bound `url`,
@@ -165,6 +258,8 @@ export async function standingAgent(options) {
165
258
  await handle.close();
166
259
  offTurnStart();
167
260
  offToken();
261
+ uninstallBarrier?.();
262
+ detachWriter?.();
168
263
  },
169
264
  };
170
265
  }
@@ -183,10 +278,27 @@ function continueConversation(prior, input) {
183
278
  checkpointedAt: Date.now(),
184
279
  };
185
280
  }
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;
281
+ /**
282
+ * Project a paused run into the part that is safe to hand back: the question,
283
+ * never the state.
284
+ *
285
+ * Every field is read from what the run actually recorded — the tool name and
286
+ * question the dispatch loop stamps on `pauseData`, the typed `checkIn`, the
287
+ * middleware `ask`. A field the run did not record is simply absent. Nothing
288
+ * here infers, summarises or invents, which is why the raw `pauseData` rides
289
+ * along untouched: the tool's author chose those words.
290
+ */
291
+ function describePause(outcome, sessionId) {
292
+ const data = outcome.pauseData;
293
+ const tool = typeof data?.toolName === 'string' ? data.toolName : undefined;
294
+ const question = typeof data?.question === 'string' ? data.question : undefined;
295
+ return {
296
+ ...(sessionId !== undefined && { sessionId }),
297
+ ...(tool !== undefined && { tool }),
298
+ ...(question !== undefined && { question }),
299
+ ...(outcome.checkIn !== undefined && { checkIn: outcome.checkIn }),
300
+ ...(outcome.ask !== undefined && { ask: outcome.ask }),
301
+ pauseData: outcome.pauseData,
302
+ };
191
303
  }
192
304
  //# sourceMappingURL=standingAgent.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"standingAgent.js","sourceRoot":"","sources":["../../../src/hosting/standingAgent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"standingAgent.js","sourceRoot":"","sources":["../../../src/hosting/standingAgent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAEH,OAAO,EAAE,QAAQ,EAA2B,MAAM,kBAAkB,CAAC;AAErE,OAAO,EAAE,aAAa,EAAsB,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAYrB;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAiC;IAEjC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,kBAAkB,IAAI,QAAQ,CAAC;IACtD,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,MAAM,CAAC;IAEhD,8EAA8E;IAC9E,+CAA+C;IAC/C,IAAI,WAAkC,CAAC;IACvC,IAAI,WAA+B,CAAC;IACpC,IAAI,aAAiC,CAAC;IACtC,+EAA+E;IAC/E,IAAI,eAAmC,CAAC;IACxC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,KAAK,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAC7C,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,MAAM,YAAY,GAAG,KAAK,CAAC,EAAE,CAAC,iCAAiC,EAAE,CAAC,KAAK,EAAE,EAAE;QACzE,WAAW,GAAI,KAAuC,CAAC,IAAI,EAAE,KAAK,CAAC;IACrE,CAAC,CAAC,CAAC;IACH,0EAA0E;IAC1E,oEAAoE;IACpE,MAAM,QAAQ,GAAG,KAAK,CAAC,EAAE,CAAC,6BAA6B,EAAE,CAAC,KAAK,EAAE,EAAE;QACjE,MAAM,OAAO,GAAI,KAA4C,CAAC,OAAO,EAAE,OAAO,CAAC;QAC/E,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,WAAW,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC;IACtF,CAAC,CAAC,CAAC;IAEH,0EAA0E;IAC1E,6EAA6E;IAC7E,6EAA6E;IAC7E,MAAM,MAAM,GACV,UAAU,KAAK,MAAM;QACnB,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,aAAa,CAAC;YACZ,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,GAAG,EAAE,CAAC,eAAe;YAC9B,KAAK,EAAE,GAAG,EAAE,CAAC,WAAW;YACxB,KAAK,EAAE,CAAC,SAAS,EAAE,YAAY,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC;SAC1F,CAAC,CAAC;IACT,MAAM,YAAY,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACxE,MAAM,gBAAgB,GAAG,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAEhD,4DAA4D;IAC5D,SAAS,SAAS,CAAC,UAAkB,EAAE,IAAyB;QAC9D,IAAI,MAAM,KAAK,QAAQ,IAAI,CAAC,aAAa,KAAK,UAAU,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;YACzF,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,kBAAkB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;QACzE,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACxB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;YACjC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;YAC7C,aAAa,GAAG,UAAU,CAAC;YAC3B,IAAI,CAAC;gBACH,MAAM,IAAI,EAAE,CAAC;YACf,CAAC;oBAAS,CAAC;gBACT,aAAa,GAAG,SAAS,CAAC;gBAC1B,WAAW,GAAG,SAAS,CAAC;YAC1B,CAAC;QACH,CAAC,CAAC,CAAC;QACH,uEAAuE;QACvE,qBAAqB;QACrB,KAAK,GAAG,IAAI,CAAC,IAAI,CACf,GAAG,EAAE,CAAC,SAAS,EACf,GAAG,EAAE,CAAC,SAAS,CAChB,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,EAAE,OAAoB,EAAE,KAAgB,EAAiB,EAAE;QAC9E,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACpC,0EAA0E;QAC1E,4EAA4E;QAC5E,uBAAuB;QACvB,MAAM,UAAU,GAAG,SAAS,IAAI,cAAc,EAAE,SAAS,EAAE,CAAC;QAC5D,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;QAC3F,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAC,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAClE,CAAC;IACH,CAAC,CAAC;IAEF,KAAK,UAAU,SAAS,CACtB,MAAa,EACb,KAAuB,EACvB,OAAoB,EACpB,KAAgB,EAChB,SAA6B;QAE7B,WAAW,GAAG,KAAK,CAAC;QACpB,qEAAqE;QACrE,oDAAoD;QACpD,eAAe,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC;YACH,IAAI,KAAqC,CAAC;YAC1C,IAAI,MAA6B,CAAC;YAClC,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC5B,qEAAqE;gBACrE,kEAAkE;gBAClE,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;gBACtF,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;gBAC9C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;oBACzB,oEAAoE;oBACpE,qEAAqE;oBACrE,IAAI,MAAM,CAAC,MAAM,KAAK,cAAc;wBAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;;wBAChE,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;gBACpC,CAAC;YACH,CAAC;YAED,oEAAoE;YACpE,qEAAqE;YACrE,iEAAiE;YACjE,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;YAEpF,oEAAoE;YACpE,sEAAsE;YACtE,sEAAsE;YACtE,wEAAwE;YACxE,cAAc;YACd,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;gBACnC,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,KAAK,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,SAAS,IAAI,aAAa,CAAC,CAAC,CAAC;oBAC9D,OAAO;gBACT,CAAC;gBACD,MAAM,OAAO,CACX,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,EACpE,MAAM,EACN,KAAK,EACL,KAAK,EACL,SAAS,CACV,CAAC;gBACF,OAAO;YACT,CAAC;YACD,IAAI,MAAM,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBACtC,qEAAqE;gBACrE,qDAAqD;gBACrD,KAAK,CAAC,IAAI,CAAC,IAAI,qBAAqB,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;gBACjE,OAAO;YACT,CAAC;YAED,MAAM,MAAM,GAAG,KAAK;gBAClB,CAAC,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC,oBAAoB,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;gBACpF,CAAC,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE,EAAE,UAAU,CAAC,CAAC;YAC7D,MAAM,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QACzD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,yEAAyE;YACzE,0EAA0E;YAC1E,uEAAuE;YACvE,0EAA0E;YAC1E,2CAA2C;YAC3C,MAAM,MAAM,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;YAC9C,MAAM,GAAG,CAAC;QACZ,CAAC;gBAAS,CAAC;YACT,WAAW,GAAG,SAAS,CAAC;YACxB,eAAe,GAAG,SAAS,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,kFAAkF;IAClF,KAAK,UAAU,OAAO,CACpB,MAAe,EACf,MAAa,EACb,KAAuB,EACvB,KAAgB,EAChB,SAA6B;QAE7B,wEAAwE;QACxE,uEAAuE;QACvE,wEAAwE;QACxE,uEAAuE;QACvE,sEAAsE;QACtE,wDAAwD;QACxD,MAAM,MAAM,EAAE,MAAM,EAAE,CAAC;QAEvB,IAAI,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACrB,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YACjD,MAAM,YAAY,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YAC/E,IAAI,SAAS,KAAK,SAAS,IAAI,YAAY,EAAE,CAAC;gBAC5C,MAAM,KAAK,CAAC,OAAO,CACjB,SAAS,EACT,gBAAgB,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,CAC3E,CAAC;gBACF,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;oBACnB,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;oBACxB,OAAO;gBACT,CAAC;YACH,CAAC;YACD,sEAAsE;YACtE,mEAAmE;YACnE,KAAK,CAAC,IAAI,CAAC,IAAI,oBAAoB,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC;YAC1F,OAAO;QACT,CAAC;QAED,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,MAAM,YAAY,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YACzC,uEAAuE;YACvE,sEAAsE;YACtE,wDAAwD;YACxD,IAAI,YAAY;gBAAE,MAAM,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC;QAC7E,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACzC,2EAA2E;IAC3E,yEAAyE;IACzE,2EAA2E;IAC3E,wDAAwD;IACxD,OAAO;QACL,GAAG,MAAM;QACT,KAAK,CAAC,KAAK;YACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACrB,YAAY,EAAE,CAAC;YACf,QAAQ,EAAE,CAAC;YACX,gBAAgB,EAAE,EAAE,CAAC;YACrB,YAAY,EAAE,EAAE,CAAC;QACnB,CAAC;KACI,CAAC;AACV,CAAC;AAED;;;;;;GAMG;AACH,SAAS,oBAAoB,CAAC,KAAyB,EAAE,KAAa;IACpE,OAAO;QACL,GAAG,KAAK;QACR,OAAO,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC7D,aAAa,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;QACjC,cAAc,EAAE,IAAI,CAAC,GAAG,EAAE;KAC3B,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,aAAa,CAAC,OAA2B,EAAE,SAA6B;IAC/E,MAAM,IAAI,GAAG,OAAO,CAAC,SAAmE,CAAC;IACzF,MAAM,IAAI,GAAG,OAAO,IAAI,EAAE,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5E,MAAM,QAAQ,GAAG,OAAO,IAAI,EAAE,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IAChF,OAAO;QACL,GAAG,CAAC,SAAS,KAAK,SAAS,IAAI,EAAE,SAAS,EAAE,CAAC;QAC7C,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,CAAC;QACnC,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC3C,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;QAClE,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;QACtD,SAAS,EAAE,OAAO,CAAC,SAAS;KAC7B,CAAC;AACJ,CAAC"}
@@ -17,7 +17,10 @@
17
17
  *
18
18
  * Pattern: Ports & adapters (hexagonal). Role: the port side, exclusively.
19
19
  */
20
+ import type { FlowchartCheckpoint } from 'footprintjs';
20
21
  import type { Agent } from '../core/Agent.js';
22
+ import type { CheckInRequest } from '../core/checkin.js';
23
+ import type { MiddlewareAsk } from '../core/pause.js';
21
24
  import type { AgentRunCheckpoint } from '../core/runCheckpoint.js';
22
25
  /**
23
26
  * Something a host can do BEYOND the baseline of "accept a request, deliver one
@@ -37,6 +40,24 @@ export type HostCapability = 'streaming';
37
40
  export interface HostRequest {
38
41
  /** What the caller is asking. */
39
42
  readonly input: string;
43
+ /**
44
+ * A person's answer to an outstanding {@link PendingAsk} — and **the one
45
+ * thing that distinguishes a resume from a new message.**
46
+ *
47
+ * Present ⇒ this request answers the run that paused on this session. Absent
48
+ * ⇒ this request is a new message. That is the whole contract, and it is a
49
+ * FIELD rather than an inference on purpose: reading approval out of prose
50
+ * ("yes, go ahead") is a guess, and a guess is not something a consent gate
51
+ * may be built on.
52
+ *
53
+ * The port never interprets it. It is handed to `agent.resume(checkpoint,
54
+ * decision)` exactly as it arrived — a {@link CheckInRequest} or a middleware
55
+ * `ask` is answered with the shipped `checkInApproved()` / `checkInDeclined()`
56
+ * vocabulary; a plain `askHuman` pause is answered with whatever that tool's
57
+ * author documented. Typed `unknown` because the library does not get to
58
+ * decide what a tool asked for.
59
+ */
60
+ readonly decision?: unknown;
40
61
  /**
41
62
  * The conversation this request CLAIMS to belong to — caller data, exactly as
42
63
  * the transport declared it (a JSON field, a header, a path segment).
@@ -57,13 +78,37 @@ export interface HostRequest {
57
78
  readonly signal?: AbortSignal;
58
79
  }
59
80
  /**
60
- * The one reply a request gets. Exactly one of {@link HostReply.complete} or
61
- * {@link HostReply.fail} ends it; a second call is ignored rather than allowed
62
- * to corrupt the wire.
81
+ * The one reply a request gets. Exactly one of {@link HostReply.complete},
82
+ * {@link HostReply.awaiting} or {@link HostReply.fail} ends it; a second call is
83
+ * ignored rather than allowed to corrupt the wire.
84
+ *
85
+ * Three terminals, because a run has three ends and only three: it answered, it
86
+ * stopped to ask a person something, or it failed. Before `'flowchart-v1'` there
87
+ * was nowhere to keep a paused run, so the middle one was delivered through
88
+ * `fail` — an error standing in for unfinished work. It is a terminal of its own
89
+ * now, and a pause is never reported as a failure again.
63
90
  */
64
91
  export interface HostReply {
65
92
  /** Deliver the final answer and end the reply. */
66
93
  complete(output: string): void;
94
+ /**
95
+ * End the reply with **unfinished work**: the run stopped to ask a person
96
+ * something, the paused run is stored, and a later request carrying
97
+ * {@link HostRequest.decision} continues it.
98
+ *
99
+ * This is not a failure and must not be reported as one. The agent did not
100
+ * break, no work was lost, and there is nothing to retry — there is a question
101
+ * outstanding. An adapter that maps this onto a 5xx, an error counter or a
102
+ * dead-letter queue is telling every dashboard it feeds something that is not
103
+ * true.
104
+ *
105
+ * Optional on the TYPE for the same reason {@link HostReply.emit} is: a
106
+ * minimal adapter need not implement it. Every shipped adapter does. When it
107
+ * is absent the composer still STORES the paused run — the store is not the
108
+ * transport's business — and ends the reply with a named refusal instead, so
109
+ * the pause is never lost merely because the wire could not describe it.
110
+ */
111
+ awaiting?(pending: PendingAsk): void;
67
112
  /**
68
113
  * A piece of the answer, as it is produced.
69
114
  *
@@ -114,37 +159,156 @@ export interface AgentHost {
114
159
  serve(handler: HostHandler): Promise<HostHandle>;
115
160
  }
116
161
  /**
117
- * A conversation packed for storage.
162
+ * A session packed for storage.
118
163
  *
119
164
  * `format` names WHAT is inside, so a reader that does not know the shape
120
- * refuses BY NAME instead of restoring a conversation it cannot actually read.
165
+ * refuses BY NAME instead of restoring a session it cannot actually read.
121
166
  * Formats are ADDED, never redefined: an old runtime meeting a new format says
122
167
  * so and stops, which is the only safe thing it can do with a payload it cannot
123
- * interpret.
124
- *
125
- * `'conversation-v1'` stores a conversation and only a conversation. A run that
126
- * paused mid-flow is a conversation PLUS an engine checkpoint, and this format
127
- * has nowhere to put the second half — which is why `standingAgent` refuses to
128
- * store a paused run rather than storing half of it. Carrying a pause would be
129
- * a NEW format name in this same envelope, read by a runtime that knows it and
130
- * refused by name everywhere else. That is what the version field is for.
168
+ * interpret. Two exist:
169
+ *
170
+ * • `'conversation-v1'` — a conversation and only a conversation. Every turn
171
+ * that ran to an answer stores this.
172
+ * • `'flowchart-v1'` — a run that stopped mid-flow to ask a person something:
173
+ * the engine's own checkpoint, the conversation as of the pause, and the
174
+ * outstanding ask. 7.14 shipped the version field for exactly this day, and
175
+ * said so; this is that day.
176
+ *
177
+ * The union is discriminated on `format`, so a reader that switches on it is
178
+ * exhaustive by construction and a third format tomorrow breaks the switch at
179
+ * compile time rather than at 3am.
131
180
  */
132
- export interface CheckpointEnvelope {
181
+ export type CheckpointEnvelope = ConversationEnvelope | PausedRunEnvelope;
182
+ /** A conversation packed for storage — what a turn that ANSWERED leaves behind. */
183
+ export interface ConversationEnvelope {
133
184
  /** Names the shape of `data`. Unknown values are refused, never guessed at. */
134
185
  readonly format: 'conversation-v1';
135
- /** The conversation itself — an `AgentRunCheckpoint` for `'conversation-v1'`. */
186
+ /** The conversation itself. */
136
187
  readonly data: AgentRunCheckpoint;
137
188
  /** Wall-clock when it was packed. Diagnostic. */
138
189
  readonly savedAt: number;
139
190
  }
191
+ /** A paused run packed for storage — what a turn that ASKED leaves behind. */
192
+ export interface PausedRunEnvelope {
193
+ /** Names the shape of `data`. Unknown values are refused, never guessed at. */
194
+ readonly format: 'flowchart-v1';
195
+ /** The paused run. */
196
+ readonly data: PausedRun;
197
+ /** Wall-clock when it was packed. Diagnostic. */
198
+ readonly savedAt: number;
199
+ }
200
+ /**
201
+ * A run that stopped to ask a person something, in the three pieces a session
202
+ * actually needs: what continues it, what it has said so far, and what it is
203
+ * waiting on.
204
+ *
205
+ * ── JSON, honestly ───────────────────────────────────────────────────────────
206
+ * A `FlowchartCheckpoint` is **JSON-safe to resume from, and not byte-identical
207
+ * through JSON.** `JSON.stringify` drops any property whose value is
208
+ * `undefined`, and a real paused agent run has a dozen of them. Every one
209
+ * measured sits in `executionTree` / `subflowResults` — the diagnostic halves
210
+ * the engine keeps for narrative and BTS. `sharedState`, which is the half
211
+ * `agent.resume()` actually reads, round-trips unchanged, because footprintjs's
212
+ * TypedScope already JSON-round-trips every object write on its way into
213
+ * committed state.
214
+ *
215
+ * So: store it anywhere that speaks JSON and resume works. Do not assert that
216
+ * what came back deep-equals what went in — `key: undefined` comes back as no
217
+ * key at all, and a test written to expect otherwise is testing `JSON`, not
218
+ * this library.
219
+ */
220
+ export interface PausedRun {
221
+ /** The engine checkpoint — everything `agent.resume(checkpoint, decision)` needs. */
222
+ readonly checkpoint: FlowchartCheckpoint;
223
+ /**
224
+ * The conversation as of the pause, in the same shape every other turn stores.
225
+ *
226
+ * Kept alongside the checkpoint so a session that is waiting on a person is
227
+ * still a readable conversation: a support view can show what was said, and a
228
+ * runtime that cannot resume this run can still see the turn that led to the
229
+ * question.
230
+ */
231
+ readonly conversation: AgentRunCheckpoint;
232
+ /** What the run is waiting on, as data. */
233
+ readonly pending: PendingAsk;
234
+ }
235
+ /**
236
+ * The question a paused run is waiting on — the part of a pause that is safe to
237
+ * hand to whoever asked.
238
+ *
239
+ * **It deliberately carries no checkpoint.** The engine checkpoint holds the
240
+ * entire shared state of the run: the system prompt, the whole conversation,
241
+ * every tool result. That belongs in the store, which the operator chose and
242
+ * controls, and not in a reply to whoever posted the request. The caller gets
243
+ * the question; the store gets the state.
244
+ */
245
+ export interface PendingAsk {
246
+ /** The session holding the paused run — where the decision has to be sent back. */
247
+ readonly sessionId?: string;
248
+ /** The tool that asked, when the run recorded which one it was. */
249
+ readonly tool?: string;
250
+ /** The question in plain words, when the pause carried one. */
251
+ readonly question?: string;
252
+ /**
253
+ * Present ONLY when a tool declared `checkIn` — the typed ask plus its
254
+ * evidence pack (what the tool will do, what context the run read, which
255
+ * context drove the choice, the run so far). Answer with `checkInApproved()`
256
+ * / `checkInDeclined()`.
257
+ */
258
+ readonly checkIn?: CheckInRequest;
259
+ /**
260
+ * Present ONLY when a `toolMiddleware` answered `ask` — the question and the
261
+ * middleware that put it. Answered with the same decision vocabulary a
262
+ * check-in uses, deliberately: a person approving is a person approving.
263
+ */
264
+ readonly ask?: MiddlewareAsk;
265
+ /**
266
+ * Exactly what the tool passed to `askHuman()` / `pauseHere()`, uninterpreted.
267
+ * For a plain pause this is the whole of what the tool's author chose to say,
268
+ * and the library is not entitled to summarise it.
269
+ */
270
+ readonly pauseData: unknown;
271
+ }
272
+ /**
273
+ * How often a run's progress is written to the session store — the trade
274
+ * between latency and how much a crash can cost you.
275
+ *
276
+ * - `'exit'` (default) — one write, when the run finishes. The behaviour every
277
+ * release before 7.19 had, spelled out rather than implied. A crash mid-run
278
+ * loses the whole turn.
279
+ * - `'async'` — a write is STARTED whenever the conversation changes and never
280
+ * waited on. The run never slows down; the store is behind by however much
281
+ * the newest un-landed write carried. At most one write is in flight and the
282
+ * newest snapshot supersedes any queued one, so what a crash leaves is always
283
+ * a PREFIX of the run, never a mixture.
284
+ * - `'sync'` — persist-then-proceed. The same trigger, but **iteration N's
285
+ * tools do not execute until iteration N-1's write has landed**, and the
286
+ * answer is not delivered until the last write has landed. You pay the
287
+ * store's latency once per iteration, knowingly, and in exchange the amount
288
+ * of work a crash can re-run has a number: **the current iteration, and
289
+ * nothing before it.**
290
+ *
291
+ * ── The bound, stated exactly ────────────────────────────────────────────────
292
+ * A commit boundary is a whole stage, and the agent dispatches ALL of one
293
+ * iteration's tool calls inside one stage body. So under `'sync'` a crash
294
+ * re-executes the tools of the iteration that was in flight — never an earlier
295
+ * one. That is the same idempotency requirement `resumeOnError` has always
296
+ * carried, now with a boundary instead of a warning: mutating tools must be
297
+ * idempotent, keyed on stable call content rather than `ctx.toolCallId`.
298
+ */
299
+ export type DurabilityMode = 'exit' | 'async' | 'sync';
140
300
  /**
141
301
  * Why a session is being woken.
142
302
  *
143
- * One member, because one thing in this release can actually fire it: a request
144
- * arrived for that session. Naming reasons nothing can produce would be an
145
- * interface describing a system that does not exist.
303
+ * - `'invoke'` — a request arrived for that session.
304
+ * - `'resume'` — that request carries a person's decision for a run which
305
+ * paused earlier.
306
+ *
307
+ * `'resume'` was absent until 7.19 because nothing could produce it: naming
308
+ * reasons nothing fires would be an interface describing a system that does not
309
+ * exist. Something produces it now.
146
310
  */
147
- export type WakeReason = 'invoke';
311
+ export type WakeReason = 'invoke' | 'resume';
148
312
  /**
149
313
  * The port: where a conversation lives between requests.
150
314
  *
@@ -203,4 +367,14 @@ export interface StandingAgentOptions<TH extends HostHandle = HostHandle> {
203
367
  };
204
368
  /** Default `'reject'`. See {@link ConcurrentInvokePolicy}. */
205
369
  readonly onConcurrentInvoke?: ConcurrentInvokePolicy;
370
+ /**
371
+ * How often a run's progress becomes crash-survivable. Default `'exit'` —
372
+ * one write when the run finishes, which is what every release before this
373
+ * one did. See {@link DurabilityMode} for what the other two buy and cost.
374
+ *
375
+ * Under `'exit'` nothing is attached to the agent at all: no observer, no
376
+ * per-commit work, no barrier. An agent served this way behaves and performs
377
+ * exactly as it did in 7.18.
378
+ */
379
+ readonly durability?: DurabilityMode;
206
380
  }