agentfootprint 9.9.0 → 9.10.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/core/Agent.js +13 -4
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/agent/stages/seed.js +38 -6
- package/dist/core/agent/stages/seed.js.map +1 -1
- package/dist/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/core/tools.js.map +1 -1
- package/dist/esm/core/Agent.d.ts +13 -4
- package/dist/esm/core/Agent.js +13 -4
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/core/agent/stages/seed.d.ts +11 -0
- package/dist/esm/core/agent/stages/seed.js +38 -6
- package/dist/esm/core/agent/stages/seed.js.map +1 -1
- package/dist/esm/core/agent/stages/toolCalls.d.ts +3 -2
- package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/esm/core/agent/types.d.ts +35 -4
- package/dist/esm/core/tools.d.ts +5 -2
- package/dist/esm/core/tools.js.map +1 -1
- package/dist/esm/hosting/browserSession.d.ts +97 -0
- package/dist/esm/hosting/browserSession.js +171 -0
- package/dist/esm/hosting/browserSession.js.map +1 -0
- package/dist/esm/hosting/httpHost.d.ts +14 -0
- package/dist/esm/hosting/httpHost.js +34 -6
- package/dist/esm/hosting/httpHost.js.map +1 -1
- package/dist/esm/hosting/index.d.ts +9 -3
- package/dist/esm/hosting/index.js +13 -1
- package/dist/esm/hosting/index.js.map +1 -1
- package/dist/esm/hosting/nodeHost.d.ts +73 -2
- package/dist/esm/hosting/nodeHost.js +110 -35
- package/dist/esm/hosting/nodeHost.js.map +1 -1
- package/dist/esm/hosting/standingAgent.d.ts +58 -14
- package/dist/esm/hosting/standingAgent.js +361 -88
- package/dist/esm/hosting/standingAgent.js.map +1 -1
- package/dist/esm/hosting/types.d.ts +112 -9
- package/dist/esm/hosting/types.js +3 -1
- package/dist/esm/hosting/types.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/hosting/browserSession.js +175 -0
- package/dist/hosting/browserSession.js.map +1 -0
- package/dist/hosting/httpHost.js +34 -6
- package/dist/hosting/httpHost.js.map +1 -1
- package/dist/hosting/index.js +16 -1
- package/dist/hosting/index.js.map +1 -1
- package/dist/hosting/nodeHost.js +112 -36
- package/dist/hosting/nodeHost.js.map +1 -1
- package/dist/hosting/standingAgent.js +361 -88
- package/dist/hosting/standingAgent.js.map +1 -1
- package/dist/hosting/types.js +4 -0
- package/dist/hosting/types.js.map +1 -1
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/types/core/Agent.d.ts +13 -4
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/core/agent/stages/seed.d.ts +11 -0
- package/dist/types/core/agent/stages/seed.d.ts.map +1 -1
- package/dist/types/core/agent/stages/toolCalls.d.ts +3 -2
- package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
- package/dist/types/core/agent/types.d.ts +35 -4
- package/dist/types/core/agent/types.d.ts.map +1 -1
- package/dist/types/core/tools.d.ts +5 -2
- package/dist/types/core/tools.d.ts.map +1 -1
- package/dist/types/hosting/browserSession.d.ts +98 -0
- package/dist/types/hosting/browserSession.d.ts.map +1 -0
- package/dist/types/hosting/httpHost.d.ts +14 -0
- package/dist/types/hosting/httpHost.d.ts.map +1 -1
- package/dist/types/hosting/index.d.ts +9 -3
- package/dist/types/hosting/index.d.ts.map +1 -1
- package/dist/types/hosting/nodeHost.d.ts +73 -2
- package/dist/types/hosting/nodeHost.d.ts.map +1 -1
- package/dist/types/hosting/standingAgent.d.ts +58 -14
- package/dist/types/hosting/standingAgent.d.ts.map +1 -1
- package/dist/types/hosting/types.d.ts +112 -9
- package/dist/types/hosting/types.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -48,46 +48,113 @@
|
|
|
48
48
|
*/
|
|
49
49
|
import { headerValue, httpHost, } from './httpHost.js';
|
|
50
50
|
const HOST_NAME = 'nodeHost';
|
|
51
|
+
/** The header this dialect reads a session id from when nobody says otherwise. */
|
|
52
|
+
export const DEFAULT_SESSION_HEADER = 'x-session-id';
|
|
51
53
|
/**
|
|
52
|
-
*
|
|
53
|
-
* out, `{ status: 'ok', uptimeMs }` on the health path
|
|
54
|
+
* Build this adapter's JSON dialect: `{ input, sessionId? }` in, `{ output }`
|
|
55
|
+
* out, `{ status: 'ok', uptimeMs }` on the health path — with the session
|
|
56
|
+
* plumbing a deployment gets to re-decide.
|
|
54
57
|
*
|
|
55
58
|
* Exported by name so a deployment that has to keep these exact bodies while
|
|
56
59
|
* changing something else about the host reuses them rather than retyping them
|
|
57
60
|
* and getting one field subtly wrong.
|
|
58
61
|
*/
|
|
59
|
-
export
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
62
|
+
export function jsonWireWith(options = {}) {
|
|
63
|
+
const sessionHeader = options.sessionHeader ?? DEFAULT_SESSION_HEADER;
|
|
64
|
+
const sessionCookie = options.sessionCookie;
|
|
65
|
+
return {
|
|
66
|
+
readRequest(facts) {
|
|
67
|
+
const input = typeof facts.body.input === 'string' ? facts.body.input : '';
|
|
68
|
+
// sessionId is caller data whichever way it arrives; the body wins so a
|
|
69
|
+
// caller that sets both is not surprised by which one the server preferred.
|
|
70
|
+
// The cookie is last, because a caller that named a session explicitly
|
|
71
|
+
// meant that one — the cookie is what the BROWSER remembered, and the
|
|
72
|
+
// explicit value is what this request said.
|
|
73
|
+
const fromBody = typeof facts.body.sessionId === 'string' ? facts.body.sessionId : undefined;
|
|
74
|
+
const fromHeader = headerValue(facts, sessionHeader);
|
|
75
|
+
const fromCookie = sessionCookie !== undefined ? cookieValue(facts.headers.cookie, sessionCookie) : undefined;
|
|
76
|
+
const carried = fromBody ?? fromHeader ?? fromCookie;
|
|
77
|
+
// Issued ONLY in cookie mode, and only when this request carried no
|
|
78
|
+
// session at all. A caller holding a session already has a conversation;
|
|
79
|
+
// handing it a second handle would split it in two.
|
|
80
|
+
const minted = sessionCookie !== undefined && carried === undefined ? mintSessionId() : undefined;
|
|
81
|
+
const sessionId = carried ?? minted;
|
|
82
|
+
// Read as-is and never coerced: `decision` is a person's answer to whatever
|
|
83
|
+
// a tool asked, and this dialect does not get to decide what that looks
|
|
84
|
+
// like. Its PRESENCE is the whole signal.
|
|
85
|
+
const decision = facts.body.decision;
|
|
86
|
+
return {
|
|
87
|
+
input,
|
|
88
|
+
...(sessionId !== undefined && { sessionId }),
|
|
89
|
+
...(decision !== undefined && { decision }),
|
|
90
|
+
...(minted !== undefined &&
|
|
91
|
+
sessionCookie !== undefined && {
|
|
92
|
+
responseHeaders: { 'set-cookie': sessionCookieHeader(sessionCookie, minted) },
|
|
93
|
+
}),
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
health: (uptimeMs) => ({ status: 'ok', uptimeMs }),
|
|
97
|
+
output: (output) => ({ output }),
|
|
98
|
+
failure: (error, code) => ({ error, ...(code !== undefined && { code }) }),
|
|
99
|
+
chunk: (text) => ({ text }),
|
|
100
|
+
awaiting: (pending) => ({ awaiting: pending }),
|
|
101
|
+
readConversation(facts) {
|
|
102
|
+
// A handshake has no body, so this dialect names the three places a
|
|
103
|
+
// session id can be: the header a server-side caller sets, the cookie the
|
|
104
|
+
// browser sends on its own (when cookie mode is on), and the query a
|
|
105
|
+
// BROWSER has to use because the WebSocket API gives it no way to set a
|
|
106
|
+
// header. Header wins, so a caller that sets both is not surprised by
|
|
107
|
+
// which one the server preferred — the same rule the request dialect uses
|
|
108
|
+
// for body-vs-header.
|
|
109
|
+
//
|
|
110
|
+
// Nothing is MINTED here, in either mode: the 101 this door writes is not
|
|
111
|
+
// a reply a wire composes, so a session issued on a handshake could never
|
|
112
|
+
// reach the client and would be a conversation nobody could resume.
|
|
113
|
+
const sessionId = headerValue(facts, sessionHeader) ??
|
|
114
|
+
(sessionCookie !== undefined
|
|
115
|
+
? cookieValue(facts.headers.cookie, sessionCookie)
|
|
116
|
+
: undefined) ??
|
|
117
|
+
facts.query.get('sessionId') ??
|
|
118
|
+
undefined;
|
|
119
|
+
return { ...(sessionId !== undefined && sessionId.length > 0 && { sessionId }) };
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* This adapter's own JSON dialect with its defaults: the session on
|
|
125
|
+
* `x-session-id`, no cookie.
|
|
126
|
+
*/
|
|
127
|
+
export const jsonWire = jsonWireWith();
|
|
128
|
+
/**
|
|
129
|
+
* One cookie's value out of a `Cookie` header, or `undefined`.
|
|
130
|
+
*
|
|
131
|
+
* Deliberately small: it splits on `;`, takes the first `=`, and trims. It does
|
|
132
|
+
* not decode, because a session id this library minted is URL-safe and a value
|
|
133
|
+
* somebody else's system minted is theirs to interpret — guessing at an
|
|
134
|
+
* encoding is how a session id silently becomes a different string.
|
|
135
|
+
*/
|
|
136
|
+
function cookieValue(header, name) {
|
|
137
|
+
if (header === undefined || header.length === 0)
|
|
138
|
+
return undefined;
|
|
139
|
+
for (const pair of header.split(';')) {
|
|
140
|
+
const at = pair.indexOf('=');
|
|
141
|
+
if (at < 0)
|
|
142
|
+
continue;
|
|
143
|
+
if (pair.slice(0, at).trim() !== name)
|
|
144
|
+
continue;
|
|
145
|
+
const value = pair.slice(at + 1).trim();
|
|
146
|
+
return value.length > 0 ? value : undefined;
|
|
147
|
+
}
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
/** The `Set-Cookie` this dialect issues. See {@link JsonWireOptions.sessionCookie}. */
|
|
151
|
+
function sessionCookieHeader(name, value) {
|
|
152
|
+
return `${name}=${value}; Path=/; HttpOnly; SameSite=Lax`;
|
|
153
|
+
}
|
|
154
|
+
/** A session id for a caller that carried none. Node 20+ has global `crypto`. */
|
|
155
|
+
function mintSessionId() {
|
|
156
|
+
return globalThis.crypto.randomUUID();
|
|
157
|
+
}
|
|
91
158
|
/**
|
|
92
159
|
* An HTTP host for one handler.
|
|
93
160
|
*
|
|
@@ -101,9 +168,17 @@ export const jsonWire = {
|
|
|
101
168
|
* await handle.close();
|
|
102
169
|
*/
|
|
103
170
|
export function nodeHost(options = {}) {
|
|
171
|
+
// The shared default instance when nothing about sessions was re-decided, so
|
|
172
|
+
// a host built with no options is the same object graph it always was.
|
|
173
|
+
const wire = options.sessionHeader === undefined && options.sessionCookie === undefined
|
|
174
|
+
? jsonWire
|
|
175
|
+
: jsonWireWith({
|
|
176
|
+
...(options.sessionHeader !== undefined && { sessionHeader: options.sessionHeader }),
|
|
177
|
+
...(options.sessionCookie !== undefined && { sessionCookie: options.sessionCookie }),
|
|
178
|
+
});
|
|
104
179
|
return httpHost({
|
|
105
180
|
name: HOST_NAME,
|
|
106
|
-
wire
|
|
181
|
+
wire,
|
|
107
182
|
// Chosen, not inherited — see the test in this folder that greps for one
|
|
108
183
|
// particular runtime's container-contract path literal.
|
|
109
184
|
invokePath: options.invokePath ?? '/invoke',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodeHost.js","sourceRoot":"","sources":["../../../src/hosting/nodeHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,OAAO,EACL,WAAW,EACX,QAAQ,GAIT,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"nodeHost.js","sourceRoot":"","sources":["../../../src/hosting/nodeHost.ts"],"names":[],"mappings":"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"}
|
|
@@ -51,33 +51,69 @@
|
|
|
51
51
|
* Resuming a PAUSED run is different in kind: it is not a replay at all — the
|
|
52
52
|
* engine continues from its own checkpoint, and no earlier tool call re-runs.
|
|
53
53
|
*
|
|
54
|
-
* ── Why one run at a time
|
|
55
|
-
* An Agent instance holds per-run state on itself
|
|
56
|
-
* instance
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
54
|
+
* ── Why one run at a time, and where that bound actually is ─────────────────
|
|
55
|
+
* An Agent instance holds per-run state on itself. Two runs overlapping on ONE
|
|
56
|
+
* instance do not crash — which is precisely the danger. They both finish, and
|
|
57
|
+
* the state the composer reads afterwards belongs to whichever started last, so
|
|
58
|
+
* one session's envelope can end up holding another session's conversation.
|
|
59
|
+
* Nothing in the recording would say so. **Runs on one instance are therefore
|
|
60
|
+
* serialized, and that is a correctness requirement rather than a tuning
|
|
61
|
+
* choice.**
|
|
62
|
+
*
|
|
63
|
+
* What CHANGED in 9.10.0 is not that rule but its scope. `{ agent }` hands this
|
|
64
|
+
* composer one instance to share, so the serialization is global — the shape
|
|
65
|
+
* every earlier release had, unchanged to the byte. `{ agentFactory }` hands it
|
|
66
|
+
* a way to MAKE instances, so each session gets its own and the same rule binds
|
|
67
|
+
* per session: sessions run in parallel, each session's turns queue behind that
|
|
68
|
+
* session's own instance.
|
|
69
|
+
*
|
|
70
|
+
* standingAgent({ agent }) → one instance, global queue
|
|
71
|
+
* standingAgent({ agentFactory }) → one per session, a queue each
|
|
72
|
+
*
|
|
73
|
+
* Everything else is identical between the two: the same stores, the same
|
|
74
|
+
* durability modes, the same pause/resume contract, the same refusals. The
|
|
75
|
+
* session store is keyed per session already, which is why the pool can evict a
|
|
76
|
+
* session's instance without the person on the other end noticing — the
|
|
77
|
+
* CONVERSATION was never in the instance.
|
|
62
78
|
*
|
|
63
79
|
* `onConcurrentInvoke` is the separate question of what to do when a second
|
|
64
80
|
* turn of the SAME conversation arrives while the first is running: refuse it
|
|
65
|
-
* (default) or queue it.
|
|
66
|
-
* there is nothing wrong with it;
|
|
81
|
+
* (default) or queue it. It means the same thing in both shapes. A request for
|
|
82
|
+
* a DIFFERENT session is never refused — there is nothing wrong with it; in the
|
|
83
|
+
* shared shape it waits its turn, and in the pooled shape it does not have to.
|
|
84
|
+
*
|
|
85
|
+
* ── The pool, in one paragraph ──────────────────────────────────────────────
|
|
86
|
+
* One instance per ACTIVE session, built on first sight by the factory, bounded
|
|
87
|
+
* by `maxActiveSessions` (default 100) and evicted least-recently-used. An
|
|
88
|
+
* evicted session's agent has its tool sessions closed with reason `'evicted'`
|
|
89
|
+
* — the 9.7.0 vocabulary, not a new one — and is then shut down; its
|
|
90
|
+
* conversation stays in the store, so its next request re-hydrates onto a fresh
|
|
91
|
+
* instance and nothing was lost. A session that is RUNNING is never evicted:
|
|
92
|
+
* the bound is on retained idle instances, and a cache policy does not get to
|
|
93
|
+
* end somebody's turn. Requests with NO session share one fallback instance,
|
|
94
|
+
* because there is no conversation to isolate and an instance per anonymous
|
|
95
|
+
* request would be an instance per request.
|
|
67
96
|
*
|
|
68
97
|
* Pattern: Composition root. It owns wiring and ordering; it invents no
|
|
69
98
|
* mechanism of its own.
|
|
70
99
|
*/
|
|
71
100
|
import type { HostHandle, StandingAgentOptions } from './types.js';
|
|
72
101
|
/**
|
|
73
|
-
* Serve
|
|
102
|
+
* Serve an agent, with per-session conversation memory, on any
|
|
74
103
|
* {@link AgentHost}.
|
|
75
104
|
*
|
|
76
105
|
* Resolves once the host is live. Closing the returned handle closes the host,
|
|
77
|
-
* detaches the listeners this composer added
|
|
78
|
-
*
|
|
106
|
+
* detaches the listeners this composer added, and removes its durability
|
|
107
|
+
* wiring.
|
|
108
|
+
*
|
|
109
|
+
* Two shapes, one composer — see the file header for the full comparison:
|
|
79
110
|
*
|
|
80
|
-
*
|
|
111
|
+
* - `{ agent }` — one instance for every session, runs serialized globally.
|
|
112
|
+
* Correct, and unchanged from every earlier release.
|
|
113
|
+
* - `{ agentFactory }` — one instance per active session, sessions in
|
|
114
|
+
* PARALLEL, each session's turns serialized on its own instance.
|
|
115
|
+
*
|
|
116
|
+
* @example One agent, every session
|
|
81
117
|
* const handle = await standingAgent({
|
|
82
118
|
* agent,
|
|
83
119
|
* sessions: memorySessions(),
|
|
@@ -86,5 +122,13 @@ import type { HostHandle, StandingAgentOptions } from './types.js';
|
|
|
86
122
|
* durability: 'sync',
|
|
87
123
|
* });
|
|
88
124
|
* process.on('SIGTERM', () => void handle.close());
|
|
125
|
+
*
|
|
126
|
+
* @example One agent per session — they answer at the same time
|
|
127
|
+
* const handle = await standingAgent({
|
|
128
|
+
* agentFactory: () => Agent.create({ provider, model }).system('…').build(),
|
|
129
|
+
* sessions: sqliteSessions({ file: './sessions.db' }),
|
|
130
|
+
* host: nodeHost({ port: 8080 }),
|
|
131
|
+
* maxActiveSessions: 200,
|
|
132
|
+
* });
|
|
89
133
|
*/
|
|
90
134
|
export declare function standingAgent<TH extends HostHandle>(options: StandingAgentOptions<TH>): Promise<TH>;
|