@arnilo/prism 0.0.3 → 0.0.5
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 +40 -0
- package/README.md +62 -26
- package/dist/agent-loops.d.ts +8 -1
- package/dist/agent-loops.js +57 -11
- package/dist/agents.js +212 -32
- package/dist/checkpoints.d.ts +11 -0
- package/dist/checkpoints.js +144 -0
- package/dist/cli-init.d.ts +41 -0
- package/dist/cli-init.js +390 -0
- package/dist/cli-runner.d.ts +7 -1
- package/dist/cli-runner.js +13 -1
- package/dist/compaction.js +9 -1
- package/dist/content.d.ts +121 -0
- package/dist/content.js +538 -0
- package/dist/contracts.d.ts +236 -11
- package/dist/contracts.js +8 -0
- package/dist/event-multiplexer.d.ts +23 -0
- package/dist/event-multiplexer.js +136 -0
- package/dist/execution-policy.d.ts +28 -0
- package/dist/execution-policy.js +24 -0
- package/dist/feedback.d.ts +48 -0
- package/dist/feedback.js +230 -0
- package/dist/index.d.ts +20 -6
- package/dist/index.js +13 -5
- package/dist/input.js +11 -1
- package/dist/leases.d.ts +8 -0
- package/dist/leases.js +111 -0
- package/dist/node/agent-definitions.js +3 -5
- package/dist/node/config.d.ts +1 -0
- package/dist/node/config.js +5 -3
- package/dist/node/contribution-discovery.js +5 -8
- package/dist/node/session-store-jsonl.js +8 -5
- package/dist/node/settings.js +2 -2
- package/dist/node/trust.js +2 -4
- package/dist/observability.d.ts +3 -0
- package/dist/observability.js +18 -0
- package/dist/providers/media.d.ts +44 -0
- package/dist/providers/media.js +126 -0
- package/dist/providers/openai-compatible.js +18 -119
- package/dist/providers/openai-primitives.d.ts +9 -0
- package/dist/providers/openai-primitives.js +129 -0
- package/dist/providers/transport.d.ts +40 -0
- package/dist/providers/transport.js +221 -0
- package/dist/redaction.js +40 -13
- package/dist/resources.d.ts +5 -0
- package/dist/resources.js +4 -0
- package/dist/structured-output.d.ts +11 -0
- package/dist/structured-output.js +59 -0
- package/dist/testing/feedback.d.ts +6 -0
- package/dist/testing/feedback.js +37 -0
- package/dist/testing/persistence-schema.d.ts +102 -0
- package/dist/testing/persistence-schema.js +487 -0
- package/dist/testing/provider-conformance.js +10 -1
- package/dist/testing/run-ledger-conformance.d.ts +33 -0
- package/dist/testing/run-ledger-conformance.js +178 -0
- package/dist/testing/session-store-conformance.d.ts +16 -0
- package/dist/testing/session-store-conformance.js +73 -0
- package/dist/tools.d.ts +17 -0
- package/dist/tools.js +29 -2
- package/docs/a2a.md +73 -0
- package/docs/agent-events.md +17 -10
- package/docs/agent-loops.md +11 -5
- package/docs/agent-session-runtime.md +15 -16
- package/docs/cli-rpc.md +36 -5
- package/docs/coding-agent-tools.md +43 -9
- package/docs/coding-security.md +88 -0
- package/docs/compaction-observational-memory.md +2 -0
- package/docs/context-and-skills.md +1 -0
- package/docs/credential-storage.md +177 -0
- package/docs/credentials-and-redaction.md +4 -3
- package/docs/database-persistence.md +52 -7
- package/docs/evaluations.md +122 -0
- package/docs/extensions.md +2 -2
- package/docs/host-security.md +33 -2
- package/docs/index.md +46 -18
- package/docs/input-and-prompt-assembly.md +6 -5
- package/docs/mcp-tools.md +184 -0
- package/docs/middleware-hooks.md +2 -0
- package/docs/migration.md +51 -28
- package/docs/model-registry.md +5 -3
- package/docs/multimodal-content.md +156 -0
- package/docs/observability.md +171 -0
- package/docs/performance.md +249 -1
- package/docs/persistence-credentials-multimodality-primitives.md +303 -0
- package/docs/postgres-persistence.md +143 -0
- package/docs/provider-conformance.md +18 -0
- package/docs/provider-layer.md +1 -1
- package/docs/provider-packages.md +2 -0
- package/docs/provider-primitives.md +281 -0
- package/docs/providers/ai-sdk.md +113 -0
- package/docs/providers/kimi.md +1 -0
- package/docs/providers/neuralwatt.md +1 -0
- package/docs/providers/openai-compatible.md +2 -1
- package/docs/providers/openai.md +8 -1
- package/docs/providers/opencode-go.md +1 -0
- package/docs/providers/openrouter.md +1 -0
- package/docs/providers/zai.md +1 -0
- package/docs/public-contracts.md +13 -5
- package/docs/rag.md +113 -0
- package/docs/release-and-install.md +237 -30
- package/docs/resource-loading.md +14 -4
- package/docs/review-coverage-2026-07-14.md +260 -0
- package/docs/review-coverage-2026-07-15.md +193 -0
- package/docs/run-ledger-conformance.md +96 -0
- package/docs/runs-and-usage.md +43 -4
- package/docs/server.md +139 -0
- package/docs/session-store-conformance.md +16 -0
- package/docs/session-stores-and-branching.md +1 -0
- package/docs/settings-auth-trust-security.md +6 -5
- package/docs/sqlite-persistence.md +123 -0
- package/docs/structured-output.md +9 -0
- package/docs/supervisors.md +71 -0
- package/docs/tool-conformance.md +1 -0
- package/docs/tool-execution-primitives.md +374 -0
- package/docs/tools.md +39 -1
- package/docs/workflow-orchestration-primitives.md +581 -0
- package/docs/workflow-tui-primitives.md +5 -0
- package/docs/workflows.md +293 -0
- package/docs/working-and-semantic-memory.md +169 -0
- package/package.json +43 -5
- package/templates/init/README.md.tmpl +28 -0
- package/templates/init/env.example.tmpl +1 -0
- package/templates/init/gitignore.tmpl +11 -0
- package/templates/init/optional/evals-example.ts.tmpl +17 -0
- package/templates/init/optional/workflows-example.ts.tmpl +27 -0
- package/templates/init/package.json.tmpl +22 -0
- package/templates/init/providers.json +76 -0
- package/templates/init/src/agent.ts.tmpl +10 -0
- package/templates/init/src/index.ts.tmpl +12 -0
- package/templates/init/src/tests/agent.test.ts.tmpl +24 -0
- package/templates/init/tsconfig.json.tmpl +15 -0
package/dist/agents.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AgentRunError } from "./contracts.js";
|
|
2
|
+
import { resolveLoop, resolveToolConcurrency } from "./agent-loops.js";
|
|
3
|
+
import { createProviderTurnMetadata, readProviderHttpStatus } from "./observability.js";
|
|
2
4
|
import { createDefaultCompactionStrategy, isCompactionEntryData } from "./compaction.js";
|
|
3
5
|
import { assembleProviderInput } from "./input.js";
|
|
4
6
|
import { providerToolCallDeltaContent, reconstructToolCallDeltas } from "./provider-events.js";
|
|
5
|
-
import { createProviderRequestPolicyChain,
|
|
7
|
+
import { createProviderRequestPolicyChain, normalizeProviderRequestPolicyResult } from "./provider-request-policy.js";
|
|
8
|
+
import { assertStructuredOutputRequestSupported, resolveRunProviderOptions } from "./structured-output.js";
|
|
6
9
|
import { errorToErrorInfo, redactAgentEvent, redactProviderRequest, redactRunLedgerRecord, redactSecrets, redactSessionEntry } from "./redaction.js";
|
|
7
10
|
import { composeSystemPrompt, mergeSystemPromptConfig } from "./system-prompts.js";
|
|
8
11
|
import { createDefaultRetryPolicy, waitForRetry } from "./retry.js";
|
|
@@ -34,7 +37,9 @@ class RuntimeAgentSession {
|
|
|
34
37
|
activeLedger;
|
|
35
38
|
activeOwnership;
|
|
36
39
|
activeIdempotencyKey;
|
|
37
|
-
|
|
40
|
+
activeLoopTurn = 1;
|
|
41
|
+
ledgerChain = Promise.resolve();
|
|
42
|
+
ledgerFailure;
|
|
38
43
|
constructor(config) {
|
|
39
44
|
this.id = config.id ?? randomId("session");
|
|
40
45
|
this.agent = config.agent;
|
|
@@ -67,6 +72,8 @@ class RuntimeAgentSession {
|
|
|
67
72
|
const model = options.model ?? this.agent.config.model;
|
|
68
73
|
const startedAt = new Date().toISOString();
|
|
69
74
|
let runError;
|
|
75
|
+
const runUsage = createUsageAccumulator();
|
|
76
|
+
let usage;
|
|
70
77
|
try {
|
|
71
78
|
this.resolveRunProvider(options);
|
|
72
79
|
throwIfAborted(controller.signal);
|
|
@@ -101,12 +108,32 @@ class RuntimeAgentSession {
|
|
|
101
108
|
// ponytail: skill context after host context; no per-skill token budget yet.
|
|
102
109
|
...activeSkills.flatMap((skill) => skill.context ?? []),
|
|
103
110
|
];
|
|
104
|
-
const providerOptions =
|
|
111
|
+
const providerOptions = resolveRunProviderOptions(options, this.agent.config);
|
|
112
|
+
assertStructuredOutputRequestSupported(options.model ?? this.agent.config.model, providerOptions);
|
|
105
113
|
const validate = options.validate ?? this.agent.config.validator;
|
|
106
114
|
// ponytail: RunOptions.instructionInjectors overrides AgentConfig.instructionInjectors (mirrors validate/loop).
|
|
107
115
|
const instructionInjectors = options.instructionInjectors ?? this.agent.config.instructionInjectors ?? [];
|
|
108
116
|
const inputLayout = options.inputLayout ?? this.agent.config.inputLayout;
|
|
109
117
|
const loop = resolveLoop(options, this.agent.config);
|
|
118
|
+
const toolConcurrency = resolveToolConcurrency(options, this.agent.config);
|
|
119
|
+
this.activeLoopTurn = 1;
|
|
120
|
+
const recordProviderUsage = async (turnUsage, turn, attempt) => {
|
|
121
|
+
runUsage.add(turnUsage);
|
|
122
|
+
if (!this.activeLedger)
|
|
123
|
+
return;
|
|
124
|
+
const usageRecord = {
|
|
125
|
+
id: randomId("usage"),
|
|
126
|
+
sessionId: this.id,
|
|
127
|
+
runId,
|
|
128
|
+
scope: "provider_turn",
|
|
129
|
+
turn,
|
|
130
|
+
attempt,
|
|
131
|
+
usage: turnUsage,
|
|
132
|
+
recordedAt: new Date().toISOString(),
|
|
133
|
+
...this.activeOwnership,
|
|
134
|
+
};
|
|
135
|
+
await this.activeLedger.appendUsage(redactRunLedgerRecord(usageRecord, this.activeRedactor));
|
|
136
|
+
};
|
|
110
137
|
// ponytail: LoopContext binds existing private helpers; loop orchestrates only.
|
|
111
138
|
const ctx = {
|
|
112
139
|
sessionId: this.id,
|
|
@@ -117,6 +144,7 @@ class RuntimeAgentSession {
|
|
|
117
144
|
input,
|
|
118
145
|
inputMessages,
|
|
119
146
|
maxToolRounds,
|
|
147
|
+
toolConcurrency,
|
|
120
148
|
assemble: async (nextInput, toolResults, turn) => assembleProviderInput({
|
|
121
149
|
model: options.model ?? this.agent.config.model,
|
|
122
150
|
input: nextInput,
|
|
@@ -144,8 +172,9 @@ class RuntimeAgentSession {
|
|
|
144
172
|
generate: async (request) => {
|
|
145
173
|
const policyResult = await this.applyProviderRequestPolicies(request, runId, options, metadata, controller.signal);
|
|
146
174
|
const middlewareRequest = await this.agent.config.middleware?.run("provider_request", policyResult.request) ?? policyResult.request;
|
|
147
|
-
return this.generateWithRetry(this.redactProviderRequest(middlewareRequest), runId, options, controller.signal, policyResult.secrets);
|
|
175
|
+
return this.generateWithRetry(this.redactProviderRequest(middlewareRequest), runId, options, controller.signal, policyResult.secrets, this.activeLoopTurn, recordProviderUsage);
|
|
148
176
|
},
|
|
177
|
+
isToolCallExclusive: (call) => registry.get(call.name)?.exclusive === true,
|
|
149
178
|
dispatchToolCall: (call) => dispatchToolCall({
|
|
150
179
|
call,
|
|
151
180
|
registry,
|
|
@@ -160,14 +189,20 @@ class RuntimeAgentSession {
|
|
|
160
189
|
validate,
|
|
161
190
|
}),
|
|
162
191
|
appendMessage: (message) => this.appendMessage(message, runId),
|
|
163
|
-
emit: (event) =>
|
|
192
|
+
emit: (event) => {
|
|
193
|
+
if (event.type === "turn_started")
|
|
194
|
+
this.activeLoopTurn = event.turn;
|
|
195
|
+
this.emit(event);
|
|
196
|
+
},
|
|
164
197
|
};
|
|
165
|
-
const
|
|
198
|
+
const loopUsage = await loop.run(ctx);
|
|
199
|
+
usage = runUsage.value() ?? loopUsage;
|
|
166
200
|
if (usage && this.activeLedger) {
|
|
167
201
|
const usageRecord = {
|
|
168
202
|
id: randomId("usage"),
|
|
169
203
|
sessionId: this.id,
|
|
170
204
|
runId,
|
|
205
|
+
scope: "run_total",
|
|
171
206
|
usage,
|
|
172
207
|
recordedAt: new Date().toISOString(),
|
|
173
208
|
...this.activeOwnership,
|
|
@@ -176,11 +211,23 @@ class RuntimeAgentSession {
|
|
|
176
211
|
}
|
|
177
212
|
await this.drainLedger();
|
|
178
213
|
this.emit({ type: "agent_finished", sessionId: this.id, runId, usage });
|
|
214
|
+
return this.buildRunResult({
|
|
215
|
+
runId,
|
|
216
|
+
status: "succeeded",
|
|
217
|
+
usage,
|
|
218
|
+
});
|
|
179
219
|
}
|
|
180
220
|
catch (error) {
|
|
181
221
|
runError = errorToErrorInfo(error);
|
|
182
222
|
this.emit({ type: "error", sessionId: this.id, runId, error: runError });
|
|
183
|
-
|
|
223
|
+
const result = this.buildRunResult({
|
|
224
|
+
runId,
|
|
225
|
+
status: controller.signal.aborted ? "aborted" : "failed",
|
|
226
|
+
usage: runUsage.value() ?? usage,
|
|
227
|
+
error: runError,
|
|
228
|
+
abortReason: controller.signal.aborted ? String(controller.signal.reason) : undefined,
|
|
229
|
+
});
|
|
230
|
+
throw new AgentRunError(result, { cause: error });
|
|
184
231
|
}
|
|
185
232
|
finally {
|
|
186
233
|
if (this.activeRun === controller)
|
|
@@ -220,6 +267,48 @@ class RuntimeAgentSession {
|
|
|
220
267
|
prompt(input, options) {
|
|
221
268
|
return this.run(input, options);
|
|
222
269
|
}
|
|
270
|
+
async *stream(input, options = {}) {
|
|
271
|
+
const { maxQueuedEvents, overflow, ...runOptions } = options;
|
|
272
|
+
const subscription = this.subscribe({ maxQueuedEvents, overflow });
|
|
273
|
+
let runOwnedId;
|
|
274
|
+
let settled = false;
|
|
275
|
+
const runPromise = this.run(input, runOptions).finally(() => {
|
|
276
|
+
settled = true;
|
|
277
|
+
});
|
|
278
|
+
try {
|
|
279
|
+
for await (const event of subscription) {
|
|
280
|
+
if ("runId" in event && typeof event.runId === "string") {
|
|
281
|
+
if (runOwnedId === undefined && event.type === "agent_started")
|
|
282
|
+
runOwnedId = event.runId;
|
|
283
|
+
if (runOwnedId !== undefined && event.runId !== runOwnedId)
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
yield event;
|
|
287
|
+
}
|
|
288
|
+
await runPromise;
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
if (!settled) {
|
|
292
|
+
this.abort(new Error("stream consumer closed"));
|
|
293
|
+
await runPromise.catch(() => undefined);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
buildRunResult(input) {
|
|
298
|
+
const final = finalAssistantMessage(this.history);
|
|
299
|
+
return {
|
|
300
|
+
sessionId: this.id,
|
|
301
|
+
runId: input.runId,
|
|
302
|
+
status: input.status,
|
|
303
|
+
leafId: this.currentLeafId,
|
|
304
|
+
text: final.text,
|
|
305
|
+
content: final.content,
|
|
306
|
+
message: final.message,
|
|
307
|
+
usage: input.usage,
|
|
308
|
+
error: input.error,
|
|
309
|
+
abortReason: input.abortReason,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
223
312
|
async compact(options = {}) {
|
|
224
313
|
if (this.activeRun)
|
|
225
314
|
throw new Error("Agent session already has an active run");
|
|
@@ -301,7 +390,17 @@ class RuntimeAgentSession {
|
|
|
301
390
|
redacted: Boolean(this.activeRedactor),
|
|
302
391
|
...this.activeOwnership,
|
|
303
392
|
};
|
|
304
|
-
this.
|
|
393
|
+
const ledger = this.activeLedger;
|
|
394
|
+
this.ledgerChain = this.ledgerChain.then(async () => {
|
|
395
|
+
if (this.ledgerFailure)
|
|
396
|
+
return;
|
|
397
|
+
try {
|
|
398
|
+
await ledger.appendEvent(redactRunLedgerRecord(record, this.activeRedactor));
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
this.ledgerFailure = error;
|
|
402
|
+
}
|
|
403
|
+
});
|
|
305
404
|
}
|
|
306
405
|
}
|
|
307
406
|
closeSubscribers() {
|
|
@@ -310,19 +409,20 @@ class RuntimeAgentSession {
|
|
|
310
409
|
this.subscribers.clear();
|
|
311
410
|
}
|
|
312
411
|
async drainLedger() {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
this.
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
412
|
+
await this.ledgerChain;
|
|
413
|
+
const failure = this.ledgerFailure;
|
|
414
|
+
this.ledgerChain = Promise.resolve();
|
|
415
|
+
this.ledgerFailure = undefined;
|
|
416
|
+
if (failure)
|
|
417
|
+
throw failure;
|
|
418
|
+
}
|
|
419
|
+
async generateWithRetry(request, runId, options, signal, requestSecrets = [], turn = 1, recordUsage) {
|
|
320
420
|
const retry = mergeRetry(this.agent.config.retry, options.retry);
|
|
321
421
|
const secrets = [...requestSecrets, ...(retry?.secrets ?? [])];
|
|
322
422
|
const policy = retry?.policy ?? (retry ? createDefaultRetryPolicy(retry) : undefined);
|
|
323
423
|
for (let attempt = 1;; attempt += 1) {
|
|
324
424
|
try {
|
|
325
|
-
return await this.generateProviderTurn(request, runId, signal, secrets);
|
|
425
|
+
return await this.generateProviderTurn(request, runId, signal, secrets, turn, attempt, recordUsage);
|
|
326
426
|
}
|
|
327
427
|
catch (error) {
|
|
328
428
|
const failure = error instanceof ProviderTurnFailure ? error : undefined;
|
|
@@ -341,32 +441,37 @@ class RuntimeAgentSession {
|
|
|
341
441
|
}
|
|
342
442
|
}
|
|
343
443
|
}
|
|
344
|
-
async generateProviderTurn(request, runId, signal, secrets = []) {
|
|
444
|
+
async generateProviderTurn(request, runId, signal, secrets = [], turn = 1, attempt = 1, recordUsage) {
|
|
445
|
+
const startedAt = performance.now();
|
|
446
|
+
const providerId = this.activeProvider?.id ?? request.model.provider;
|
|
447
|
+
const buildMetadata = (extra = {}) => createProviderTurnMetadata(request, providerId, { attempt, ...extra });
|
|
448
|
+
this.emit({
|
|
449
|
+
type: "provider_turn_started",
|
|
450
|
+
sessionId: this.id,
|
|
451
|
+
runId,
|
|
452
|
+
turn,
|
|
453
|
+
metadata: buildMetadata(),
|
|
454
|
+
});
|
|
345
455
|
const content = [];
|
|
346
456
|
const calls = [];
|
|
347
457
|
const toolDeltas = [];
|
|
348
458
|
let messageId;
|
|
349
459
|
let started = false;
|
|
350
460
|
let usage;
|
|
461
|
+
let usageRecorded = false;
|
|
462
|
+
const recordTurnUsage = async () => {
|
|
463
|
+
if (!usage || usageRecorded)
|
|
464
|
+
return;
|
|
465
|
+
usageRecorded = true;
|
|
466
|
+
await recordUsage?.(usage, turn, attempt);
|
|
467
|
+
};
|
|
351
468
|
try {
|
|
352
469
|
for await (const event of this.activeProvider.generate(request)) {
|
|
353
470
|
throwIfAborted(signal);
|
|
354
471
|
if (event.type === "error")
|
|
355
472
|
throw new ProviderTurnFailure(event.error, started);
|
|
356
|
-
if (event.type === "usage")
|
|
473
|
+
if (event.type === "usage")
|
|
357
474
|
usage = event.usage;
|
|
358
|
-
if (this.activeLedger) {
|
|
359
|
-
const usageRecord = {
|
|
360
|
-
id: randomId("usage"),
|
|
361
|
-
sessionId: this.id,
|
|
362
|
-
runId,
|
|
363
|
-
usage: event.usage,
|
|
364
|
-
recordedAt: new Date().toISOString(),
|
|
365
|
-
...this.activeOwnership,
|
|
366
|
-
};
|
|
367
|
-
await this.activeLedger.appendUsage(redactRunLedgerRecord(usageRecord, this.activeRedactor));
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
475
|
if (event.type === "done") {
|
|
371
476
|
usage = event.usage ?? usage;
|
|
372
477
|
break;
|
|
@@ -399,12 +504,34 @@ class RuntimeAgentSession {
|
|
|
399
504
|
calls.push(call);
|
|
400
505
|
this.emit({ type: "message_delta", sessionId: this.id, runId, content: call });
|
|
401
506
|
}
|
|
507
|
+
await recordTurnUsage();
|
|
508
|
+
const latencyMs = Math.round(performance.now() - startedAt);
|
|
509
|
+
this.emit({
|
|
510
|
+
type: "provider_turn_finished",
|
|
511
|
+
sessionId: this.id,
|
|
512
|
+
runId,
|
|
513
|
+
turn,
|
|
514
|
+
metadata: buildMetadata({ latencyMs }),
|
|
515
|
+
usage,
|
|
516
|
+
});
|
|
402
517
|
return { content, calls, messageId, started, usage };
|
|
403
518
|
}
|
|
404
519
|
catch (error) {
|
|
520
|
+
const latencyMs = Math.round(performance.now() - startedAt);
|
|
521
|
+
const info = error instanceof ProviderTurnFailure ? redactSecrets(error.info, secrets) : errorToErrorInfo(error, secrets);
|
|
522
|
+
await recordTurnUsage();
|
|
523
|
+
this.emit({
|
|
524
|
+
type: "provider_turn_finished",
|
|
525
|
+
sessionId: this.id,
|
|
526
|
+
runId,
|
|
527
|
+
turn,
|
|
528
|
+
metadata: buildMetadata({ latencyMs, httpStatus: readProviderHttpStatus(info) }),
|
|
529
|
+
usage,
|
|
530
|
+
error: info,
|
|
531
|
+
});
|
|
405
532
|
if (error instanceof ProviderTurnFailure)
|
|
406
533
|
throw error;
|
|
407
|
-
throw new ProviderTurnFailure(
|
|
534
|
+
throw new ProviderTurnFailure(info, started);
|
|
408
535
|
}
|
|
409
536
|
}
|
|
410
537
|
async applyProviderRequestPolicies(request, runId, options, metadata, signal) {
|
|
@@ -555,6 +682,19 @@ function inputToMessages(input) {
|
|
|
555
682
|
return [input];
|
|
556
683
|
return [...input];
|
|
557
684
|
}
|
|
685
|
+
function finalAssistantMessage(history) {
|
|
686
|
+
for (let index = history.length - 1; index >= 0; index -= 1) {
|
|
687
|
+
const message = history[index];
|
|
688
|
+
if (message.role !== "assistant")
|
|
689
|
+
continue;
|
|
690
|
+
const text = message.content
|
|
691
|
+
.filter((block) => block.type === "text")
|
|
692
|
+
.map((block) => block.text)
|
|
693
|
+
.join("");
|
|
694
|
+
return { message, content: message.content, text };
|
|
695
|
+
}
|
|
696
|
+
return { content: [], text: "" };
|
|
697
|
+
}
|
|
558
698
|
function activeTools(tools) {
|
|
559
699
|
if (!tools)
|
|
560
700
|
return { registry: createToolRegistry(), tools: [] };
|
|
@@ -620,6 +760,46 @@ function throwIfAbortedSignal(signal) {
|
|
|
620
760
|
if (signal?.aborted)
|
|
621
761
|
throw signal.reason instanceof Error ? signal.reason : new Error("Agent run aborted");
|
|
622
762
|
}
|
|
763
|
+
function createUsageAccumulator() {
|
|
764
|
+
const sums = new Map();
|
|
765
|
+
let costCurrency;
|
|
766
|
+
let costCompatible = true;
|
|
767
|
+
return {
|
|
768
|
+
add(usage) {
|
|
769
|
+
for (const key of ["inputTokens", "outputTokens", "cacheReadTokens", "cacheWriteTokens"]) {
|
|
770
|
+
const value = usage[key];
|
|
771
|
+
if (value !== undefined)
|
|
772
|
+
sums.set(key, (sums.get(key) ?? 0) + value);
|
|
773
|
+
}
|
|
774
|
+
const total = usage.totalTokens
|
|
775
|
+
?? (usage.inputTokens !== undefined || usage.outputTokens !== undefined
|
|
776
|
+
? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0)
|
|
777
|
+
: undefined);
|
|
778
|
+
if (total !== undefined)
|
|
779
|
+
sums.set("totalTokens", (sums.get("totalTokens") ?? 0) + total);
|
|
780
|
+
if (usage.cost !== undefined && costCompatible) {
|
|
781
|
+
if (!sums.has("cost"))
|
|
782
|
+
costCurrency = usage.currency;
|
|
783
|
+
else if (usage.currency !== costCurrency)
|
|
784
|
+
costCompatible = false;
|
|
785
|
+
if (costCompatible)
|
|
786
|
+
sums.set("cost", (sums.get("cost") ?? 0) + usage.cost);
|
|
787
|
+
}
|
|
788
|
+
},
|
|
789
|
+
value() {
|
|
790
|
+
if (sums.size === 0)
|
|
791
|
+
return undefined;
|
|
792
|
+
const usage = {};
|
|
793
|
+
for (const [key, value] of sums) {
|
|
794
|
+
if (key !== "cost" || costCompatible)
|
|
795
|
+
usage[key] = value;
|
|
796
|
+
}
|
|
797
|
+
if (costCompatible && sums.has("cost") && costCurrency !== undefined)
|
|
798
|
+
usage.currency = costCurrency;
|
|
799
|
+
return Object.keys(usage).length > 0 ? usage : undefined;
|
|
800
|
+
},
|
|
801
|
+
};
|
|
802
|
+
}
|
|
623
803
|
function randomId(prefix) {
|
|
624
804
|
return `${prefix}_${globalThis.crypto?.randomUUID?.() ?? Math.random().toString(36).slice(2)}`;
|
|
625
805
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CheckpointStore } from "./contracts.js";
|
|
2
|
+
export declare const CHECKPOINT_CONFLICT_CODE = "ERR_PRISM_CHECKPOINT_CONFLICT";
|
|
3
|
+
export declare class CheckpointConflictError extends Error {
|
|
4
|
+
readonly code = "ERR_PRISM_CHECKPOINT_CONFLICT";
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
export interface MemoryCheckpointStoreOptions {
|
|
8
|
+
readonly maxPageSize?: number;
|
|
9
|
+
}
|
|
10
|
+
/** In-process reference implementation of the generic checkpoint contract. */
|
|
11
|
+
export declare function createMemoryCheckpointStore(options?: MemoryCheckpointStoreOptions): CheckpointStore;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
export const CHECKPOINT_CONFLICT_CODE = "ERR_PRISM_CHECKPOINT_CONFLICT";
|
|
2
|
+
export class CheckpointConflictError extends Error {
|
|
3
|
+
code = CHECKPOINT_CONFLICT_CODE;
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "CheckpointConflictError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/** In-process reference implementation of the generic checkpoint contract. */
|
|
10
|
+
export function createMemoryCheckpointStore(options = {}) {
|
|
11
|
+
const records = new Map();
|
|
12
|
+
const maxPageSize = Math.max(1, options.maxPageSize ?? 500);
|
|
13
|
+
return {
|
|
14
|
+
async saveCheckpoint(input) {
|
|
15
|
+
throwIfAborted(input.signal);
|
|
16
|
+
assertKey(input);
|
|
17
|
+
if (!Number.isSafeInteger(input.version) || input.version < 1) {
|
|
18
|
+
throw new CheckpointConflictError("Checkpoint version must be a positive safe integer");
|
|
19
|
+
}
|
|
20
|
+
const id = recordKey(input);
|
|
21
|
+
const existing = records.get(id);
|
|
22
|
+
if (existing)
|
|
23
|
+
assertOwnership(input, existing);
|
|
24
|
+
if (input.expectedVersion !== undefined && input.expectedVersion !== (existing?.version ?? 0)) {
|
|
25
|
+
throw new CheckpointConflictError(`Checkpoint compare-and-swap failed (expected ${input.expectedVersion}, current ${existing?.version ?? 0})`);
|
|
26
|
+
}
|
|
27
|
+
if (existing && input.version <= existing.version) {
|
|
28
|
+
throw new CheckpointConflictError(`Stale checkpoint version ${input.version} (current ${existing.version})`);
|
|
29
|
+
}
|
|
30
|
+
if (existing?.fencingToken !== undefined && (input.fencingToken === undefined || input.fencingToken < existing.fencingToken)) {
|
|
31
|
+
throw new CheckpointConflictError(`Stale checkpoint fencing token ${input.fencingToken ?? "missing"} (current ${existing.fencingToken})`);
|
|
32
|
+
}
|
|
33
|
+
const now = new Date().toISOString();
|
|
34
|
+
const value = cloneJson(input.value, "Checkpoint value");
|
|
35
|
+
const metadata = input.metadata === undefined ? undefined : cloneJson(input.metadata, "Checkpoint metadata");
|
|
36
|
+
const record = {
|
|
37
|
+
namespace: input.namespace,
|
|
38
|
+
key: input.key,
|
|
39
|
+
version: input.version,
|
|
40
|
+
...(input.fencingToken === undefined ? {} : { fencingToken: input.fencingToken }),
|
|
41
|
+
value,
|
|
42
|
+
...(input.category === undefined ? {} : { category: input.category }),
|
|
43
|
+
...ownership(input),
|
|
44
|
+
createdAt: existing?.createdAt ?? now,
|
|
45
|
+
updatedAt: now,
|
|
46
|
+
...(metadata === undefined ? {} : { metadata }),
|
|
47
|
+
};
|
|
48
|
+
records.set(id, record);
|
|
49
|
+
return record;
|
|
50
|
+
},
|
|
51
|
+
async loadCheckpoint(input) {
|
|
52
|
+
throwIfAborted(input.signal);
|
|
53
|
+
assertKey(input);
|
|
54
|
+
const record = records.get(recordKey(input));
|
|
55
|
+
if (!record)
|
|
56
|
+
return null;
|
|
57
|
+
assertOwnership(input, record);
|
|
58
|
+
return record;
|
|
59
|
+
},
|
|
60
|
+
async listCheckpoints(query = {}) {
|
|
61
|
+
throwIfAborted(query.signal);
|
|
62
|
+
const offset = decodeCursor(query.cursor);
|
|
63
|
+
const limit = Math.min(Math.max(1, query.limit ?? 100), maxPageSize);
|
|
64
|
+
const categories = query.category === undefined
|
|
65
|
+
? undefined
|
|
66
|
+
: new Set(Array.isArray(query.category) ? query.category : [query.category]);
|
|
67
|
+
const items = [...records.values()]
|
|
68
|
+
.filter((record) => query.namespace === undefined || record.namespace === query.namespace)
|
|
69
|
+
.filter((record) => query.keyPrefix === undefined || record.key.startsWith(query.keyPrefix))
|
|
70
|
+
.filter((record) => categories === undefined || (record.category !== undefined && categories.has(record.category)))
|
|
71
|
+
.filter((record) => ownershipFilterMatches(query, record))
|
|
72
|
+
.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt) || a.key.localeCompare(b.key));
|
|
73
|
+
const page = items.slice(offset, offset + limit);
|
|
74
|
+
const next = offset + page.length;
|
|
75
|
+
return {
|
|
76
|
+
items: page,
|
|
77
|
+
...(next < items.length ? { nextCursor: String(next) } : {}),
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
async deleteCheckpoint(input) {
|
|
81
|
+
throwIfAborted(input.signal);
|
|
82
|
+
assertKey(input);
|
|
83
|
+
const id = recordKey(input);
|
|
84
|
+
const record = records.get(id);
|
|
85
|
+
if (!record)
|
|
86
|
+
return false;
|
|
87
|
+
assertOwnership(input, record);
|
|
88
|
+
return records.delete(id);
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function recordKey(input) {
|
|
93
|
+
return `${input.namespace}\0${input.key}`;
|
|
94
|
+
}
|
|
95
|
+
function assertKey(input) {
|
|
96
|
+
if (!input.namespace || !input.key)
|
|
97
|
+
throw new CheckpointConflictError("Checkpoint namespace and key are required");
|
|
98
|
+
}
|
|
99
|
+
function ownership(input) {
|
|
100
|
+
return {
|
|
101
|
+
...(input.tenantId === undefined ? {} : { tenantId: input.tenantId }),
|
|
102
|
+
...(input.accountId === undefined ? {} : { accountId: input.accountId }),
|
|
103
|
+
...(input.userId === undefined ? {} : { userId: input.userId }),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function ownershipMatches(expected, actual) {
|
|
107
|
+
return expected.tenantId === actual.tenantId
|
|
108
|
+
&& expected.accountId === actual.accountId
|
|
109
|
+
&& expected.userId === actual.userId;
|
|
110
|
+
}
|
|
111
|
+
function ownershipFilterMatches(expected, actual) {
|
|
112
|
+
return (expected.tenantId === undefined || expected.tenantId === actual.tenantId)
|
|
113
|
+
&& (expected.accountId === undefined || expected.accountId === actual.accountId)
|
|
114
|
+
&& (expected.userId === undefined || expected.userId === actual.userId);
|
|
115
|
+
}
|
|
116
|
+
function assertOwnership(expected, actual) {
|
|
117
|
+
if (!ownershipMatches(expected, actual)) {
|
|
118
|
+
throw new CheckpointConflictError("Checkpoint ownership mismatch");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function cloneJson(value, label) {
|
|
122
|
+
try {
|
|
123
|
+
const encoded = JSON.stringify(value);
|
|
124
|
+
if (encoded === undefined)
|
|
125
|
+
throw new Error("not JSON serializable");
|
|
126
|
+
return JSON.parse(encoded);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
throw new CheckpointConflictError(`${label} must be JSON serializable: ${error instanceof Error ? error.message : String(error)}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function decodeCursor(cursor) {
|
|
133
|
+
if (cursor === undefined)
|
|
134
|
+
return 0;
|
|
135
|
+
const value = Number(cursor);
|
|
136
|
+
if (!Number.isSafeInteger(value) || value < 0)
|
|
137
|
+
throw new CheckpointConflictError("Invalid checkpoint cursor");
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
function throwIfAborted(signal) {
|
|
141
|
+
if (signal?.aborted)
|
|
142
|
+
throw signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=checkpoints.js.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Writable } from "node:stream";
|
|
2
|
+
export declare class InitUsageError extends Error {
|
|
3
|
+
}
|
|
4
|
+
export type InitProvider = string;
|
|
5
|
+
export interface InitOptions {
|
|
6
|
+
readonly directory: string;
|
|
7
|
+
readonly provider: InitProvider;
|
|
8
|
+
readonly withWorkflows: boolean;
|
|
9
|
+
readonly withEvals: boolean;
|
|
10
|
+
readonly force: boolean;
|
|
11
|
+
readonly help: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface InitRuntime {
|
|
14
|
+
readonly stdout: Writable;
|
|
15
|
+
readonly stderr: Writable;
|
|
16
|
+
/** Override template root (tests). Defaults to package `templates/init`. */
|
|
17
|
+
readonly templatesRoot?: string;
|
|
18
|
+
/** Override package version stamped into generated package.json. */
|
|
19
|
+
readonly packageVersion?: string;
|
|
20
|
+
/** Working directory used to resolve relative destinations. Defaults to process.cwd(). */
|
|
21
|
+
readonly cwd?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface InitResult {
|
|
24
|
+
readonly targetDir: string;
|
|
25
|
+
readonly writtenFiles: readonly string[];
|
|
26
|
+
readonly provider: InitProvider;
|
|
27
|
+
readonly withWorkflows: boolean;
|
|
28
|
+
readonly withEvals: boolean;
|
|
29
|
+
readonly totalBytes: number;
|
|
30
|
+
}
|
|
31
|
+
/** Provider ids supported by `prism init --provider`. Loaded from templates data. */
|
|
32
|
+
export declare function listInitProviders(templatesRoot?: string): readonly string[];
|
|
33
|
+
/** @deprecated Prefer listInitProviders(); retained for tests that import the name. */
|
|
34
|
+
export declare const INIT_PROVIDERS: readonly string[];
|
|
35
|
+
export declare function getInitUsage(templatesRoot?: string): string;
|
|
36
|
+
export declare const initUsage: string;
|
|
37
|
+
export declare function parseInitArgs(argv: readonly string[], templatesRoot?: string): InitOptions;
|
|
38
|
+
export declare function runInitCommand(argv: readonly string[], runtime: InitRuntime): Promise<number>;
|
|
39
|
+
export declare function createInitProject(options: InitOptions, runtime?: InitRuntime): Promise<InitResult>;
|
|
40
|
+
export declare function defaultTemplatesRoot(): string;
|
|
41
|
+
export declare function isInitProvider(value: string, templatesRoot?: string): value is InitProvider;
|