@arnilo/prism 0.0.96 → 0.1.1
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/CHANGELOG.md +290 -2
- package/README.md +17 -3
- package/dist/agent-definitions.js +2 -3
- package/dist/agent-event-source.d.ts +11 -0
- package/dist/agent-event-source.js +512 -0
- package/dist/agent-loops.d.ts +5 -0
- package/dist/agent-loops.js +99 -14
- package/dist/agent-run-lifecycle.d.ts +5 -2
- package/dist/agent-run-lifecycle.js +18 -2
- package/dist/agent-run-state.d.ts +27 -1
- package/dist/agent-run-state.js +113 -7
- package/dist/agents.d.ts +3 -1
- package/dist/agents.js +1255 -129
- package/dist/artifacts.d.ts +132 -0
- package/dist/artifacts.js +44 -0
- package/dist/cache-helpers.js +18 -9
- package/dist/checkpoints.d.ts +4 -0
- package/dist/checkpoints.js +17 -9
- package/dist/cli-init.js +3 -7
- package/dist/cli-runner.d.ts +2 -6
- package/dist/cli-runner.js +71 -33
- package/dist/compaction.js +5 -4
- package/dist/config.js +7 -4
- package/dist/content.js +26 -24
- package/dist/context-budget.d.ts +67 -0
- package/dist/context-budget.js +288 -0
- package/dist/contracts.d.ts +590 -8
- package/dist/contracts.js +142 -1
- package/dist/contribution-parsing.js +6 -2
- package/dist/contributions.d.ts +2 -0
- package/dist/contributions.js +3 -0
- package/dist/conversations.d.ts +50 -0
- package/dist/conversations.js +98 -0
- package/dist/credentials.d.ts +22 -2
- package/dist/credentials.js +18 -3
- package/dist/devices.d.ts +94 -0
- package/dist/devices.js +138 -0
- package/dist/event-multiplexer.js +18 -4
- package/dist/extensions.d.ts +18 -1
- package/dist/extensions.js +79 -6
- package/dist/feedback.js +12 -10
- package/dist/guardrails.d.ts +1 -1
- package/dist/guardrails.js +26 -17
- package/dist/identity.d.ts +92 -0
- package/dist/identity.js +265 -0
- package/dist/index.d.ts +94 -72
- package/dist/index.js +48 -36
- package/dist/input.d.ts +10 -1
- package/dist/input.js +152 -52
- package/dist/instruction-injection.d.ts +1 -1
- package/dist/middleware.js +9 -1
- package/dist/models.d.ts +2 -0
- package/dist/models.js +3 -0
- package/dist/node/agent-definitions.js +16 -8
- package/dist/node/contribution-discovery.d.ts +1 -2
- package/dist/node/contribution-discovery.js +3 -3
- package/dist/node/session-store-jsonl.js +13 -7
- package/dist/node/settings.d.ts +1 -1
- package/dist/node/settings.js +1 -1
- package/dist/node/system-project-prompts.js +2 -4
- package/dist/node/trust.js +1 -1
- package/dist/persistence-lifecycle.d.ts +103 -0
- package/dist/persistence-lifecycle.js +202 -0
- package/dist/provider-events.d.ts +1 -0
- package/dist/provider-events.js +6 -1
- package/dist/provider-request-policy.js +3 -4
- package/dist/providers/media.d.ts +1 -1
- package/dist/providers/openai-compatible.d.ts +46 -1
- package/dist/providers/openai-compatible.js +123 -53
- package/dist/providers/openai-primitives.js +10 -7
- package/dist/providers/transport.d.ts +6 -0
- package/dist/providers/transport.js +21 -0
- package/dist/providers.d.ts +2 -0
- package/dist/providers.js +3 -0
- package/dist/redaction.d.ts +1 -0
- package/dist/redaction.js +26 -9
- package/dist/resources.d.ts +2 -2
- package/dist/resources.js +2 -2
- package/dist/retry.d.ts +5 -0
- package/dist/retry.js +8 -1
- package/dist/rpc.js +55 -11
- package/dist/run-ledger.d.ts +6 -0
- package/dist/run-ledger.js +16 -13
- package/dist/run-limits.js +49 -10
- package/dist/secure-agent.js +8 -2
- package/dist/security.js +7 -2
- package/dist/session-stores.d.ts +7 -2
- package/dist/session-stores.js +195 -21
- package/dist/skill-disclosure.d.ts +35 -0
- package/dist/skill-disclosure.js +101 -0
- package/dist/skill-load.d.ts +25 -0
- package/dist/skill-load.js +112 -0
- package/dist/structured-output.d.ts +5 -1
- package/dist/structured-output.js +20 -2
- package/dist/system-prompts.js +7 -2
- package/dist/testing/agent-event-source-conformance.d.ts +4 -0
- package/dist/testing/agent-event-source-conformance.js +54 -0
- package/dist/testing/compaction-conformance.js +5 -1
- package/dist/testing/extension-conformance.js +15 -3
- package/dist/testing/feedback.d.ts +1 -3
- package/dist/testing/feedback.js +1 -1
- package/dist/testing/persistence-schema.d.ts +2 -2
- package/dist/testing/persistence-schema.js +280 -35
- package/dist/testing/provider-conformance.js +3 -3
- package/dist/testing/run-ledger-conformance.js +1 -1
- package/dist/testing/session-store-conformance.d.ts +6 -0
- package/dist/testing/session-store-conformance.js +37 -2
- package/dist/testing/tool-conformance.js +30 -5
- package/dist/testing/tool-effect-store-conformance.d.ts +9 -0
- package/dist/testing/tool-effect-store-conformance.js +85 -0
- package/dist/thinking.js +4 -1
- package/dist/tool-effects.d.ts +15 -0
- package/dist/tool-effects.js +352 -0
- package/dist/tool-result-fold.d.ts +40 -0
- package/dist/tool-result-fold.js +176 -0
- package/dist/tools.d.ts +8 -3
- package/dist/tools.js +248 -13
- package/docs/0.1.0-readiness.md +215 -0
- package/docs/a2a.md +33 -2
- package/docs/acp.md +152 -0
- package/docs/ag-ui-adoption.md +77 -0
- package/docs/ag-ui.md +225 -0
- package/docs/agent-events.md +34 -3
- package/docs/agent-identity.md +144 -0
- package/docs/agent-loops.md +17 -2
- package/docs/agent-session-runtime.md +21 -4
- package/docs/browser-automation.md +5 -0
- package/docs/caveman.md +129 -0
- package/docs/cli-rpc.md +3 -6
- package/docs/coding-agent-tools.md +229 -25
- package/docs/coding-security.md +77 -11
- package/docs/compaction-and-retry.md +5 -2
- package/docs/compaction-llm.md +20 -1
- package/docs/compaction-observational-memory.md +52 -8
- package/docs/context-and-skills.md +94 -7
- package/docs/contribution-registries.md +1 -0
- package/docs/conversations.md +135 -0
- package/docs/credential-storage.md +34 -1
- package/docs/credentials-and-redaction.md +11 -1
- package/docs/database-persistence.md +27 -7
- package/docs/device-adapters.md +97 -0
- package/docs/enterprise-postgres-state.md +178 -0
- package/docs/evaluations.md +14 -1
- package/docs/extensions.md +4 -1
- package/docs/forge-integration.md +113 -0
- package/docs/guardrails.md +16 -2
- package/docs/host-security.md +35 -4
- package/docs/index.md +69 -37
- package/docs/input-and-prompt-assembly.md +8 -7
- package/docs/language-intelligence.md +162 -0
- package/docs/mcp-tools.md +62 -5
- package/docs/middleware-hooks.md +2 -2
- package/docs/migration.md +427 -2
- package/docs/model-routing.md +111 -0
- package/docs/multimodal-content.md +8 -5
- package/docs/node-jsonl-session-store.md +1 -1
- package/docs/observability.md +2 -0
- package/docs/openapi-tools.md +56 -0
- package/docs/performance.md +282 -0
- package/docs/policy-and-audit.md +171 -0
- package/docs/ponytail.md +127 -0
- package/docs/postgres-persistence.md +8 -4
- package/docs/process-sessions.md +147 -0
- package/docs/provider-caching.md +13 -1
- package/docs/provider-conformance.md +29 -5
- package/docs/provider-packages.md +43 -2
- package/docs/provider-request-policies.md +2 -0
- package/docs/providers/ai-sdk.md +24 -7
- package/docs/providers/alibaba.md +179 -0
- package/docs/providers/anthropic.md +93 -0
- package/docs/providers/azure.md +74 -0
- package/docs/providers/bedrock.md +72 -0
- package/docs/providers/google.md +89 -0
- package/docs/providers/ollama.md +166 -0
- package/docs/providers/openai-compatible.md +31 -2
- package/docs/providers/openai.md +24 -5
- package/docs/providers/openrouter.md +2 -0
- package/docs/providers/vertex.md +71 -0
- package/docs/public-contracts.md +68 -4
- package/docs/rag.md +41 -12
- package/docs/release-and-install.md +362 -208
- package/docs/resource-loading.md +3 -0
- package/docs/runs-and-usage.md +3 -0
- package/docs/server.md +44 -6
- package/docs/session-store-conformance.md +2 -0
- package/docs/session-stores.md +41 -2
- package/docs/sqlite-persistence.md +11 -3
- package/docs/structured-output.md +7 -1
- package/docs/supervisors.md +8 -0
- package/docs/tool-effects.md +95 -0
- package/docs/tools.md +5 -0
- package/docs/work-artifacts-and-review.md +102 -0
- package/docs/work-connectors.md +32 -0
- package/docs/work-tools.md +137 -0
- package/docs/workflows.md +6 -0
- package/docs/working-and-semantic-memory.md +40 -7
- package/package.json +30 -7
- package/templates/init/providers.json +22 -0
- package/docs/review-coverage-2026-07-14.md +0 -260
- package/docs/review-coverage-2026-07-15.md +0 -193
- package/docs/review-coverage-2026-07-17-provider-validation.md +0 -192
- package/docs/review-coverage-2026-07-19-phase-3.md +0 -174
- package/docs/review-coverage-2026-07-20-phase-4.md +0 -175
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
+
export class AgentEventSourceError extends Error {
|
|
3
|
+
code;
|
|
4
|
+
constructor(code, message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.code = code;
|
|
7
|
+
this.name = "AgentEventSourceError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
const DEFAULT_MAX_EVENT_BYTES = 64 * 1024;
|
|
11
|
+
const HARD_MAX_EVENT_BYTES = 1024 * 1024;
|
|
12
|
+
const DEFAULT_MAX_PAGE_SIZE = 100;
|
|
13
|
+
const HARD_MAX_PAGE_SIZE = 500;
|
|
14
|
+
const DEFAULT_MAX_CURSOR_BYTES = 4 * 1024;
|
|
15
|
+
const HARD_MAX_CURSOR_BYTES = 16 * 1024;
|
|
16
|
+
const DEFAULT_MAX_QUEUED_EVENTS = 128;
|
|
17
|
+
const HARD_MAX_QUEUED_EVENTS = 4096;
|
|
18
|
+
const DEFAULT_MAX_SUBSCRIBERS = 256;
|
|
19
|
+
const HARD_MAX_SUBSCRIBERS = 4096;
|
|
20
|
+
const DEFAULT_POLL_INTERVAL_MS = 1000;
|
|
21
|
+
const HARD_POLL_INTERVAL_MS = 30_000;
|
|
22
|
+
const DEFAULT_RECONNECT_INITIAL_MS = 100;
|
|
23
|
+
const HARD_RECONNECT_INITIAL_MS = 5000;
|
|
24
|
+
const DEFAULT_RECONNECT_MAX_MS = 5000;
|
|
25
|
+
const HARD_RECONNECT_MAX_MS = 30_000;
|
|
26
|
+
const DEFAULT_MAX_RETAINED_EVENTS_PER_RUN = 10_000;
|
|
27
|
+
const HARD_MAX_RETAINED_EVENTS_PER_RUN = 100_000;
|
|
28
|
+
const DEFAULT_MAX_RETENTION_AGE_MS = 30 * 24 * 60 * 60 * 1000;
|
|
29
|
+
const HARD_MAX_RETENTION_AGE_MS = 365 * 24 * 60 * 60 * 1000;
|
|
30
|
+
const DEFAULT_CLEANUP_BATCH = 100;
|
|
31
|
+
const HARD_CLEANUP_BATCH = 500;
|
|
32
|
+
/**
|
|
33
|
+
* In-process, non-production reference implementation. It has no cross-process
|
|
34
|
+
* wakeup or persistence; use a database-backed source for distributed delivery.
|
|
35
|
+
*/
|
|
36
|
+
export function createMemoryAgentEventSource(options = {}) {
|
|
37
|
+
const limits = resolveOptions(options);
|
|
38
|
+
const streams = new Map();
|
|
39
|
+
const byId = new Map();
|
|
40
|
+
const subscribers = new Set();
|
|
41
|
+
const secret = randomBytes(32);
|
|
42
|
+
let closed = false;
|
|
43
|
+
const source = {
|
|
44
|
+
async append(input) {
|
|
45
|
+
assertOpen();
|
|
46
|
+
const record = normalizeRecord(input, limits.maxEventBytes);
|
|
47
|
+
const existing = byId.get(record.id);
|
|
48
|
+
if (existing) {
|
|
49
|
+
if (sameRecord(existing, record))
|
|
50
|
+
return clone(existing);
|
|
51
|
+
throw inputError();
|
|
52
|
+
}
|
|
53
|
+
const stream = getStream(record, true);
|
|
54
|
+
const sequence = record.sequence ?? stream.nextSequence;
|
|
55
|
+
if (!Number.isSafeInteger(sequence) || sequence < 1 || sequence !== stream.nextSequence)
|
|
56
|
+
throw inputError();
|
|
57
|
+
const stored = { ...record, sequence };
|
|
58
|
+
assertCursorFits(stored);
|
|
59
|
+
stream.records.set(stored.sequence, stored);
|
|
60
|
+
stream.nextSequence += 1;
|
|
61
|
+
byId.set(stored.id, stored);
|
|
62
|
+
while (stream.records.size > limits.maxRetainedEventsPerRun)
|
|
63
|
+
drop(stream, stream.records.values().next().value);
|
|
64
|
+
for (const subscriber of subscribers)
|
|
65
|
+
subscriber.notify();
|
|
66
|
+
return clone(stored);
|
|
67
|
+
},
|
|
68
|
+
async page(input) {
|
|
69
|
+
assertOpen();
|
|
70
|
+
throwIfAborted(input.signal);
|
|
71
|
+
const read = normalizeRead(input, limits.maxPageSize);
|
|
72
|
+
const stream = getStream({ ...read.ownership, sessionId: read.sessionId, runId: read.runId }, false);
|
|
73
|
+
const records = orderedRecords(stream);
|
|
74
|
+
const after = read.after === undefined ? undefined : decodeCursor(read.after, read, stream, records);
|
|
75
|
+
const start = after === undefined ? 0 : positionAfter(records, after, stream);
|
|
76
|
+
const selected = records.slice(start, start + read.limit);
|
|
77
|
+
const hasMore = start + selected.length < records.length;
|
|
78
|
+
return {
|
|
79
|
+
items: selected.map((record) => ({ record: clone(record), cursor: encodeCursor(record) })),
|
|
80
|
+
...(hasMore && selected.length > 0 ? { nextCursor: encodeCursor(selected.at(-1)) } : {}),
|
|
81
|
+
terminal: !hasMore && terminalAt(records, after),
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
subscribe(input) {
|
|
85
|
+
return handoffAgentEvents((after) => source.page({ ...input, ...(after === undefined ? {} : { after }) }), () => createWake(input.signal), input.after, limits.maxRetainedEventsPerRun);
|
|
86
|
+
},
|
|
87
|
+
async cleanup(input) {
|
|
88
|
+
assertOpen();
|
|
89
|
+
throwIfAborted(input.signal);
|
|
90
|
+
const ownership = normalizeOwnership(input.ownership);
|
|
91
|
+
const before = parseTimestamp(input.before, "input");
|
|
92
|
+
const limit = bounded(input.limit, DEFAULT_CLEANUP_BATCH, HARD_CLEANUP_BATCH, "cleanup limit");
|
|
93
|
+
const matches = [];
|
|
94
|
+
for (const stream of streams.values()) {
|
|
95
|
+
if (!sameOwnership(ownership, stream.ownership))
|
|
96
|
+
continue;
|
|
97
|
+
for (const record of stream.records.values()) {
|
|
98
|
+
if (Date.parse(record.timestamp) < before)
|
|
99
|
+
matches.push({ stream, record });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
matches.sort((a, b) => a.record.timestamp.localeCompare(b.record.timestamp) ||
|
|
103
|
+
a.record.sequence - b.record.sequence ||
|
|
104
|
+
a.record.id.localeCompare(b.record.id));
|
|
105
|
+
for (const { stream, record } of matches.slice(0, limit))
|
|
106
|
+
drop(stream, record);
|
|
107
|
+
return { deleted: Math.min(matches.length, limit) };
|
|
108
|
+
},
|
|
109
|
+
close() {
|
|
110
|
+
if (closed)
|
|
111
|
+
return;
|
|
112
|
+
closed = true;
|
|
113
|
+
for (const subscriber of subscribers)
|
|
114
|
+
subscriber.fail(closedError());
|
|
115
|
+
subscribers.clear();
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
return source;
|
|
119
|
+
function getStream(input, create) {
|
|
120
|
+
const ownership = normalizeOwnership(input);
|
|
121
|
+
const sessionId = requiredText(input.sessionId);
|
|
122
|
+
const runId = requiredText(input.runId);
|
|
123
|
+
const key = streamKey(ownership, sessionId, runId);
|
|
124
|
+
let stream = streams.get(key);
|
|
125
|
+
if (!stream && create) {
|
|
126
|
+
stream = { ownership, sessionId, runId, records: new Map(), nextSequence: 1 };
|
|
127
|
+
streams.set(key, stream);
|
|
128
|
+
}
|
|
129
|
+
return stream;
|
|
130
|
+
}
|
|
131
|
+
function encodeCursor(record) {
|
|
132
|
+
const payload = {
|
|
133
|
+
v: 1,
|
|
134
|
+
tenantId: record.tenantId,
|
|
135
|
+
...(record.accountId === undefined ? {} : { accountId: record.accountId }),
|
|
136
|
+
...(record.userId === undefined ? {} : { userId: record.userId }),
|
|
137
|
+
sessionId: record.sessionId,
|
|
138
|
+
runId: record.runId,
|
|
139
|
+
sequence: record.sequence,
|
|
140
|
+
id: record.id,
|
|
141
|
+
};
|
|
142
|
+
const body = Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
|
|
143
|
+
const signature = createHmac("sha256", secret).update(body).digest("base64url");
|
|
144
|
+
const encoded = `${body}.${signature}`;
|
|
145
|
+
if (Buffer.byteLength(encoded, "utf8") > limits.maxCursorBytes)
|
|
146
|
+
throw inputError();
|
|
147
|
+
return encoded;
|
|
148
|
+
}
|
|
149
|
+
function decodeCursor(encoded, input, stream, records) {
|
|
150
|
+
if (typeof encoded !== "string" || encoded.length === 0 || Buffer.byteLength(encoded, "utf8") > limits.maxCursorBytes) {
|
|
151
|
+
throw cursorError();
|
|
152
|
+
}
|
|
153
|
+
const [body, signature, extra] = encoded.split(".");
|
|
154
|
+
if (!body || !signature || extra !== undefined)
|
|
155
|
+
throw cursorError();
|
|
156
|
+
const expected = createHmac("sha256", secret).update(body).digest();
|
|
157
|
+
let actual;
|
|
158
|
+
try {
|
|
159
|
+
actual = Buffer.from(signature, "base64url");
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
throw cursorError();
|
|
163
|
+
}
|
|
164
|
+
if (actual.length !== expected.length || !timingSafeEqual(actual, expected))
|
|
165
|
+
throw cursorError();
|
|
166
|
+
let value;
|
|
167
|
+
try {
|
|
168
|
+
value = JSON.parse(Buffer.from(body, "base64url").toString("utf8"));
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
throw cursorError();
|
|
172
|
+
}
|
|
173
|
+
if (!validCursor(value))
|
|
174
|
+
throw cursorError();
|
|
175
|
+
const cursor = value;
|
|
176
|
+
if (!sameOwnership(input.ownership, cursor) || cursor.sessionId !== input.sessionId || cursor.runId !== input.runId) {
|
|
177
|
+
throw cursorError();
|
|
178
|
+
}
|
|
179
|
+
if (!stream || positionOf(records, cursor) < 0)
|
|
180
|
+
throw retentionError();
|
|
181
|
+
return cursor;
|
|
182
|
+
}
|
|
183
|
+
function assertCursorFits(record) {
|
|
184
|
+
void encodeCursor(record);
|
|
185
|
+
}
|
|
186
|
+
function createWake(signal) {
|
|
187
|
+
throwIfAborted(signal);
|
|
188
|
+
if (subscribers.size >= limits.maxSubscribers)
|
|
189
|
+
throw overflowError();
|
|
190
|
+
const subscription = new WakeSubscription(limits.maxQueuedEvents, signal, () => subscribers.delete(subscription));
|
|
191
|
+
subscribers.add(subscription);
|
|
192
|
+
return subscription;
|
|
193
|
+
}
|
|
194
|
+
function drop(stream, record) {
|
|
195
|
+
if (stream.records.get(record.sequence) === record)
|
|
196
|
+
stream.records.delete(record.sequence);
|
|
197
|
+
if (byId.get(record.id) === record)
|
|
198
|
+
byId.delete(record.id);
|
|
199
|
+
}
|
|
200
|
+
function assertOpen() {
|
|
201
|
+
if (closed)
|
|
202
|
+
throw closedError();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/** Shared replay/live loop. Its live iterator is started before every page, closing the query-to-wait race. */
|
|
206
|
+
function handoffAgentEvents(page, createWake, initialAfter, maxSeen) {
|
|
207
|
+
return {
|
|
208
|
+
[Symbol.asyncIterator]() {
|
|
209
|
+
const wake = createWake();
|
|
210
|
+
const iterator = run(wake);
|
|
211
|
+
return {
|
|
212
|
+
next: () => iterator.next(),
|
|
213
|
+
async return() {
|
|
214
|
+
await wake.return?.();
|
|
215
|
+
return (await iterator.return?.(undefined)) ?? { value: undefined, done: true };
|
|
216
|
+
},
|
|
217
|
+
async throw(error) {
|
|
218
|
+
await wake.return?.();
|
|
219
|
+
if (iterator.throw)
|
|
220
|
+
return iterator.throw(error);
|
|
221
|
+
throw error;
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
async function* run(wake) {
|
|
227
|
+
const seen = new Set();
|
|
228
|
+
let after = initialAfter;
|
|
229
|
+
let waiting = nextWake(wake); // Register live delivery before replaying durable rows.
|
|
230
|
+
try {
|
|
231
|
+
while (true) {
|
|
232
|
+
const replay = await page(after);
|
|
233
|
+
for (const item of replay.items) {
|
|
234
|
+
after = item.cursor;
|
|
235
|
+
if (seen.has(item.record.id))
|
|
236
|
+
continue;
|
|
237
|
+
seen.add(item.record.id);
|
|
238
|
+
if (seen.size > maxSeen)
|
|
239
|
+
seen.delete(seen.values().next().value);
|
|
240
|
+
yield item;
|
|
241
|
+
}
|
|
242
|
+
if (replay.terminal)
|
|
243
|
+
return;
|
|
244
|
+
const next = await waiting;
|
|
245
|
+
if (next.done)
|
|
246
|
+
return;
|
|
247
|
+
waiting = nextWake(wake);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
finally {
|
|
251
|
+
await wake.return?.();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function nextWake(wake) {
|
|
256
|
+
const next = wake.next();
|
|
257
|
+
// A slow subscriber can overflow while replay items are yielded. Keep the
|
|
258
|
+
// rejection observed until the generator reaches its next wait.
|
|
259
|
+
void next.catch(() => undefined);
|
|
260
|
+
return next;
|
|
261
|
+
}
|
|
262
|
+
class WakeSubscription {
|
|
263
|
+
maxQueuedEvents;
|
|
264
|
+
onFinish;
|
|
265
|
+
queued = 0;
|
|
266
|
+
waiting;
|
|
267
|
+
failure;
|
|
268
|
+
done = false;
|
|
269
|
+
abort;
|
|
270
|
+
constructor(maxQueuedEvents, signal, onFinish) {
|
|
271
|
+
this.maxQueuedEvents = maxQueuedEvents;
|
|
272
|
+
this.onFinish = onFinish;
|
|
273
|
+
this.abort = () => this.fail(signal?.reason ?? new DOMException("Aborted", "AbortError"));
|
|
274
|
+
if (signal?.aborted)
|
|
275
|
+
this.abort();
|
|
276
|
+
else
|
|
277
|
+
signal?.addEventListener("abort", this.abort, { once: true });
|
|
278
|
+
}
|
|
279
|
+
notify() {
|
|
280
|
+
if (this.done)
|
|
281
|
+
return;
|
|
282
|
+
if (this.waiting) {
|
|
283
|
+
const { resolve } = this.waiting;
|
|
284
|
+
this.waiting = undefined;
|
|
285
|
+
resolve({ value: undefined, done: false });
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (this.queued >= this.maxQueuedEvents) {
|
|
289
|
+
this.fail(overflowError());
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
this.queued += 1;
|
|
293
|
+
}
|
|
294
|
+
fail(error) {
|
|
295
|
+
if (this.done)
|
|
296
|
+
return;
|
|
297
|
+
this.failure = error;
|
|
298
|
+
this.finish();
|
|
299
|
+
}
|
|
300
|
+
async next() {
|
|
301
|
+
if (this.failure !== undefined)
|
|
302
|
+
throw this.failure;
|
|
303
|
+
if (this.done)
|
|
304
|
+
return { value: undefined, done: true };
|
|
305
|
+
if (this.queued > 0) {
|
|
306
|
+
this.queued -= 1;
|
|
307
|
+
return { value: undefined, done: false };
|
|
308
|
+
}
|
|
309
|
+
return new Promise((resolve, reject) => {
|
|
310
|
+
this.waiting = { resolve, reject };
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
async return() {
|
|
314
|
+
this.finish();
|
|
315
|
+
return { value: undefined, done: true };
|
|
316
|
+
}
|
|
317
|
+
finish() {
|
|
318
|
+
if (this.done)
|
|
319
|
+
return;
|
|
320
|
+
this.done = true;
|
|
321
|
+
this.onFinish();
|
|
322
|
+
const waiting = this.waiting;
|
|
323
|
+
this.waiting = undefined;
|
|
324
|
+
if (this.failure !== undefined)
|
|
325
|
+
waiting?.reject(this.failure);
|
|
326
|
+
else
|
|
327
|
+
waiting?.resolve({ value: undefined, done: true });
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
function resolveOptions(options) {
|
|
331
|
+
const resolved = {
|
|
332
|
+
maxEventBytes: bounded(options.maxEventBytes, DEFAULT_MAX_EVENT_BYTES, HARD_MAX_EVENT_BYTES, "maxEventBytes"),
|
|
333
|
+
maxPageSize: bounded(options.maxPageSize, DEFAULT_MAX_PAGE_SIZE, HARD_MAX_PAGE_SIZE, "maxPageSize"),
|
|
334
|
+
maxCursorBytes: bounded(options.maxCursorBytes, DEFAULT_MAX_CURSOR_BYTES, HARD_MAX_CURSOR_BYTES, "maxCursorBytes"),
|
|
335
|
+
maxQueuedEvents: bounded(options.maxQueuedEvents, DEFAULT_MAX_QUEUED_EVENTS, HARD_MAX_QUEUED_EVENTS, "maxQueuedEvents"),
|
|
336
|
+
maxSubscribers: bounded(options.maxSubscribers, DEFAULT_MAX_SUBSCRIBERS, HARD_MAX_SUBSCRIBERS, "maxSubscribers"),
|
|
337
|
+
pollIntervalMs: bounded(options.pollIntervalMs, DEFAULT_POLL_INTERVAL_MS, HARD_POLL_INTERVAL_MS, "pollIntervalMs"),
|
|
338
|
+
reconnectInitialMs: bounded(options.reconnectInitialMs, DEFAULT_RECONNECT_INITIAL_MS, HARD_RECONNECT_INITIAL_MS, "reconnectInitialMs"),
|
|
339
|
+
reconnectMaxMs: bounded(options.reconnectMaxMs, DEFAULT_RECONNECT_MAX_MS, HARD_RECONNECT_MAX_MS, "reconnectMaxMs"),
|
|
340
|
+
maxRetainedEventsPerRun: bounded(options.maxRetainedEventsPerRun, DEFAULT_MAX_RETAINED_EVENTS_PER_RUN, HARD_MAX_RETAINED_EVENTS_PER_RUN, "maxRetainedEventsPerRun"),
|
|
341
|
+
maxRetentionAgeMs: bounded(options.maxRetentionAgeMs, DEFAULT_MAX_RETENTION_AGE_MS, HARD_MAX_RETENTION_AGE_MS, "maxRetentionAgeMs"),
|
|
342
|
+
};
|
|
343
|
+
if (resolved.reconnectInitialMs > resolved.reconnectMaxMs) {
|
|
344
|
+
throw new RangeError("reconnectInitialMs must not exceed reconnectMaxMs");
|
|
345
|
+
}
|
|
346
|
+
return resolved;
|
|
347
|
+
}
|
|
348
|
+
function normalizeRecord(input, maxEventBytes) {
|
|
349
|
+
if (!input || typeof input !== "object")
|
|
350
|
+
throw inputError();
|
|
351
|
+
const record = clone(input);
|
|
352
|
+
const ownership = normalizeOwnership(record);
|
|
353
|
+
const sessionId = requiredText(record.sessionId);
|
|
354
|
+
const runId = requiredText(record.runId);
|
|
355
|
+
const id = requiredText(record.id);
|
|
356
|
+
if (record.redacted !== true ||
|
|
357
|
+
record.type !== record.event?.type ||
|
|
358
|
+
record.event.sessionId !== sessionId ||
|
|
359
|
+
record.event.runId !== runId) {
|
|
360
|
+
throw inputError();
|
|
361
|
+
}
|
|
362
|
+
parseTimestamp(record.timestamp, "input");
|
|
363
|
+
if (record.sequence !== undefined && (!Number.isSafeInteger(record.sequence) || record.sequence < 1))
|
|
364
|
+
throw inputError();
|
|
365
|
+
if (Buffer.byteLength(JSON.stringify(record), "utf8") > maxEventBytes)
|
|
366
|
+
throw inputError();
|
|
367
|
+
return {
|
|
368
|
+
...record,
|
|
369
|
+
...ownership,
|
|
370
|
+
id,
|
|
371
|
+
sessionId,
|
|
372
|
+
runId,
|
|
373
|
+
...(record.sequence === undefined ? {} : { sequence: record.sequence }),
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
function normalizeRead(input, maxPageSize) {
|
|
377
|
+
if (!input || typeof input !== "object")
|
|
378
|
+
throw inputError();
|
|
379
|
+
return {
|
|
380
|
+
...input,
|
|
381
|
+
ownership: normalizeOwnership(input.ownership),
|
|
382
|
+
sessionId: requiredText(input.sessionId),
|
|
383
|
+
runId: requiredText(input.runId),
|
|
384
|
+
limit: bounded(input.limit, maxPageSize, maxPageSize, "page limit"),
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function normalizeOwnership(input) {
|
|
388
|
+
if (!input || typeof input.tenantId !== "string" || input.tenantId.length === 0)
|
|
389
|
+
throw inputError();
|
|
390
|
+
if (input.accountId !== undefined && (typeof input.accountId !== "string" || input.accountId.length === 0))
|
|
391
|
+
throw inputError();
|
|
392
|
+
if (input.userId !== undefined && (typeof input.userId !== "string" || input.userId.length === 0))
|
|
393
|
+
throw inputError();
|
|
394
|
+
return {
|
|
395
|
+
tenantId: input.tenantId,
|
|
396
|
+
...(input.accountId === undefined ? {} : { accountId: input.accountId }),
|
|
397
|
+
...(input.userId === undefined ? {} : { userId: input.userId }),
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
function requiredText(value) {
|
|
401
|
+
if (typeof value !== "string" || value.length === 0)
|
|
402
|
+
throw inputError();
|
|
403
|
+
return value;
|
|
404
|
+
}
|
|
405
|
+
function parseTimestamp(value, kind) {
|
|
406
|
+
if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value)) {
|
|
407
|
+
throw kind === "cursor" ? cursorError() : inputError();
|
|
408
|
+
}
|
|
409
|
+
const parsed = Date.parse(value);
|
|
410
|
+
if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value)
|
|
411
|
+
throw kind === "cursor" ? cursorError() : inputError();
|
|
412
|
+
return parsed;
|
|
413
|
+
}
|
|
414
|
+
function bounded(value, fallback, hard, name) {
|
|
415
|
+
const resolved = value ?? fallback;
|
|
416
|
+
if (!Number.isSafeInteger(resolved) || resolved < 1 || resolved > hard)
|
|
417
|
+
throw inputError(`${name} is outside its allowed range`);
|
|
418
|
+
return resolved;
|
|
419
|
+
}
|
|
420
|
+
function sameOwnership(a, b) {
|
|
421
|
+
return a.tenantId === b.tenantId && a.accountId === b.accountId && a.userId === b.userId;
|
|
422
|
+
}
|
|
423
|
+
function streamKey(ownership, sessionId, runId) {
|
|
424
|
+
return JSON.stringify([ownership.tenantId, ownership.accountId, ownership.userId, sessionId, runId]);
|
|
425
|
+
}
|
|
426
|
+
function orderedRecords(stream) {
|
|
427
|
+
return stream ? [...stream.records.values()] : [];
|
|
428
|
+
}
|
|
429
|
+
function positionOf(records, cursor) {
|
|
430
|
+
return records.findIndex((record) => record.sequence === cursor.sequence && record.id === cursor.id);
|
|
431
|
+
}
|
|
432
|
+
function positionAfter(records, cursor, stream) {
|
|
433
|
+
const position = positionOf(records, cursor);
|
|
434
|
+
if (position >= 0)
|
|
435
|
+
return position + 1;
|
|
436
|
+
if (!stream || cursor.sequence < stream.nextSequence)
|
|
437
|
+
throw retentionError();
|
|
438
|
+
throw cursorError();
|
|
439
|
+
}
|
|
440
|
+
function terminalAt(records, after) {
|
|
441
|
+
const last = records.at(-1);
|
|
442
|
+
if (!last || !isTerminal(last))
|
|
443
|
+
return false;
|
|
444
|
+
return after === undefined || after.sequence <= last.sequence;
|
|
445
|
+
}
|
|
446
|
+
function isTerminal(record) {
|
|
447
|
+
return (record.type === "agent_finished" || record.type === "agent_denied" || record.type === "run_limit_exceeded" || record.type === "error");
|
|
448
|
+
}
|
|
449
|
+
function sameRecord(existing, input) {
|
|
450
|
+
const { sequence: _existingSequence, ...existingWithoutSequence } = existing;
|
|
451
|
+
const { sequence: _inputSequence, ...inputWithoutSequence } = input;
|
|
452
|
+
return stableJson(existingWithoutSequence) === stableJson(inputWithoutSequence);
|
|
453
|
+
}
|
|
454
|
+
function stableJson(value) {
|
|
455
|
+
if (value === null || typeof value !== "object")
|
|
456
|
+
return JSON.stringify(value);
|
|
457
|
+
if (Array.isArray(value))
|
|
458
|
+
return `[${value.map(stableJson).join(",")}]`;
|
|
459
|
+
return `{${Object.entries(value)
|
|
460
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
461
|
+
.map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`)
|
|
462
|
+
.join(",")}}`;
|
|
463
|
+
}
|
|
464
|
+
function clone(value) {
|
|
465
|
+
try {
|
|
466
|
+
const encoded = JSON.stringify(value);
|
|
467
|
+
if (encoded === undefined)
|
|
468
|
+
throw new Error("not JSON serializable");
|
|
469
|
+
return JSON.parse(encoded);
|
|
470
|
+
}
|
|
471
|
+
catch {
|
|
472
|
+
throw inputError();
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
function validCursor(value) {
|
|
476
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
477
|
+
return false;
|
|
478
|
+
const cursor = value;
|
|
479
|
+
return (cursor.v === 1 &&
|
|
480
|
+
typeof cursor.tenantId === "string" &&
|
|
481
|
+
cursor.tenantId.length > 0 &&
|
|
482
|
+
(cursor.accountId === undefined || (typeof cursor.accountId === "string" && cursor.accountId.length > 0)) &&
|
|
483
|
+
(cursor.userId === undefined || (typeof cursor.userId === "string" && cursor.userId.length > 0)) &&
|
|
484
|
+
typeof cursor.sessionId === "string" &&
|
|
485
|
+
cursor.sessionId.length > 0 &&
|
|
486
|
+
typeof cursor.runId === "string" &&
|
|
487
|
+
cursor.runId.length > 0 &&
|
|
488
|
+
Number.isSafeInteger(cursor.sequence) &&
|
|
489
|
+
cursor.sequence > 0 &&
|
|
490
|
+
typeof cursor.id === "string" &&
|
|
491
|
+
cursor.id.length > 0);
|
|
492
|
+
}
|
|
493
|
+
function throwIfAborted(signal) {
|
|
494
|
+
if (signal?.aborted)
|
|
495
|
+
throw signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
496
|
+
}
|
|
497
|
+
function inputError(message = "Invalid agent event source input") {
|
|
498
|
+
return new AgentEventSourceError("ERR_PRISM_AGENT_EVENT_SOURCE_INPUT", message);
|
|
499
|
+
}
|
|
500
|
+
function cursorError() {
|
|
501
|
+
return new AgentEventSourceError("ERR_PRISM_AGENT_EVENT_SOURCE_CURSOR", "Invalid agent event cursor");
|
|
502
|
+
}
|
|
503
|
+
function retentionError() {
|
|
504
|
+
return new AgentEventSourceError("ERR_PRISM_AGENT_EVENT_SOURCE_RETENTION", "Agent event history is unavailable");
|
|
505
|
+
}
|
|
506
|
+
function overflowError() {
|
|
507
|
+
return new AgentEventSourceError("ERR_PRISM_AGENT_EVENT_SOURCE_OVERFLOW", "Agent event subscription overflow");
|
|
508
|
+
}
|
|
509
|
+
function closedError() {
|
|
510
|
+
return new AgentEventSourceError("ERR_PRISM_AGENT_EVENT_SOURCE_CLOSED", "Agent event source is closed");
|
|
511
|
+
}
|
|
512
|
+
//# sourceMappingURL=agent-event-source.js.map
|
package/dist/agent-loops.d.ts
CHANGED
|
@@ -7,12 +7,17 @@ export declare function generateValidateReviseLoop(opts: {
|
|
|
7
7
|
readonly repairer?: ArtifactRepairer<unknown>;
|
|
8
8
|
readonly maxRevisions?: number;
|
|
9
9
|
readonly toolCalls?: "disabled" | "bounded";
|
|
10
|
+
readonly structuredOutputTiming?: "every-turn" | "final-turn-only";
|
|
10
11
|
}): AgentLoopStrategy;
|
|
11
12
|
export declare function resolveToolConcurrency(options: {
|
|
12
13
|
loop?: AgentLoopStrategy | AgentLoopOptions;
|
|
13
14
|
}, config: {
|
|
14
15
|
loop?: AgentLoopStrategy | AgentLoopOptions;
|
|
15
16
|
}): number;
|
|
17
|
+
/** Calls the host must dispatch. Provider-hosted calls (`authority: "provider-hosted"`)
|
|
18
|
+
* were already executed server-side; the assistant response text carries their effect, so
|
|
19
|
+
* the host neither dispatches them nor appends a `tool_result`. */
|
|
20
|
+
export declare function dispatchableToolCalls(calls: readonly ToolCallContent[]): readonly ToolCallContent[];
|
|
16
21
|
/** Dispatch tool calls with bounded concurrency; append transcript rows in call order. */
|
|
17
22
|
export declare function dispatchToolCallsInOrder(calls: readonly ToolCallContent[], ctx: LoopContext): Promise<void>;
|
|
18
23
|
export declare function resolveLoop(options: {
|