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.
- package/dist/adapters/hosting/agentcore.js +23 -9
- package/dist/adapters/hosting/agentcore.js.map +1 -1
- package/dist/core/Agent.js +6 -0
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/agent/stages/toolCalls.js +13 -0
- package/dist/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/core/durabilityBarrier.js +68 -0
- package/dist/core/durabilityBarrier.js.map +1 -0
- package/dist/esm/adapters/hosting/agentcore.d.ts +4 -4
- package/dist/esm/adapters/hosting/agentcore.js +24 -10
- package/dist/esm/adapters/hosting/agentcore.js.map +1 -1
- package/dist/esm/core/Agent.js +6 -0
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/core/agent/stages/toolCalls.d.ts +21 -0
- package/dist/esm/core/agent/stages/toolCalls.js +13 -0
- package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/esm/core/durabilityBarrier.d.ts +61 -0
- package/dist/esm/core/durabilityBarrier.js +63 -0
- package/dist/esm/core/durabilityBarrier.js.map +1 -0
- package/dist/esm/hosting/durability.d.ts +92 -0
- package/dist/esm/hosting/durability.js +174 -0
- package/dist/esm/hosting/durability.js.map +1 -0
- package/dist/esm/hosting/envelope.d.ts +75 -14
- package/dist/esm/hosting/envelope.js +141 -16
- package/dist/esm/hosting/envelope.js.map +1 -1
- package/dist/esm/hosting/errors.d.ts +60 -11
- package/dist/esm/hosting/errors.js +92 -18
- package/dist/esm/hosting/errors.js.map +1 -1
- package/dist/esm/hosting/httpHost.d.ts +17 -1
- package/dist/esm/hosting/httpHost.js +42 -6
- package/dist/esm/hosting/httpHost.js.map +1 -1
- package/dist/esm/hosting/index.d.ts +7 -4
- package/dist/esm/hosting/index.js +6 -3
- package/dist/esm/hosting/index.js.map +1 -1
- package/dist/esm/hosting/nodeHost.d.ts +4 -2
- package/dist/esm/hosting/nodeHost.js +14 -3
- package/dist/esm/hosting/nodeHost.js.map +1 -1
- package/dist/esm/hosting/standingAgent.d.ts +22 -7
- package/dist/esm/hosting/standingAgent.js +144 -32
- package/dist/esm/hosting/standingAgent.js.map +1 -1
- package/dist/esm/hosting/types.d.ts +193 -19
- package/dist/hosting/durability.js +178 -0
- package/dist/hosting/durability.js.map +1 -0
- package/dist/hosting/envelope.js +146 -18
- package/dist/hosting/envelope.js.map +1 -1
- package/dist/hosting/errors.js +95 -19
- package/dist/hosting/errors.js.map +1 -1
- package/dist/hosting/httpHost.js +42 -6
- package/dist/hosting/httpHost.js.map +1 -1
- package/dist/hosting/index.js +10 -2
- package/dist/hosting/index.js.map +1 -1
- package/dist/hosting/nodeHost.js +14 -3
- package/dist/hosting/nodeHost.js.map +1 -1
- package/dist/hosting/standingAgent.js +142 -30
- package/dist/hosting/standingAgent.js.map +1 -1
- package/dist/types/adapters/hosting/agentcore.d.ts +4 -4
- package/dist/types/adapters/hosting/agentcore.d.ts.map +1 -1
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/core/agent/stages/toolCalls.d.ts +21 -0
- package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
- package/dist/types/core/durabilityBarrier.d.ts +62 -0
- package/dist/types/core/durabilityBarrier.d.ts.map +1 -0
- package/dist/types/hosting/durability.d.ts +93 -0
- package/dist/types/hosting/durability.d.ts.map +1 -0
- package/dist/types/hosting/envelope.d.ts +75 -14
- package/dist/types/hosting/envelope.d.ts.map +1 -1
- package/dist/types/hosting/errors.d.ts +60 -11
- package/dist/types/hosting/errors.d.ts.map +1 -1
- package/dist/types/hosting/httpHost.d.ts +17 -1
- package/dist/types/hosting/httpHost.d.ts.map +1 -1
- package/dist/types/hosting/index.d.ts +7 -4
- package/dist/types/hosting/index.d.ts.map +1 -1
- package/dist/types/hosting/nodeHost.d.ts +4 -2
- package/dist/types/hosting/nodeHost.d.ts.map +1 -1
- package/dist/types/hosting/standingAgent.d.ts +22 -7
- package/dist/types/hosting/standingAgent.d.ts.map +1 -1
- package/dist/types/hosting/types.d.ts +193 -19
- package/dist/types/hosting/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hosting/durability — how often a run's progress becomes crash-survivable.
|
|
3
|
+
*
|
|
4
|
+
* A standing agent that only writes at the end of a turn is one restart away
|
|
5
|
+
* from losing everything the turn had done: the tool calls it made, the results
|
|
6
|
+
* it read, the iterations it spent. This is the dial that decides how much of
|
|
7
|
+
* that survives, and what it costs.
|
|
8
|
+
*
|
|
9
|
+
* ── Where a mid-run write can honestly come from ─────────────────────────────
|
|
10
|
+
* From the COMMIT boundary, and nowhere else. footprintjs commits a stage's
|
|
11
|
+
* writes after the stage function returns, and committed state is
|
|
12
|
+
* immutable-after-swap — so `ScopeRecorder.onCommit` is the one moment where
|
|
13
|
+
* "what the run has agreed on so far" is a real, complete, consistent thing.
|
|
14
|
+
* Reading the agent's live state at any other moment would be reading a stage's
|
|
15
|
+
* work in progress.
|
|
16
|
+
*
|
|
17
|
+
* There is no mid-run engine checkpoint to store: footprintjs builds a
|
|
18
|
+
* `FlowchartCheckpoint` only at a pause (`getCheckpoint()` is documented as "the
|
|
19
|
+
* most recent PAUSED execution"). So what a mid-run write can carry is a
|
|
20
|
+
* CONVERSATION — the same `AgentRunCheckpoint` every finished turn stores — and
|
|
21
|
+
* that is exactly enough, because that is what the next turn resumes from.
|
|
22
|
+
*
|
|
23
|
+
* ── Why it writes on SOME commits and not all of them ────────────────────────
|
|
24
|
+
* A two-iteration turn commits about forty times. Exactly two of those commits
|
|
25
|
+
* change the conversation: `Initialize` (the user's message lands) and
|
|
26
|
+
* `ToolCalls` (an iteration's assistant turn and its tool results land). Every
|
|
27
|
+
* other commit would store bytes identical to the last write. So the trigger is
|
|
28
|
+
* "this commit wrote `history`", which is not an optimisation but the honest
|
|
29
|
+
* reading of the question: the conversation moved iff `history` moved.
|
|
30
|
+
*
|
|
31
|
+
* ── What a commit boundary actually guarantees, and what it does not ─────────
|
|
32
|
+
* It guarantees the whole stage. The agent dispatches ALL of one iteration's
|
|
33
|
+
* tool calls inside one stage body, so a crash part-way through that body stores
|
|
34
|
+
* nothing from it and a replay re-runs that iteration's tools. That is the
|
|
35
|
+
* shipped idempotency requirement, unchanged — mutating tools must be
|
|
36
|
+
* idempotent, keyed on stable call content rather than `ctx.toolCallId`. What
|
|
37
|
+
* `'sync'` adds is a BOUND on it: iteration N's tools do not start until
|
|
38
|
+
* iteration N-1's write has landed, so the replay is the current iteration and
|
|
39
|
+
* never an earlier one.
|
|
40
|
+
*
|
|
41
|
+
* Pattern: an observer (`CombinedRecorder`) for the snapshot, a serialiser for
|
|
42
|
+
* the writes, and — for `'sync'` only — a barrier the tool dispatch waits on.
|
|
43
|
+
* Role: internal to `standingAgent`. Deliberately not exported: it is how the
|
|
44
|
+
* composer keeps its promise, not a second way to write to a store.
|
|
45
|
+
*/
|
|
46
|
+
import type { CombinedRecorder } from 'footprintjs';
|
|
47
|
+
import type { AgentRunCheckpoint } from '../core/runCheckpoint.js';
|
|
48
|
+
import type { DurabilityMode } from './types.js';
|
|
49
|
+
/** What the writer needs from the composer around it. */
|
|
50
|
+
export interface DurableWriterOptions {
|
|
51
|
+
/** `'async'` or `'sync'`. `'exit'` never builds a writer at all. */
|
|
52
|
+
readonly mode: Exclude<DurabilityMode, 'exit'>;
|
|
53
|
+
/**
|
|
54
|
+
* The session this run belongs to, asked at every commit. `undefined` means
|
|
55
|
+
* "nothing to write to" — an anonymous request, or no run of ours in flight —
|
|
56
|
+
* and the commit is ignored.
|
|
57
|
+
*/
|
|
58
|
+
readonly session: () => string | undefined;
|
|
59
|
+
/** The run id to stamp on the conversation, for correlating back to the run. */
|
|
60
|
+
readonly runId: () => string | undefined;
|
|
61
|
+
/** Where the conversation goes. */
|
|
62
|
+
readonly write: (sessionId: string, conversation: AgentRunCheckpoint) => Promise<void>;
|
|
63
|
+
}
|
|
64
|
+
/** The composer's handle on its own durability. */
|
|
65
|
+
export interface DurableWriter {
|
|
66
|
+
/** Attach this to the agent to start observing commits. */
|
|
67
|
+
readonly recorder: CombinedRecorder;
|
|
68
|
+
/** Install the tool-dispatch barrier. Returns the uninstall function. */
|
|
69
|
+
install(agent: object): () => void;
|
|
70
|
+
/**
|
|
71
|
+
* Everything outstanding has landed.
|
|
72
|
+
*
|
|
73
|
+
* The composer awaits this before writing a run's FINAL envelope, and that
|
|
74
|
+
* ordering is load-bearing rather than tidy: an `'async'` conversation write
|
|
75
|
+
* still in flight would otherwise land AFTER the terminal envelope and
|
|
76
|
+
* overwrite it — turning a stored pause back into a plain conversation and
|
|
77
|
+
* losing the question a person was asked.
|
|
78
|
+
*
|
|
79
|
+
* **Rejects when the newest write did not land.** Fail-closed on purpose: a
|
|
80
|
+
* store that refused the run's progress has not made it durable, and both
|
|
81
|
+
* things waiting on this — the next tool call under `'sync'`, and the reply —
|
|
82
|
+
* would otherwise proceed on a promise nobody kept.
|
|
83
|
+
*/
|
|
84
|
+
settle(): Promise<void>;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Build the writer.
|
|
88
|
+
*
|
|
89
|
+
* Under `'sync'` it also answers the tool-dispatch barrier, which is what turns
|
|
90
|
+
* "we write often" into a bound on how much can re-run.
|
|
91
|
+
*/
|
|
92
|
+
export declare function durableWriter(options: DurableWriterOptions): DurableWriter;
|
|
93
|
+
//# sourceMappingURL=durability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"durability.d.ts","sourceRoot":"","sources":["../../../src/hosting/durability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAe,MAAM,aAAa,CAAC;AAIjE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,yDAAyD;AACzD,MAAM,WAAW,oBAAoB;IACnC,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAC/C;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAC3C,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IACzC,mCAAmC;IACnC,QAAQ,CAAC,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,kBAAkB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACxF;AAED,mDAAmD;AACnD,MAAM,WAAW,aAAa;IAC5B,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IACpC,yEAAyE;IACzE,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,IAAI,CAAC;IACnC;;;;;;;;;;;;;OAaG;IACH,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAgI1E"}
|
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* hosting/envelope — pack a
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* still running will read it. The only
|
|
9
|
-
* is say which format it found, which
|
|
10
|
-
* "restore what I can and hope" means an agent
|
|
11
|
-
* that is missing whatever the older reader did not
|
|
2
|
+
* hosting/envelope — pack a session for storage, and refuse to unpack one you
|
|
3
|
+
* cannot read.
|
|
4
|
+
*
|
|
5
|
+
* One rule, and everything here is a consequence of it: **an envelope this
|
|
6
|
+
* runtime cannot read is refused BY NAME, never guessed at.** A store outlives
|
|
7
|
+
* the code that wrote to it. Somebody will deploy a newer runtime, it will write
|
|
8
|
+
* a newer format, and an older instance still running will read it. The only
|
|
9
|
+
* honest thing that older instance can do is say which format it found, which
|
|
10
|
+
* ones it knows, and stop — because "restore what I can and hope" means an agent
|
|
11
|
+
* answering from a session that is missing whatever the older reader did not
|
|
12
|
+
* understand.
|
|
13
|
+
*
|
|
14
|
+
* ── Two formats, two readers, and why they refuse each other ─────────────────
|
|
15
|
+
* `readEnvelope` unpacks a CONVERSATION; `readPausedRun` unpacks a PAUSED RUN.
|
|
16
|
+
* Each refuses the other's format by name and points at its sibling. That looks
|
|
17
|
+
* fussy until you notice the alternative: one reader that quietly returned the
|
|
18
|
+
* conversation inside a paused run would hand back a session that LOOKS finished
|
|
19
|
+
* while a person is still waiting on a question nobody mentioned. That is a
|
|
20
|
+
* half-restore wearing a happy path, which is the exact failure the format field
|
|
21
|
+
* exists to prevent.
|
|
12
22
|
*/
|
|
13
23
|
import { type AgentRunCheckpoint } from '../core/runCheckpoint.js';
|
|
14
|
-
import type { CheckpointEnvelope } from './types.js';
|
|
24
|
+
import type { CheckpointEnvelope, ConversationEnvelope, PausedRun, PausedRunEnvelope } from './types.js';
|
|
15
25
|
/**
|
|
16
26
|
* Pack a conversation checkpoint for storage.
|
|
17
27
|
*
|
|
@@ -19,7 +29,28 @@ import type { CheckpointEnvelope } from './types.js';
|
|
|
19
29
|
* const conversation = agent.checkpoint();
|
|
20
30
|
* if (conversation) await sessions.persist(sessionId, toEnvelope(conversation));
|
|
21
31
|
*/
|
|
22
|
-
export declare function toEnvelope(checkpoint: AgentRunCheckpoint):
|
|
32
|
+
export declare function toEnvelope(checkpoint: AgentRunCheckpoint): ConversationEnvelope;
|
|
33
|
+
/**
|
|
34
|
+
* Pack a paused run for storage — the engine checkpoint, the conversation as of
|
|
35
|
+
* the pause, and the question it is waiting on.
|
|
36
|
+
*
|
|
37
|
+
* Store it anywhere that speaks JSON. Note what JSON does and does not preserve
|
|
38
|
+
* here: `agent.resume()` reads `checkpoint.sharedState`, which round-trips
|
|
39
|
+
* unchanged; the engine's diagnostic halves lose their explicitly-`undefined`
|
|
40
|
+
* properties, because that is what `JSON.stringify` does to them. See
|
|
41
|
+
* {@link PausedRun}.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* const outcome = await agent.run({ message });
|
|
45
|
+
* if (isPaused(outcome)) {
|
|
46
|
+
* await sessions.persist(sessionId, toPausedEnvelope({
|
|
47
|
+
* checkpoint: outcome.checkpoint,
|
|
48
|
+
* conversation: agent.checkpoint()!,
|
|
49
|
+
* pending: { pauseData: outcome.pauseData },
|
|
50
|
+
* }));
|
|
51
|
+
* }
|
|
52
|
+
*/
|
|
53
|
+
export declare function toPausedEnvelope(paused: PausedRun): PausedRunEnvelope;
|
|
23
54
|
/**
|
|
24
55
|
* Unpack a stored envelope back into a conversation checkpoint.
|
|
25
56
|
*
|
|
@@ -27,8 +58,38 @@ export declare function toEnvelope(checkpoint: AgentRunCheckpoint): CheckpointEn
|
|
|
27
58
|
* else wrote, in a format this runtime may not know, and typing the parameter
|
|
28
59
|
* as the happy shape would be assuming the very thing that needs checking.
|
|
29
60
|
*
|
|
30
|
-
* @throws TypeError naming the format when it is one this runtime cannot read
|
|
31
|
-
*
|
|
61
|
+
* @throws TypeError naming the format when it is one this runtime cannot read;
|
|
62
|
+
* naming the missing field when the conversation inside is malformed; and
|
|
63
|
+
* pointing at {@link readPausedRun} when the envelope holds a paused run,
|
|
64
|
+
* which is a session with a question outstanding rather than a conversation.
|
|
32
65
|
*/
|
|
33
66
|
export declare function readEnvelope(envelope: unknown): AgentRunCheckpoint;
|
|
67
|
+
/**
|
|
68
|
+
* Unpack a stored envelope back into a paused run.
|
|
69
|
+
*
|
|
70
|
+
* @throws TypeError naming the format when it is one this runtime cannot read;
|
|
71
|
+
* pointing at {@link readEnvelope} when the envelope holds a plain
|
|
72
|
+
* conversation; and naming the missing field when the paused run inside is
|
|
73
|
+
* malformed.
|
|
74
|
+
*/
|
|
75
|
+
export declare function readPausedRun(envelope: unknown): PausedRun;
|
|
76
|
+
/**
|
|
77
|
+
* Check that an envelope is one this runtime can read, and hand it back
|
|
78
|
+
* unchanged — without committing to which half you wanted.
|
|
79
|
+
*
|
|
80
|
+
* This is what a STORE wants. A store's job is to notice that the bytes it is
|
|
81
|
+
* about to hand over are unreadable, so the refusal names the store that
|
|
82
|
+
* produced them rather than whoever read them next; it has no business caring
|
|
83
|
+
* whether the session inside is mid-conversation or mid-question.
|
|
84
|
+
*
|
|
85
|
+
* @throws TypeError naming the format when this runtime cannot read it, or the
|
|
86
|
+
* missing field when the payload is malformed.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* async hydrate(sessionId) {
|
|
90
|
+
* const stored = await myStore.get(sessionId);
|
|
91
|
+
* return stored === undefined ? undefined : checkEnvelope(stored);
|
|
92
|
+
* }
|
|
93
|
+
*/
|
|
94
|
+
export declare function checkEnvelope(envelope: unknown): CheckpointEnvelope;
|
|
34
95
|
//# sourceMappingURL=envelope.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../../src/hosting/envelope.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../../src/hosting/envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACvF,OAAO,KAAK,EACV,kBAAkB,EAClB,oBAAoB,EACpB,SAAS,EACT,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAKpB;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,kBAAkB,GAAG,oBAAoB,CAE/E;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,iBAAiB,CAErE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,OAAO,GAAG,kBAAkB,CAUlE;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAQ1D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,OAAO,GAAG,kBAAkB,CAOnE"}
|
|
@@ -3,17 +3,21 @@
|
|
|
3
3
|
* same words.
|
|
4
4
|
*
|
|
5
5
|
* A refusal that varies by adapter is a refusal nobody can write a test or a
|
|
6
|
-
* runbook against. These
|
|
6
|
+
* runbook against. These five carry a stable `code`, name WHO refused, and say
|
|
7
7
|
* what the caller should do instead. Adapters map the codes onto whatever their
|
|
8
8
|
* transport uses to say "no" — that mapping is the adapter's business and lives
|
|
9
9
|
* in the adapter, never here.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* Note what is NOT here: a run that paused. That is unfinished work rather than
|
|
12
|
+
* a refusal, and it leaves through `reply.awaiting(...)` — its own terminal —
|
|
13
|
+
* not through an error dressed up as one.
|
|
14
|
+
*
|
|
15
|
+
* `requireCapability` is the last refusal and the only one that is a
|
|
12
16
|
* programming mistake rather than a runtime condition, so it throws a plain
|
|
13
17
|
* `Error`: nothing branches on "I forgot to feature-detect", it just needs to
|
|
14
18
|
* say so loudly and name the adapter it is talking about.
|
|
15
19
|
*/
|
|
16
|
-
import type { AgentHost, HostCapability } from './types.js';
|
|
20
|
+
import type { AgentHost, HostCapability, PendingAsk } from './types.js';
|
|
17
21
|
/**
|
|
18
22
|
* Thrown when a request arrives at a host that is shutting down or shut down.
|
|
19
23
|
*
|
|
@@ -44,22 +48,67 @@ export declare class ConcurrentRunError extends Error {
|
|
|
44
48
|
constructor(sessionId: string, activeRunId?: string);
|
|
45
49
|
}
|
|
46
50
|
/**
|
|
47
|
-
* Raised when a run paused to ask a person something and
|
|
48
|
-
*
|
|
51
|
+
* Raised when a run paused to ask a person something and there is **nowhere to
|
|
52
|
+
* keep it**.
|
|
49
53
|
*
|
|
50
54
|
* **The run did not fail.** A pause is unfinished work: the agent stopped to ask
|
|
51
|
-
* and is waiting for an answer.
|
|
52
|
-
* `'
|
|
53
|
-
*
|
|
54
|
-
* session
|
|
55
|
+
* and is waiting for an answer. Since 7.19 a paused run is stored as
|
|
56
|
+
* `'flowchart-v1'` and continued by a later request carrying a decision — so the
|
|
57
|
+
* one case left where a pause genuinely cannot be carried is a request with no
|
|
58
|
+
* session id. There is no session to store it under, and therefore no later
|
|
59
|
+
* request that could ever answer it.
|
|
60
|
+
*
|
|
61
|
+
* The other half of the old meaning — "the reply cannot carry a pause" — is
|
|
62
|
+
* gone: {@link HostReply.awaiting} carries it now. An adapter that has not
|
|
63
|
+
* implemented that terminal still gets its pause STORED (the store is not the
|
|
64
|
+
* transport's business) and this refusal on the wire, naming the session it can
|
|
65
|
+
* be answered on.
|
|
55
66
|
*/
|
|
56
67
|
export declare class PauseNotCarriedError extends Error {
|
|
57
68
|
readonly code: "ERR_PAUSE_NOT_CARRIED";
|
|
58
69
|
/** The tool that asked, when the run recorded which one it was. */
|
|
59
70
|
readonly toolName?: string;
|
|
60
|
-
/** The session
|
|
71
|
+
/** The session the paused run was stored under, when there was one. */
|
|
61
72
|
readonly sessionId?: string;
|
|
62
|
-
|
|
73
|
+
/** Whether the paused run was stored. `false` means it is gone. */
|
|
74
|
+
readonly stored: boolean;
|
|
75
|
+
constructor(toolName?: string, sessionId?: string, stored?: boolean);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Thrown when a new message arrives for a session whose run is waiting on a
|
|
79
|
+
* person's decision.
|
|
80
|
+
*
|
|
81
|
+
* The message is NOT run and the pause is NOT discarded — those are the two ways
|
|
82
|
+
* this could have gone wrong. Answering the message would step over an
|
|
83
|
+
* outstanding consent gate; dropping the paused run to make room for the message
|
|
84
|
+
* would throw away work a person was asked about. So the request is refused, the
|
|
85
|
+
* pending question is named, and the session sits exactly where it was.
|
|
86
|
+
*
|
|
87
|
+
* Answer it by sending the same session a request carrying
|
|
88
|
+
* {@link HostRequest.decision}.
|
|
89
|
+
*/
|
|
90
|
+
export declare class AwaitingDecisionError extends Error {
|
|
91
|
+
readonly code: "ERR_AWAITING_DECISION";
|
|
92
|
+
/** The session that is waiting. */
|
|
93
|
+
readonly sessionId: string;
|
|
94
|
+
/** What it is waiting on — the same payload `reply.awaiting()` delivered. */
|
|
95
|
+
readonly pending: PendingAsk;
|
|
96
|
+
constructor(sessionId: string, pending: PendingAsk);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Thrown when a request carries a decision for a session that is not waiting on
|
|
100
|
+
* one.
|
|
101
|
+
*
|
|
102
|
+
* Usually a duplicate delivery: the run was already continued, or already
|
|
103
|
+
* answered, and the same decision arrived twice. Running it as an ordinary
|
|
104
|
+
* message would put a raw approval into the conversation as if the user had
|
|
105
|
+
* typed it, so it is refused by name instead.
|
|
106
|
+
*/
|
|
107
|
+
export declare class NoPendingAskError extends Error {
|
|
108
|
+
readonly code: "ERR_NO_PENDING_ASK";
|
|
109
|
+
/** The session the decision was addressed to. */
|
|
110
|
+
readonly sessionId: string;
|
|
111
|
+
constructor(sessionId: string);
|
|
63
112
|
}
|
|
64
113
|
/**
|
|
65
114
|
* Assert that a host can do something, and throw a corrective error naming the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/hosting/errors.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/hosting/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExE;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,oBAA8B;IAC3C,6BAA6B;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,QAAQ,EAAE,MAAM;CAS7B;AAED;;;;;;;;GAQG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,uBAAiC;IAC9C,gDAAgD;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,mEAAmE;IACnE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;gBAElB,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;CAapD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,0BAAoC;IACjD,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;gBAEb,QAAQ,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,UAAQ;CAsBlE;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,IAAI,0BAAoC;IACjD,mCAAmC;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;gBAEjB,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU;CAgBnD;AAED;;;;;;;;GAQG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,uBAAiC;IAC9C,iDAAiD;IACjD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,SAAS,EAAE,MAAM;CAW9B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,GAAG,IAAI,CASnF"}
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* Pattern: Template method via configuration (Strategy on the wire format).
|
|
29
29
|
* Everything HTTP lives here and in the wires; `types.ts` knows none of it.
|
|
30
30
|
*/
|
|
31
|
-
import type { AgentHost, HostCapability, HostHandle, HostHandler } from './types.js';
|
|
31
|
+
import type { AgentHost, HostCapability, HostHandle, HostHandler, PendingAsk } from './types.js';
|
|
32
32
|
/** Everything a {@link HttpWire} may read when pulling a request apart. */
|
|
33
33
|
export interface HttpRequestFacts {
|
|
34
34
|
/** The parsed JSON body, or `{}` for an empty one. */
|
|
@@ -57,6 +57,12 @@ export interface HttpWire {
|
|
|
57
57
|
readRequest(facts: HttpRequestFacts): {
|
|
58
58
|
readonly input: string;
|
|
59
59
|
readonly sessionId?: string;
|
|
60
|
+
/**
|
|
61
|
+
* A person's answer to an outstanding question, when this request carries
|
|
62
|
+
* one. Its presence is what makes a request a RESUME rather than a new
|
|
63
|
+
* message, so a wire that never returns it can only ever start new turns.
|
|
64
|
+
*/
|
|
65
|
+
readonly decision?: unknown;
|
|
60
66
|
};
|
|
61
67
|
/** Body for a health probe. `uptimeMs` is how long this host has been serving. */
|
|
62
68
|
health(uptimeMs: number): unknown;
|
|
@@ -66,6 +72,16 @@ export interface HttpWire {
|
|
|
66
72
|
failure(message: string, code?: string): unknown;
|
|
67
73
|
/** Body for one streamed piece, when the caller asked for Server-Sent Events. */
|
|
68
74
|
chunk(text: string): unknown;
|
|
75
|
+
/**
|
|
76
|
+
* Body for a reply that is WAITING on a person — the run paused, it is stored,
|
|
77
|
+
* and a later request carrying a decision continues it.
|
|
78
|
+
*
|
|
79
|
+
* Optional so a wire written before this terminal existed keeps compiling and
|
|
80
|
+
* keeps working. A host whose wire has no `awaiting` cannot describe the
|
|
81
|
+
* question, so it reports the named refusal instead — the run is still stored
|
|
82
|
+
* either way.
|
|
83
|
+
*/
|
|
84
|
+
awaiting?(pending: PendingAsk): unknown;
|
|
69
85
|
}
|
|
70
86
|
/** Options for {@link httpHost}. */
|
|
71
87
|
export interface HttpHostOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"httpHost.d.ts","sourceRoot":"","sources":["../../../src/hosting/httpHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAMH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"httpHost.d.ts","sourceRoot":"","sources":["../../../src/hosting/httpHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAMH,OAAO,KAAK,EACV,SAAS,EACT,cAAc,EACd,UAAU,EACV,WAAW,EAEX,UAAU,EACX,MAAM,YAAY,CAAC;AAEpB,2EAA2E;AAC3E,MAAM,WAAW,gBAAgB;IAC/B,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACjD,oFAAoF;IACpF,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,wCAAwC;IACxC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,QAAQ;IACvB;;;;;;OAMG;IACH,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG;QACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAC5B;;;;WAIG;QACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;KAC7B,CAAC;IACF,kFAAkF;IAClF,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,uCAAuC;IACvC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,0FAA0F;IAC1F,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjD,iFAAiF;IACjF,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7B;;;;;;;;OAQG;IACH,QAAQ,CAAC,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC;CACzC;AAED,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,oEAAoE;IACpE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,6EAA6E;IAC7E,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;CACnD;AAED,2DAA2D;AAC3D,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,qEAAqE;IACrE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,oDAAoD;AACpD,MAAM,WAAW,QAAS,SAAQ,SAAS;IACzC,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CACtD;AA8BD;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,QAAQ,CAsE3D;AAyJD;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,gBAAgB,EACvB,IAAI,EAAE,MAAM,EACZ,GAAG,SAAS,EAAE,MAAM,EAAE,GACrB,MAAM,GAAG,SAAS,CAMpB"}
|
|
@@ -33,9 +33,12 @@
|
|
|
33
33
|
* another. Two paths and five body shapes are all a second HTTP adapter
|
|
34
34
|
* re-decides.
|
|
35
35
|
* • `memorySessions()` — conversations in a Map, for tests and local dev.
|
|
36
|
-
* • `standingAgent({ agent, sessions, host })` — the composer.
|
|
36
|
+
* • `standingAgent({ agent, sessions, host, durability? })` — the composer.
|
|
37
37
|
* • `toEnvelope` / `readEnvelope` — pack a conversation, and refuse by name to
|
|
38
38
|
* unpack a format this runtime does not know.
|
|
39
|
+
* • `toPausedEnvelope` / `readPausedRun` — the same for a run that stopped to
|
|
40
|
+
* ask a person something (`'flowchart-v1'`). `checkEnvelope` validates
|
|
41
|
+
* either without committing to which half you wanted — what a STORE wants.
|
|
39
42
|
* • `requireCapability` — feature-detection with teeth.
|
|
40
43
|
*
|
|
41
44
|
* @example An agent that stays up and remembers
|
|
@@ -54,8 +57,8 @@ export type { NodeHost, NodeHostHandle, NodeHostOptions } from './nodeHost.js';
|
|
|
54
57
|
export { httpHost, headerValue } from './httpHost.js';
|
|
55
58
|
export type { HttpHost, HttpHostHandle, HttpHostOptions, HttpRequestFacts, HttpWire, } from './httpHost.js';
|
|
56
59
|
export { memorySessions } from './memorySessions.js';
|
|
57
|
-
export { toEnvelope, readEnvelope } from './envelope.js';
|
|
60
|
+
export { toEnvelope, toPausedEnvelope, readEnvelope, readPausedRun, checkEnvelope, } from './envelope.js';
|
|
58
61
|
export { standingAgent } from './standingAgent.js';
|
|
59
|
-
export { requireCapability, HostClosedError, ConcurrentRunError, PauseNotCarriedError, } from './errors.js';
|
|
60
|
-
export type { AgentHost, CheckpointEnvelope, ConcurrentInvokePolicy, HostCapability, HostHandle, HostHandler, HostReply, HostRequest, SessionLifecycle, StandingAgentOptions, WakeReason, } from './types.js';
|
|
62
|
+
export { requireCapability, HostClosedError, ConcurrentRunError, PauseNotCarriedError, AwaitingDecisionError, NoPendingAskError, } from './errors.js';
|
|
63
|
+
export type { AgentHost, CheckpointEnvelope, ConcurrentInvokePolicy, ConversationEnvelope, DurabilityMode, HostCapability, HostHandle, HostHandler, HostReply, HostRequest, PausedRun, PausedRunEnvelope, PendingAsk, SessionLifecycle, StandingAgentOptions, WakeReason, } from './types.js';
|
|
61
64
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hosting/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hosting/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAE/E,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACtD,YAAY,EACV,QAAQ,EACR,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,QAAQ,GACT,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,SAAS,EACT,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,cAAc,EACd,UAAU,EACV,WAAW,EACX,SAAS,EACT,WAAW,EACX,SAAS,EACT,iBAAiB,EACjB,UAAU,EACV,gBAAgB,EAChB,oBAAoB,EACpB,UAAU,GACX,MAAM,YAAY,CAAC"}
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* reply.complete(await answer(request.input));
|
|
8
8
|
* });
|
|
9
9
|
*
|
|
10
|
-
* Two routes: `POST /invoke` takes `{ input, sessionId? }` and
|
|
11
|
-
* `{ output }
|
|
10
|
+
* Two routes: `POST /invoke` takes `{ input, sessionId?, decision? }` and
|
|
11
|
+
* answers `{ output }` — or `{ awaiting }` with a **202** when the run stopped
|
|
12
|
+
* to ask a person something, which a later `POST` carrying `decision` continues;
|
|
13
|
+
* `GET /health` answers `{ status: 'ok' }`. Both paths are
|
|
12
14
|
* options, because the paths are the part most likely to be dictated to you by
|
|
13
15
|
* whatever is in front of the process — a load balancer, a container contract,
|
|
14
16
|
* a colleague's convention. A path is a deployment detail, so it is a knob
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodeHost.d.ts","sourceRoot":"","sources":["../../../src/hosting/nodeHost.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"nodeHost.d.ts","sourceRoot":"","sources":["../../../src/hosting/nodeHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAY,KAAK,QAAQ,EAAE,KAAK,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE5F,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,6DAA6D;IAC7D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,cAAc,CAAC;AAE5C,qDAAqD;AACrD,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC;AAIhC;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,EAAE,QAsBtB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,QAAQ,CAWhE"}
|
|
@@ -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
|
-
*
|
|
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
|
-
* ──
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
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 —
|
|
@@ -47,8 +60,9 @@ import type { HostHandle, StandingAgentOptions } from './types.js';
|
|
|
47
60
|
* Serve one agent, with per-session conversation memory, on any
|
|
48
61
|
* {@link AgentHost}.
|
|
49
62
|
*
|
|
50
|
-
* Resolves once the host is live. Closing the returned handle closes the host
|
|
51
|
-
*
|
|
63
|
+
* Resolves once the host is live. Closing the returned handle closes the host,
|
|
64
|
+
* detaches the listeners this composer added to the agent, and removes its
|
|
65
|
+
* durability wiring.
|
|
52
66
|
*
|
|
53
67
|
* @example
|
|
54
68
|
* const handle = await standingAgent({
|
|
@@ -56,6 +70,7 @@ import type { HostHandle, StandingAgentOptions } from './types.js';
|
|
|
56
70
|
* sessions: memorySessions(),
|
|
57
71
|
* host: nodeHost({ port: 0 }),
|
|
58
72
|
* onConcurrentInvoke: 'enqueue',
|
|
73
|
+
* durability: 'sync',
|
|
59
74
|
* });
|
|
60
75
|
* process.on('SIGTERM', () => void handle.close());
|
|
61
76
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standingAgent.d.ts","sourceRoot":"","sources":["../../../src/hosting/standingAgent.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"standingAgent.d.ts","sourceRoot":"","sources":["../../../src/hosting/standingAgent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAYH,OAAO,KAAK,EACV,UAAU,EAKV,oBAAoB,EAErB,MAAM,YAAY,CAAC;AAGpB;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,aAAa,CAAC,EAAE,SAAS,UAAU,EACvD,OAAO,EAAE,oBAAoB,CAAC,EAAE,CAAC,GAChC,OAAO,CAAC,EAAE,CAAC,CAuNb"}
|