@arnilo/prism 0.0.4 → 0.0.6
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 +46 -1
- package/README.md +34 -10
- package/dist/agent-loops.d.ts +1 -0
- package/dist/agent-loops.js +26 -16
- package/dist/agents.js +147 -21
- 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/content.d.ts +19 -0
- package/dist/content.js +197 -69
- package/dist/contracts.d.ts +96 -9
- package/dist/contracts.js +8 -0
- package/dist/feedback.d.ts +48 -0
- package/dist/feedback.js +230 -0
- package/dist/ids.d.ts +2 -0
- package/dist/ids.js +6 -0
- package/dist/index.d.ts +10 -4
- package/dist/index.js +6 -3
- package/dist/providers/media.d.ts +3 -1
- package/dist/providers/media.js +11 -1
- package/dist/session-stores.js +2 -3
- package/dist/testing/feedback.d.ts +6 -0
- package/dist/testing/feedback.js +37 -0
- package/dist/testing/persistence-schema.d.ts +48 -10
- package/dist/testing/persistence-schema.js +166 -22
- package/dist/testing/run-ledger-conformance.js +7 -1
- package/dist/thinking.d.ts +42 -0
- package/dist/thinking.js +92 -0
- package/dist/tools.js +2 -3
- package/dist/use-case-model.d.ts +63 -0
- package/dist/use-case-model.js +52 -0
- package/docs/a2a.md +75 -0
- package/docs/agent-events.md +14 -21
- package/docs/agent-loops.md +12 -9
- package/docs/agent-session-runtime.md +14 -16
- package/docs/cli-rpc.md +35 -7
- package/docs/coding-agent-tools.md +35 -14
- package/docs/coding-security.md +7 -3
- package/docs/compaction-llm.md +17 -7
- package/docs/compaction-observational-memory.md +30 -4
- package/docs/context-and-skills.md +1 -0
- package/docs/credential-storage.md +58 -9
- package/docs/credentials-and-redaction.md +3 -3
- package/docs/database-persistence.md +17 -9
- package/docs/evaluations.md +122 -0
- package/docs/extensions.md +2 -2
- package/docs/host-security.md +26 -5
- package/docs/index.md +43 -28
- package/docs/mcp-tools.md +74 -13
- package/docs/migration.md +177 -3
- package/docs/multimodal-content.md +14 -6
- package/docs/node-filesystem-config.md +1 -0
- package/docs/node-jsonl-session-store.md +5 -4
- package/docs/observability.md +14 -6
- package/docs/performance.md +209 -0
- package/docs/postgres-persistence.md +8 -6
- package/docs/provider-caching.md +16 -4
- package/docs/provider-conformance.md +40 -1
- package/docs/provider-packages.md +62 -3
- package/docs/providers/ai-sdk.md +149 -0
- package/docs/providers/kimi.md +124 -61
- package/docs/providers/neuralwatt.md +19 -13
- package/docs/providers/openai.md +56 -13
- package/docs/providers/opencode-go.md +118 -30
- package/docs/providers/openrouter.md +105 -35
- package/docs/providers/zai.md +94 -45
- package/docs/public-contracts.md +6 -5
- package/docs/rag.md +113 -0
- package/docs/release-and-install.md +100 -79
- package/docs/review-coverage-2026-07-15.md +193 -0
- package/docs/review-coverage-2026-07-17-provider-validation.md +192 -0
- package/docs/runs-and-usage.md +42 -5
- package/docs/server.md +139 -0
- package/docs/settings-auth-trust-security.md +5 -5
- package/docs/sqlite-persistence.md +6 -5
- package/docs/structured-output.md +1 -1
- package/docs/supervisors.md +71 -0
- package/docs/thinking-and-reasoning.md +98 -0
- package/docs/tool-execution-primitives.md +3 -3
- package/docs/tools.md +15 -0
- package/docs/use-case-model-selection.md +109 -0
- package/docs/workflow-orchestration-primitives.md +20 -3
- package/docs/workflows.md +114 -33
- package/docs/working-and-semantic-memory.md +170 -0
- package/package.json +13 -3
- 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/feedback.js
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
export const DEFAULT_MAX_FEEDBACK_COMMENT_BYTES = 4096;
|
|
2
|
+
export const HARD_MAX_FEEDBACK_COMMENT_BYTES = 16384;
|
|
3
|
+
export const DEFAULT_MAX_FEEDBACK_TAGS = 16;
|
|
4
|
+
export const HARD_MAX_FEEDBACK_TAGS = 64;
|
|
5
|
+
export const DEFAULT_MAX_FEEDBACK_LINKS = 16;
|
|
6
|
+
export const HARD_MAX_FEEDBACK_LINKS = 64;
|
|
7
|
+
export const DEFAULT_MAX_FEEDBACK_METADATA_BYTES = 16384;
|
|
8
|
+
export const HARD_MAX_FEEDBACK_METADATA_BYTES = 65536;
|
|
9
|
+
export const DEFAULT_FEEDBACK_PAGE_SIZE = 100;
|
|
10
|
+
export const HARD_FEEDBACK_PAGE_SIZE = 500;
|
|
11
|
+
export const MAX_FEEDBACK_TAG_LENGTH = 64;
|
|
12
|
+
export const MAX_FEEDBACK_ID_LENGTH = 128;
|
|
13
|
+
export class RunFeedbackError extends Error {
|
|
14
|
+
code;
|
|
15
|
+
constructor(message, code = "ERR_PRISM_RUN_FEEDBACK") {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "RunFeedbackError";
|
|
18
|
+
this.code = code;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Validate, ownership-check, redact, bound, and freeze one feedback record. */
|
|
22
|
+
export async function prepareRunFeedback(input, options) {
|
|
23
|
+
input.signal?.throwIfAborted();
|
|
24
|
+
const ownership = requireOwnership(input);
|
|
25
|
+
const id = requireId(input.id, "feedback id");
|
|
26
|
+
const runId = requireId(input.runId, "runId");
|
|
27
|
+
const run = await options.resolveRun({ runId, ownership, signal: input.signal });
|
|
28
|
+
input.signal?.throwIfAborted();
|
|
29
|
+
if (!run || run.runId !== runId || !sameOwnership(ownership, run)) {
|
|
30
|
+
throw new RunFeedbackError("Run not found", "ERR_PRISM_RUN_FEEDBACK_RUN_NOT_FOUND");
|
|
31
|
+
}
|
|
32
|
+
if (input.sessionId !== undefined && input.sessionId !== run.sessionId) {
|
|
33
|
+
throw new RunFeedbackError("Run not found", "ERR_PRISM_RUN_FEEDBACK_RUN_NOT_FOUND");
|
|
34
|
+
}
|
|
35
|
+
if (input.traceId !== undefined && run.traceId !== undefined && input.traceId !== run.traceId) {
|
|
36
|
+
throw new RunFeedbackError("Run not found", "ERR_PRISM_RUN_FEEDBACK_RUN_NOT_FOUND");
|
|
37
|
+
}
|
|
38
|
+
if (input.comment !== undefined && !input.comment.trim()) {
|
|
39
|
+
throw new RunFeedbackError("comment must not be empty", "ERR_PRISM_RUN_FEEDBACK_VALIDATION");
|
|
40
|
+
}
|
|
41
|
+
if (input.rating !== undefined && (!Number.isFinite(input.rating) || input.rating < -1 || input.rating > 1)) {
|
|
42
|
+
throw new RunFeedbackError("rating must be a finite number in [-1, 1]", "ERR_PRISM_RUN_FEEDBACK_BOUNDS");
|
|
43
|
+
}
|
|
44
|
+
const maxCommentBytes = boundedLimit(options.maxCommentBytes, DEFAULT_MAX_FEEDBACK_COMMENT_BYTES, HARD_MAX_FEEDBACK_COMMENT_BYTES, "maxCommentBytes");
|
|
45
|
+
const maxTags = boundedLimit(options.maxTags, DEFAULT_MAX_FEEDBACK_TAGS, HARD_MAX_FEEDBACK_TAGS, "maxTags");
|
|
46
|
+
const maxLinks = boundedLimit(options.maxLinks, DEFAULT_MAX_FEEDBACK_LINKS, HARD_MAX_FEEDBACK_LINKS, "maxLinks");
|
|
47
|
+
const maxMetadataBytes = boundedLimit(options.maxMetadataBytes, DEFAULT_MAX_FEEDBACK_METADATA_BYTES, HARD_MAX_FEEDBACK_METADATA_BYTES, "maxMetadataBytes");
|
|
48
|
+
const tags = normalizeList(input.tags, maxTags, MAX_FEEDBACK_TAG_LENGTH, "tag");
|
|
49
|
+
const scorerIds = normalizeList(input.scorerIds, maxLinks, MAX_FEEDBACK_ID_LENGTH, "scorer id", true);
|
|
50
|
+
const evaluationIds = normalizeList(input.evaluationIds, maxLinks, MAX_FEEDBACK_ID_LENGTH, "evaluation id", true);
|
|
51
|
+
if (input.rating === undefined && input.comment === undefined && tags.length === 0 && scorerIds.length === 0 && evaluationIds.length === 0) {
|
|
52
|
+
throw new RunFeedbackError("feedback requires rating, comment, tag, scorer, or evaluation", "ERR_PRISM_RUN_FEEDBACK_EMPTY");
|
|
53
|
+
}
|
|
54
|
+
const redact = (value) => options.redactor ? options.redactor.redact(value) : value;
|
|
55
|
+
const comment = input.comment === undefined ? undefined : redact(input.comment);
|
|
56
|
+
const safeTags = Object.freeze(normalizeList(redact(tags), maxTags, MAX_FEEDBACK_TAG_LENGTH, "tag"));
|
|
57
|
+
const metadata = input.metadata === undefined ? undefined : redact(input.metadata);
|
|
58
|
+
assertBytes(comment, maxCommentBytes, "comment");
|
|
59
|
+
assertBytes(metadata, maxMetadataBytes, "metadata");
|
|
60
|
+
const createdAt = input.createdAt ?? new Date().toISOString();
|
|
61
|
+
if (!Number.isFinite(Date.parse(createdAt)))
|
|
62
|
+
throw new RunFeedbackError("createdAt must be an ISO timestamp", "ERR_PRISM_RUN_FEEDBACK_VALIDATION");
|
|
63
|
+
return Object.freeze({
|
|
64
|
+
id,
|
|
65
|
+
runId,
|
|
66
|
+
sessionId: run.sessionId,
|
|
67
|
+
traceId: input.traceId ?? run.traceId,
|
|
68
|
+
rating: input.rating,
|
|
69
|
+
comment,
|
|
70
|
+
tags: safeTags,
|
|
71
|
+
scorerIds: Object.freeze(scorerIds),
|
|
72
|
+
evaluationIds: Object.freeze(evaluationIds),
|
|
73
|
+
createdAt,
|
|
74
|
+
createdBy: input.createdBy === undefined ? undefined : requireId(input.createdBy, "createdBy"),
|
|
75
|
+
metadata: metadata === undefined ? undefined : cloneFrozenJsonObject(metadata),
|
|
76
|
+
...ownership,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/** In-memory implementation with the same validation/ownership semantics as durable adapters. */
|
|
80
|
+
export function createMemoryRunFeedbackStore(options) {
|
|
81
|
+
const records = new Map();
|
|
82
|
+
for (const record of options.initial ?? [])
|
|
83
|
+
records.set(record.id, freezeRecord(record));
|
|
84
|
+
const maxPageSize = boundedLimit(options.maxPageSize, DEFAULT_FEEDBACK_PAGE_SIZE, HARD_FEEDBACK_PAGE_SIZE, "maxPageSize");
|
|
85
|
+
return {
|
|
86
|
+
async append(input) {
|
|
87
|
+
if (records.has(input.id))
|
|
88
|
+
throw new RunFeedbackError("Duplicate feedback id", "ERR_PRISM_RUN_FEEDBACK_DUPLICATE");
|
|
89
|
+
const record = await prepareRunFeedback(input, options);
|
|
90
|
+
records.set(record.id, record);
|
|
91
|
+
return record;
|
|
92
|
+
},
|
|
93
|
+
async query(query) {
|
|
94
|
+
query.signal?.throwIfAborted();
|
|
95
|
+
const ownership = requireOwnership(query);
|
|
96
|
+
const limit = pageLimit(query.limit, maxPageSize);
|
|
97
|
+
const order = query.order === "desc" ? -1 : 1;
|
|
98
|
+
const sorted = [...records.values()]
|
|
99
|
+
.filter((record) => sameOwnership(ownership, record) && matchesQuery(record, query))
|
|
100
|
+
.sort((a, b) => order * (a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id)));
|
|
101
|
+
const start = query.cursor ? sorted.findIndex((record) => record.id === query.cursor) + 1 : 0;
|
|
102
|
+
if (query.cursor && start === 0)
|
|
103
|
+
throw new RunFeedbackError("Unknown feedback cursor", "ERR_PRISM_RUN_FEEDBACK_CURSOR");
|
|
104
|
+
const items = sorted.slice(start, start + limit);
|
|
105
|
+
return { items, nextCursor: start + items.length < sorted.length ? items.at(-1)?.id : undefined, total: sorted.length };
|
|
106
|
+
},
|
|
107
|
+
async delete(input) {
|
|
108
|
+
input.signal?.throwIfAborted();
|
|
109
|
+
const ownership = requireOwnership(input);
|
|
110
|
+
const current = records.get(input.id);
|
|
111
|
+
if (!current || !sameOwnership(ownership, current))
|
|
112
|
+
return false;
|
|
113
|
+
return records.delete(input.id);
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export function requireRunFeedbackOwnership(input) {
|
|
118
|
+
return requireOwnership(input);
|
|
119
|
+
}
|
|
120
|
+
export function runFeedbackPageLimit(limit, maximum = HARD_FEEDBACK_PAGE_SIZE) {
|
|
121
|
+
return pageLimit(limit, maximum);
|
|
122
|
+
}
|
|
123
|
+
function requireOwnership(input) {
|
|
124
|
+
if (!input.tenantId?.trim()
|
|
125
|
+
|| (input.accountId !== undefined && !input.accountId.trim())
|
|
126
|
+
|| (input.userId !== undefined && !input.userId.trim())
|
|
127
|
+
|| (!input.accountId && !input.userId)) {
|
|
128
|
+
throw new RunFeedbackError("tenantId and non-empty accountId or userId are required", "ERR_PRISM_RUN_FEEDBACK_OWNERSHIP");
|
|
129
|
+
}
|
|
130
|
+
return { tenantId: input.tenantId, accountId: input.accountId, userId: input.userId };
|
|
131
|
+
}
|
|
132
|
+
function sameOwnership(expected, actual) {
|
|
133
|
+
return expected.tenantId === actual.tenantId
|
|
134
|
+
&& expected.accountId === actual.accountId
|
|
135
|
+
&& expected.userId === actual.userId;
|
|
136
|
+
}
|
|
137
|
+
function requireId(value, label) {
|
|
138
|
+
if (!value || value.length > MAX_FEEDBACK_ID_LENGTH || !/^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(value)) {
|
|
139
|
+
throw new RunFeedbackError(`${label} is invalid`, "ERR_PRISM_RUN_FEEDBACK_VALIDATION");
|
|
140
|
+
}
|
|
141
|
+
return value;
|
|
142
|
+
}
|
|
143
|
+
function normalizeList(values, maximum, maxLength, label, ids = false) {
|
|
144
|
+
if (!values)
|
|
145
|
+
return [];
|
|
146
|
+
if (values.length > maximum)
|
|
147
|
+
throw new RunFeedbackError(`Too many ${label}s`, "ERR_PRISM_RUN_FEEDBACK_BOUNDS");
|
|
148
|
+
const result = values.map((value) => {
|
|
149
|
+
if (typeof value !== "string" || !value.trim() || value.length > maxLength || /[\r\n]/.test(value)) {
|
|
150
|
+
throw new RunFeedbackError(`${label} is invalid`, "ERR_PRISM_RUN_FEEDBACK_VALIDATION");
|
|
151
|
+
}
|
|
152
|
+
return ids ? requireId(value, label) : value;
|
|
153
|
+
});
|
|
154
|
+
if (new Set(result).size !== result.length)
|
|
155
|
+
throw new RunFeedbackError(`Duplicate ${label}`, "ERR_PRISM_RUN_FEEDBACK_VALIDATION");
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
function boundedLimit(value, fallback, hard, label) {
|
|
159
|
+
const resolved = value ?? fallback;
|
|
160
|
+
if (!Number.isSafeInteger(resolved) || resolved < 1 || resolved > hard) {
|
|
161
|
+
throw new RunFeedbackError(`${label} must be an integer in [1, ${hard}]`, "ERR_PRISM_RUN_FEEDBACK_BOUNDS");
|
|
162
|
+
}
|
|
163
|
+
return resolved;
|
|
164
|
+
}
|
|
165
|
+
function pageLimit(value, maximum) {
|
|
166
|
+
if (value === undefined)
|
|
167
|
+
return Math.min(DEFAULT_FEEDBACK_PAGE_SIZE, maximum);
|
|
168
|
+
if (!Number.isSafeInteger(value) || value < 1)
|
|
169
|
+
throw new RunFeedbackError("limit must be a positive integer", "ERR_PRISM_RUN_FEEDBACK_BOUNDS");
|
|
170
|
+
return Math.min(value, maximum);
|
|
171
|
+
}
|
|
172
|
+
function assertBytes(value, maximum, label) {
|
|
173
|
+
if (value === undefined)
|
|
174
|
+
return;
|
|
175
|
+
let encoded;
|
|
176
|
+
try {
|
|
177
|
+
encoded = typeof value === "string" ? value : JSON.stringify(value);
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
throw new RunFeedbackError(`${label} must be JSON serializable`, "ERR_PRISM_RUN_FEEDBACK_VALIDATION");
|
|
181
|
+
}
|
|
182
|
+
if (new TextEncoder().encode(encoded).byteLength > maximum) {
|
|
183
|
+
throw new RunFeedbackError(`${label} exceeds ${maximum} bytes`, "ERR_PRISM_RUN_FEEDBACK_BOUNDS");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function matchesQuery(record, query) {
|
|
187
|
+
if (query.runId !== undefined && record.runId !== query.runId)
|
|
188
|
+
return false;
|
|
189
|
+
if (query.sessionId !== undefined && record.sessionId !== query.sessionId)
|
|
190
|
+
return false;
|
|
191
|
+
if (query.traceId !== undefined && record.traceId !== query.traceId)
|
|
192
|
+
return false;
|
|
193
|
+
if (query.rating !== undefined && record.rating !== query.rating)
|
|
194
|
+
return false;
|
|
195
|
+
if (query.scorerId !== undefined && !record.scorerIds.includes(query.scorerId))
|
|
196
|
+
return false;
|
|
197
|
+
if (query.evaluationId !== undefined && !record.evaluationIds.includes(query.evaluationId))
|
|
198
|
+
return false;
|
|
199
|
+
if (query.tag !== undefined && !record.tags.includes(query.tag))
|
|
200
|
+
return false;
|
|
201
|
+
if (query.fromCreatedAt !== undefined && record.createdAt < query.fromCreatedAt)
|
|
202
|
+
return false;
|
|
203
|
+
if (query.toCreatedAt !== undefined && record.createdAt > query.toCreatedAt)
|
|
204
|
+
return false;
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
function freezeRecord(record) {
|
|
208
|
+
return Object.freeze({
|
|
209
|
+
...record,
|
|
210
|
+
tags: Object.freeze([...record.tags]),
|
|
211
|
+
scorerIds: Object.freeze([...record.scorerIds]),
|
|
212
|
+
evaluationIds: Object.freeze([...record.evaluationIds]),
|
|
213
|
+
metadata: record.metadata ? cloneFrozenJsonObject(record.metadata) : undefined,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
function cloneFrozenJsonObject(value) {
|
|
217
|
+
const cloned = JSON.parse(JSON.stringify(value));
|
|
218
|
+
if (!cloned || typeof cloned !== "object" || Array.isArray(cloned))
|
|
219
|
+
throw new RunFeedbackError("metadata must be a JSON object");
|
|
220
|
+
return deepFreeze(cloned);
|
|
221
|
+
}
|
|
222
|
+
function deepFreeze(value) {
|
|
223
|
+
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
224
|
+
for (const child of Object.values(value))
|
|
225
|
+
deepFreeze(child);
|
|
226
|
+
Object.freeze(value);
|
|
227
|
+
}
|
|
228
|
+
return value;
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=feedback.js.map
|
package/dist/ids.d.ts
ADDED
package/dist/ids.js
ADDED
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export type * from "./contracts.js";
|
|
2
|
-
export { isSessionEntryKind, SESSION_APPEND_CONFLICT_CODE, SESSION_ENTRY_KINDS, SESSION_ENTRY_SCHEMA_VERSION, SessionAppendConflictError, isSessionAppendConflict } from "./contracts.js";
|
|
2
|
+
export { isSessionEntryKind, SESSION_APPEND_CONFLICT_CODE, SESSION_ENTRY_KINDS, SESSION_ENTRY_SCHEMA_VERSION, SessionAppendConflictError, isSessionAppendConflict, AgentRunError } from "./contracts.js";
|
|
3
3
|
export { createAgent, createAgentSession } from "./agents.js";
|
|
4
|
+
export { createMemoryRunFeedbackStore, prepareRunFeedback, requireRunFeedbackOwnership, runFeedbackPageLimit, RunFeedbackError, } from "./feedback.js";
|
|
5
|
+
export type { MemoryRunFeedbackStoreOptions, PrepareRunFeedbackOptions, RunFeedbackLimits, RunFeedbackRun, RunFeedbackRunResolver, } from "./feedback.js";
|
|
4
6
|
export { CHECKPOINT_CONFLICT_CODE, CheckpointConflictError, createMemoryCheckpointStore } from "./checkpoints.js";
|
|
5
7
|
export type { MemoryCheckpointStoreOptions } from "./checkpoints.js";
|
|
6
8
|
export { LEASE_CONFLICT_CODE, LeaseConflictError, createMemoryLeaseStore } from "./leases.js";
|
|
@@ -28,13 +30,17 @@ export { assertStructuredOutputRequestSupported, DEFAULT_MAX_STRUCTURED_OUTPUT_N
|
|
|
28
30
|
export { createProviderTurnMetadata, readProviderHttpStatus } from "./observability.js";
|
|
29
31
|
export { createProviderRequestPolicyChain, createSessionCachePolicy, mergeProviderRequestOptions } from "./provider-request-policy.js";
|
|
30
32
|
export type { SessionCachePolicyOptions } from "./provider-request-policy.js";
|
|
33
|
+
export { THINKING_LEVELS, applyThinkingLevel, isThinkingLevel, normalizeThinkingLevel, thinkingCompatFor, thinkingFamilyForModel, } from "./thinking.js";
|
|
34
|
+
export type { ThinkingCompatFamily, ThinkingLevel } from "./thinking.js";
|
|
35
|
+
export { resolveUseCaseModel, resolveUseCaseModelBinding, useCaseCredentialProviderId, } from "./use-case-model.js";
|
|
36
|
+
export type { ResolveUseCaseModelInput, ResolvedUseCaseModel, UseCaseModelBinding, } from "./use-case-model.js";
|
|
31
37
|
export { composeSystemPrompt, mergeSystemPromptConfig } from "./system-prompts.js";
|
|
32
38
|
export type { ComposeSystemPromptOptions } from "./system-prompts.js";
|
|
33
39
|
export type { ModelRegistry, ModelRegistryOptions } from "./models.js";
|
|
34
40
|
export { definePrismManifest, parsePrismManifest } from "./manifests.js";
|
|
35
41
|
export type { ManifestContributionDeclaration, ManifestContributionKind, ManifestResourceDeclaration, PrismManifest } from "./manifests.js";
|
|
36
|
-
export { assertDeclaredMediaTypeMatches, assertMediaBlocksWithinBounds, assertMessagesSupportModelCapabilities, assertModelSupportsContentBlocks, assertSsrfAllowedUrl, collectMessageContentBlocks, contentBlockInputModality, DEFAULT_MAX_AUDIO_DURATION_MS, DEFAULT_MAX_MEDIA_ITEM_BYTES, DEFAULT_MAX_MEDIA_ITEMS_PER_REQUEST, DEFAULT_MAX_MEDIA_REQUEST_BYTES, DEFAULT_MEDIA_FETCH_TIMEOUT_MS, loadBoundedBinaryResource, MediaContentError, MODEL_INPUT_CAPABILITIES, resolveMediaContentBlock, sniffMediaMimeType, UnsupportedModalityError, } from "./content.js";
|
|
37
|
-
export type { AudioContent, DocumentContent, FileContent, MediaContentBlock, MediaContentBounds, MediaMimePolicy, ModelInputCapability, ResolvedMediaContent, ResolveMediaContentOptions, SsrfPolicy, } from "./content.js";
|
|
42
|
+
export { assertDeclaredMediaTypeMatches, assertMediaBlocksWithinBounds, assertMessagesSupportModelCapabilities, assertModelSupportsContentBlocks, assertSsrfAllowedUrl, collectMessageContentBlocks, contentBlockInputModality, DEFAULT_MAX_AUDIO_DURATION_MS, DEFAULT_MAX_MEDIA_ITEM_BYTES, DEFAULT_MAX_MEDIA_ITEMS_PER_REQUEST, DEFAULT_MAX_MEDIA_REQUEST_BYTES, DEFAULT_MEDIA_FETCH_TIMEOUT_MS, loadBoundedBinaryResource, MediaContentError, MODEL_INPUT_CAPABILITIES, resolveMediaContentBlock, resolveMediaContentBlocks, sniffMediaMimeType, UnsupportedModalityError, } from "./content.js";
|
|
43
|
+
export type { AudioContent, DocumentContent, FileContent, MediaContentBlock, MediaContentBounds, MediaHostAddress, MediaHostnameResolver, MediaMimePolicy, MediaUrlRequest, MediaUrlRequester, ModelInputCapability, ResolvedMediaContent, ResolveMediaContentOptions, SsrfPolicy, } from "./content.js";
|
|
38
44
|
export { loadBinaryResource, loadJsonResource, loadManifestResource, loadTextResource } from "./resources.js";
|
|
39
45
|
export type { LoadBinaryResourceOptions } from "./resources.js";
|
|
40
46
|
export { createChainedSettingsProvider, createStaticSettingsProvider } from "./settings.js";
|
|
@@ -65,5 +71,5 @@ export type { DispatchToolCallOptions, ToolArgumentValidationError, ToolArgument
|
|
|
65
71
|
export type { DuplicateRegistrationOptions, DuplicateRegistrationPolicy } from "./registry-options.js";
|
|
66
72
|
export { dispatchToolCallsInOrder, generateValidateReviseLoop, isAgentLoopOptions, resolveLoop, resolveToolConcurrency, singleShotLoop } from "./agent-loops.js";
|
|
67
73
|
export declare const name = "prism";
|
|
68
|
-
export declare const version = "0.0.
|
|
74
|
+
export declare const version = "0.0.6";
|
|
69
75
|
export declare const description = "Agent harness for AI providers, agents, sessions, and tools.";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { isSessionEntryKind, SESSION_APPEND_CONFLICT_CODE, SESSION_ENTRY_KINDS, SESSION_ENTRY_SCHEMA_VERSION, SessionAppendConflictError, isSessionAppendConflict } from "./contracts.js";
|
|
1
|
+
export { isSessionEntryKind, SESSION_APPEND_CONFLICT_CODE, SESSION_ENTRY_KINDS, SESSION_ENTRY_SCHEMA_VERSION, SessionAppendConflictError, isSessionAppendConflict, AgentRunError } from "./contracts.js";
|
|
2
2
|
export { createAgent, createAgentSession } from "./agents.js";
|
|
3
|
+
export { createMemoryRunFeedbackStore, prepareRunFeedback, requireRunFeedbackOwnership, runFeedbackPageLimit, RunFeedbackError, } from "./feedback.js";
|
|
3
4
|
export { CHECKPOINT_CONFLICT_CODE, CheckpointConflictError, createMemoryCheckpointStore } from "./checkpoints.js";
|
|
4
5
|
export { LEASE_CONFLICT_CODE, LeaseConflictError, createMemoryLeaseStore } from "./leases.js";
|
|
5
6
|
export { createEventMultiplexer } from "./event-multiplexer.js";
|
|
@@ -17,9 +18,11 @@ export { authMethodKey, defineProviderPackage, systemPromptContributionKey } fro
|
|
|
17
18
|
export { assertStructuredOutputRequestSupported, DEFAULT_MAX_STRUCTURED_OUTPUT_NAME_LENGTH, DEFAULT_MAX_STRUCTURED_OUTPUT_SCHEMA_BYTES, modelSupportsStructuredOutput, resolveRunProviderOptions, StructuredOutputError, validateStructuredOutputOptions, } from "./structured-output.js";
|
|
18
19
|
export { createProviderTurnMetadata, readProviderHttpStatus } from "./observability.js";
|
|
19
20
|
export { createProviderRequestPolicyChain, createSessionCachePolicy, mergeProviderRequestOptions } from "./provider-request-policy.js";
|
|
21
|
+
export { THINKING_LEVELS, applyThinkingLevel, isThinkingLevel, normalizeThinkingLevel, thinkingCompatFor, thinkingFamilyForModel, } from "./thinking.js";
|
|
22
|
+
export { resolveUseCaseModel, resolveUseCaseModelBinding, useCaseCredentialProviderId, } from "./use-case-model.js";
|
|
20
23
|
export { composeSystemPrompt, mergeSystemPromptConfig } from "./system-prompts.js";
|
|
21
24
|
export { definePrismManifest, parsePrismManifest } from "./manifests.js";
|
|
22
|
-
export { assertDeclaredMediaTypeMatches, assertMediaBlocksWithinBounds, assertMessagesSupportModelCapabilities, assertModelSupportsContentBlocks, assertSsrfAllowedUrl, collectMessageContentBlocks, contentBlockInputModality, DEFAULT_MAX_AUDIO_DURATION_MS, DEFAULT_MAX_MEDIA_ITEM_BYTES, DEFAULT_MAX_MEDIA_ITEMS_PER_REQUEST, DEFAULT_MAX_MEDIA_REQUEST_BYTES, DEFAULT_MEDIA_FETCH_TIMEOUT_MS, loadBoundedBinaryResource, MediaContentError, MODEL_INPUT_CAPABILITIES, resolveMediaContentBlock, sniffMediaMimeType, UnsupportedModalityError, } from "./content.js";
|
|
25
|
+
export { assertDeclaredMediaTypeMatches, assertMediaBlocksWithinBounds, assertMessagesSupportModelCapabilities, assertModelSupportsContentBlocks, assertSsrfAllowedUrl, collectMessageContentBlocks, contentBlockInputModality, DEFAULT_MAX_AUDIO_DURATION_MS, DEFAULT_MAX_MEDIA_ITEM_BYTES, DEFAULT_MAX_MEDIA_ITEMS_PER_REQUEST, DEFAULT_MAX_MEDIA_REQUEST_BYTES, DEFAULT_MEDIA_FETCH_TIMEOUT_MS, loadBoundedBinaryResource, MediaContentError, MODEL_INPUT_CAPABILITIES, resolveMediaContentBlock, resolveMediaContentBlocks, sniffMediaMimeType, UnsupportedModalityError, } from "./content.js";
|
|
23
26
|
export { loadBinaryResource, loadJsonResource, loadManifestResource, loadTextResource } from "./resources.js";
|
|
24
27
|
export { createChainedSettingsProvider, createStaticSettingsProvider } from "./settings.js";
|
|
25
28
|
export { createMiddlewareRegistry } from "./middleware.js";
|
|
@@ -36,6 +39,6 @@ export { resolveInstructionInjectors, runInstructionInjectors } from "./instruct
|
|
|
36
39
|
export { createToolRegistry, dispatchToolCall, filterTools, createToolParameterValidator } from "./tools.js";
|
|
37
40
|
export { dispatchToolCallsInOrder, generateValidateReviseLoop, isAgentLoopOptions, resolveLoop, resolveToolConcurrency, singleShotLoop } from "./agent-loops.js";
|
|
38
41
|
export const name = "prism";
|
|
39
|
-
export const version = "0.0.
|
|
42
|
+
export const version = "0.0.6";
|
|
40
43
|
export const description = "Agent harness for AI providers, agents, sessions, and tools.";
|
|
41
44
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AudioContent, ContentBlock, DocumentContent, FileContent, JsonObject, ModelCapabilities, ModelConfig } from "../contracts.js";
|
|
1
|
+
import type { AudioContent, ContentBlock, DocumentContent, FileContent, JsonObject, Message, ModelCapabilities, ModelConfig } from "../contracts.js";
|
|
2
2
|
import { type MediaContentBlock, type ModelInputCapability, type ResolveMediaContentOptions, type ResolvedMediaContent } from "../content.js";
|
|
3
3
|
/** Default upload-cache entry cap per provider media session. */
|
|
4
4
|
export declare const DEFAULT_PROVIDER_UPLOAD_CACHE_ENTRIES = 32;
|
|
@@ -38,5 +38,7 @@ export declare function serializePdfDocumentWireBlock(options: {
|
|
|
38
38
|
readonly title?: string;
|
|
39
39
|
}): JsonObject;
|
|
40
40
|
export declare function resolveProviderMediaBlock(block: AudioContent | FileContent | DocumentContent, options?: ResolveMediaContentOptions): Promise<ResolvedMediaContent>;
|
|
41
|
+
/** Resolve every media block once and enforce aggregate request bounds before provider I/O. */
|
|
42
|
+
export declare function resolveProviderMediaMessages(messages: readonly Message[], model: ModelConfig, options?: ResolveMediaContentOptions): Promise<ReadonlyMap<MediaContentBlock, ResolvedMediaContent>>;
|
|
41
43
|
export declare function defaultProviderFilename(block: MediaContentBlock, fallback: string): string;
|
|
42
44
|
export declare const DEFAULT_PROVIDER_MEDIA_ITEM_BYTES = 10000000;
|
package/dist/providers/media.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { contentBlockInputModality, DEFAULT_MAX_MEDIA_ITEM_BYTES, resolveMediaContentBlock, UnsupportedModalityError, } from "../content.js";
|
|
2
|
+
import { assertMessagesSupportModelCapabilities, contentBlockInputModality, DEFAULT_MAX_MEDIA_ITEM_BYTES, resolveMediaContentBlock, resolveMediaContentBlocks, UnsupportedModalityError, } from "../content.js";
|
|
3
3
|
/** Default upload-cache entry cap per provider media session. */
|
|
4
4
|
export const DEFAULT_PROVIDER_UPLOAD_CACHE_ENTRIES = 32;
|
|
5
5
|
/** Inline OpenAI file_data ceiling before preferring Files API upload. */
|
|
@@ -96,6 +96,16 @@ export function serializePdfDocumentWireBlock(options) {
|
|
|
96
96
|
export async function resolveProviderMediaBlock(block, options = {}) {
|
|
97
97
|
return resolveMediaContentBlock(block, options);
|
|
98
98
|
}
|
|
99
|
+
/** Resolve every media block once and enforce aggregate request bounds before provider I/O. */
|
|
100
|
+
export async function resolveProviderMediaMessages(messages, model, options = {}) {
|
|
101
|
+
assertMessagesSupportModelCapabilities(model, messages);
|
|
102
|
+
const blocks = messages.flatMap((message) => message.content.filter(isMediaContentBlock));
|
|
103
|
+
const resolved = await resolveMediaContentBlocks(blocks, options);
|
|
104
|
+
return new Map(blocks.map((block, index) => [block, resolved[index]]));
|
|
105
|
+
}
|
|
106
|
+
function isMediaContentBlock(block) {
|
|
107
|
+
return contentBlockInputModality(block) !== undefined;
|
|
108
|
+
}
|
|
99
109
|
export function defaultProviderFilename(block, fallback) {
|
|
100
110
|
if ("name" in block && block.name)
|
|
101
111
|
return block.name;
|
package/dist/session-stores.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SESSION_APPEND_CONFLICT_CODE, SessionAppendConflictError } from "./contracts.js";
|
|
2
|
+
import { createId } from "./ids.js";
|
|
2
3
|
export function createSessionEntry(options) {
|
|
3
4
|
const { createId, now, ...entry } = options;
|
|
4
5
|
return {
|
|
@@ -171,7 +172,5 @@ function indexEntries(entries) {
|
|
|
171
172
|
}
|
|
172
173
|
return { byId, parentIds };
|
|
173
174
|
}
|
|
174
|
-
|
|
175
|
-
return `${prefix}_${globalThis.crypto?.randomUUID?.() ?? Math.random().toString(36).slice(2)}`;
|
|
176
|
-
}
|
|
175
|
+
const randomId = createId;
|
|
177
176
|
//# sourceMappingURL=session-stores.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { RunFeedbackStore } from "../contracts.js";
|
|
2
|
+
export interface RunFeedbackConformanceFactory {
|
|
3
|
+
(): RunFeedbackStore | Promise<RunFeedbackStore>;
|
|
4
|
+
}
|
|
5
|
+
/** Shared minimum behavior for memory and production feedback stores. */
|
|
6
|
+
export declare function runFeedbackConformance(factory: RunFeedbackConformanceFactory): Promise<void>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** Shared minimum behavior for memory and production feedback stores. */
|
|
2
|
+
export async function runFeedbackConformance(factory) {
|
|
3
|
+
const store = await factory();
|
|
4
|
+
const owner = { tenantId: "feedback-tenant", userId: "feedback-user" };
|
|
5
|
+
await store.append({
|
|
6
|
+
id: "feedback-1",
|
|
7
|
+
runId: "feedback-run-a",
|
|
8
|
+
rating: 1,
|
|
9
|
+
comment: "useful",
|
|
10
|
+
tags: ["reviewed"],
|
|
11
|
+
evaluationIds: ["eval-1"],
|
|
12
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
13
|
+
...owner,
|
|
14
|
+
});
|
|
15
|
+
await store.append({
|
|
16
|
+
id: "feedback-2",
|
|
17
|
+
runId: "feedback-run-a",
|
|
18
|
+
rating: 0,
|
|
19
|
+
createdAt: "2026-01-01T00:00:01.000Z",
|
|
20
|
+
...owner,
|
|
21
|
+
});
|
|
22
|
+
const first = await store.query({ ...owner, runId: "feedback-run-a", limit: 1 });
|
|
23
|
+
if (first.items.length !== 1 || !first.nextCursor)
|
|
24
|
+
throw new Error("feedback first page is invalid");
|
|
25
|
+
const second = await store.query({ ...owner, runId: "feedback-run-a", cursor: first.nextCursor, limit: 1 });
|
|
26
|
+
if (second.items.length !== 1 || second.items[0]?.id === first.items[0]?.id)
|
|
27
|
+
throw new Error("feedback cursor did not advance");
|
|
28
|
+
const linked = await store.query({ ...owner, evaluationId: "eval-1" });
|
|
29
|
+
if (linked.items.length !== 1 || linked.items[0]?.id !== "feedback-1")
|
|
30
|
+
throw new Error("feedback evaluation filter failed");
|
|
31
|
+
if (await store.delete({ id: "feedback-1", tenantId: "feedback-tenant", userId: "other" })) {
|
|
32
|
+
throw new Error("cross-owner feedback deletion succeeded");
|
|
33
|
+
}
|
|
34
|
+
if (!await store.delete({ id: "feedback-1", ...owner }))
|
|
35
|
+
throw new Error("owned feedback deletion failed");
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=feedback.js.map
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { PersistencePage, SessionEntry, SessionEntryQuery } from "../contracts.js";
|
|
2
2
|
/** Current shared persistence schema version for production database adapters. */
|
|
3
|
-
export declare const PERSISTENCE_SCHEMA_VERSION =
|
|
4
|
-
export type PersistenceTableName = "prism_tenants" | "prism_accounts" | "prism_users" | "prism_agent_definitions" | "prism_sessions" | "prism_branches" | "prism_session_entries" | "prism_session_append_idempotency" | "prism_runs" | "prism_agent_events" | "prism_tool_calls" | "prism_usage" | "prism_retention_policies" | "prism_migrations";
|
|
5
|
-
export type PersistenceColumnType = "text" | "integer" | "boolean" | "json" | "timestamp";
|
|
3
|
+
export declare const PERSISTENCE_SCHEMA_VERSION = 3;
|
|
4
|
+
export type PersistenceTableName = "prism_tenants" | "prism_accounts" | "prism_users" | "prism_agent_definitions" | "prism_sessions" | "prism_branches" | "prism_session_entries" | "prism_session_append_idempotency" | "prism_runs" | "prism_agent_events" | "prism_tool_calls" | "prism_usage" | "prism_run_feedback" | "prism_retention_policies" | "prism_migrations";
|
|
5
|
+
export type PersistenceColumnType = "text" | "integer" | "number" | "boolean" | "json" | "timestamp";
|
|
6
6
|
export interface PersistenceColumnDefinition {
|
|
7
7
|
readonly name: string;
|
|
8
8
|
readonly type: PersistenceColumnType;
|
|
9
9
|
readonly nullable?: boolean;
|
|
10
|
+
/** Portable SQL default literal, if the schema requires one. */
|
|
11
|
+
readonly defaultValue?: string;
|
|
10
12
|
/** Column participates in tenant isolation boundaries when true. */
|
|
11
13
|
readonly tenantScoped?: boolean;
|
|
12
14
|
}
|
|
@@ -42,6 +44,8 @@ export interface PersistenceMigrationStep {
|
|
|
42
44
|
readonly version: number;
|
|
43
45
|
readonly name: string;
|
|
44
46
|
readonly description?: string;
|
|
47
|
+
/** SHA-256 of the canonical checked-in migration schema content. */
|
|
48
|
+
readonly checksum: string;
|
|
45
49
|
}
|
|
46
50
|
/** Versioned migration expectations shared by production database adapters. */
|
|
47
51
|
export interface PersistenceMigrationContract {
|
|
@@ -74,6 +78,46 @@ export declare function tenantScopedUniqueKey(baseColumns: readonly string[], te
|
|
|
74
78
|
export declare function assertPersistenceSchemaModel(model: PersistenceSchemaModel): void;
|
|
75
79
|
/** Assert migration steps are strictly increasing and end at the target schema version. */
|
|
76
80
|
export declare function assertPersistenceMigrationContract(contract: PersistenceMigrationContract): void;
|
|
81
|
+
export type PersistenceSchemaDialect = "sqlite" | "postgres";
|
|
82
|
+
export interface PersistenceSchemaShapeColumn {
|
|
83
|
+
readonly name: string;
|
|
84
|
+
readonly type: string;
|
|
85
|
+
readonly nullable: boolean;
|
|
86
|
+
readonly defaultValue?: string;
|
|
87
|
+
}
|
|
88
|
+
export interface PersistenceSchemaShapeForeignKey {
|
|
89
|
+
readonly columns: readonly string[];
|
|
90
|
+
readonly referencesTable: string;
|
|
91
|
+
readonly referencesColumns: readonly string[];
|
|
92
|
+
}
|
|
93
|
+
export interface PersistenceSchemaShapeTable {
|
|
94
|
+
readonly name: string;
|
|
95
|
+
readonly columns: readonly PersistenceSchemaShapeColumn[];
|
|
96
|
+
readonly primaryKey: readonly string[];
|
|
97
|
+
readonly uniqueKeys: readonly (readonly string[])[];
|
|
98
|
+
readonly foreignKeys: readonly PersistenceSchemaShapeForeignKey[];
|
|
99
|
+
}
|
|
100
|
+
export interface PersistenceSchemaShapeIndex {
|
|
101
|
+
readonly name: string;
|
|
102
|
+
readonly table: string;
|
|
103
|
+
readonly columns: readonly string[];
|
|
104
|
+
readonly unique: boolean;
|
|
105
|
+
}
|
|
106
|
+
export interface PersistenceSchemaShape {
|
|
107
|
+
readonly tables: readonly PersistenceSchemaShapeTable[];
|
|
108
|
+
readonly indexes: readonly PersistenceSchemaShapeIndex[];
|
|
109
|
+
}
|
|
110
|
+
/** Compare bounded dialect catalog output against every required schema-v3 detail. */
|
|
111
|
+
export declare function assertPersistenceSchemaShape(shape: PersistenceSchemaShape, dialect: PersistenceSchemaDialect, model?: PersistenceSchemaModel): void;
|
|
112
|
+
export interface AppliedPersistenceMigration {
|
|
113
|
+
readonly name: string;
|
|
114
|
+
readonly version: string;
|
|
115
|
+
readonly checksum: string | null;
|
|
116
|
+
}
|
|
117
|
+
/** Reject altered migration history before any new DDL or runtime write. */
|
|
118
|
+
export declare function assertAppliedPersistenceMigrations(contract: PersistenceMigrationContract, applied: readonly AppliedPersistenceMigration[]): {
|
|
119
|
+
readonly legacyChecksums: boolean;
|
|
120
|
+
};
|
|
77
121
|
/**
|
|
78
122
|
* Assert a dialect-local adapter exposes the canonical table and index names.
|
|
79
123
|
* Adapters pass the table/index names their migration runner created.
|
|
@@ -82,13 +126,7 @@ export declare function assertAdapterSchemaMatchesModel(adapterTables: readonly
|
|
|
82
126
|
/** Guard adapter SQL tests: reject obvious value interpolation into statement text. */
|
|
83
127
|
export declare function assertParameterizedQuery(sql: string, boundValues: readonly unknown[]): void;
|
|
84
128
|
/** Simulate migration up + reopen: applied steps must match the contract in order. */
|
|
85
|
-
export declare function assertMigrationUpAndReopen(contract: PersistenceMigrationContract, appliedAfterUp: readonly
|
|
86
|
-
readonly name: string;
|
|
87
|
-
readonly version: string;
|
|
88
|
-
}[], appliedAfterReopen: readonly {
|
|
89
|
-
readonly name: string;
|
|
90
|
-
readonly version: string;
|
|
91
|
-
}[]): void;
|
|
129
|
+
export declare function assertMigrationUpAndReopen(contract: PersistenceMigrationContract, appliedAfterUp: readonly AppliedPersistenceMigration[], appliedAfterReopen: readonly AppliedPersistenceMigration[]): void;
|
|
92
130
|
export interface PersistenceQueryConformanceFixture {
|
|
93
131
|
readonly seedEntries: (entries: readonly SessionEntry[]) => Promise<void> | void;
|
|
94
132
|
readonly queryEntries: (query: SessionEntryQuery) => Promise<PersistencePage<SessionEntry>>;
|