gemi 0.60.0 → 0.61.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/ai/Agent.d.ts +533 -25
- package/dist/ai/Agent.d.ts.map +1 -1
- package/dist/ai/Agent.test-d.d.ts +2 -0
- package/dist/ai/Agent.test-d.d.ts.map +1 -0
- package/dist/ai/AgentController.d.ts +275 -6
- package/dist/ai/AgentController.d.ts.map +1 -1
- package/dist/ai/AgentProvider.d.ts +255 -0
- package/dist/ai/AgentProvider.d.ts.map +1 -0
- package/dist/ai/Schema.d.ts +125 -0
- package/dist/ai/Schema.d.ts.map +1 -0
- package/dist/ai/Schema.test-d.d.ts +2 -0
- package/dist/ai/Schema.test-d.d.ts.map +1 -0
- package/dist/ai/client/index.d.ts +24 -0
- package/dist/ai/client/index.d.ts.map +1 -0
- package/dist/ai/client/index.js +1053 -0
- package/dist/ai/client/index.js.map +1 -0
- package/dist/ai/client/reducer.d.ts +127 -0
- package/dist/ai/client/reducer.d.ts.map +1 -0
- package/dist/ai/client/reducer.test-d.d.ts +2 -0
- package/dist/ai/client/reducer.test-d.d.ts.map +1 -0
- package/dist/ai/client/sse.d.ts +54 -0
- package/dist/ai/client/sse.d.ts.map +1 -0
- package/dist/ai/example.d.ts +129 -0
- package/dist/ai/example.d.ts.map +1 -0
- package/dist/ai/index.d.ts +35 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +20 -0
- package/dist/ai/index.js.map +23 -0
- package/dist/ai/live/harness.d.ts +137 -0
- package/dist/ai/live/harness.d.ts.map +1 -0
- package/dist/ai/providers/call.d.ts +42 -0
- package/dist/ai/providers/call.d.ts.map +1 -0
- package/dist/ai/providers/capabilities.d.ts +50 -0
- package/dist/ai/providers/capabilities.d.ts.map +1 -0
- package/dist/ai/providers/errors.d.ts +37 -0
- package/dist/ai/providers/errors.d.ts.map +1 -0
- package/dist/ai/providers/fakeProvider.d.ts +35 -0
- package/dist/ai/providers/fakeProvider.d.ts.map +1 -0
- package/dist/ai/providers/http.d.ts +46 -0
- package/dist/ai/providers/http.d.ts.map +1 -0
- package/dist/ai/providers/request.d.ts +69 -0
- package/dist/ai/providers/request.d.ts.map +1 -0
- package/dist/ai/providers/stream.d.ts +42 -0
- package/dist/ai/providers/stream.d.ts.map +1 -0
- package/dist/ai/signing.d.ts +195 -0
- package/dist/ai/signing.d.ts.map +1 -0
- package/dist/ai/store/LiveRuns.d.ts +142 -0
- package/dist/ai/store/LiveRuns.d.ts.map +1 -0
- package/dist/ai/store/MemoryAgentStore.d.ts +61 -0
- package/dist/ai/store/MemoryAgentStore.d.ts.map +1 -0
- package/dist/ai/store/index.d.ts +4 -0
- package/dist/ai/store/index.d.ts.map +1 -0
- package/dist/ai/store/sse.d.ts +58 -0
- package/dist/ai/store/sse.d.ts.map +1 -0
- package/dist/ai/store/stubAgentRun.d.ts +56 -0
- package/dist/ai/store/stubAgentRun.d.ts.map +1 -0
- package/dist/ai/types.d.ts +443 -0
- package/dist/ai/types.d.ts.map +1 -0
- package/dist/ai/useChat.d.ts +253 -7
- package/dist/ai/useChat.d.ts.map +1 -1
- package/dist/chunk-1aqzcgfr.js +5 -0
- package/dist/chunk-1aqzcgfr.js.map +10 -0
- package/dist/{chunk-get4mkx8.js → chunk-1b7e9rj7.js} +2 -2
- package/dist/{chunk-get4mkx8.js.map → chunk-1b7e9rj7.js.map} +1 -1
- package/dist/{chunk-x8beq9c4.js → chunk-3aemqfdr.js} +2 -2
- package/dist/{chunk-x8beq9c4.js.map → chunk-3aemqfdr.js.map} +1 -1
- package/dist/{chunk-j06g4sqc.js → chunk-528n3vgy.js} +2 -2
- package/dist/{chunk-j06g4sqc.js.map → chunk-528n3vgy.js.map} +1 -1
- package/dist/{chunk-2khdxyjb.js → chunk-57a0nqfj.js} +1 -1
- package/dist/chunk-57a0nqfj.js.map +10 -0
- package/dist/{chunk-c40n5r4v.js → chunk-5mhcwnyd.js} +2 -2
- package/dist/{chunk-c40n5r4v.js.map → chunk-5mhcwnyd.js.map} +1 -1
- package/dist/{chunk-gwchvzdp.js → chunk-71pk1mxx.js} +2 -2
- package/dist/{chunk-gwchvzdp.js.map → chunk-71pk1mxx.js.map} +1 -1
- package/dist/{chunk-spbgpndn.js → chunk-7t1hjs9f.js} +2 -2
- package/dist/{chunk-spbgpndn.js.map → chunk-7t1hjs9f.js.map} +1 -1
- package/dist/{chunk-9gsdcjt7.js → chunk-7xvaace2.js} +3 -3
- package/dist/{chunk-9gsdcjt7.js.map → chunk-7xvaace2.js.map} +1 -1
- package/dist/{chunk-fxy42w6n.js → chunk-8ag0da2s.js} +2 -2
- package/dist/{chunk-fxy42w6n.js.map → chunk-8ag0da2s.js.map} +1 -1
- package/dist/chunk-8r8epsef.js +5 -0
- package/dist/chunk-8r8epsef.js.map +11 -0
- package/dist/chunk-91cj3nxk.js +6 -0
- package/dist/{chunk-y3zz410b.js.map → chunk-91cj3nxk.js.map} +2 -2
- package/dist/{chunk-0fm6jh9b.js → chunk-9nmvm20t.js} +2 -2
- package/dist/{chunk-0fm6jh9b.js.map → chunk-9nmvm20t.js.map} +1 -1
- package/dist/{chunk-98a3k7bp.js → chunk-9xpa7dpy.js} +2 -2
- package/dist/{chunk-98a3k7bp.js.map → chunk-9xpa7dpy.js.map} +1 -1
- package/dist/{chunk-qva4841r.js → chunk-a1exbqcq.js} +3 -3
- package/dist/{chunk-qva4841r.js.map → chunk-a1exbqcq.js.map} +1 -1
- package/dist/{chunk-cw9y6k15.js → chunk-bb19bwg6.js} +2 -2
- package/dist/{chunk-cw9y6k15.js.map → chunk-bb19bwg6.js.map} +1 -1
- package/dist/{chunk-3gvjn3q4.js → chunk-cf7bvd12.js} +1 -1
- package/dist/{chunk-rkbv3df7.js → chunk-djp2xeqe.js} +2 -2
- package/dist/{chunk-rkbv3df7.js.map → chunk-djp2xeqe.js.map} +1 -1
- package/dist/chunk-ds44bqr9.js +4 -0
- package/dist/{chunk-4mcyyh1v.js.map → chunk-ds44bqr9.js.map} +4 -9
- package/dist/{chunk-wzvs3sym.js → chunk-exndjhza.js} +3 -3
- package/dist/{chunk-wzvs3sym.js.map → chunk-exndjhza.js.map} +1 -1
- package/dist/{chunk-f6dd4gd8.js → chunk-f233yzxf.js} +2 -2
- package/dist/{chunk-f6dd4gd8.js.map → chunk-f233yzxf.js.map} +1 -1
- package/dist/chunk-fz5g2z6h.js +4 -0
- package/dist/{chunk-fbvvqf9b.js.map → chunk-fz5g2z6h.js.map} +2 -2
- package/dist/{chunk-vj9538yn.js → chunk-gcszdwcb.js} +2 -2
- package/dist/{chunk-vj9538yn.js.map → chunk-gcszdwcb.js.map} +1 -1
- package/dist/{chunk-pkjq9833.js → chunk-htesx7ym.js} +4 -4
- package/dist/{chunk-pkjq9833.js.map → chunk-htesx7ym.js.map} +1 -1
- package/dist/chunk-hyxmmj9b.js +5 -0
- package/dist/{chunk-khf9xda6.js.map → chunk-hyxmmj9b.js.map} +2 -2
- package/dist/{chunk-stq96kya.js → chunk-k2sjvt0c.js} +2 -2
- package/dist/{chunk-stq96kya.js.map → chunk-k2sjvt0c.js.map} +1 -1
- package/dist/{chunk-zhbrkpb3.js → chunk-k75phgj4.js} +4 -4
- package/dist/{chunk-zhbrkpb3.js.map → chunk-k75phgj4.js.map} +1 -1
- package/dist/{chunk-y64j80v9.js → chunk-m0tp7zjp.js} +2 -2
- package/dist/{chunk-y64j80v9.js.map → chunk-m0tp7zjp.js.map} +1 -1
- package/dist/{chunk-06j6rsew.js → chunk-m45j7p1y.js} +2 -2
- package/dist/{chunk-06j6rsew.js.map → chunk-m45j7p1y.js.map} +1 -1
- package/dist/chunk-mca9wsvs.js +5 -0
- package/dist/{chunk-z2tcxwyr.js.map → chunk-mca9wsvs.js.map} +3 -4
- package/dist/{chunk-tey1xayb.js → chunk-ms13evzp.js} +2 -2
- package/dist/{chunk-tey1xayb.js.map → chunk-ms13evzp.js.map} +1 -1
- package/dist/{chunk-bn1v4sfs.js → chunk-r962ae93.js} +2 -2
- package/dist/{chunk-bn1v4sfs.js.map → chunk-r962ae93.js.map} +1 -1
- package/dist/{chunk-23h0dmx2.js → chunk-s41ees18.js} +2 -2
- package/dist/{chunk-23h0dmx2.js.map → chunk-s41ees18.js.map} +1 -1
- package/dist/chunk-snb68dgr.js +4 -0
- package/dist/{chunk-hwhw98hc.js.map → chunk-snb68dgr.js.map} +1 -1
- package/dist/chunk-sz051605.js +5 -0
- package/dist/chunk-sz051605.js.map +14 -0
- package/dist/{chunk-2cwcfwg3.js → chunk-tr3cbx8k.js} +2 -2
- package/dist/{chunk-2cwcfwg3.js.map → chunk-tr3cbx8k.js.map} +2 -2
- package/dist/{chunk-dgasxgsm.js → chunk-vqcswg7h.js} +2 -2
- package/dist/{chunk-dgasxgsm.js.map → chunk-vqcswg7h.js.map} +2 -2
- package/dist/{chunk-zqsfanvk.js → chunk-wpb1xpdp.js} +2 -2
- package/dist/{chunk-zqsfanvk.js.map → chunk-wpb1xpdp.js.map} +1 -1
- package/dist/{chunk-z1e55w67.js → chunk-ybqss0jy.js} +2 -2
- package/dist/{chunk-z1e55w67.js.map → chunk-ybqss0jy.js.map} +1 -1
- package/dist/{chunk-cejf873g.js → chunk-yk5wqmyh.js} +2 -2
- package/dist/{chunk-cejf873g.js.map → chunk-yk5wqmyh.js.map} +1 -1
- package/dist/{chunk-8kj3zrm9.js → chunk-ywntv8yw.js} +4 -4
- package/dist/{chunk-8kj3zrm9.js.map → chunk-ywntv8yw.js.map} +1 -1
- package/dist/chunks/{ThemeProvider-ByU4BQdL.js → ThemeProvider-BZ2SsSZ3.js} +60 -39
- package/dist/chunks/ThemeProvider-BZ2SsSZ3.js.map +1 -0
- package/dist/chunks/useParams-BN3XXfmG.js +20 -0
- package/dist/chunks/useParams-BN3XXfmG.js.map +1 -0
- package/dist/client/index.js +3 -3
- package/dist/client/index.js.map +1 -1
- package/dist/client/useDictionary.d.ts.map +1 -1
- package/dist/database/index.js +1 -1
- package/dist/facades/index.js +2 -2
- package/dist/facades/index.js.map +1 -1
- package/dist/http/ApiRouter.d.ts +30 -2
- package/dist/http/ApiRouter.d.ts.map +1 -1
- package/dist/http/index.js +2 -2
- package/dist/http/index.js.map +1 -1
- package/dist/i18n/defineDictionary.d.ts +7 -4
- package/dist/i18n/defineDictionary.d.ts.map +1 -1
- package/dist/i18n/dictionaryRegistry.d.ts +38 -14
- package/dist/i18n/dictionaryRegistry.d.ts.map +1 -1
- package/dist/i18n/dictionaryRuntime.js +1 -1
- package/dist/i18n/index.js +2 -2
- package/dist/i18n/index.js.map +2 -2
- package/dist/kernel/index.js +2 -2
- package/dist/kernel/index.js.map +2 -2
- package/dist/orm/index.js +2 -2
- package/dist/orm/index.js.map +2 -2
- package/dist/server/index.js +1 -1
- package/dist/services/index.js +2 -2
- package/dist/services/index.js.map +2 -2
- package/dist/testing/index.js +2 -1
- package/dist/testing/index.js.map +1 -1
- package/package.json +3 -1
- package/dist/chunk-2khdxyjb.js.map +0 -10
- package/dist/chunk-4mcyyh1v.js +0 -4
- package/dist/chunk-fbvvqf9b.js +0 -4
- package/dist/chunk-hwhw98hc.js +0 -4
- package/dist/chunk-khf9xda6.js +0 -5
- package/dist/chunk-q0y0j3ne.js +0 -5
- package/dist/chunk-q0y0j3ne.js.map +0 -11
- package/dist/chunk-y3zz410b.js +0 -6
- package/dist/chunk-z2tcxwyr.js +0 -5
- package/dist/chunks/ThemeProvider-ByU4BQdL.js.map +0 -1
- /package/dist/{chunk-3gvjn3q4.js.map → chunk-cf7bvd12.js.map} +0 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signing for pending tool calls.
|
|
3
|
+
*
|
|
4
|
+
* A pending call travels through the browser and comes back — in stateless mode
|
|
5
|
+
* the whole history does — so the server cannot trust that what it gets back is
|
|
6
|
+
* what it sent. Without a signature the client asserts not just *that* a call
|
|
7
|
+
* was approved but *what* was approved, and nothing would stop it from
|
|
8
|
+
* returning `approve: true` against an input it rewrote on the way. Signing is
|
|
9
|
+
* what makes the round trip safe, and it is why approvals need no server-side
|
|
10
|
+
* storage at all.
|
|
11
|
+
*
|
|
12
|
+
* What is signed, and what deliberately is not:
|
|
13
|
+
*
|
|
14
|
+
* signed — `runId`, `toolCallId`, the tool `name`, the `kind` of pending call
|
|
15
|
+
* and a canonical serialization of the input, plus a nonce, an
|
|
16
|
+
* expiry, and — for a call a sub-agent asked — the `path` of
|
|
17
|
+
* tool-call ids it is nested under.
|
|
18
|
+
* not — the client's answer. `approve: true` / `approve: false` and a
|
|
19
|
+
* question's output are the *point* of asking; a client that flips
|
|
20
|
+
* its own answer has refused, not forged. What the signature buys is
|
|
21
|
+
* that the answer is bound to the call the server actually made,
|
|
22
|
+
* with the input the server actually saw.
|
|
23
|
+
*
|
|
24
|
+
* A verified token is not yet an answer the server may act on: it says the
|
|
25
|
+
* question was asked, not that it is still open. `consumePendingCall` at the
|
|
26
|
+
* bottom of this file spends the nonce, which is what makes an approval
|
|
27
|
+
* single-use — see the note there for what that guarantee is worth.
|
|
28
|
+
*
|
|
29
|
+
* `kind` is in there for a specific attack: an `approval`-kind call is one the
|
|
30
|
+
* *server* runs, so a client that reused its signature on the "here is the
|
|
31
|
+
* output" arm of `ClientToolResult` would be fabricating a server tool's result
|
|
32
|
+
* rather than approving it. Binding the kind makes that a forgery instead of a
|
|
33
|
+
* shape the caller has to remember to check.
|
|
34
|
+
*/
|
|
35
|
+
/** Everything the signature commits to. */
|
|
36
|
+
export type PendingCallClaims = {
|
|
37
|
+
runId: string;
|
|
38
|
+
toolCallId: string;
|
|
39
|
+
name: string;
|
|
40
|
+
kind: "approval" | "question" | "client";
|
|
41
|
+
input: unknown;
|
|
42
|
+
/**
|
|
43
|
+
* The chain of tool-call ids the call is nested under, outermost first.
|
|
44
|
+
* Absent — or empty, which means the same thing — for a top-level call.
|
|
45
|
+
*
|
|
46
|
+
* A sub-agent's question reaches the user through its parent's pending list,
|
|
47
|
+
* so `toolCallId` stops being an address on its own: two sub-runs under two
|
|
48
|
+
* different tools can each hold a call the outer run never made. Binding the
|
|
49
|
+
* path is what stops a token minted for a call nested under tool call X from
|
|
50
|
+
* being replayed as a top-level call, or as one nested under Y.
|
|
51
|
+
*/
|
|
52
|
+
path?: string[];
|
|
53
|
+
};
|
|
54
|
+
export type SignOptions = {
|
|
55
|
+
/** Overrides `process.env.SECRET`. Exists for tests; apps use the app key. */
|
|
56
|
+
secret?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Default 24 hours. An approval waits on a human, and humans go to lunch —
|
|
59
|
+
* a short expiry turns "I approved it after standup" into an unexplained
|
|
60
|
+
* failure. Long enough to survive a working day, short enough that a token
|
|
61
|
+
* lifted from a log is not useful next month.
|
|
62
|
+
*/
|
|
63
|
+
ttlMs?: number;
|
|
64
|
+
/** Injected clock, so the expiry path is testable without waiting. */
|
|
65
|
+
now?: number;
|
|
66
|
+
};
|
|
67
|
+
export type VerifyOptions = {
|
|
68
|
+
secret?: string;
|
|
69
|
+
now?: number;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* A discriminated result rather than a boolean, because the two failures are
|
|
73
|
+
* different events: `expired` is a sentence to show the user, `forged` is worth
|
|
74
|
+
* logging and possibly alerting on. Collapsing them loses the only signal that
|
|
75
|
+
* says someone is probing.
|
|
76
|
+
*/
|
|
77
|
+
export type VerifyResult = {
|
|
78
|
+
ok: true;
|
|
79
|
+
runId: string;
|
|
80
|
+
nonce: string;
|
|
81
|
+
expiresAt: number;
|
|
82
|
+
} | {
|
|
83
|
+
ok: false;
|
|
84
|
+
reason: "malformed" | "expired" | "forged";
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Serializes a value so that the same value always produces the same string.
|
|
88
|
+
*
|
|
89
|
+
* `JSON.stringify` is not enough: it preserves insertion order, so an input
|
|
90
|
+
* that made a round trip through a client — parsed and re-serialized, with the
|
|
91
|
+
* keys in whatever order the parser produced — would hash differently and a
|
|
92
|
+
* legitimate approval would come back looking forged. Keys are sorted,
|
|
93
|
+
* `undefined` members are dropped (they do not survive JSON anyway), and arrays
|
|
94
|
+
* keep their order because in an array order *is* the value.
|
|
95
|
+
*/
|
|
96
|
+
export declare function canonicalize(value: unknown): string;
|
|
97
|
+
/** `agt1.<runId>.<nonce>.<expiry>.<mac>`, all base64url or base36. */
|
|
98
|
+
export declare function signPendingCall(claims: PendingCallClaims, options?: SignOptions): string;
|
|
99
|
+
/**
|
|
100
|
+
* The metadata a signature carries in the clear.
|
|
101
|
+
*
|
|
102
|
+
* `Agent` needs the issuing `runId` before it can verify anything: the call was
|
|
103
|
+
* signed under the run that made it, and the turn answering it is a *new* run
|
|
104
|
+
* with a new id. Reading it out of the token is safe because the token's own
|
|
105
|
+
* MAC covers it — a client that edits the runId here fails verification, so
|
|
106
|
+
* this is "which run does this claim to belong to", not "which run does the
|
|
107
|
+
* client say it belongs to".
|
|
108
|
+
*/
|
|
109
|
+
export declare function readSignature(signature: string): {
|
|
110
|
+
runId: string;
|
|
111
|
+
nonce: string;
|
|
112
|
+
expiresAt: number;
|
|
113
|
+
} | null;
|
|
114
|
+
export declare function verifyPendingCall(signature: string, claims: PendingCallClaims, options?: VerifyOptions): VerifyResult;
|
|
115
|
+
/**
|
|
116
|
+
* What a parked sub-run's record is signed over.
|
|
117
|
+
*
|
|
118
|
+
* `ToolCallPart.nested` is the parent's own record of where a sub-run stopped,
|
|
119
|
+
* and in stateless mode it makes the same trip through the browser a pending
|
|
120
|
+
* call does. The next turn *runs a tool* on the strength of that record — the
|
|
121
|
+
* tool is re-entered because the record says a sub-run under it is waiting on
|
|
122
|
+
* the question being answered — so an unsigned record lets the client choose
|
|
123
|
+
* which tools run, with what input, before any answer is verified. A MAC over
|
|
124
|
+
* what the server actually recorded is what makes the record safe to carry.
|
|
125
|
+
*
|
|
126
|
+
* Only a parked record is signed, because only a parked record executes
|
|
127
|
+
* anything: a finished sub-run is replayed out of its transcript and spends
|
|
128
|
+
* nothing, and a client that forges one has fed its own tool a made-up answer,
|
|
129
|
+
* which a client-carried history already allows everywhere.
|
|
130
|
+
*
|
|
131
|
+
* Deliberately not signed: the transcript. The sub-run resumes from messages
|
|
132
|
+
* the client carried, exactly as the parent does in stateless mode, and the
|
|
133
|
+
* same argument applies — what the signature pins is that the server parked
|
|
134
|
+
* *here*, on *these* calls, with *this* input, and not what was said on the
|
|
135
|
+
* way.
|
|
136
|
+
*/
|
|
137
|
+
export type NestedRunClaims = {
|
|
138
|
+
/** The root run's id, the one every pending call of the tree is minted under. */
|
|
139
|
+
runId: string;
|
|
140
|
+
/**
|
|
141
|
+
* Tool-call ids from the root down to and including the call the record
|
|
142
|
+
* hangs off. A sub-run's id is not an address on its own for the same reason
|
|
143
|
+
* a nested call's is not: two sub-runs under two different tools can carry
|
|
144
|
+
* the same one.
|
|
145
|
+
*/
|
|
146
|
+
path: string[];
|
|
147
|
+
/** The sub-run's own id, so a record cannot be moved between sub-runs. */
|
|
148
|
+
nestedRunId: string;
|
|
149
|
+
/** The tool calls the sub-run is waiting on: every call left open in its transcript. */
|
|
150
|
+
open: string[];
|
|
151
|
+
/**
|
|
152
|
+
* The input the tool that parked was running on, as the transcript carries
|
|
153
|
+
* it. Re-entry runs the tool body with the input the history holds, and the
|
|
154
|
+
* history is the client's — so a record that pinned where the sub-run parked
|
|
155
|
+
* but not what its tool was given would let the client keep the run and
|
|
156
|
+
* rewrite the arguments to anything the schema accepts. The same bargain a
|
|
157
|
+
* pending call makes: the input executed is the input signed.
|
|
158
|
+
*/
|
|
159
|
+
input: unknown;
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Same shape as a pending call's token, so the same reader serves both — nonce
|
|
163
|
+
* included, and the nonce is spent, by `consumeNestedRun` below. A verified
|
|
164
|
+
* record is permission to run the tool it hangs off, and the tool body runs
|
|
165
|
+
* before the sub-run gets to look at the answer's own nonce; a record that
|
|
166
|
+
* could be presented twice would run the body twice before anything refused
|
|
167
|
+
* the replay. Every park mints a fresh record, so spending one costs a
|
|
168
|
+
* legitimate re-park nothing.
|
|
169
|
+
*/
|
|
170
|
+
export declare function signNestedRun(claims: NestedRunClaims, options?: SignOptions): string;
|
|
171
|
+
/**
|
|
172
|
+
* The run that verifies a record is never the run that minted it — the turn
|
|
173
|
+
* answering a question is a new run with a new id — so the minting run's id is
|
|
174
|
+
* read out of the token rather than asked of the caller, which has no other
|
|
175
|
+
* source for it. The MAC covers it, so a client that edits the id in the clear
|
|
176
|
+
* fails here rather than being believed.
|
|
177
|
+
*/
|
|
178
|
+
export declare function verifyNestedRun(signature: string, claims: Omit<NestedRunClaims, "runId">, options?: VerifyOptions): VerifyResult;
|
|
179
|
+
/**
|
|
180
|
+
* Spends a signature's nonce. `false` means it was already spent — the answer
|
|
181
|
+
* is a replay and must not be acted on.
|
|
182
|
+
*
|
|
183
|
+
* Separate from `verifyPendingCall` rather than folded into it, because verify
|
|
184
|
+
* is a pure question a caller may want to ask twice (logging a forgery, say)
|
|
185
|
+
* and this one is a state change that must happen exactly once per answer.
|
|
186
|
+
*/
|
|
187
|
+
export declare function consumePendingCall(signature: string, options?: VerifyOptions): boolean;
|
|
188
|
+
/**
|
|
189
|
+
* Spends a parked-run record's nonce, on the same registry and the same terms.
|
|
190
|
+
* `false` means the record has already re-entered its tool once — the turn is
|
|
191
|
+
* a replay of a history from before the answer was delivered, and the body
|
|
192
|
+
* must not run again on it.
|
|
193
|
+
*/
|
|
194
|
+
export declare function consumeNestedRun(signature: string, options?: VerifyOptions): boolean;
|
|
195
|
+
//# sourceMappingURL=signing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signing.d.ts","sourceRoot":"","sources":["../../ai/signing.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,2CAA2C;AAC3C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;IACzC,KAAK,EAAE,OAAO,CAAC;IACf;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC7D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAA;CAAE,CAAC;AAkB9D;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAYnD;AAkDD,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,GAAE,WAAgB,GAAG,MAAM,CAO5F;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,GAChB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAE5D;AA6BD,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,iBAAiB,EACzB,OAAO,GAAE,aAAkB,GAC1B,YAAY,CAwBd;AAID;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;IACpB,wFAAwF;IACxF,IAAI,EAAE,MAAM,EAAE,CAAC;IACf;;;;;;;OAOG;IACH,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAmBF;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,OAAO,GAAE,WAAgB,GAAG,MAAM,CAOxF;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,EACtC,OAAO,GAAE,aAAkB,GAC1B,YAAY,CAwBd;AA2CD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAE1F;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAExF"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { AgentRun } from "../Agent";
|
|
2
|
+
import type { LiveRuns } from "../AgentController";
|
|
3
|
+
import type { AgentStreamEvent, AgentStreamFrame } from "../types";
|
|
4
|
+
/**
|
|
5
|
+
* A cursor older than anything still buffered.
|
|
6
|
+
*
|
|
7
|
+
* Deliberately an error rather than "here is the tail I still have". A client
|
|
8
|
+
* that asked for frame 12 and silently got frame 300 onwards has a transcript
|
|
9
|
+
* with a hole in it and no way to know — it will render a half-message, or an
|
|
10
|
+
* `awaiting-input` for a tool call it never saw. Saying so lets the client do
|
|
11
|
+
* the only correct thing, which is to reload the thread from the store.
|
|
12
|
+
*/
|
|
13
|
+
export declare class FrameCursorEvictedError extends Error {
|
|
14
|
+
readonly runId: string;
|
|
15
|
+
readonly requested: number;
|
|
16
|
+
readonly oldest: number;
|
|
17
|
+
readonly code = "frame_cursor_evicted";
|
|
18
|
+
constructor(runId: string, requested: number, oldest: number);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* No run under that id in *this* process.
|
|
22
|
+
*
|
|
23
|
+
* Which is the honest answer, and the one worth being loud about: the run may
|
|
24
|
+
* well be alive on the box next door. See the note on `MemoryLiveRuns` — behind
|
|
25
|
+
* a round-robin load balancer this is what a refresh hits roughly (n-1)/n of
|
|
26
|
+
* the time, and an explicit miss is the difference between a bug someone finds
|
|
27
|
+
* in an hour and one that presents as "reattach sometimes does nothing".
|
|
28
|
+
*/
|
|
29
|
+
export declare class LiveRunNotFoundError extends Error {
|
|
30
|
+
readonly runId: string;
|
|
31
|
+
readonly code = "live_run_not_found";
|
|
32
|
+
constructor(runId: string);
|
|
33
|
+
}
|
|
34
|
+
export type RegisterParams = {
|
|
35
|
+
threadId?: string;
|
|
36
|
+
/**
|
|
37
|
+
* The client's own name for this run, minted before the run had one.
|
|
38
|
+
*
|
|
39
|
+
* `runId` does not reach the client until `run-start`, and a stateless first
|
|
40
|
+
* turn has no `threadId` either, so for the length of a network round trip
|
|
41
|
+
* plus the provider's time to first token there is nothing for `/stop` to
|
|
42
|
+
* name — which is exactly the window a user cancels in. `useChat` sends a
|
|
43
|
+
* `clientRunId` with every turn it starts; recording it here is what makes
|
|
44
|
+
* that window stoppable.
|
|
45
|
+
*/
|
|
46
|
+
clientRunId?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Called once per frame, in order, off the buffering path.
|
|
49
|
+
*
|
|
50
|
+
* The controller's `on*` hooks hang off this. It is a callback rather than a
|
|
51
|
+
* second `run.frames()` subscription because every extra subscriber is
|
|
52
|
+
* another consumer of a generator whose multi-subscriber behaviour we do not
|
|
53
|
+
* own, and one pump is one thing to reason about.
|
|
54
|
+
*/
|
|
55
|
+
onEvent?: (event: AgentStreamEvent) => void | Promise<void>;
|
|
56
|
+
/** Reported failures: a hook that threw, or a run whose frame iterator did.
|
|
57
|
+
* Injectable so tests can assert on it instead of reading stderr. */
|
|
58
|
+
onInternalError?: (error: unknown) => void;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* The runs currently in flight in this process, and their recent frames.
|
|
62
|
+
*
|
|
63
|
+
* PER-PROCESS IS NOT AN IMPLEMENTATION SHORTCUT THAT A BETTER STORE FIXES. A
|
|
64
|
+
* running generator lives in one process, and a second server cannot attach to
|
|
65
|
+
* it — no amount of Redis moves an in-flight async iterator across a socket.
|
|
66
|
+
* Reattachment therefore needs the request to land where the run is: one
|
|
67
|
+
* server, sticky routing, or a proxy that forwards by `runId`. Worth saying out
|
|
68
|
+
* loud, because the failure mode behind a round-robin load balancer is a
|
|
69
|
+
* refresh that usually works.
|
|
70
|
+
*
|
|
71
|
+
* `find` and `replay` are built so that failure is an explicit miss — a 404
|
|
72
|
+
* naming the run, a 410 naming the cursor — and never an SSE stream that opens,
|
|
73
|
+
* says nothing and closes. An empty stream is indistinguishable from a run that
|
|
74
|
+
* finished quietly, which is exactly the confusion this is supposed to avoid.
|
|
75
|
+
*/
|
|
76
|
+
export declare class MemoryLiveRuns implements LiveRuns {
|
|
77
|
+
ttlMs: number;
|
|
78
|
+
readonly maxFrames: number;
|
|
79
|
+
private runs;
|
|
80
|
+
/** Thread to the most recently registered run for it. */
|
|
81
|
+
private byThread;
|
|
82
|
+
/** The client's pre-`run-start` name for a run, to the run. */
|
|
83
|
+
private byClientRun;
|
|
84
|
+
constructor(params?: {
|
|
85
|
+
ttlMs?: number;
|
|
86
|
+
maxFrames?: number;
|
|
87
|
+
});
|
|
88
|
+
/**
|
|
89
|
+
* Takes ownership of a run: starts buffering its frames and holds it until
|
|
90
|
+
* `ttlMs` past the end.
|
|
91
|
+
*/
|
|
92
|
+
register(run: AgentRun, params?: RegisterParams): void;
|
|
93
|
+
/**
|
|
94
|
+
* The run a client named before the server had named it.
|
|
95
|
+
*
|
|
96
|
+
* Deliberately not folded into `find`, whose parameter is part of the
|
|
97
|
+
* read-side `LiveRuns` interface an app may already implement — widening that
|
|
98
|
+
* parameter would break every such implementation, and this lookup is only
|
|
99
|
+
* ever asked by `/stop`.
|
|
100
|
+
*/
|
|
101
|
+
findByClientRunId(clientRunId: string): string | null;
|
|
102
|
+
/** What the client asks after a refresh: is anything still going here? */
|
|
103
|
+
find(params: {
|
|
104
|
+
threadId: string;
|
|
105
|
+
}): Promise<{
|
|
106
|
+
runId: string;
|
|
107
|
+
seq: number;
|
|
108
|
+
} | null>;
|
|
109
|
+
get(runId: string): AgentRun | null;
|
|
110
|
+
/**
|
|
111
|
+
* The buffered frames from `from` onwards, followed by live ones until the
|
|
112
|
+
* run ends.
|
|
113
|
+
*
|
|
114
|
+
* Throws before returning anything, so an evicted cursor and an unknown run
|
|
115
|
+
* are still HTTP statuses rather than events on a stream that already
|
|
116
|
+
* committed to a 200.
|
|
117
|
+
*
|
|
118
|
+
* `from` omitted means "start wherever you still can", not "start at 0".
|
|
119
|
+
* These are genuinely different requests: a client that names a cursor is
|
|
120
|
+
* telling us where its transcript ends, and handing it a later frame leaves
|
|
121
|
+
* an invisible hole — that is the 410. A client with no cursor at all — the
|
|
122
|
+
* browser reattaching on mount, which is the case `/attach` exists for — has
|
|
123
|
+
* no transcript to put a hole in, and refusing it the tail because the run is
|
|
124
|
+
* older than the buffer would 410 every run past `maxFrames`, i.e. every run
|
|
125
|
+
* long enough to be worth reattaching to.
|
|
126
|
+
*/
|
|
127
|
+
replay(runId: string, from?: number): AsyncIterable<AgentStreamFrame>;
|
|
128
|
+
/** Test seam: drops everything and cancels the pending eviction timers. */
|
|
129
|
+
clear(): void;
|
|
130
|
+
get size(): number;
|
|
131
|
+
private pump;
|
|
132
|
+
private drain;
|
|
133
|
+
private wait;
|
|
134
|
+
private notify;
|
|
135
|
+
private scheduleEviction;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The process-wide default, shared by every `AgentController` that does not
|
|
139
|
+
* bring its own. One map per process is the whole point — see the class note.
|
|
140
|
+
*/
|
|
141
|
+
export declare const liveRuns: MemoryLiveRuns;
|
|
142
|
+
//# sourceMappingURL=LiveRuns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LiveRuns.d.ts","sourceRoot":"","sources":["../../../ai/store/LiveRuns.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAqCnE;;;;;;;;GAQG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IAI9C,QAAQ,CAAC,KAAK,EAAE,MAAM;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM;IALzB,QAAQ,CAAC,IAAI,0BAA0B;gBAG5B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM;CAO1B;AAED;;;;;;;;GAQG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAGjC,QAAQ,CAAC,KAAK,EAAE,MAAM;IAFlC,QAAQ,CAAC,IAAI,wBAAwB;gBAEhB,KAAK,EAAE,MAAM;CAGnC;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D;0EACsE;IACtE,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC5C,CAAC;AAqCF;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,cAAe,YAAW,QAAQ;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,OAAO,CAAC,IAAI,CAA4B;IACxC,yDAAyD;IACzD,OAAO,CAAC,QAAQ,CAA6B;IAC7C,+DAA+D;IAC/D,OAAO,CAAC,WAAW,CAA6B;gBAEpC,MAAM,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO;IAK/D;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,GAAE,cAAmB,GAAG,IAAI;IAuB1D;;;;;;;OAOG;IACH,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKrD,0EAA0E;IACpE,IAAI,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAexF,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAInC;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,gBAAgB,CAAC;IAcrE,2EAA2E;IAC3E,KAAK,IAAI,IAAI;IAeb,IAAI,IAAI,IAAI,MAAM,CAEjB;YAEa,IAAI;YA4CH,KAAK;IA4CpB,OAAO,CAAC,IAAI;IAOZ,OAAO,CAAC,MAAM;IASd,OAAO,CAAC,gBAAgB;CAqBzB;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ,gBAAuB,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { AgentStore } from "../AgentController";
|
|
2
|
+
import type { AgentMessage } from "../types";
|
|
3
|
+
/**
|
|
4
|
+
* The default store: conversations last as long as the process.
|
|
5
|
+
*
|
|
6
|
+
* It exists so that `threadId` works out of the box, not so that anything is
|
|
7
|
+
* durable — a restart loses every thread, and a second server never had them.
|
|
8
|
+
* That is the honest default for a framework store, and it is why stateless is
|
|
9
|
+
* still the mode an app gets without asking: the client carrying its own
|
|
10
|
+
* history survives a deploy, and this does not.
|
|
11
|
+
*
|
|
12
|
+
* Expiry is swept lazily rather than on a timer. A `setTimeout` per thread is a
|
|
13
|
+
* timer per conversation and a reference the GC cannot collect, and an interval
|
|
14
|
+
* running forever keeps a process alive that has nothing else to do — so the
|
|
15
|
+
* sweep happens on access, at most once a minute, and an untouched process
|
|
16
|
+
* simply stops sweeping.
|
|
17
|
+
*
|
|
18
|
+
* A thread the store does not have is `null` from `loadThread` and an error
|
|
19
|
+
* from `appendMessages`, never an empty conversation. It used to be the other
|
|
20
|
+
* way, and the three ways a thread goes missing — it expired, the id was
|
|
21
|
+
* mistyped, it lived on an instance that was scaled in — all read as a fresh
|
|
22
|
+
* chat: the history was gone with no signal, and the next turn was persisted
|
|
23
|
+
* under the dead id as though it were the first. `clientOwnedIds` is the one
|
|
24
|
+
* setup where an unknown id is not a lost thread, because the client minted it.
|
|
25
|
+
*/
|
|
26
|
+
export declare class MemoryAgentStore implements AgentStore {
|
|
27
|
+
readonly ttlMs: number;
|
|
28
|
+
/** The ids come from the client, not from `createThread`, so an id this
|
|
29
|
+
* store has never seen is a conversation starting rather than one lost. */
|
|
30
|
+
readonly clientOwnedIds: boolean;
|
|
31
|
+
private threads;
|
|
32
|
+
private lastSweep;
|
|
33
|
+
constructor(params?: {
|
|
34
|
+
ttlMs?: number;
|
|
35
|
+
clientOwnedIds?: boolean;
|
|
36
|
+
});
|
|
37
|
+
createThread(params: {
|
|
38
|
+
userId?: string | number;
|
|
39
|
+
}): Promise<{
|
|
40
|
+
threadId: string;
|
|
41
|
+
}>;
|
|
42
|
+
loadThread(threadId: string): Promise<AgentMessage[] | null>;
|
|
43
|
+
appendMessages(threadId: string, messages: AgentMessage[]): Promise<void>;
|
|
44
|
+
/** Test seam, and a way for an app to drop a conversation on request. */
|
|
45
|
+
delete(threadId: string): void;
|
|
46
|
+
get size(): number;
|
|
47
|
+
sweep(now?: number): void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The process-wide default every `AgentController` uses unless it is given
|
|
51
|
+
* another.
|
|
52
|
+
*
|
|
53
|
+
* It has to be a shared instance, not a field initializer. A gemi controller is
|
|
54
|
+
* constructed per request — `RouteHandler.run()` does `new Controller()` every
|
|
55
|
+
* time — so `store = new MemoryAgentStore()` written in a controller field is a
|
|
56
|
+
* brand new, empty store on every turn, and a threaded conversation would read
|
|
57
|
+
* back nothing while looking like it was configured correctly. Anything
|
|
58
|
+
* process-lived that a controller holds has to be created outside it.
|
|
59
|
+
*/
|
|
60
|
+
export declare const defaultAgentStore: MemoryAgentStore;
|
|
61
|
+
//# sourceMappingURL=MemoryAgentStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MemoryAgentStore.d.ts","sourceRoot":"","sources":["../../../ai/store/MemoryAgentStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAgB7C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,gBAAiB,YAAW,UAAU;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;gFAC4E;IAC5E,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IAEjC,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAAK;gBAEV,MAAM,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAO;IAK/D,YAAY,CAAC,MAAM,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAWjF,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,GAAG,IAAI,CAAC;IAiB5D,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAiC/E,yEAAyE;IACzE,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAI9B,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,KAAK,CAAC,GAAG,SAAa,GAAG,IAAI;CAW9B;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,kBAAyB,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { defaultAgentStore, MemoryAgentStore } from "./MemoryAgentStore";
|
|
2
|
+
export { FrameCursorEvictedError, LiveRunNotFoundError, liveRuns, MemoryLiveRuns, type RegisterParams, } from "./LiveRuns";
|
|
3
|
+
export { encodeFrame, sseHeaders, sseResponse } from "./sse";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../ai/store/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,QAAQ,EACR,cAAc,EACd,KAAK,cAAc,GACpB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { AgentStreamFrame } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* One frame, one SSE event.
|
|
4
|
+
*
|
|
5
|
+
* `id:` carries the frame's `seq`, which is what makes the browser's own
|
|
6
|
+
* `Last-Event-ID` the right cursor on reconnect — the transport asks the
|
|
7
|
+
* question the run already knows how to answer, and the client never has to
|
|
8
|
+
* track a position of its own.
|
|
9
|
+
*/
|
|
10
|
+
export declare function encodeFrame(frame: AgentStreamFrame): string;
|
|
11
|
+
/**
|
|
12
|
+
* How long a connection may sit idle before a comment line goes out.
|
|
13
|
+
*
|
|
14
|
+
* The nearest ceiling is not a proxy but our own server: Bun's `idleTimeout`
|
|
15
|
+
* counts socket silence, a streaming body included, and gemi runs at its
|
|
16
|
+
* 10-second default unless `SERVER_IDLE_TIMEOUT` says otherwise. A comment
|
|
17
|
+
* line every 25 seconds was measured to lose the connection at 12; every 5
|
|
18
|
+
* keeps it open, with room for a write that lands late. Azure App Service's
|
|
19
|
+
* front end, at about 230 seconds, is the far ceiling, and 5 clears it by the
|
|
20
|
+
* same margin. A thousand quiet streams cost two hundred thirteen-byte writes
|
|
21
|
+
* a second, which is nothing. Both encoders read this one value, so the two
|
|
22
|
+
* cannot drift apart.
|
|
23
|
+
*/
|
|
24
|
+
export declare const SSE_KEEPALIVE_INTERVAL_MS = 5000;
|
|
25
|
+
/**
|
|
26
|
+
* The comment line itself. A line starting with `:` is a comment under the SSE
|
|
27
|
+
* spec: every parser on our side skips it, and so does the browser's own
|
|
28
|
+
* `EventSource`.
|
|
29
|
+
*/
|
|
30
|
+
export declare const SSE_KEEPALIVE = ": keepalive\n\n";
|
|
31
|
+
/**
|
|
32
|
+
* Writes a keepalive whenever the stream has been silent for the interval.
|
|
33
|
+
*
|
|
34
|
+
* Armed on creation because the silence before the first frame is real
|
|
35
|
+
* silence too — a model thinking is the common case. `touch()` after every
|
|
36
|
+
* frame is what makes it measure silence rather than elapsed time; `stop()`
|
|
37
|
+
* on close or cancel is what keeps a finished stream from holding a timer.
|
|
38
|
+
*
|
|
39
|
+
* The write is guarded because a cancel can land between the timer firing and
|
|
40
|
+
* the enqueue, and a closed controller throws. There is nothing to do about
|
|
41
|
+
* that except stop. `arm` checks `stopped` too, so a `touch()` that arrives
|
|
42
|
+
* after `stop()` cannot hand a finished stream a timer for one more interval.
|
|
43
|
+
*/
|
|
44
|
+
export declare function sseKeepalive(controller: ReadableStreamDefaultController<Uint8Array>, intervalMs?: number): {
|
|
45
|
+
touch(): void;
|
|
46
|
+
stop(): void;
|
|
47
|
+
};
|
|
48
|
+
export declare function sseHeaders(): Record<string, string>;
|
|
49
|
+
/**
|
|
50
|
+
* Encodes an async iterable of frames as an SSE response.
|
|
51
|
+
*
|
|
52
|
+
* Pulls one frame per `pull` rather than looping inside `start`: a `start` that
|
|
53
|
+
* awaits the whole run does not resolve until the run is over, and the stream
|
|
54
|
+
* is not readable until it does — which would turn every streamed answer into a
|
|
55
|
+
* single delivery at the end.
|
|
56
|
+
*/
|
|
57
|
+
export declare function sseResponse(frames: AsyncIterable<AgentStreamFrame>, status?: number): Response;
|
|
58
|
+
//# sourceMappingURL=sse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../../ai/store/sse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAI7D;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAE3D;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB,OAAQ,CAAC;AAE/C;;;;GAIG;AACH,eAAO,MAAM,aAAa,oBAAoB,CAAC;AAE/C;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,+BAA+B,CAAC,UAAU,CAAC,EACvD,UAAU,SAA4B,GACrC;IAAE,KAAK,IAAI,IAAI,CAAC;IAAC,IAAI,IAAI,IAAI,CAAA;CAAE,CA4BjC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWnD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,gBAAgB,CAAC,EAAE,MAAM,SAAM,GAAG,QAAQ,CAwC3F"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { AgentRun, AgentRunResult } from "../Agent";
|
|
2
|
+
import type { AgentStreamEvent, AgentStreamFrame, ToolShapes } from "../types";
|
|
3
|
+
/**
|
|
4
|
+
* A hand-driven `AgentRun`, for tests of everything that consumes one.
|
|
5
|
+
*
|
|
6
|
+
* It lives in source rather than in a test file because both the `LiveRuns`
|
|
7
|
+
* tests and the controller tests need the same fake, and a test file that
|
|
8
|
+
* imports another test file gets that file's suites collected twice.
|
|
9
|
+
*
|
|
10
|
+
* It is deliberately a *stub satisfying the interface*, not a shrunken `Agent`:
|
|
11
|
+
* the real agent is a separate slice, and a test that imported it would be
|
|
12
|
+
* testing whichever half of the module was written last.
|
|
13
|
+
*/
|
|
14
|
+
export declare class StubAgentRun implements AgentRun<ToolShapes, unknown> {
|
|
15
|
+
readonly runId: string;
|
|
16
|
+
stopped: boolean;
|
|
17
|
+
stopReason: string | undefined;
|
|
18
|
+
/** Every `frames()` call ever made, so a test can assert on subscribers. */
|
|
19
|
+
subscriptions: number;
|
|
20
|
+
private buffer;
|
|
21
|
+
/**
|
|
22
|
+
* Frames are numbered from 1, because `AgentRunImpl` numbers them from 1.
|
|
23
|
+
*
|
|
24
|
+
* This used to start at 0, and the difference was not cosmetic: `LiveRuns`
|
|
25
|
+
* decided whether a cursor had been evicted by comparing it against the
|
|
26
|
+
* oldest buffered `seq`, which is 1 on a real run that has evicted nothing
|
|
27
|
+
* and 0 on this one. Every test here passed against a stub that could not
|
|
28
|
+
* express the case, and a ten-frame run answered a refresh with 410.
|
|
29
|
+
*/
|
|
30
|
+
private nextSeq;
|
|
31
|
+
private done;
|
|
32
|
+
private wake;
|
|
33
|
+
/** See the note on `Entry.version` in `LiveRuns`: same missed-wakeup window,
|
|
34
|
+
* same fix. */
|
|
35
|
+
private version;
|
|
36
|
+
private outcome;
|
|
37
|
+
private settle;
|
|
38
|
+
private settled;
|
|
39
|
+
constructor(runId?: string);
|
|
40
|
+
/** Pushes one event onto the run, numbering it the way a real run would. */
|
|
41
|
+
emit(event: AgentStreamEvent): AgentStreamFrame;
|
|
42
|
+
/** Ends the run. Everything parked on `frames()` or `result()` unblocks. */
|
|
43
|
+
finish(result?: Partial<AgentRunResult<ToolShapes, unknown>>): void;
|
|
44
|
+
frames(from?: number): AsyncIterable<AgentStreamFrame>;
|
|
45
|
+
[Symbol.asyncIterator](): AsyncGenerator<AgentStreamEvent, void, void>;
|
|
46
|
+
toResponse(params?: {
|
|
47
|
+
from?: number;
|
|
48
|
+
}): Response;
|
|
49
|
+
result(): Promise<AgentRunResult<ToolShapes, unknown>>;
|
|
50
|
+
stop(params?: {
|
|
51
|
+
reason?: string;
|
|
52
|
+
}): void;
|
|
53
|
+
private replay;
|
|
54
|
+
private notify;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=stubAgentRun.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stubAgentRun.d.ts","sourceRoot":"","sources":["../../../ai/store/stubAgentRun.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE/E;;;;;;;;;;GAUG;AACH,qBAAa,YAAa,YAAW,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAChE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,OAAO,UAAS;IAChB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,4EAA4E;IAC5E,aAAa,SAAK;IAElB,OAAO,CAAC,MAAM,CAA0B;IACxC;;;;;;;;OAQG;IACH,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,IAAI,CAAyB;IACrC;oBACgB;IAChB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,OAAO,CAAoD;IACnE,OAAO,CAAC,MAAM,CAAwE;IACtF,OAAO,CAAC,OAAO,CAA+C;gBAElD,KAAK,SAAa;IAO9B,4EAA4E;IAC5E,IAAI,CAAC,KAAK,EAAE,gBAAgB,GAAG,gBAAgB;IAO/C,4EAA4E;IAC5E,MAAM,CAAC,MAAM,GAAE,OAAO,CAAC,cAAc,CAAC,UAAU,EAAE,OAAO,CAAC,CAAM,GAAG,IAAI;IAavE,MAAM,CAAC,IAAI,SAAI,GAAG,aAAa,CAAC,gBAAgB,CAAC;IAK1C,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,cAAc,CAAC,gBAAgB,EAAE,IAAI,EAAE,IAAI,CAAC;IAM7E,UAAU,CAAC,MAAM,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,QAAQ;IAWpD,MAAM,IAAI,OAAO,CAAC,cAAc,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAItD,IAAI,CAAC,MAAM,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,IAAI;YAK7B,MAAM;IAmBrB,OAAO,CAAC,MAAM;CAQf"}
|