@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,132 @@
|
|
|
1
|
+
import type { OwnershipScope } from "./contracts.js";
|
|
2
|
+
/**
|
|
3
|
+
* Durable artifact co-work review types (Phase 9 / 0.0.14). Core exports types only;
|
|
4
|
+
* the service + delivery-link signer live in `@arnilo/prism-server`. Prism persists bounded
|
|
5
|
+
* metadata, revisions, approvals, and delivery references — never file bodies (hosts own blobs).
|
|
6
|
+
*/
|
|
7
|
+
/** Review state of an artifact's latest revision. */
|
|
8
|
+
export type ArtifactApprovalState = "pending" | "approved" | "rejected";
|
|
9
|
+
/** A resolved decision on one revision (pending is the absence of a decision). */
|
|
10
|
+
export type ArtifactDecisionState = Exclude<ArtifactApprovalState, "pending">;
|
|
11
|
+
/** Bounded citation / data-source reference. Host resolves the body; Prism stores the ref only. */
|
|
12
|
+
export interface ArtifactCitation {
|
|
13
|
+
readonly uri: string;
|
|
14
|
+
readonly title?: string;
|
|
15
|
+
/** Data-source kind (e.g. "web", "database", "upload"); host-defined, bounded. */
|
|
16
|
+
readonly kind?: string;
|
|
17
|
+
}
|
|
18
|
+
/** One immutable revision of an artifact. `uri`/`hash` reference host-owned content. */
|
|
19
|
+
export interface ArtifactRevision {
|
|
20
|
+
/** 1-based, monotonic within the artifact. */
|
|
21
|
+
readonly version: number;
|
|
22
|
+
/** Host-owned blob reference (redacted; never a local filesystem path). */
|
|
23
|
+
readonly uri: string;
|
|
24
|
+
readonly mime: string;
|
|
25
|
+
/** Host-computed content hash for integrity compare. */
|
|
26
|
+
readonly hash: string;
|
|
27
|
+
/** Expected body byte length; required when a blob store is wired for delivery. */
|
|
28
|
+
readonly size?: number;
|
|
29
|
+
readonly changeNote?: string;
|
|
30
|
+
/** Run that produced this revision, if any. */
|
|
31
|
+
readonly producerRunId?: string;
|
|
32
|
+
readonly citations?: readonly ArtifactCitation[];
|
|
33
|
+
/** Preview metadata only; the host renders content. */
|
|
34
|
+
readonly preview?: Readonly<Record<string, unknown>>;
|
|
35
|
+
readonly createdAt: string;
|
|
36
|
+
}
|
|
37
|
+
/** A reviewer decision on a specific revision. */
|
|
38
|
+
export interface ArtifactApproval {
|
|
39
|
+
readonly version: number;
|
|
40
|
+
readonly state: ArtifactDecisionState;
|
|
41
|
+
/** Redacted reviewer actor reference. */
|
|
42
|
+
readonly reviewer: string;
|
|
43
|
+
/** Change-request / rejection note. */
|
|
44
|
+
readonly note?: string;
|
|
45
|
+
readonly decidedAt: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Durable artifact record. Stored as a versioned checkpoint value; the checkpoint version
|
|
49
|
+
* is the CAS counter for concurrent reviewers, distinct from revision numbers.
|
|
50
|
+
*/
|
|
51
|
+
export interface ArtifactRecord extends OwnershipScope {
|
|
52
|
+
readonly id: string;
|
|
53
|
+
readonly threadId: string;
|
|
54
|
+
readonly title?: string;
|
|
55
|
+
readonly revisions: readonly ArtifactRevision[];
|
|
56
|
+
readonly approvals: readonly ArtifactApproval[];
|
|
57
|
+
/** Last approved revision; remains recoverable after a later rejection. */
|
|
58
|
+
readonly lastValidatedVersion?: number;
|
|
59
|
+
readonly createdAt: string;
|
|
60
|
+
readonly updatedAt: string;
|
|
61
|
+
}
|
|
62
|
+
/** Signed, expiring delivery authorization. Reauthorized per download; never a bearer secret. */
|
|
63
|
+
export interface ArtifactDeliveryToken extends OwnershipScope {
|
|
64
|
+
readonly artifactId: string;
|
|
65
|
+
readonly threadId: string;
|
|
66
|
+
readonly version: number;
|
|
67
|
+
readonly issuedAt: string;
|
|
68
|
+
readonly expiresAt: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Opaque, ownership-scoped reference to one artifact body revision. The store derives its
|
|
72
|
+
* internal object key from these fields; hosts never see or store bucket/path/key internals.
|
|
73
|
+
* `size` is the expected byte length and `hash` the expected SHA-256 hex; both are verified
|
|
74
|
+
* on every put/get (fail closed on mismatch).
|
|
75
|
+
*/
|
|
76
|
+
export interface ArtifactBodyRef extends OwnershipScope {
|
|
77
|
+
readonly artifactId: string;
|
|
78
|
+
readonly threadId: string;
|
|
79
|
+
readonly version: number;
|
|
80
|
+
readonly mime: string;
|
|
81
|
+
/** Expected body byte length; verified against the actual body on put and get. */
|
|
82
|
+
readonly size: number;
|
|
83
|
+
/** Expected SHA-256 hex digest of the body; verified on put and get. */
|
|
84
|
+
readonly hash: string;
|
|
85
|
+
}
|
|
86
|
+
/** Transfer options shared by put/get/delete. */
|
|
87
|
+
export interface ArtifactBodyTransferOptions {
|
|
88
|
+
readonly signal?: AbortSignal;
|
|
89
|
+
}
|
|
90
|
+
/** Presign options: bounded TTL for the returned delivery URL. */
|
|
91
|
+
export interface ArtifactBodyPresignOptions extends ArtifactBodyTransferOptions {
|
|
92
|
+
/** Bounded by the store's presignTtlMs cap; defaults to the store default. */
|
|
93
|
+
readonly ttlMs?: number;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Host-owned blob storage contract (Phase 11 / 0.0.28). Core exports the contract only;
|
|
97
|
+
* the reference S3-compatible adapter lives in `@arnilo/prism-server/artifact-bodies`.
|
|
98
|
+
* Implementations must verify ownership on every operation, verify hash/size/MIME on
|
|
99
|
+
* put/get (fail closed), refuse delete under legal hold, and never disclose bucket/path/key
|
|
100
|
+
* in errors, telemetry, or records. All failures surface typed errors, never silent success.
|
|
101
|
+
*/
|
|
102
|
+
export interface ArtifactBodyStore {
|
|
103
|
+
/** Store a body; verifies size + SHA-256 hash against the ref before persisting. */
|
|
104
|
+
put(ref: ArtifactBodyRef, body: Uint8Array | ReadableStream<Uint8Array>, options?: ArtifactBodyTransferOptions): Promise<void>;
|
|
105
|
+
/** Retrieve a body; verifies size, MIME, and SHA-256 hash before returning bytes. */
|
|
106
|
+
get(ref: ArtifactBodyRef, options?: ArtifactBodyTransferOptions): Promise<ReadableStream<Uint8Array>>;
|
|
107
|
+
/** Delete a body; idempotent. Refuses while the resource is under legal hold. */
|
|
108
|
+
delete(ref: ArtifactBodyRef, options?: ArtifactBodyTransferOptions): Promise<void>;
|
|
109
|
+
/** Return a bounded-TTL, single-object delivery URL (never a bucket listing or wildcard). */
|
|
110
|
+
presign(ref: ArtifactBodyRef, options?: ArtifactBodyPresignOptions): Promise<string>;
|
|
111
|
+
}
|
|
112
|
+
/** Frozen ArtifactBodyStore failure reasons (fail-closed posture). */
|
|
113
|
+
export type ArtifactBodyErrorCode = "OWNERSHIP" | "HASH_MISMATCH" | "SIZE_MISMATCH" | "MIME_MISMATCH" | "HELD" | "STORE";
|
|
114
|
+
/** Well-known error codes for ArtifactBodyStore failures. */
|
|
115
|
+
export declare const ARTIFACT_BODY_ERROR_CODES: Readonly<Record<ArtifactBodyErrorCode, `ERR_PRISM_ARTIFACT_BODY_${ArtifactBodyErrorCode}`>>;
|
|
116
|
+
/** Typed ArtifactBodyStore failure; `code` is one of the frozen ERR_PRISM_ARTIFACT_BODY_* codes. */
|
|
117
|
+
export declare class ArtifactBodyStoreError extends Error {
|
|
118
|
+
readonly reason: ArtifactBodyErrorCode;
|
|
119
|
+
readonly code: `ERR_PRISM_ARTIFACT_BODY_${ArtifactBodyErrorCode}`;
|
|
120
|
+
constructor(message: string, reason: ArtifactBodyErrorCode);
|
|
121
|
+
}
|
|
122
|
+
/** Well-known checkpoint namespace for artifact records. */
|
|
123
|
+
export declare const ARTIFACT_CHECKPOINT_NAMESPACE = "prism.artifact";
|
|
124
|
+
export declare class ArtifactError extends Error {
|
|
125
|
+
readonly reason: string;
|
|
126
|
+
readonly code = "ERR_PRISM_ARTIFACT";
|
|
127
|
+
constructor(message: string, reason: string);
|
|
128
|
+
}
|
|
129
|
+
/** Checkpoint key for an artifact: thread-scoped so per-thread listing uses a key prefix. */
|
|
130
|
+
export declare function artifactCheckpointKey(threadId: string, artifactId: string): string;
|
|
131
|
+
/** Current review state: the decision on the latest revision, or pending when undecided. */
|
|
132
|
+
export declare function artifactApprovalState(record: ArtifactRecord): ArtifactApprovalState;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Well-known error codes for ArtifactBodyStore failures. */
|
|
2
|
+
export const ARTIFACT_BODY_ERROR_CODES = {
|
|
3
|
+
OWNERSHIP: "ERR_PRISM_ARTIFACT_BODY_OWNERSHIP",
|
|
4
|
+
HASH_MISMATCH: "ERR_PRISM_ARTIFACT_BODY_HASH_MISMATCH",
|
|
5
|
+
SIZE_MISMATCH: "ERR_PRISM_ARTIFACT_BODY_SIZE_MISMATCH",
|
|
6
|
+
MIME_MISMATCH: "ERR_PRISM_ARTIFACT_BODY_MIME_MISMATCH",
|
|
7
|
+
HELD: "ERR_PRISM_ARTIFACT_BODY_HELD",
|
|
8
|
+
STORE: "ERR_PRISM_ARTIFACT_BODY_STORE",
|
|
9
|
+
};
|
|
10
|
+
/** Typed ArtifactBodyStore failure; `code` is one of the frozen ERR_PRISM_ARTIFACT_BODY_* codes. */
|
|
11
|
+
export class ArtifactBodyStoreError extends Error {
|
|
12
|
+
reason;
|
|
13
|
+
code;
|
|
14
|
+
constructor(message, reason) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.reason = reason;
|
|
17
|
+
this.name = "ArtifactBodyStoreError";
|
|
18
|
+
this.code = ARTIFACT_BODY_ERROR_CODES[reason];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Well-known checkpoint namespace for artifact records. */
|
|
22
|
+
export const ARTIFACT_CHECKPOINT_NAMESPACE = "prism.artifact";
|
|
23
|
+
export class ArtifactError extends Error {
|
|
24
|
+
reason;
|
|
25
|
+
code = "ERR_PRISM_ARTIFACT";
|
|
26
|
+
constructor(message, reason) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.reason = reason;
|
|
29
|
+
this.name = "ArtifactError";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Checkpoint key for an artifact: thread-scoped so per-thread listing uses a key prefix. */
|
|
33
|
+
export function artifactCheckpointKey(threadId, artifactId) {
|
|
34
|
+
return `${threadId}:${artifactId}`;
|
|
35
|
+
}
|
|
36
|
+
/** Current review state: the decision on the latest revision, or pending when undecided. */
|
|
37
|
+
export function artifactApprovalState(record) {
|
|
38
|
+
const latest = record.revisions[record.revisions.length - 1];
|
|
39
|
+
if (latest === undefined)
|
|
40
|
+
return "pending";
|
|
41
|
+
const decision = record.approvals.find((approval) => approval.version === latest.version);
|
|
42
|
+
return decision?.state ?? "pending";
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=artifacts.js.map
|
package/dist/cache-helpers.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export function sanitizeCacheKey(value, maxLength) {
|
|
2
|
-
const key = value
|
|
2
|
+
const key = value
|
|
3
|
+
?.replace(/[^A-Za-z0-9_.:-]+/g, "-")
|
|
4
|
+
.replace(/^-+|-+$/g, "")
|
|
5
|
+
.slice(0, Math.max(0, maxLength));
|
|
3
6
|
return key || undefined;
|
|
4
7
|
}
|
|
5
8
|
export function mapCacheRetention(retention, model) {
|
|
@@ -37,7 +40,7 @@ export function cacheSavings(usage, model) {
|
|
|
37
40
|
const cacheRead = model.cost?.cacheRead;
|
|
38
41
|
if (read === undefined || input === undefined || cacheRead === undefined)
|
|
39
42
|
return undefined;
|
|
40
|
-
return read * Math.max(0, input - cacheRead) / costUnitDivisor(model.cost?.unit);
|
|
43
|
+
return (read * Math.max(0, input - cacheRead)) / costUnitDivisor(model.cost?.unit);
|
|
41
44
|
}
|
|
42
45
|
export function cacheUsageReport(usage, model) {
|
|
43
46
|
if (!usage)
|
|
@@ -48,17 +51,23 @@ export function cacheUsageReport(usage, model) {
|
|
|
48
51
|
cacheWriteTokens: usage.cacheWriteTokens ?? 0,
|
|
49
52
|
hitRate: cacheHitRate(usage),
|
|
50
53
|
estimatedSavings,
|
|
51
|
-
currency: estimatedSavings === undefined ? usage.currency : model?.cost?.currency ?? usage.currency,
|
|
54
|
+
currency: estimatedSavings === undefined ? usage.currency : (model?.cost?.currency ?? usage.currency),
|
|
52
55
|
};
|
|
53
56
|
}
|
|
54
57
|
function resolveBreakpoint(messages, breakpoint) {
|
|
55
58
|
switch (breakpoint.location) {
|
|
56
|
-
case "system_prompt":
|
|
57
|
-
|
|
58
|
-
case "
|
|
59
|
-
|
|
60
|
-
case "
|
|
61
|
-
|
|
59
|
+
case "system_prompt":
|
|
60
|
+
return messages.findIndex((message) => message.role === "system");
|
|
61
|
+
case "tools":
|
|
62
|
+
return messages.findIndex((message) => message.role === "tool" || message.content.some((block) => block.type === "tool_call" || block.type === "tool_result"));
|
|
63
|
+
case "stable_context":
|
|
64
|
+
return messages.findIndex((message) => message.role !== "system");
|
|
65
|
+
case "last_stable_message":
|
|
66
|
+
return messages.length > 1 ? messages.length - 2 : messages.length - 1;
|
|
67
|
+
case "last_user_message":
|
|
68
|
+
return findLastIndex(messages, (message) => message.role === "user");
|
|
69
|
+
case "message_id":
|
|
70
|
+
return messages.findIndex((message) => message.id === breakpoint.messageId);
|
|
62
71
|
}
|
|
63
72
|
}
|
|
64
73
|
function findLastIndex(items, predicate) {
|
package/dist/checkpoints.d.ts
CHANGED
|
@@ -6,6 +6,10 @@ export declare class CheckpointConflictError extends Error {
|
|
|
6
6
|
}
|
|
7
7
|
export interface MemoryCheckpointStoreOptions {
|
|
8
8
|
readonly maxPageSize?: number;
|
|
9
|
+
/** Capacity bound: least-recently-saved records are evicted past this count (default 10_000). */
|
|
10
|
+
readonly maxRecords?: number;
|
|
11
|
+
/** Per-record bound on the JSON-encoded value in bytes (default 1 MiB); oversized values are rejected. */
|
|
12
|
+
readonly maxValueBytes?: number;
|
|
9
13
|
}
|
|
10
14
|
/** In-process reference implementation of the generic checkpoint contract. */
|
|
11
15
|
export declare function createMemoryCheckpointStore(options?: MemoryCheckpointStoreOptions): CheckpointStore;
|
package/dist/checkpoints.js
CHANGED
|
@@ -6,10 +6,14 @@ export class CheckpointConflictError extends Error {
|
|
|
6
6
|
this.name = "CheckpointConflictError";
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
|
+
const DEFAULT_MAX_CHECKPOINT_RECORDS = 10_000;
|
|
10
|
+
const DEFAULT_MAX_CHECKPOINT_VALUE_BYTES = 1024 * 1024;
|
|
9
11
|
/** In-process reference implementation of the generic checkpoint contract. */
|
|
10
12
|
export function createMemoryCheckpointStore(options = {}) {
|
|
11
13
|
const records = new Map();
|
|
12
14
|
const maxPageSize = Math.max(1, options.maxPageSize ?? 500);
|
|
15
|
+
const maxRecords = Math.max(1, options.maxRecords ?? DEFAULT_MAX_CHECKPOINT_RECORDS);
|
|
16
|
+
const maxValueBytes = Math.max(1, options.maxValueBytes ?? DEFAULT_MAX_CHECKPOINT_VALUE_BYTES);
|
|
13
17
|
return {
|
|
14
18
|
async saveCheckpoint(input) {
|
|
15
19
|
throwIfAborted(input.signal);
|
|
@@ -32,6 +36,9 @@ export function createMemoryCheckpointStore(options = {}) {
|
|
|
32
36
|
}
|
|
33
37
|
const now = new Date().toISOString();
|
|
34
38
|
const value = cloneJson(input.value, "Checkpoint value");
|
|
39
|
+
if (new TextEncoder().encode(JSON.stringify(value)).length > maxValueBytes) {
|
|
40
|
+
throw new RangeError(`Checkpoint value exceeds maxValueBytes (${maxValueBytes})`);
|
|
41
|
+
}
|
|
35
42
|
const metadata = input.metadata === undefined ? undefined : cloneJson(input.metadata, "Checkpoint metadata");
|
|
36
43
|
const record = {
|
|
37
44
|
namespace: input.namespace,
|
|
@@ -45,7 +52,12 @@ export function createMemoryCheckpointStore(options = {}) {
|
|
|
45
52
|
updatedAt: now,
|
|
46
53
|
...(metadata === undefined ? {} : { metadata }),
|
|
47
54
|
};
|
|
55
|
+
// delete+set moves an updated record to the end, keeping Map order = least- to
|
|
56
|
+
// most-recently-saved, so capacity eviction is O(1) off the front.
|
|
57
|
+
records.delete(id);
|
|
48
58
|
records.set(id, record);
|
|
59
|
+
while (records.size > maxRecords)
|
|
60
|
+
records.delete(records.keys().next().value);
|
|
49
61
|
return record;
|
|
50
62
|
},
|
|
51
63
|
async loadCheckpoint(input) {
|
|
@@ -61,9 +73,7 @@ export function createMemoryCheckpointStore(options = {}) {
|
|
|
61
73
|
throwIfAborted(query.signal);
|
|
62
74
|
const offset = decodeCursor(query.cursor);
|
|
63
75
|
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]);
|
|
76
|
+
const categories = query.category === undefined ? undefined : new Set(Array.isArray(query.category) ? query.category : [query.category]);
|
|
67
77
|
const items = [...records.values()]
|
|
68
78
|
.filter((record) => query.namespace === undefined || record.namespace === query.namespace)
|
|
69
79
|
.filter((record) => query.keyPrefix === undefined || record.key.startsWith(query.keyPrefix))
|
|
@@ -104,14 +114,12 @@ function ownership(input) {
|
|
|
104
114
|
};
|
|
105
115
|
}
|
|
106
116
|
function ownershipMatches(expected, actual) {
|
|
107
|
-
return expected.tenantId === actual.tenantId
|
|
108
|
-
&& expected.accountId === actual.accountId
|
|
109
|
-
&& expected.userId === actual.userId;
|
|
117
|
+
return expected.tenantId === actual.tenantId && expected.accountId === actual.accountId && expected.userId === actual.userId;
|
|
110
118
|
}
|
|
111
119
|
function ownershipFilterMatches(expected, actual) {
|
|
112
|
-
return (expected.tenantId === undefined || expected.tenantId === actual.tenantId)
|
|
113
|
-
|
|
114
|
-
|
|
120
|
+
return ((expected.tenantId === undefined || expected.tenantId === actual.tenantId) &&
|
|
121
|
+
(expected.accountId === undefined || expected.accountId === actual.accountId) &&
|
|
122
|
+
(expected.userId === undefined || expected.userId === actual.userId));
|
|
115
123
|
}
|
|
116
124
|
function assertOwnership(expected, actual) {
|
|
117
125
|
if (!ownershipMatches(expected, actual)) {
|
package/dist/cli-init.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { access, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
2
1
|
import { constants as fsConstants, readFileSync } from "node:fs";
|
|
2
|
+
import { access, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
export class InitUsageError extends Error {
|
|
@@ -305,12 +305,8 @@ function buildTokens(input) {
|
|
|
305
305
|
? `2. Copy \`.env.example\` to \`.env\` and set \`${input.provider.envKey}\`.\n3. Run \`npm start\` for a live provider call.`
|
|
306
306
|
: `2. Run \`npm start\` (mock provider; no network or credentials).`;
|
|
307
307
|
const optionalDocs = [
|
|
308
|
-
...(input.withWorkflows
|
|
309
|
-
|
|
310
|
-
: []),
|
|
311
|
-
...(input.withEvals
|
|
312
|
-
? ["- `src/evals-example.ts` — deterministic scorer example using `@arnilo/prism-evals`."]
|
|
313
|
-
: []),
|
|
308
|
+
...(input.withWorkflows ? ["- `src/workflows-example.ts` — tiny DAG example using `@arnilo/prism-workflows`."] : []),
|
|
309
|
+
...(input.withEvals ? ["- `src/evals-example.ts` — deterministic scorer example using `@arnilo/prism-evals`."] : []),
|
|
314
310
|
].join("\n");
|
|
315
311
|
return {
|
|
316
312
|
__PROJECT_NAME__: input.projectName,
|
package/dist/cli-runner.d.ts
CHANGED
|
@@ -9,10 +9,6 @@ export interface CliOptions {
|
|
|
9
9
|
readonly provider?: string;
|
|
10
10
|
readonly model?: string;
|
|
11
11
|
readonly session?: string;
|
|
12
|
-
readonly config: readonly string[];
|
|
13
|
-
readonly resources: readonly string[];
|
|
14
|
-
readonly extensions: readonly string[];
|
|
15
|
-
readonly tools: readonly string[];
|
|
16
12
|
readonly system?: string;
|
|
17
13
|
readonly context: readonly string[];
|
|
18
14
|
readonly compact?: number;
|
|
@@ -46,7 +42,7 @@ export interface CliOptions {
|
|
|
46
42
|
readonly agentsMdFile?: string;
|
|
47
43
|
readonly systemMdFile?: string;
|
|
48
44
|
/** Runtime-populated: AGENTS.md/SYSTEM.md layers loaded from disk (print/json modes only;
|
|
49
|
-
|
|
45
|
+
* RPC is host-owned). Threaded into `AgentConfig.systemPrompt` by `defaultCreateSession`. */
|
|
50
46
|
readonly systemPromptLayers: readonly SystemPromptContribution[];
|
|
51
47
|
}
|
|
52
48
|
export interface CliRuntime {
|
|
@@ -67,7 +63,7 @@ export interface CliRuntime {
|
|
|
67
63
|
/** Working directory for relative `prism init` destinations (tests). */
|
|
68
64
|
readonly cwd?: string;
|
|
69
65
|
}
|
|
70
|
-
export declare const usage = "Usage: prism [--mode print|json|rpc] [-p prompt] [options]\n prism init <dir> [--provider <name>] [--with-workflows] [--with-evals] [--force]\n\nOptions:\n -p, --prompt <text> Prompt to run in print/json mode\n --provider <name> Explicit provider id (mock is built in for smoke tests)\n --model <name> Explicit model name\n --session <id> Session id\n --
|
|
66
|
+
export declare const usage = "Usage: prism [--mode print|json|rpc] [-p prompt] [options]\n prism init <dir> [--provider <name>] [--with-workflows] [--with-evals] [--force]\n\nOptions:\n -p, --prompt <text> Prompt to run in print/json mode\n --provider <name> Explicit provider id (mock is built in for smoke tests)\n --model <name> Explicit model name\n --session <id> Session id\n --system <text> System instructions\n --context <text> Context text\n --compact <entries> Auto-compaction threshold\n --max-tool-rounds <n> Maximum tool rounds\n --discover Enable workspace contribution discovery (opt-in)\n --discover-kinds <csv> Kinds to discover (default: skill; skill,tool,context,instructions)\n --no-discovery Disable discovery even if --discover is set\n --agents-config <path> App config root holding agents/<name>/AGENT.md bundles (opt-in)\n --no-agents-md Skip auto-loading <workspaceRoot>/AGENTS.md\n --no-system-md Skip auto-loading the global SYSTEM.md layer\n --agents-md-file <path> Read AGENTS.md from <path> instead (trust-gated, source: app)\n --system-md-file <path> Read SYSTEM.md from <path> instead (source: user)\n -h, --help Show this help\n";
|
|
71
67
|
export declare function parseCliArgs(argv: readonly string[]): CliOptions;
|
|
72
68
|
export declare function runCli(argv: readonly string[], runtime: CliRuntime): Promise<number>;
|
|
73
69
|
export declare function runPromptMode(session: AgentSession, options: CliOptions, stdout: Writable, mode: "print" | "json"): Promise<void>;
|
package/dist/cli-runner.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import process from "node:process";
|
|
2
|
-
import { createAgent, createMockProvider, providerDone, providerTextDelta, resolveInstructionInjectors, createContributionRegistry } from "./index.js";
|
|
3
1
|
import { readFile } from "node:fs/promises";
|
|
4
2
|
import { basename, dirname } from "node:path";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import { initUsage, runInitCommand } from "./cli-init.js";
|
|
5
5
|
import { createContributionRegistries, registerDiscoveredContributions } from "./contributions.js";
|
|
6
|
-
import {
|
|
7
|
-
import { discoverContributions } from "./node/contribution-discovery.js";
|
|
6
|
+
import { createAgent, createContributionRegistry, createMockProvider, providerDone, providerTextDelta, resolveInstructionInjectors, } from "./index.js";
|
|
8
7
|
import { discoverAgentBundles } from "./node/agent-definitions.js";
|
|
8
|
+
import { discoverContributions } from "./node/contribution-discovery.js";
|
|
9
9
|
import { registerDiscoveredInstructionInjectors } from "./node/instruction-injectors.js";
|
|
10
10
|
import { loadSystemPromptFiles } from "./node/system-project-prompts.js";
|
|
11
11
|
import { createPathTrustPolicy } from "./node/trust.js";
|
|
12
12
|
import { runRpcServer } from "./rpc.js";
|
|
13
|
-
import {
|
|
13
|
+
import { createSkillRegistry } from "./skills.js";
|
|
14
14
|
export const usage = `Usage: prism [--mode print|json|rpc] [-p prompt] [options]
|
|
15
15
|
prism init <dir> [--provider <name>] [--with-workflows] [--with-evals] [--force]
|
|
16
16
|
|
|
@@ -19,10 +19,6 @@ Options:
|
|
|
19
19
|
--provider <name> Explicit provider id (mock is built in for smoke tests)
|
|
20
20
|
--model <name> Explicit model name
|
|
21
21
|
--session <id> Session id
|
|
22
|
-
--config <path> Explicit config path (recorded, not auto-loaded)
|
|
23
|
-
--resource <uri> Explicit resource URI (recorded, not auto-loaded)
|
|
24
|
-
--extension <name> Explicit extension name (recorded, not auto-loaded)
|
|
25
|
-
--tool <name> Explicit tool name (recorded, not auto-enabled)
|
|
26
22
|
--system <text> System instructions
|
|
27
23
|
--context <text> Context text
|
|
28
24
|
--compact <entries> Auto-compaction threshold
|
|
@@ -37,8 +33,27 @@ Options:
|
|
|
37
33
|
--system-md-file <path> Read SYSTEM.md from <path> instead (source: user)
|
|
38
34
|
-h, --help Show this help
|
|
39
35
|
`;
|
|
40
|
-
const valueFlags = new Set([
|
|
36
|
+
const valueFlags = new Set([
|
|
37
|
+
"--prompt",
|
|
38
|
+
"--mode",
|
|
39
|
+
"--provider",
|
|
40
|
+
"--model",
|
|
41
|
+
"--session",
|
|
42
|
+
"--system",
|
|
43
|
+
"--context",
|
|
44
|
+
"--compact",
|
|
45
|
+
"--max-tool-rounds",
|
|
46
|
+
"--discover-kinds",
|
|
47
|
+
"--instruction",
|
|
48
|
+
"--injector-file",
|
|
49
|
+
"--agents-md-file",
|
|
50
|
+
"--system-md-file",
|
|
51
|
+
"--agents-config",
|
|
52
|
+
]);
|
|
41
53
|
const boolFlags = new Set(["--discover", "--no-discovery", "--no-agents-md", "--no-system-md"]);
|
|
54
|
+
// Known-but-inert flags: parsed by earlier builds, never wired to any behavior.
|
|
55
|
+
// Rejected loudly (rather than silently ignored) until a CLI-harness plan wires them.
|
|
56
|
+
const unsupportedFlags = new Set(["--config", "--resource", "--extension", "--tool"]);
|
|
42
57
|
const ALL_KINDS = ["skill", "tool", "context", "instructions"];
|
|
43
58
|
export function parseCliArgs(argv) {
|
|
44
59
|
let mode = "print";
|
|
@@ -58,10 +73,6 @@ export function parseCliArgs(argv) {
|
|
|
58
73
|
let agentsMdFile;
|
|
59
74
|
let systemMdFile;
|
|
60
75
|
let agentsConfig;
|
|
61
|
-
const config = [];
|
|
62
|
-
const resources = [];
|
|
63
|
-
const extensions = [];
|
|
64
|
-
const tools = [];
|
|
65
76
|
const context = [];
|
|
66
77
|
const instructions = [];
|
|
67
78
|
const injectorFiles = [];
|
|
@@ -72,6 +83,8 @@ export function parseCliArgs(argv) {
|
|
|
72
83
|
continue;
|
|
73
84
|
}
|
|
74
85
|
const name = flag === "-p" ? "--prompt" : flag;
|
|
86
|
+
if (unsupportedFlags.has(name))
|
|
87
|
+
throw new CliUsageError(`${name} is not supported in this build`);
|
|
75
88
|
if (boolFlags.has(name)) {
|
|
76
89
|
switch (name) {
|
|
77
90
|
case "--discover":
|
|
@@ -113,18 +126,6 @@ export function parseCliArgs(argv) {
|
|
|
113
126
|
case "--session":
|
|
114
127
|
session = value;
|
|
115
128
|
break;
|
|
116
|
-
case "--config":
|
|
117
|
-
config.push(value);
|
|
118
|
-
break;
|
|
119
|
-
case "--resource":
|
|
120
|
-
resources.push(value);
|
|
121
|
-
break;
|
|
122
|
-
case "--extension":
|
|
123
|
-
extensions.push(value);
|
|
124
|
-
break;
|
|
125
|
-
case "--tool":
|
|
126
|
-
tools.push(value);
|
|
127
|
-
break;
|
|
128
129
|
case "--system":
|
|
129
130
|
system = value;
|
|
130
131
|
break;
|
|
@@ -157,10 +158,39 @@ export function parseCliArgs(argv) {
|
|
|
157
158
|
break;
|
|
158
159
|
}
|
|
159
160
|
}
|
|
160
|
-
return {
|
|
161
|
+
return {
|
|
162
|
+
mode,
|
|
163
|
+
prompt,
|
|
164
|
+
provider,
|
|
165
|
+
model,
|
|
166
|
+
session,
|
|
167
|
+
system,
|
|
168
|
+
context,
|
|
169
|
+
compact,
|
|
170
|
+
maxToolRounds,
|
|
171
|
+
help,
|
|
172
|
+
discover,
|
|
173
|
+
discoverKinds,
|
|
174
|
+
noDiscovery,
|
|
175
|
+
agentsConfig,
|
|
176
|
+
discoveredSkills: [],
|
|
177
|
+
discoveredInjectors: [],
|
|
178
|
+
discoveredAgents: [],
|
|
179
|
+
instructions,
|
|
180
|
+
injectorFiles,
|
|
181
|
+
resolvedInstructionInjectors: [],
|
|
182
|
+
noAgentsMd,
|
|
183
|
+
noSystemMd,
|
|
184
|
+
agentsMdFile,
|
|
185
|
+
systemMdFile,
|
|
186
|
+
systemPromptLayers: [],
|
|
187
|
+
};
|
|
161
188
|
}
|
|
162
189
|
function parseKinds(csv, flag) {
|
|
163
|
-
const kinds = csv
|
|
190
|
+
const kinds = csv
|
|
191
|
+
.split(",")
|
|
192
|
+
.map((k) => k.trim())
|
|
193
|
+
.filter(Boolean);
|
|
164
194
|
if (kinds.length === 0)
|
|
165
195
|
throw new CliUsageError(`Invalid value for ${flag}: ${csv}`);
|
|
166
196
|
const invalid = kinds.filter((k) => !ALL_KINDS.includes(k));
|
|
@@ -193,7 +223,12 @@ export async function runCli(argv, runtime) {
|
|
|
193
223
|
}
|
|
194
224
|
try {
|
|
195
225
|
if (options.mode === "rpc") {
|
|
196
|
-
await runRpcServer({
|
|
226
|
+
await runRpcServer({
|
|
227
|
+
stdin: runtime.stdin,
|
|
228
|
+
stdout: runtime.stdout,
|
|
229
|
+
createSession: (id) => (runtime.createSession ?? defaultCreateSession)({ ...options, session: id ?? options.session }),
|
|
230
|
+
commands: runtime.commands,
|
|
231
|
+
});
|
|
197
232
|
return 0;
|
|
198
233
|
}
|
|
199
234
|
if (!options.prompt)
|
|
@@ -248,7 +283,12 @@ export async function runCli(argv, runtime) {
|
|
|
248
283
|
// the containment check (the user named it, so it's trusted by that act).
|
|
249
284
|
const layers = await loadSystemPromptFiles({
|
|
250
285
|
...(options.noAgentsMd ? {} : { workspaceRoot, trust, ...(options.agentsMdFile ? { agentsMdPath: options.agentsMdFile } : {}) }),
|
|
251
|
-
...(options.noSystemMd
|
|
286
|
+
...(options.noSystemMd
|
|
287
|
+
? {}
|
|
288
|
+
: {
|
|
289
|
+
...(runtime.globalRoot !== undefined ? { globalRoot: runtime.globalRoot } : {}),
|
|
290
|
+
...(options.systemMdFile ? { systemMdPath: options.systemMdFile } : {}),
|
|
291
|
+
}),
|
|
252
292
|
});
|
|
253
293
|
options = { ...options, systemPromptLayers: layers };
|
|
254
294
|
}
|
|
@@ -306,9 +346,7 @@ function runOptions(options) {
|
|
|
306
346
|
...(options.discover && !options.noDiscovery && options.discoveredSkills.length > 0
|
|
307
347
|
? { activeSkills: options.discoveredSkills.map((s) => s.name) }
|
|
308
348
|
: {}),
|
|
309
|
-
...(options.resolvedInstructionInjectors.length > 0
|
|
310
|
-
? { instructionInjectors: options.resolvedInstructionInjectors }
|
|
311
|
-
: {}),
|
|
349
|
+
...(options.resolvedInstructionInjectors.length > 0 ? { instructionInjectors: options.resolvedInstructionInjectors } : {}),
|
|
312
350
|
};
|
|
313
351
|
}
|
|
314
352
|
// ponytail: resolve --instruction names against discovered injectors (fail-closed) and load
|
package/dist/compaction.js
CHANGED
|
@@ -27,10 +27,11 @@ export function isCompactionEntryData(value) {
|
|
|
27
27
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
28
28
|
return false;
|
|
29
29
|
const data = value;
|
|
30
|
-
return (data.throughEntryId === undefined || typeof data.throughEntryId === "string")
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
return ((data.throughEntryId === undefined || typeof data.throughEntryId === "string") &&
|
|
31
|
+
(data.keepEntryIds === undefined ||
|
|
32
|
+
(Array.isArray(data.keepEntryIds) && data.keepEntryIds.every((item) => typeof item === "string"))) &&
|
|
33
|
+
(data.strategy === undefined || typeof data.strategy === "string") &&
|
|
34
|
+
(data.trigger === undefined || typeof data.trigger === "string"));
|
|
34
35
|
}
|
|
35
36
|
function summarize(entries) {
|
|
36
37
|
const lines = entries.flatMap((entry) => entryText(entry));
|
package/dist/config.js
CHANGED
|
@@ -31,9 +31,10 @@ function mergeObjects(base, override, path = "config") {
|
|
|
31
31
|
const childPath = `${path}.${key}`;
|
|
32
32
|
assertSafeJsonKey(key, childPath);
|
|
33
33
|
const current = result[key];
|
|
34
|
-
result[key] =
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
result[key] =
|
|
35
|
+
isPlainObject(current) && isPlainObject(value)
|
|
36
|
+
? mergeObjects(current, value, childPath)
|
|
37
|
+
: cloneJsonValue(value, childPath);
|
|
37
38
|
}
|
|
38
39
|
return result;
|
|
39
40
|
}
|
|
@@ -77,7 +78,9 @@ function isSafeJsonValue(value) {
|
|
|
77
78
|
}
|
|
78
79
|
function assertSafeJsonValue(value, path) {
|
|
79
80
|
if (Array.isArray(value)) {
|
|
80
|
-
value.forEach((entry, index) =>
|
|
81
|
+
value.forEach((entry, index) => {
|
|
82
|
+
assertSafeJsonValue(entry, `${path}[${index}]`);
|
|
83
|
+
});
|
|
81
84
|
return;
|
|
82
85
|
}
|
|
83
86
|
if (!isPlainObject(value))
|