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.
Files changed (77) hide show
  1. package/dist/core/Agent.js +13 -4
  2. package/dist/core/Agent.js.map +1 -1
  3. package/dist/core/agent/stages/seed.js +38 -6
  4. package/dist/core/agent/stages/seed.js.map +1 -1
  5. package/dist/core/agent/stages/toolCalls.js.map +1 -1
  6. package/dist/core/tools.js.map +1 -1
  7. package/dist/esm/core/Agent.d.ts +13 -4
  8. package/dist/esm/core/Agent.js +13 -4
  9. package/dist/esm/core/Agent.js.map +1 -1
  10. package/dist/esm/core/agent/stages/seed.d.ts +11 -0
  11. package/dist/esm/core/agent/stages/seed.js +38 -6
  12. package/dist/esm/core/agent/stages/seed.js.map +1 -1
  13. package/dist/esm/core/agent/stages/toolCalls.d.ts +3 -2
  14. package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
  15. package/dist/esm/core/agent/types.d.ts +35 -4
  16. package/dist/esm/core/tools.d.ts +5 -2
  17. package/dist/esm/core/tools.js.map +1 -1
  18. package/dist/esm/hosting/browserSession.d.ts +97 -0
  19. package/dist/esm/hosting/browserSession.js +171 -0
  20. package/dist/esm/hosting/browserSession.js.map +1 -0
  21. package/dist/esm/hosting/httpHost.d.ts +14 -0
  22. package/dist/esm/hosting/httpHost.js +34 -6
  23. package/dist/esm/hosting/httpHost.js.map +1 -1
  24. package/dist/esm/hosting/index.d.ts +9 -3
  25. package/dist/esm/hosting/index.js +13 -1
  26. package/dist/esm/hosting/index.js.map +1 -1
  27. package/dist/esm/hosting/nodeHost.d.ts +73 -2
  28. package/dist/esm/hosting/nodeHost.js +110 -35
  29. package/dist/esm/hosting/nodeHost.js.map +1 -1
  30. package/dist/esm/hosting/standingAgent.d.ts +58 -14
  31. package/dist/esm/hosting/standingAgent.js +361 -88
  32. package/dist/esm/hosting/standingAgent.js.map +1 -1
  33. package/dist/esm/hosting/types.d.ts +112 -9
  34. package/dist/esm/hosting/types.js +3 -1
  35. package/dist/esm/hosting/types.js.map +1 -1
  36. package/dist/esm/index.d.ts +1 -0
  37. package/dist/esm/index.js +6 -0
  38. package/dist/esm/index.js.map +1 -1
  39. package/dist/hosting/browserSession.js +175 -0
  40. package/dist/hosting/browserSession.js.map +1 -0
  41. package/dist/hosting/httpHost.js +34 -6
  42. package/dist/hosting/httpHost.js.map +1 -1
  43. package/dist/hosting/index.js +16 -1
  44. package/dist/hosting/index.js.map +1 -1
  45. package/dist/hosting/nodeHost.js +112 -36
  46. package/dist/hosting/nodeHost.js.map +1 -1
  47. package/dist/hosting/standingAgent.js +361 -88
  48. package/dist/hosting/standingAgent.js.map +1 -1
  49. package/dist/hosting/types.js +4 -0
  50. package/dist/hosting/types.js.map +1 -1
  51. package/dist/index.js +10 -2
  52. package/dist/index.js.map +1 -1
  53. package/dist/types/core/Agent.d.ts +13 -4
  54. package/dist/types/core/Agent.d.ts.map +1 -1
  55. package/dist/types/core/agent/stages/seed.d.ts +11 -0
  56. package/dist/types/core/agent/stages/seed.d.ts.map +1 -1
  57. package/dist/types/core/agent/stages/toolCalls.d.ts +3 -2
  58. package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
  59. package/dist/types/core/agent/types.d.ts +35 -4
  60. package/dist/types/core/agent/types.d.ts.map +1 -1
  61. package/dist/types/core/tools.d.ts +5 -2
  62. package/dist/types/core/tools.d.ts.map +1 -1
  63. package/dist/types/hosting/browserSession.d.ts +98 -0
  64. package/dist/types/hosting/browserSession.d.ts.map +1 -0
  65. package/dist/types/hosting/httpHost.d.ts +14 -0
  66. package/dist/types/hosting/httpHost.d.ts.map +1 -1
  67. package/dist/types/hosting/index.d.ts +9 -3
  68. package/dist/types/hosting/index.d.ts.map +1 -1
  69. package/dist/types/hosting/nodeHost.d.ts +73 -2
  70. package/dist/types/hosting/nodeHost.d.ts.map +1 -1
  71. package/dist/types/hosting/standingAgent.d.ts +58 -14
  72. package/dist/types/hosting/standingAgent.d.ts.map +1 -1
  73. package/dist/types/hosting/types.d.ts +112 -9
  74. package/dist/types/hosting/types.d.ts.map +1 -1
  75. package/dist/types/index.d.ts +1 -0
  76. package/dist/types/index.d.ts.map +1 -1
  77. 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
- * This adapter's own JSON dialect: `{ input, sessionId? }` in, `{ output }`
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 const jsonWire = {
60
- readRequest(facts) {
61
- const input = typeof facts.body.input === 'string' ? facts.body.input : '';
62
- // sessionId is caller data whichever way it arrives; the body wins so a
63
- // caller that sets both is not surprised by which one the server preferred.
64
- const fromBody = typeof facts.body.sessionId === 'string' ? facts.body.sessionId : undefined;
65
- const sessionId = fromBody ?? facts.headers['x-session-id'];
66
- // Read as-is and never coerced: `decision` is a person's answer to whatever
67
- // a tool asked, and this dialect does not get to decide what that looks
68
- // like. Its PRESENCE is the whole signal.
69
- const decision = facts.body.decision;
70
- return {
71
- input,
72
- ...(sessionId !== undefined && { sessionId }),
73
- ...(decision !== undefined && { decision }),
74
- };
75
- },
76
- health: (uptimeMs) => ({ status: 'ok', uptimeMs }),
77
- output: (output) => ({ output }),
78
- failure: (error, code) => ({ error, ...(code !== undefined && { code }) }),
79
- chunk: (text) => ({ text }),
80
- awaiting: (pending) => ({ awaiting: pending }),
81
- readConversation(facts) {
82
- // A handshake has no body, so this dialect names the two places a session
83
- // id can be: the header a server-side caller sets, and the query a BROWSER
84
- // has to use because the WebSocket API gives it no way to set a header.
85
- // Header wins, so a caller that sets both is not surprised by which one the
86
- // server preferred — the same rule the request dialect uses for body-vs-header.
87
- const sessionId = headerValue(facts, 'x-session-id') ?? facts.query.get('sessionId') ?? undefined;
88
- return { ...(sessionId !== undefined && sessionId.length > 0 && { sessionId }) };
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: jsonWire,
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":"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"}
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, and this composer shares ONE
56
- * instance across every session. Two runs overlapping on it do not crash —
57
- * which is precisely the danger. They both finish, and the state the composer
58
- * reads afterwards belongs to whichever started last, so one session's envelope
59
- * can end up holding another session's conversation. Nothing in the recording
60
- * would say so. Runs are therefore serialized, globally, and that is a
61
- * correctness requirement rather than a tuning choice.
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. A request for a DIFFERENT session is never refused —
66
- * there is nothing wrong with it; it simply waits its turn.
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 one agent, with per-session conversation memory, on any
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 to the agent, and removes its
78
- * durability wiring.
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
- * @example
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>;