@arnilo/prism 0.5.3 → 0.5.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 +13 -0
- package/README.md +10 -10
- package/dist/agent-run-state.d.ts +3 -2
- package/dist/agent-run-state.js +1 -2
- package/dist/agent-session/session/assemble.js +4 -2
- package/dist/contracts-core/run-limits.d.ts +30 -8
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/run-limits.d.ts +27 -8
- package/dist/run-limits.js +93 -29
- package/docs/agent-session-runtime.md +1 -1
- package/docs/credential-storage.md +1 -1
- package/docs/graft.md +1 -1
- package/docs/{0.1.0-readiness.md → history/0.1.0-readiness.md} +8 -8
- package/docs/history/README.md +11 -0
- package/docs/{migrate-to-0.4.md → history/migrate-to-0.4.md} +4 -4
- package/docs/history/migration-0.0.md +738 -0
- package/docs/history/migration-0.1.md +151 -0
- package/docs/history/migration-0.2.md +173 -0
- package/docs/history/migration-0.3.md +39 -0
- package/docs/history/migration-0.4.md +6 -0
- package/docs/{persistence-credentials-multimodality-primitives.md → history/persistence-credentials-multimodality-primitives.md} +13 -13
- package/docs/history/release-handoffs.md +848 -0
- package/docs/{workflow-orchestration-primitives.md → history/workflow-orchestration-primitives.md} +11 -11
- package/docs/host-security.md +1 -1
- package/docs/index.md +157 -143
- package/docs/migrate-to-0.5.md +11 -1
- package/docs/migration.md +19 -1042
- package/docs/multimodal-content.md +1 -1
- package/docs/performance.md +1 -1
- package/docs/postgres-persistence.md +1 -1
- package/docs/provider-packages.md +20 -20
- package/docs/release-and-install.md +57 -842
- package/docs/runs-and-usage.md +2 -2
- package/docs/session-stores-and-branching.md +1 -122
- package/docs/sqlite-persistence.md +1 -1
- package/docs/workflows.md +1 -1
- package/package.json +1 -1
- /package/docs/{workflow-tui-primitives.md → history/workflow-tui-primitives.md} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
## [0.5.5] - 2026-09-08
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
- **Byte limits are per-frame, not cumulative.** `maxRequestBytes`/`maxResponseBytes` now compare each individual provider frame (request payload, streamed event) against the cap instead of a run-lifetime byte sum, so long autonomous coding runs (many megabyte-scale frames, each far under 64 MiB) no longer trip `Run limit exceeded: maxRequestBytes` after ~40 turns. HARD stays per-frame process safety — one giant frame still fails, 40 legal frames never do. Cumulative byte counters remain in snapshots/telemetry; token/turn/wall/cost axes stay cumulative.
|
|
5
|
+
|
|
6
|
+
## [0.5.4] - 2026-09-08 (plan 067)
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- **Run limits: process-safety HARD split from host policy.** `DEFAULT_RUN_LIMITS` stays the unconfigured fence (turns 16, attempts 24, tool rounds 8, calls 32, wall 120s, bytes 8 MiB, tokens 40k/10k/50k), but `HARD_RUN_LIMITS` shrinks to the two process-integrity axes (`maxRequestBytes`/`maxResponseBytes`, 64 MiB) so a bug cannot OOM the host through a giant provider frame — and hosts can legally raise or disable everything else. Policy axes accept `number | null`: omit for the default, set a positive safe integer, or set `null` to disable the axis (disabled wall still honors `RunOptions.signal`). Resolution stays narrowing-only with `null` as +Infinity (agent 16 + run `null` → 16); an omitted `maxProviderAttempts` lifts to at least a raised/disabled `maxTurns` so attempts cannot undercut turns. Byte axes reject `null` and >64 MiB. The former `$10k` `maxCost` ceiling is removed (any finite non-negative amount). Documented ceiling: vendors that omit usage charge zero to token counters; a configured `maxCost` remains fail-closed. Durable checkpoints omit `deadlineAt` on no-wall runs; older checkpoints with a deadline still resume. New `ResolvedRunLimits` type; `resolveRunLimits` returns it.
|
|
10
|
+
|
|
11
|
+
### Removed
|
|
12
|
+
- **`HARD_MAX_RUN_COST` export** (was a `$10k` validation ceiling; `maxCost` now accepts any finite non-negative amount plus one currency).
|
|
13
|
+
|
|
1
14
|
## [0.5.3] - 2026-09-08
|
|
2
15
|
|
|
3
16
|
### Fixed
|
package/README.md
CHANGED
|
@@ -162,16 +162,16 @@ printf '{"id":"1","command":"prompt","params":{"input":"Hi"}}\n' \
|
|
|
162
162
|
|
|
163
163
|
| package | version | notes |
|
|
164
164
|
| --- | --- | --- |
|
|
165
|
-
| `@arnilo/prism` | 0.5.
|
|
166
|
-
| `@arnilo/prism-coding-tools` | 0.5.
|
|
167
|
-
| `@arnilo/prism-core` | 0.5.
|
|
168
|
-
| `@arnilo/prism-providers` | 0.5.
|
|
169
|
-
| `@arnilo/prism-acp-agent` | 0.5.
|
|
170
|
-
| `@arnilo/prism-ag-ui` | 0.5.
|
|
171
|
-
| `@arnilo/prism-mcp` | 0.5.
|
|
172
|
-
| `@arnilo/prism-memory` | 0.5.
|
|
173
|
-
| `@arnilo/prism-office` | 0.5.
|
|
174
|
-
| `@arnilo/prism-web-tools` | 0.5.
|
|
165
|
+
| `@arnilo/prism` | 0.5.5 | core — runtime, CLI/RPC, templates, docs |
|
|
166
|
+
| `@arnilo/prism-coding-tools` | 0.5.5 | family — /agent, /security, /document-reader, /openapi, /computer-use-linux, /dev, /caveman, /ponytail, /impeccable subpaths |
|
|
167
|
+
| `@arnilo/prism-core` | 0.5.5 | family — /runtime, /sessions, /governance, /credentials, /enterprise, /work, /validation subpaths |
|
|
168
|
+
| `@arnilo/prism-providers` | 0.5.5 | family — all provider adapters as `/<adapter>` subpaths |
|
|
169
|
+
| `@arnilo/prism-acp-agent` | 0.5.5 | capability — ACP adapter |
|
|
170
|
+
| `@arnilo/prism-ag-ui` | 0.5.5 | capability — AG-UI/A2A/A2UI adapter |
|
|
171
|
+
| `@arnilo/prism-mcp` | 0.5.5 | capability — MCP client/server/OAuth interop |
|
|
172
|
+
| `@arnilo/prism-memory` | 0.5.5 | capability — memory plus /rag, /compaction/*, /graft, /wiki subpaths |
|
|
173
|
+
| `@arnilo/prism-office` | 0.5.5 | capability — /documents, /sheets, /diagrams subpaths |
|
|
174
|
+
| `@arnilo/prism-web-tools` | 0.5.5 | capability — Brave/Exa/Firecrawl plus peer-gated /browser and /obscura subpaths |
|
|
175
175
|
<!-- generated:package-truth:inventory end -->
|
|
176
176
|
|
|
177
177
|
## Scripts
|
|
@@ -28,7 +28,8 @@ export interface StoredAgentRunState extends AgentRunState {
|
|
|
28
28
|
readonly stickyDecisions?: readonly StickyDecision[];
|
|
29
29
|
readonly interruptBeforeTool?: boolean;
|
|
30
30
|
readonly counters: RunLimitCounters;
|
|
31
|
-
|
|
31
|
+
/** Wall deadline; absent when the run has no wall limit. Old snapshots with a deadline still parse. */
|
|
32
|
+
readonly deadlineAt?: string;
|
|
32
33
|
/** Loop-local durable state captured by the strategy's snapshot hook at suspension. */
|
|
33
34
|
readonly loopState?: {
|
|
34
35
|
readonly name: string;
|
|
@@ -84,7 +85,7 @@ export declare function initialAgentRunState(input: {
|
|
|
84
85
|
readonly leafId?: string;
|
|
85
86
|
readonly model: ModelConfig;
|
|
86
87
|
readonly counters: RunLimitCounters;
|
|
87
|
-
readonly deadlineAt
|
|
88
|
+
readonly deadlineAt?: string;
|
|
88
89
|
readonly status: "suspended" | "running";
|
|
89
90
|
readonly interruption?: AgentRunInterruption;
|
|
90
91
|
readonly messages?: readonly Message[];
|
package/dist/agent-run-state.js
CHANGED
|
@@ -165,8 +165,7 @@ export function parseAgentRunState(value, version) {
|
|
|
165
165
|
!state.sessionId ||
|
|
166
166
|
!state.model ||
|
|
167
167
|
!state.status ||
|
|
168
|
-
!state.counters
|
|
169
|
-
!state.deadlineAt) {
|
|
168
|
+
!state.counters) {
|
|
170
169
|
throw new AgentRunStateError("Malformed agent run state");
|
|
171
170
|
}
|
|
172
171
|
if (state.pendingCalls !== undefined &&
|
|
@@ -106,7 +106,8 @@ async function assembleRoundContext(params) {
|
|
|
106
106
|
for (const message of inputMessages)
|
|
107
107
|
await session.appendMessage(message, runId);
|
|
108
108
|
await session.autoCompact(runId, options, controller.signal, inputMessages);
|
|
109
|
-
|
|
109
|
+
// Disabled cap (`null`) maps to +Infinity so loop comparisons never trip (`n >= null` would be true).
|
|
110
|
+
const maxToolRounds = resolveRunLimits(session.agent.config.limits, options.limits).maxToolRounds ?? Number.POSITIVE_INFINITY;
|
|
110
111
|
const systemInstructions = composeSystemPrompt(mergeSystemPromptConfig(session.agent.config.systemPrompt, options.systemPrompt), {
|
|
111
112
|
base: session.agent.config.instructions,
|
|
112
113
|
});
|
|
@@ -317,7 +318,8 @@ export async function executeRun(session, input, options, runId, resumed) {
|
|
|
317
318
|
deadlineAt: resumed?.state?.deadlineAt,
|
|
318
319
|
});
|
|
319
320
|
session.activeLimits = limits;
|
|
320
|
-
|
|
321
|
+
const hasFiniteTokenCap = (value) => typeof value === "number" && Number.isFinite(value);
|
|
322
|
+
session.activeLimitOutputBuffer = [session.agent.config.limits, requestedLimits].some((value) => hasFiniteTokenCap(value?.maxOutputTokens) || hasFiniteTokenCap(value?.maxTotalTokens) || value?.maxCost !== undefined);
|
|
321
323
|
try {
|
|
322
324
|
const ctx = await assembleRoundContext({
|
|
323
325
|
session,
|
|
@@ -2,17 +2,39 @@
|
|
|
2
2
|
* Moved verbatim from contracts-core.ts; public surface unchanged behind the barrel. */
|
|
3
3
|
import type { ProviderTurnResult, ToolResult } from "../contracts-protocol.js";
|
|
4
4
|
import type { Message, ToolCallContent } from "./content.js";
|
|
5
|
+
/**
|
|
6
|
+
* Host-authored run limits. Policy axes accept `null` to explicitly disable the cap
|
|
7
|
+
* (process safety still caps request/response bytes, which reject `null`). Omitted keys
|
|
8
|
+
* resolve to `DEFAULT_RUN_LIMITS`.
|
|
9
|
+
*/
|
|
5
10
|
export interface RunLimits {
|
|
6
|
-
readonly maxTurns?: number;
|
|
7
|
-
readonly maxProviderAttempts?: number;
|
|
8
|
-
readonly maxToolRounds?: number;
|
|
9
|
-
readonly maxToolCalls?: number;
|
|
10
|
-
readonly maxWallTimeMs?: number;
|
|
11
|
+
readonly maxTurns?: number | null;
|
|
12
|
+
readonly maxProviderAttempts?: number | null;
|
|
13
|
+
readonly maxToolRounds?: number | null;
|
|
14
|
+
readonly maxToolCalls?: number | null;
|
|
15
|
+
readonly maxWallTimeMs?: number | null;
|
|
11
16
|
readonly maxRequestBytes?: number;
|
|
12
17
|
readonly maxResponseBytes?: number;
|
|
13
|
-
readonly maxInputTokens?: number;
|
|
14
|
-
readonly maxOutputTokens?: number;
|
|
15
|
-
readonly maxTotalTokens?: number;
|
|
18
|
+
readonly maxInputTokens?: number | null;
|
|
19
|
+
readonly maxOutputTokens?: number | null;
|
|
20
|
+
readonly maxTotalTokens?: number | null;
|
|
21
|
+
readonly maxCost?: {
|
|
22
|
+
readonly amount: number;
|
|
23
|
+
readonly currency: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** Fully resolved limits after `resolveRunLimits`: every policy axis is a finite cap or `null` (disabled). */
|
|
27
|
+
export interface ResolvedRunLimits {
|
|
28
|
+
readonly maxTurns: number | null;
|
|
29
|
+
readonly maxProviderAttempts: number | null;
|
|
30
|
+
readonly maxToolRounds: number | null;
|
|
31
|
+
readonly maxToolCalls: number | null;
|
|
32
|
+
readonly maxWallTimeMs: number | null;
|
|
33
|
+
readonly maxRequestBytes: number;
|
|
34
|
+
readonly maxResponseBytes: number;
|
|
35
|
+
readonly maxInputTokens: number | null;
|
|
36
|
+
readonly maxOutputTokens: number | null;
|
|
37
|
+
readonly maxTotalTokens: number | null;
|
|
16
38
|
readonly maxCost?: {
|
|
17
39
|
readonly amount: number;
|
|
18
40
|
readonly currency: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -88,7 +88,7 @@ export { createDefaultRetryPolicy, isTransientErrorInfo, waitForRetry } from "./
|
|
|
88
88
|
export type { BatchedRunLedgerOptions } from "./run-ledger.js";
|
|
89
89
|
export { createBatchedRunLedger, DEFAULT_LEDGER_BATCH_BYTES, DEFAULT_LEDGER_BATCH_DELAY_MS, DEFAULT_LEDGER_BATCH_ENTRIES, HARD_LEDGER_BATCH_BYTES, HARD_LEDGER_BATCH_DELAY_MS, HARD_LEDGER_BATCH_ENTRIES, isFlushableRunLedger, } from "./run-ledger.js";
|
|
90
90
|
export type { RunLimitTrackerOptions } from "./run-limits.js";
|
|
91
|
-
export { createRunLimitTracker, DEFAULT_RUN_LIMITS,
|
|
91
|
+
export { createRunLimitTracker, DEFAULT_RUN_LIMITS, HARD_RUN_LIMITS, RunLimitError, RunLimitTracker, resolveRunLimits, } from "./run-limits.js";
|
|
92
92
|
export { createSecureAgent } from "./secure-agent.js";
|
|
93
93
|
export type { PermissionDecision, PermissionPolicy, PermissionRequest, TrustDecision, TrustPolicy, TrustRequest } from "./security.js";
|
|
94
94
|
export { assertPermission, assertTrusted, checkPermission, createStaticPermissionPolicy, createStaticTrustPolicy, denialToErrorInfo, isTrusted, PermissionDeniedError, TrustDeniedError, } from "./security.js";
|
|
@@ -119,5 +119,5 @@ export { trimTrailingSlashes } from "./trim-trailing-slashes.js";
|
|
|
119
119
|
export type { ResolvedUseCaseModel, ResolveUseCaseModelInput, UseCaseModelBinding, } from "./use-case-model.js";
|
|
120
120
|
export { resolveUseCaseModel, resolveUseCaseModelBinding, useCaseCredentialProviderId, } from "./use-case-model.js";
|
|
121
121
|
export declare const name = "prism";
|
|
122
|
-
export declare const version = "0.5.
|
|
122
|
+
export declare const version = "0.5.5";
|
|
123
123
|
export declare const description = "Agent harness for AI providers, agents, sessions, and tools.";
|
package/dist/index.js
CHANGED
|
@@ -47,7 +47,7 @@ export { createSecretRedactor, errorToErrorInfo, redactAgentEvent, redactMessage
|
|
|
47
47
|
export { loadBinaryResource, loadJsonResource, loadManifestResource, loadTextResource } from "./resources.js";
|
|
48
48
|
export { createDefaultRetryPolicy, isTransientErrorInfo, waitForRetry } from "./retry.js";
|
|
49
49
|
export { createBatchedRunLedger, DEFAULT_LEDGER_BATCH_BYTES, DEFAULT_LEDGER_BATCH_DELAY_MS, DEFAULT_LEDGER_BATCH_ENTRIES, HARD_LEDGER_BATCH_BYTES, HARD_LEDGER_BATCH_DELAY_MS, HARD_LEDGER_BATCH_ENTRIES, isFlushableRunLedger, } from "./run-ledger.js";
|
|
50
|
-
export { createRunLimitTracker, DEFAULT_RUN_LIMITS,
|
|
50
|
+
export { createRunLimitTracker, DEFAULT_RUN_LIMITS, HARD_RUN_LIMITS, RunLimitError, RunLimitTracker, resolveRunLimits, } from "./run-limits.js";
|
|
51
51
|
export { createSecureAgent } from "./secure-agent.js";
|
|
52
52
|
export { assertPermission, assertTrusted, checkPermission, createStaticPermissionPolicy, createStaticTrustPolicy, denialToErrorInfo, isTrusted, PermissionDeniedError, TrustDeniedError, } from "./security.js";
|
|
53
53
|
export { createMemorySessionStore, createSessionEntry, getSessionBranchEntries, listSessionBranches, rebuildSessionContext, } from "./session-stores.js";
|
|
@@ -66,6 +66,6 @@ export { createToolParameterValidator, createToolRegistry, dispatchToolCall, fil
|
|
|
66
66
|
export { trimTrailingSlashes } from "./trim-trailing-slashes.js";
|
|
67
67
|
export { resolveUseCaseModel, resolveUseCaseModelBinding, useCaseCredentialProviderId, } from "./use-case-model.js";
|
|
68
68
|
export const name = "prism";
|
|
69
|
-
export const version = "0.5.
|
|
69
|
+
export const version = "0.5.5";
|
|
70
70
|
export const description = "Agent harness for AI providers, agents, sessions, and tools.";
|
|
71
71
|
//# sourceMappingURL=index.js.map
|
package/dist/run-limits.d.ts
CHANGED
|
@@ -1,7 +1,25 @@
|
|
|
1
|
-
import type { RunLimitBreach, RunLimitCounters, RunLimitName, RunLimits, Usage } from "./contracts.js";
|
|
2
|
-
export declare const DEFAULT_RUN_LIMITS:
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type { ResolvedRunLimits, RunLimitBreach, RunLimitCounters, RunLimitName, RunLimits, Usage } from "./contracts.js";
|
|
2
|
+
export declare const DEFAULT_RUN_LIMITS: Readonly<{
|
|
3
|
+
maxTurns: 16;
|
|
4
|
+
maxProviderAttempts: 24;
|
|
5
|
+
maxToolRounds: 8;
|
|
6
|
+
maxToolCalls: 32;
|
|
7
|
+
maxWallTimeMs: 120000;
|
|
8
|
+
maxRequestBytes: number;
|
|
9
|
+
maxResponseBytes: number;
|
|
10
|
+
maxInputTokens: 40000;
|
|
11
|
+
maxOutputTokens: 10000;
|
|
12
|
+
maxTotalTokens: 50000;
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* Process-safety ceilings that exist so a bug cannot OOM the host via JSON.parse of giant
|
|
16
|
+
* provider frames. Product axes (turns, wall time, tokens, …) have no hard cap: hosts set
|
|
17
|
+
* them per workload, and `null` explicitly disables an axis.
|
|
18
|
+
*/
|
|
19
|
+
export declare const HARD_RUN_LIMITS: Readonly<{
|
|
20
|
+
maxRequestBytes: number;
|
|
21
|
+
maxResponseBytes: number;
|
|
22
|
+
}>;
|
|
5
23
|
export declare class RunLimitError extends Error {
|
|
6
24
|
readonly breach: RunLimitBreach;
|
|
7
25
|
readonly code = "ERR_PRISM_RUN_LIMIT";
|
|
@@ -14,16 +32,17 @@ export interface RunLimitTrackerOptions {
|
|
|
14
32
|
readonly deadlineAt?: string;
|
|
15
33
|
}
|
|
16
34
|
/** Validate one host-authored layer. Defaults are applied only after inheritance is resolved. */
|
|
17
|
-
export declare function resolveRunLimits(agent?: RunLimits, run?: RunLimits): Readonly<
|
|
35
|
+
export declare function resolveRunLimits(agent?: RunLimits, run?: RunLimits): Readonly<ResolvedRunLimits>;
|
|
18
36
|
export declare class RunLimitTracker {
|
|
19
37
|
private readonly options;
|
|
20
|
-
readonly limits: Readonly<
|
|
38
|
+
readonly limits: Readonly<ResolvedRunLimits>;
|
|
21
39
|
private readonly startedAt;
|
|
22
|
-
|
|
40
|
+
/** Wall deadline ISO string; absent when the run has no wall limit. */
|
|
41
|
+
readonly deadlineAt: string | undefined;
|
|
23
42
|
private readonly counters;
|
|
24
43
|
private timer?;
|
|
25
44
|
private exceeded?;
|
|
26
|
-
constructor(limits: Readonly<
|
|
45
|
+
constructor(limits: Readonly<ResolvedRunLimits>, options?: RunLimitTrackerOptions);
|
|
27
46
|
get breach(): RunLimitBreach | undefined;
|
|
28
47
|
snapshot(): RunLimitCounters;
|
|
29
48
|
dispose(): void;
|
package/dist/run-limits.js
CHANGED
|
@@ -10,20 +10,17 @@ export const DEFAULT_RUN_LIMITS = Object.freeze({
|
|
|
10
10
|
maxOutputTokens: 10_000,
|
|
11
11
|
maxTotalTokens: 50_000,
|
|
12
12
|
});
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Process-safety ceilings that exist so a bug cannot OOM the host via JSON.parse of giant
|
|
15
|
+
* provider frames. Product axes (turns, wall time, tokens, …) have no hard cap: hosts set
|
|
16
|
+
* them per workload, and `null` explicitly disables an axis.
|
|
17
|
+
*/
|
|
14
18
|
export const HARD_RUN_LIMITS = Object.freeze({
|
|
15
|
-
maxTurns: 64,
|
|
16
|
-
maxProviderAttempts: 256,
|
|
17
|
-
maxToolRounds: 64,
|
|
18
|
-
maxToolCalls: 256,
|
|
19
|
-
maxWallTimeMs: 30 * 60_000,
|
|
20
19
|
maxRequestBytes: 64 * 1024 * 1024,
|
|
21
20
|
maxResponseBytes: 64 * 1024 * 1024,
|
|
22
|
-
maxInputTokens: 1_000_000,
|
|
23
|
-
maxOutputTokens: 250_000,
|
|
24
|
-
maxTotalTokens: 1_000_000,
|
|
25
21
|
});
|
|
26
22
|
const LIMIT_NAMES = Object.keys(DEFAULT_RUN_LIMITS);
|
|
23
|
+
const POLICY_NAMES = LIMIT_NAMES.filter((name) => name !== "maxRequestBytes" && name !== "maxResponseBytes");
|
|
27
24
|
const COUNTER_FOR = {
|
|
28
25
|
maxTurns: "turns",
|
|
29
26
|
maxProviderAttempts: "providerAttempts",
|
|
@@ -46,16 +43,44 @@ export class RunLimitError extends Error {
|
|
|
46
43
|
this.name = "RunLimitError";
|
|
47
44
|
}
|
|
48
45
|
}
|
|
46
|
+
/** `null` means "no cap" and behaves as +Infinity when narrowing against a finite layer. */
|
|
47
|
+
function minCap(a, b) {
|
|
48
|
+
if (a === undefined)
|
|
49
|
+
return b;
|
|
50
|
+
if (b === undefined)
|
|
51
|
+
return a;
|
|
52
|
+
if (a === null)
|
|
53
|
+
return b;
|
|
54
|
+
if (b === null)
|
|
55
|
+
return a;
|
|
56
|
+
return Math.min(a, b);
|
|
57
|
+
}
|
|
49
58
|
/** Validate one host-authored layer. Defaults are applied only after inheritance is resolved. */
|
|
50
59
|
export function resolveRunLimits(agent, run) {
|
|
51
60
|
const base = agent ? validateLimits(agent) : undefined;
|
|
52
61
|
const override = run ? validateLimits(run) : undefined;
|
|
53
62
|
const resolved = { ...DEFAULT_RUN_LIMITS };
|
|
54
|
-
for (const name of
|
|
63
|
+
for (const name of POLICY_NAMES) {
|
|
64
|
+
const narrowed = minCap(base?.[name], override?.[name]);
|
|
65
|
+
resolved[name] = narrowed !== undefined ? narrowed : DEFAULT_RUN_LIMITS[name];
|
|
66
|
+
}
|
|
67
|
+
for (const name of ["maxRequestBytes", "maxResponseBytes"]) {
|
|
55
68
|
if (base?.[name] !== undefined)
|
|
56
|
-
resolved[name] = base[name];
|
|
69
|
+
resolved[name] = Math.min(resolved[name], base[name]);
|
|
57
70
|
if (override?.[name] !== undefined)
|
|
58
|
-
resolved[name] =
|
|
71
|
+
resolved[name] = Math.min(resolved[name], override[name]);
|
|
72
|
+
}
|
|
73
|
+
// A raised/disabled maxTurns must not be silently undercut by the attempts default:
|
|
74
|
+
// generate-then-tool-loop needs at least one attempt per turn (plus retries).
|
|
75
|
+
if (base?.maxProviderAttempts === undefined && override?.maxProviderAttempts === undefined) {
|
|
76
|
+
const turns = resolved.maxTurns;
|
|
77
|
+
resolved.maxProviderAttempts = turns === null ? null : Math.max(DEFAULT_RUN_LIMITS.maxProviderAttempts, turns);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
const attempts = resolved.maxProviderAttempts;
|
|
81
|
+
const turns = resolved.maxTurns;
|
|
82
|
+
if (attempts !== null && turns !== null && attempts < turns)
|
|
83
|
+
resolved.maxProviderAttempts = turns;
|
|
59
84
|
}
|
|
60
85
|
const maxCost = override?.maxCost ?? base?.maxCost;
|
|
61
86
|
return Object.freeze({
|
|
@@ -80,14 +105,21 @@ function validateLimits(input) {
|
|
|
80
105
|
const value = input[name];
|
|
81
106
|
if (value === undefined)
|
|
82
107
|
continue;
|
|
83
|
-
if (
|
|
84
|
-
|
|
108
|
+
if (name === "maxRequestBytes" || name === "maxResponseBytes") {
|
|
109
|
+
// Process-safety axes: a giant frame cannot be host-approved away, so `null` is rejected.
|
|
110
|
+
if (value === null || !Number.isSafeInteger(value) || value < 1 || value > HARD_RUN_LIMITS[name])
|
|
111
|
+
throw new TypeError(`${name} must be a positive safe integer at most ${HARD_RUN_LIMITS[name]}`);
|
|
112
|
+
continue;
|
|
85
113
|
}
|
|
114
|
+
if (value === null)
|
|
115
|
+
continue;
|
|
116
|
+
if (!Number.isSafeInteger(value) || value < 1)
|
|
117
|
+
throw new TypeError(`${name} must be a positive safe integer or null to disable`);
|
|
86
118
|
}
|
|
87
119
|
if (input.maxCost) {
|
|
88
120
|
const { amount, currency } = input.maxCost;
|
|
89
|
-
if (!Number.isFinite(amount) || amount < 0 ||
|
|
90
|
-
throw new TypeError(
|
|
121
|
+
if (!Number.isFinite(amount) || amount < 0 || !currency.trim())
|
|
122
|
+
throw new TypeError("maxCost requires a finite non-negative amount and currency");
|
|
91
123
|
}
|
|
92
124
|
return input;
|
|
93
125
|
}
|
|
@@ -95,6 +127,7 @@ export class RunLimitTracker {
|
|
|
95
127
|
options;
|
|
96
128
|
limits;
|
|
97
129
|
startedAt = performance.now();
|
|
130
|
+
/** Wall deadline ISO string; absent when the run has no wall limit. */
|
|
98
131
|
deadlineAt;
|
|
99
132
|
counters;
|
|
100
133
|
timer;
|
|
@@ -121,21 +154,46 @@ export class RunLimitTracker {
|
|
|
121
154
|
throw new TypeError("Run limit snapshot must contain finite non-negative counters");
|
|
122
155
|
}
|
|
123
156
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
157
|
+
// A restored durable deadline wins even when the wall limit is now disabled (never drop an existing wall).
|
|
158
|
+
const deadline = options.deadlineAt
|
|
159
|
+
? Date.parse(options.deadlineAt)
|
|
160
|
+
: limits.maxWallTimeMs === null
|
|
161
|
+
? undefined
|
|
162
|
+
: Date.now() + limits.maxWallTimeMs;
|
|
163
|
+
if (deadline === undefined) {
|
|
164
|
+
this.deadlineAt = undefined;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
if (!Number.isFinite(deadline))
|
|
168
|
+
throw new TypeError("Run limit deadlineAt is invalid");
|
|
169
|
+
this.deadlineAt = new Date(deadline).toISOString();
|
|
170
|
+
const remaining = Math.max(0, deadline - Date.now());
|
|
171
|
+
// Node clamps setTimeout delays above 2^31-1 (~24.8 days) to 1ms, which would breach early;
|
|
172
|
+
// arm capped and re-check the real clock before exceeding.
|
|
173
|
+
const arm = (delay) => {
|
|
174
|
+
this.timer = setTimeout(() => {
|
|
175
|
+
const left = deadline - Date.now();
|
|
176
|
+
if (left > 0)
|
|
177
|
+
arm(Math.min(left, 2_147_483_647));
|
|
178
|
+
else
|
|
179
|
+
this.exceed("maxWallTimeMs", limits.maxWallTimeMs ?? 0);
|
|
180
|
+
}, delay);
|
|
181
|
+
this.timer.unref?.();
|
|
182
|
+
};
|
|
183
|
+
arm(Math.min(remaining, 2_147_483_647));
|
|
184
|
+
if (remaining === 0)
|
|
185
|
+
this.exceed("maxWallTimeMs", limits.maxWallTimeMs ?? 0);
|
|
186
|
+
}
|
|
133
187
|
}
|
|
134
188
|
get breach() {
|
|
135
189
|
return this.exceeded;
|
|
136
190
|
}
|
|
137
191
|
snapshot() {
|
|
138
|
-
|
|
192
|
+
const elapsed = Math.ceil(performance.now() - this.startedAt);
|
|
193
|
+
return {
|
|
194
|
+
...this.counters,
|
|
195
|
+
wallTimeMs: this.limits.maxWallTimeMs === null ? elapsed : Math.min(this.limits.maxWallTimeMs, elapsed),
|
|
196
|
+
};
|
|
139
197
|
}
|
|
140
198
|
dispose() {
|
|
141
199
|
if (this.timer)
|
|
@@ -150,8 +208,14 @@ export class RunLimitTracker {
|
|
|
150
208
|
if (!Number.isSafeInteger(observed))
|
|
151
209
|
this.exceed(limit, Number.MAX_SAFE_INTEGER + 1);
|
|
152
210
|
this.counters[counter] = observed;
|
|
153
|
-
|
|
154
|
-
|
|
211
|
+
const cap = this.limits[limit];
|
|
212
|
+
if (cap === null)
|
|
213
|
+
return;
|
|
214
|
+
// Byte caps are per-frame (HARD exists so one giant provider frame cannot OOM the host),
|
|
215
|
+
// not run-lifetime sums; every other axis stays cumulative.
|
|
216
|
+
const against = limit === "maxRequestBytes" || limit === "maxResponseBytes" ? delta : observed;
|
|
217
|
+
if (against > cap)
|
|
218
|
+
this.exceed(limit, against);
|
|
155
219
|
}
|
|
156
220
|
recordUsage(usage) {
|
|
157
221
|
if (!usage) {
|
|
@@ -183,7 +247,7 @@ export class RunLimitTracker {
|
|
|
183
247
|
}
|
|
184
248
|
exceed(limit, observed) {
|
|
185
249
|
if (!this.exceeded) {
|
|
186
|
-
const maximum = limit === "maxCost" ? (this.limits.maxCost?.amount ?? 0) : this.limits[limit];
|
|
250
|
+
const maximum = limit === "maxCost" ? (this.limits.maxCost?.amount ?? 0) : (this.limits[limit] ?? 0);
|
|
187
251
|
this.exceeded = {
|
|
188
252
|
limit,
|
|
189
253
|
maximum,
|
|
@@ -47,7 +47,7 @@ string | Message | readonly Message[]
|
|
|
47
47
|
|
|
48
48
|
`AgentSessionConfig.store` overrides `AgentConfig.store`; otherwise the session gets a private memory store. `AgentSessionConfig.leafId` selects the branch leaf to resume from.
|
|
49
49
|
|
|
50
|
-
`AgentConfig.limits` sets run ceilings; `RunOptions.limits` may only narrow configured agent values. Limits cover turns, provider attempts, tool rounds/calls, wall time, request/response bytes, tokens, and optional single-currency cost. A breach emits one `run_limit_exceeded` event and throws `AgentRunError` with `result.limit`; see [Runs and usage ledger](runs-and-usage.md#run-limits).
|
|
50
|
+
`AgentConfig.limits` sets run ceilings; `RunOptions.limits` may only narrow configured agent values (`null` counts as no cap, so a configured finite ceiling still wins). Limits cover turns, provider attempts, tool rounds/calls, wall time, request/response bytes, tokens, and optional single-currency cost. Policy axes accept `null` (0.5.4) to disable the axis; request/response bytes reject `null` and stay process-hard at 64 MiB. A breach emits one `run_limit_exceeded` event and throws `AgentRunError` with `result.limit`; see [Runs and usage ledger](runs-and-usage.md#run-limits).
|
|
51
51
|
|
|
52
52
|
`RunOptions.model` can override the request model for a run. Model overrides append a `model_change` entry. `AgentConfig.inputLayout` selects the default input assembly layout (`"cache_aware"` by default, or opt-in `"legacy"`); `RunOptions.inputLayout` wins for one run. `AgentConfig.thinkingLevel` / `RunOptions.thinkingLevel` (run wins) is the session thinking intent — Prism snaps it onto the request after host `providerOptions`. `AgentConfig.providerOptions`/`RunOptions.providerOptions` supply generic provider request options (session/cache/header/compat/extra hints only — provider-level timeout/retry hints were removed in 0.1.5). Kernel construction always stamps `options.sessionId`/`cacheKey` from `session.id` when missing; `createSessionCachePolicy` is an overlay, not required. Use `RunOptions.signal`/host abort controllers for timeouts and `AgentConfig.retry`/`RunOptions.retry` for retry. `AgentConfig.providerRequestPolicies`/`RunOptions.providerRequestPolicies` run before `AIProvider.generate()` and before `provider_request` middleware. `AgentConfig.systemPrompt` and `RunOptions.systemPrompt` add explicit layered system prompt contributions; `RunOptions.systemPrompt: false` disables configured prompt layers for that run while keeping `AgentConfig.instructions` as the base path. `RunOptions.compaction` can enable auto-compaction for that run or use `false` to disable configured auto-compaction. `RunOptions.retry` can enable provider-turn retry for that run or use `false` to disable configured retry. `RunOptions.metadata` is merged with agent/session metadata for assembly, provider requests, and tool contexts. Run tool-round limits via `RunOptions.limits.maxToolRounds`. `RunOptions.signal` is bridged into the per-run abort signal passed to assembly, providers, tools, auto-compaction, and retry backoff.
|
|
53
53
|
|
|
@@ -269,5 +269,5 @@ MCP credentials remain host inputs: resolve them before constructing client `req
|
|
|
269
269
|
- [Azure OpenAI / Foundry](providers/azure.md) / [Amazon Bedrock](providers/bedrock.md) / [Google Vertex AI](providers/vertex.md): enterprise workload-identity credential callbacks
|
|
270
270
|
- [Web search, fetch, and extraction](web-tools.md): late-bound Brave/Exa/Firecrawl credentials
|
|
271
271
|
- [Security/auth/trust](settings-auth-trust-security.md): host-owned settings/credentials boundaries
|
|
272
|
-
- [Persistence, credentials, and multimodality primitives](persistence-credentials-multimodality-primitives.md): Plan 056 threat model and conformance matrix rows 7–10
|
|
272
|
+
- [Persistence, credentials, and multimodality primitives](history/persistence-credentials-multimodality-primitives.md): Plan 056 threat model and conformance matrix rows 7–10
|
|
273
273
|
- `@arnilo/prism`: `CredentialResolver`, `OAuthCredentialStore`, `createMemoryCredentialStore()`
|
package/docs/graft.md
CHANGED
|
@@ -105,7 +105,7 @@ await kernel.load([
|
|
|
105
105
|
- Import alone registers nothing (`sideEffects: false`); no timers, watchers, or network. The only child processes are budgeted graft CLI calls.
|
|
106
106
|
- Retrieval happens in-process via Prism primitives (context provider, injector, tool_result middleware) — no external hook shims.
|
|
107
107
|
- Ask result shape is parsed tolerantly (`nodes|results|matches|hits`) because graft is pre-1.0; formatters emit pointers (`title` + `file:line` + `[[wikilink]]`), never source bodies.
|
|
108
|
-
-
|
|
108
|
+
- Deliberate opt-out from family/umbrella packaging (like Caveman/Ponytail) — opt-in install only.
|
|
109
109
|
- Multi-repo layouts work as upstream graft defines them (workspaces, submodules with `--follow-submodules`, sibling repos); point `projectDir` at the graft root that owns the target repo.
|
|
110
110
|
|
|
111
111
|
## Security and performance notes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 0.1.0 / 1.0 Readiness Gates
|
|
2
2
|
|
|
3
|
-
Status: **0.5.
|
|
3
|
+
Status: **0.5.4** is the current release line (10 active packages, family subpaths, `^0.5.4` peers); **0.3.3** was the terminal 0.3.x cut; **0.1.7** was the terminal 0.1.x baseline; **1.0** readiness remains operator-gated, not automatic.
|
|
4
4
|
|
|
5
5
|
This page distills runnable readiness gates into one command-per-gate table.
|
|
6
6
|
The **Last evidence** column records the 0.1.0-tree snapshot (plan 012 Tasks
|
|
@@ -10,17 +10,17 @@ the release tree before cutting 1.0. The decision to cut 1.0 stays with the
|
|
|
10
10
|
operator after operator-gated legs run in a protected environment and Phase
|
|
11
11
|
12 demand evidence exists.
|
|
12
12
|
|
|
13
|
-
Evidence trail: [`docs/_evidence/review-coverage-2026-07-26-phase-11.md`](
|
|
14
|
-
(addenda 0–9), [`docs/release-and-install.md`](
|
|
15
|
-
[`docs/migration.md`](
|
|
16
|
-
[`docs/public-contracts.md`](
|
|
17
|
-
The per-phase review-coverage evidence archive lives in [`docs/_evidence/`](
|
|
13
|
+
Evidence trail: [`docs/_evidence/review-coverage-2026-07-26-phase-11.md`](.././_evidence/review-coverage-2026-07-26-phase-11.md)
|
|
14
|
+
(addenda 0–9), [`docs/release-and-install.md`](.././release-and-install.md),
|
|
15
|
+
[`docs/migration.md`](.././migration.md), [`docs/performance.md`](.././performance.md),
|
|
16
|
+
[`docs/public-contracts.md`](.././public-contracts.md) (frozen 0.1.x contract).
|
|
17
|
+
The per-phase review-coverage evidence archive lives in [`docs/_evidence/`](.././_evidence/)
|
|
18
18
|
(plans 067–079, releases 0.0.4–0.0.16; tarball-excluded, kept in-repo for audit).
|
|
19
19
|
Historical release lines (0.0.16 floor → 0.0.27 Phase 10 ACP interop → 0.1.0)
|
|
20
20
|
keep their per-phase evidence in the pages above; this page records the 0.2.6
|
|
21
21
|
snapshot (plan 026) with the 0.1.x tables below as the historical record.
|
|
22
22
|
|
|
23
|
-
## Current line (0.5.
|
|
23
|
+
## Current line (0.5.4)
|
|
24
24
|
|
|
25
25
|
| Item | Status |
|
|
26
26
|
|---|---|
|
|
@@ -82,7 +82,7 @@ material in published artifacts) and exact version-range drift. A genuine break
|
|
|
82
82
|
requires `--allow-break` **and** a `docs/migration.md` entry mentioning the
|
|
83
83
|
version. The frozen 0.1.x contract (declaration/exports, events, protocol
|
|
84
84
|
payloads, migration checksums, patch-release compatibility promise) is
|
|
85
|
-
published in [docs/public-contracts.md](public-contracts.md).
|
|
85
|
+
published in [docs/public-contracts.md](../public-contracts.md).
|
|
86
86
|
|
|
87
87
|
**Baseline maintenance:** `scripts/compat-baseline/` must stay committed.
|
|
88
88
|
Regenerate only after review with `node scripts/release.mjs gate --update-baseline`,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Documentation archive
|
|
2
|
+
|
|
3
|
+
Historical records kept verbatim for audit and freeze tests. Not read on the
|
|
4
|
+
hot path — do not link these pages from current-contract docs; link the live
|
|
5
|
+
page instead and note the archive where provenance matters.
|
|
6
|
+
|
|
7
|
+
- `migration-0.0.md` … `migration-0.4.md`: per-era release migrations (the live [migration guide](../migration.md) keeps 0.5.x only).
|
|
8
|
+
- `release-handoffs.md`: operator publish handoffs per release line (the live [release and install](../release-and-install.md) page keeps current install, inventory, gates, and peer policy).
|
|
9
|
+
- `migrate-to-0.4.md`: retired 0.3-era package reorganization guide (current line: [migrate-to-0.5.md](../migrate-to-0.5.md)).
|
|
10
|
+
- `0.1.0-readiness.md`: frozen 0.1.x readiness record.
|
|
11
|
+
- `persistence-credentials-multimodality-primitives.md`, `workflow-orchestration-primitives.md`, `workflow-tui-primitives.md`: plan-era primitive-review inventories.
|
|
@@ -306,7 +306,7 @@ API page says so.
|
|
|
306
306
|
|
|
307
307
|
## Related APIs
|
|
308
308
|
|
|
309
|
-
- [Release and install](release-and-install.md): package contents, peer rules, and publication.
|
|
310
|
-
- [Migration guide](migration.md): migration history for prior releases.
|
|
311
|
-
- [Provider packages](provider-packages.md): provider adapter behavior.
|
|
312
|
-
- [Host security guide](host-security.md): preserve host trust boundaries while upgrading.
|
|
309
|
+
- [Release and install](../release-and-install.md): package contents, peer rules, and publication.
|
|
310
|
+
- [Migration guide](../migration.md): migration history for prior releases.
|
|
311
|
+
- [Provider packages](../provider-packages.md): provider adapter behavior.
|
|
312
|
+
- [Host security guide](../host-security.md): preserve host trust boundaries while upgrading.
|