@arnilo/prism 0.1.3 → 0.1.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 +10 -0
- package/dist/agent-approval.d.ts +49 -0
- package/dist/agent-approval.js +178 -0
- package/dist/agent-run-lifecycle.d.ts +5 -1
- package/dist/agent-run-lifecycle.js +211 -3
- package/dist/agent-session.d.ts +98 -0
- package/dist/agent-session.js +1811 -0
- package/dist/agent-tool-dispatch.d.ts +13 -0
- package/dist/agent-tool-dispatch.js +92 -0
- package/dist/agents.d.ts +16 -9
- package/dist/agents.js +14 -2267
- package/dist/cli-init.d.ts +0 -2
- package/dist/cli-init.js +0 -2
- package/dist/cli-runner.js +1 -1
- package/dist/contracts-core.d.ts +1396 -0
- package/dist/contracts-core.js +119 -0
- package/dist/contracts-protocol.d.ts +594 -0
- package/dist/contracts-protocol.js +2 -0
- package/dist/contracts-run-state.d.ts +285 -0
- package/dist/contracts-run-state.js +77 -0
- package/dist/contracts.d.ts +7 -2269
- package/dist/contracts.js +7 -192
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/rpc.js +2 -1
- package/docs/agent-loops.md +1 -1
- package/docs/agent-session-runtime.md +4 -2
- package/docs/browser-automation.md +13 -9
- package/docs/coding-agent-tools.md +1 -2
- package/docs/compaction-observational-memory.md +5 -6
- package/docs/index.md +4 -4
- package/docs/migration.md +114 -1
- package/docs/performance.md +10 -0
- package/docs/provider-conformance.md +2 -2
- package/docs/provider-layer.md +1 -1
- package/docs/provider-packages.md +1 -1
- package/docs/provider-primitives.md +1 -1
- package/docs/public-contracts.md +4 -2
- package/docs/release-and-install.md +64 -0
- package/docs/tool-execution-primitives.md +3 -3
- package/docs/tools.md +2 -2
- package/docs/use-case-model-selection.md +12 -11
- package/package.json +2 -2
package/dist/contracts.js
CHANGED
|
@@ -1,195 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Contracts barrel (0.1.4 god-module split): re-exports the full public
|
|
3
|
+
* contracts surface from the three concern-split modules
|
|
4
|
+
* (contracts-core / contracts-run-state / contracts-protocol) so the
|
|
5
|
+
* import surface of `./contracts.js` is unchanged.
|
|
5
6
|
*/
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
path;
|
|
10
|
-
code = "ERR_PRISM_DELEGATION_SUSPENDED";
|
|
11
|
-
toolCall;
|
|
12
|
-
constructor(ref, pendingDecisions,
|
|
13
|
-
/** Redacted delegation path (child ids) used when decisions carry no attribution. */
|
|
14
|
-
path) {
|
|
15
|
-
super("Delegated run suspended");
|
|
16
|
-
this.ref = ref;
|
|
17
|
-
this.pendingDecisions = pendingDecisions;
|
|
18
|
-
this.path = path;
|
|
19
|
-
this.name = "AgentDelegationSuspendedError";
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
/** Shared decision-contract violations. Unknown and foreign approval ids share one non-enumerating error. */
|
|
23
|
-
export class AgentDecisionError extends Error {
|
|
24
|
-
code;
|
|
25
|
-
constructor(code, message, options) {
|
|
26
|
-
super(message, options);
|
|
27
|
-
this.code = code;
|
|
28
|
-
this.name = "AgentDecisionError";
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
export const DEFAULT_MAX_PENDING_DECISIONS = 32;
|
|
32
|
-
export const HARD_MAX_PENDING_DECISIONS = 128;
|
|
33
|
-
export const DEFAULT_MAX_STICKY_DECISIONS = 64;
|
|
34
|
-
export const HARD_MAX_STICKY_DECISIONS = 256;
|
|
35
|
-
export const MAX_DECISION_REASON_BYTES = 2 * 1024;
|
|
36
|
-
export const HARD_MAX_DECISION_REASON_BYTES = 8 * 1024;
|
|
37
|
-
export const MAX_ELICITATION_BYTES = 16 * 1024;
|
|
38
|
-
export const HARD_MAX_ELICITATION_BYTES = 64 * 1024;
|
|
39
|
-
export const MAX_ACTION_CONSTRAINTS = 32;
|
|
40
|
-
export const HARD_MAX_ACTION_CONSTRAINTS = 64;
|
|
41
|
-
/** Maximum delegation attribution depth for surfaced nested pending decisions. */
|
|
42
|
-
export const MAX_ATTRIBUTION_DEPTH = 8;
|
|
43
|
-
export const MAX_ACTION_CONSTRAINT_BYTES = 4 * 1024;
|
|
44
|
-
export const HARD_MAX_ACTION_CONSTRAINT_BYTES = 16 * 1024;
|
|
45
|
-
export class AgentRunStateError extends Error {
|
|
46
|
-
code = "ERR_PRISM_AGENT_RUN_STATE";
|
|
47
|
-
constructor(message) {
|
|
48
|
-
super(message);
|
|
49
|
-
this.name = "AgentRunStateError";
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
/** Durable-loop contract violations: hook-less custom strategy on a durable run, invalid snapshot, or revision drift. */
|
|
53
|
-
export class AgentLoopStateError extends Error {
|
|
54
|
-
code;
|
|
55
|
-
constructor(code, message, options) {
|
|
56
|
-
super(message, options);
|
|
57
|
-
this.code = code;
|
|
58
|
-
this.name = "AgentLoopStateError";
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
export class AgentRunError extends Error {
|
|
62
|
-
result;
|
|
63
|
-
constructor(result, options) {
|
|
64
|
-
super(result.error?.message ?? (result.status === "aborted" ? "Agent run aborted" : "Agent run failed"), options);
|
|
65
|
-
this.name = "AgentRunError";
|
|
66
|
-
this.result = result;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
/** Mid-run steer queue: default pending message count (fail closed at this cap). */
|
|
70
|
-
export const DEFAULT_MAX_PENDING_STEERS = 8;
|
|
71
|
-
/** Absolute pending steer count ceiling if hosts later expose overrides. */
|
|
72
|
-
export const HARD_MAX_PENDING_STEERS = 32;
|
|
73
|
-
/** Mid-run steer queue: default total UTF-8 byte budget across pending messages. */
|
|
74
|
-
export const DEFAULT_MAX_PENDING_STEER_BYTES = 64 * 1024;
|
|
75
|
-
/** Absolute pending steer byte ceiling if hosts later expose overrides. */
|
|
76
|
-
export const HARD_MAX_PENDING_STEER_BYTES = 256 * 1024;
|
|
77
|
-
export const SESSION_ENTRY_KINDS = [
|
|
78
|
-
"message",
|
|
79
|
-
"event",
|
|
80
|
-
"summary",
|
|
81
|
-
"metadata",
|
|
82
|
-
"model_change",
|
|
83
|
-
"label",
|
|
84
|
-
"custom",
|
|
85
|
-
"compaction",
|
|
86
|
-
];
|
|
87
|
-
const SESSION_ENTRY_KIND_SET = new Set(SESSION_ENTRY_KINDS);
|
|
88
|
-
export const SESSION_ENTRY_SCHEMA_VERSION = 1;
|
|
89
|
-
export function isSessionEntryKind(value) {
|
|
90
|
-
return typeof value === "string" && SESSION_ENTRY_KIND_SET.has(value);
|
|
91
|
-
}
|
|
92
|
-
/** Host-written `SessionRecord.metadata` / session metadata key for workspace filtering. */
|
|
93
|
-
export const SESSION_SEARCH_WORKSPACE_METADATA_KEY = "workspaceRoot";
|
|
94
|
-
export const DEFAULT_SESSION_SEARCH_LIMIT = 20;
|
|
95
|
-
export const HARD_MAX_SESSION_SEARCH_LIMIT = 100;
|
|
96
|
-
export const DEFAULT_MAX_SESSION_SEARCH_QUERY_BYTES = 4 * 1024;
|
|
97
|
-
export const HARD_MAX_SESSION_SEARCH_QUERY_BYTES = 16 * 1024;
|
|
98
|
-
export const DEFAULT_MAX_SESSION_SEARCH_SNIPPET_BYTES = 512;
|
|
99
|
-
export const HARD_MAX_SESSION_SEARCH_SNIPPET_BYTES = 4 * 1024;
|
|
100
|
-
export const DEFAULT_MAX_SESSION_SEARCH_CURSOR_BYTES = 1 * 1024;
|
|
101
|
-
export const HARD_MAX_SESSION_SEARCH_CURSOR_BYTES = 4 * 1024;
|
|
102
|
-
export const DEFAULT_MAX_SESSION_SEARCH_LINEAR_SESSIONS = 1_000;
|
|
103
|
-
export const HARD_MAX_SESSION_SEARCH_LINEAR_SESSIONS = 5_000;
|
|
104
|
-
export const DEFAULT_MAX_SESSION_SEARCH_LINEAR_ENTRIES = 10_000;
|
|
105
|
-
export const HARD_MAX_SESSION_SEARCH_LINEAR_ENTRIES = 50_000;
|
|
106
|
-
export const DEFAULT_MAX_SESSION_SEARCH_LINEAR_BYTES = 8 * 1024 * 1024;
|
|
107
|
-
export const HARD_MAX_SESSION_SEARCH_LINEAR_BYTES = 64 * 1024 * 1024;
|
|
108
|
-
export const DEFAULT_MAX_SESSION_SEARCH_FTS_CANDIDATES = 1_000;
|
|
109
|
-
export const HARD_MAX_SESSION_SEARCH_FTS_CANDIDATES = 5_000;
|
|
110
|
-
/**
|
|
111
|
-
* O(1) validation before any scan/query. Applies default page limit; rejects NaN,
|
|
112
|
-
* non-positive limits, oversize query/cursor/filter strings, and invalid order.
|
|
113
|
-
*/
|
|
114
|
-
export function resolveSessionSearchQuery(query) {
|
|
115
|
-
const limit = query.limit === undefined ? DEFAULT_SESSION_SEARCH_LIMIT : query.limit;
|
|
116
|
-
if (!Number.isSafeInteger(limit) || limit < 1 || limit > HARD_MAX_SESSION_SEARCH_LIMIT) {
|
|
117
|
-
throw new TypeError(`SessionSearchQuery.limit must be a safe integer from 1 to ${HARD_MAX_SESSION_SEARCH_LIMIT}`);
|
|
118
|
-
}
|
|
119
|
-
const order = query.order ?? "desc";
|
|
120
|
-
if (order !== "asc" && order !== "desc") {
|
|
121
|
-
throw new TypeError('SessionSearchQuery.order must be "asc" or "desc"');
|
|
122
|
-
}
|
|
123
|
-
assertSearchStringBytes(query.query, "query", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
|
|
124
|
-
assertSearchStringBytes(query.cursor, "cursor", HARD_MAX_SESSION_SEARCH_CURSOR_BYTES);
|
|
125
|
-
assertSearchStringBytes(query.workspaceRoot, "workspaceRoot", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
|
|
126
|
-
assertSearchStringBytes(query.provider, "provider", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
|
|
127
|
-
assertSearchStringBytes(query.model, "model", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
|
|
128
|
-
assertSearchStringBytes(query.label, "label", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
|
|
129
|
-
assertSearchStringBytes(query.summary, "summary", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
|
|
130
|
-
assertSearchStringBytes(query.tenantId, "tenantId", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
|
|
131
|
-
assertSearchStringBytes(query.accountId, "accountId", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
|
|
132
|
-
assertSearchStringBytes(query.userId, "userId", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
|
|
133
|
-
assertSearchStringBytes(query.fromUpdatedAt, "fromUpdatedAt", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
|
|
134
|
-
assertSearchStringBytes(query.toUpdatedAt, "toUpdatedAt", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
|
|
135
|
-
return { ...query, limit, order };
|
|
136
|
-
}
|
|
137
|
-
function assertSearchStringBytes(value, name, hardMax) {
|
|
138
|
-
if (value === undefined)
|
|
139
|
-
return;
|
|
140
|
-
if (typeof value !== "string") {
|
|
141
|
-
throw new TypeError(`SessionSearchQuery.${name} must be a string`);
|
|
142
|
-
}
|
|
143
|
-
// ponytail: UTF-8 byte length via TextEncoder; upgrade only if a non-Unicode host appears.
|
|
144
|
-
const bytes = new TextEncoder().encode(value).byteLength;
|
|
145
|
-
if (bytes > hardMax) {
|
|
146
|
-
throw new TypeError(`SessionSearchQuery.${name} exceeds ${hardMax} bytes`);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
export const SESSION_SEARCH_UNSUPPORTED_CODE = "session_search_unsupported";
|
|
150
|
-
/** Thrown when a store opts out of `searchSessions` (memory `unsupported`, JSONL). */
|
|
151
|
-
export class SessionSearchUnsupportedError extends Error {
|
|
152
|
-
code = SESSION_SEARCH_UNSUPPORTED_CODE;
|
|
153
|
-
constructor(message = "session search is unsupported by this store") {
|
|
154
|
-
super(message);
|
|
155
|
-
this.name = "SessionSearchUnsupportedError";
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
export function isSessionSearchUnsupported(error) {
|
|
159
|
-
return error instanceof Error && error.code === SESSION_SEARCH_UNSUPPORTED_CODE;
|
|
160
|
-
}
|
|
161
|
-
/** Stable error code carried by `SessionAppendConflictError`. */
|
|
162
|
-
export const SESSION_APPEND_CONFLICT_CODE = "session_append_conflict";
|
|
163
|
-
/**
|
|
164
|
-
* Thrown when `SessionStore.append` rejects an entry under `SessionAppendOptions`
|
|
165
|
-
* (dangling/stale `expectedParentId`, stricter adapter CAS failure, or duplicate
|
|
166
|
-
* idempotency key for the same parent). Recognize via the stable `code` and
|
|
167
|
-
* `isSessionAppendConflict`, not message text.
|
|
168
|
-
*/
|
|
169
|
-
export class SessionAppendConflictError extends Error {
|
|
170
|
-
conflict;
|
|
171
|
-
code = SESSION_APPEND_CONFLICT_CODE;
|
|
172
|
-
constructor(conflict) {
|
|
173
|
-
const detail = conflict.idempotencyDuplicate
|
|
174
|
-
? `idempotency key already used`
|
|
175
|
-
: conflict.currentLeafId !== undefined
|
|
176
|
-
? `expected parent ${conflict.expectedParentId ?? "<none>"} does not match current leaf ${conflict.currentLeafId}`
|
|
177
|
-
: `expected parent ${conflict.expectedParentId ?? "<none>"} is unavailable`;
|
|
178
|
-
super(`session append conflict: ${detail}`);
|
|
179
|
-
this.conflict = conflict;
|
|
180
|
-
this.name = "SessionAppendConflictError";
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
/** Type guard keyed off the stable `code` (works across bundles; not message text). */
|
|
184
|
-
export function isSessionAppendConflict(error) {
|
|
185
|
-
return error instanceof Error && error.code === SESSION_APPEND_CONFLICT_CODE;
|
|
186
|
-
}
|
|
187
|
-
const SESSION_METADATA_KEY_PATTERN = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/;
|
|
188
|
-
/** Validate a top-level `SessionRecord.metadata` key used by `SessionQuery.metadataKey` filters. */
|
|
189
|
-
export function assertSessionMetadataKey(key) {
|
|
190
|
-
if (typeof key !== "string" || !SESSION_METADATA_KEY_PATTERN.test(key)) {
|
|
191
|
-
throw new RangeError("metadataKey must match /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/");
|
|
192
|
-
}
|
|
193
|
-
return key;
|
|
194
|
-
}
|
|
7
|
+
export * from "./contracts-core.js";
|
|
8
|
+
export * from "./contracts-run-state.js";
|
|
9
|
+
export * from "./contracts-protocol.js";
|
|
195
10
|
//# sourceMappingURL=contracts.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -105,5 +105,5 @@ export { createToolParameterValidator, createToolRegistry, dispatchToolCall, fil
|
|
|
105
105
|
export type { ResolvedUseCaseModel, ResolveUseCaseModelInput, UseCaseModelBinding, } from "./use-case-model.js";
|
|
106
106
|
export { resolveUseCaseModel, resolveUseCaseModelBinding, useCaseCredentialProviderId, } from "./use-case-model.js";
|
|
107
107
|
export declare const name = "prism";
|
|
108
|
-
export declare const version = "0.1.
|
|
108
|
+
export declare const version = "0.1.5";
|
|
109
109
|
export declare const description = "Agent harness for AI providers, agents, sessions, and tools.";
|
package/dist/index.js
CHANGED
|
@@ -57,6 +57,6 @@ export { DEFAULT_TOOL_RESULT_FOLD_MAX_SUMMARY_BYTES, DEFAULT_TOOL_RESULT_FOLD_MI
|
|
|
57
57
|
export { createToolParameterValidator, createToolRegistry, dispatchToolCall, filterTools } from "./tools.js";
|
|
58
58
|
export { resolveUseCaseModel, resolveUseCaseModelBinding, useCaseCredentialProviderId, } from "./use-case-model.js";
|
|
59
59
|
export const name = "prism";
|
|
60
|
-
export const version = "0.1.
|
|
60
|
+
export const version = "0.1.5";
|
|
61
61
|
export const description = "Agent harness for AI providers, agents, sessions, and tools.";
|
|
62
62
|
//# sourceMappingURL=index.js.map
|
package/dist/rpc.js
CHANGED
|
@@ -225,9 +225,10 @@ function runOptions(state, params) {
|
|
|
225
225
|
const names = stringArrayParam(params, "instructionInjectors");
|
|
226
226
|
// ponytail: fail-closed — unknown name throws (caller surfaces as RPC error), matching CLI.
|
|
227
227
|
const injectors = names.length ? resolveInstructionInjectors({ registry: state.instructionInjectors, names }) : undefined;
|
|
228
|
+
const maxToolRounds = numberParam(params, "maxToolRounds");
|
|
228
229
|
return {
|
|
229
230
|
model: modelParam(params) ?? state.model,
|
|
230
|
-
maxToolRounds:
|
|
231
|
+
...(maxToolRounds !== undefined ? { limits: { maxToolRounds } } : {}),
|
|
231
232
|
...(injectors ? { instructionInjectors: injectors } : {}),
|
|
232
233
|
};
|
|
233
234
|
}
|
package/docs/agent-loops.md
CHANGED
|
@@ -221,7 +221,7 @@ await session.run(input, { loop: twoShotLoop });
|
|
|
221
221
|
- `{ strategy: "single-shot" }` resolves to the exported `singleShotLoop`; `{ strategy: "generate-validate-revise", ... }` is mapped by `resolveLoop()` to `generateValidateReviseLoop(opts)`. An unknown `strategy` throws before the first turn. Passing an `AgentLoopStrategy` instance bypasses the options form entirely (custom-loop escape hatch).
|
|
222
222
|
- The loop is resolved once per run inside `RuntimeAgentSession.run()`, after the usual setup (provider/skills/tools resolution, history rebuild, model-change entry, input append, auto-compaction). The runtime's outer try/catch/finally, run-exclusivity, abort bridging, and subscriber close remain in place around `loop.run(ctx)`.
|
|
223
223
|
- `LoopContext.assemble(nextInput, toolResults?)` accepts an optional tool-result accumulator so `singleShotLoop` can pass its loop-local results. Bounded artifact tools append results directly to shared history, then assemble the next turn with empty new input; no second transcript path exists.
|
|
224
|
-
- `limits.maxToolRounds` bounds both `singleShotLoop` and opt-in bounded artifact tool rounds across the whole run. Artifact mode always dispatches sequentially, regardless of `toolConcurrency`; all dispatches still use existing registry/filter/permission/validator/middleware/redactor/ledger guards.
|
|
224
|
+
- `limits.maxToolRounds` bounds both `singleShotLoop` and opt-in bounded artifact tool rounds across the whole run. Artifact mode always dispatches sequentially, regardless of `toolConcurrency`; all dispatches still use existing registry/filter/permission/validator/middleware/redactor/ledger guards.
|
|
225
225
|
- `maxRevisions` (default 3) counts only failed call-free artifact candidates. Bounded artifact runs make at most `1 + maxRevisions + maxToolRounds` provider turns. A tool-round limit is terminal and returns last usage after `artifact_failed`; it does not throw.
|
|
226
226
|
- A revision cycle appends one assistant draft and one repair user message per revision to the session store, so store entries reflect every attempted draft. The original user input is stored once by the runtime and pushed into loop history once on the first turn. Repair messages are assembled as the next provider `nextInput` and only pushed into live history after that revision request has been generated, so the model never receives a duplicated repair instruction.
|
|
227
227
|
|
|
@@ -49,7 +49,7 @@ string | Message | readonly Message[]
|
|
|
49
49
|
|
|
50
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).
|
|
51
51
|
|
|
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.providerOptions`/`RunOptions.providerOptions` supply generic provider request options
|
|
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.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). 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
|
|
|
54
54
|
`RunOptions.activeSkills` selects named skills from a configured `SkillRegistry`; `RunOptions.skills` replaces a plain `Skill[]` config for one run. When `AgentConfig.skills` is a registry and neither is set, **no skills activate** unless `activateAllSkills: true` (run or agent). `skillsDisclosure` (`"progressive"` default, `"eager"` opt-in; run wins) controls catalog vs full instruction bodies; the session-owned `LoadedSkillSet` is populated by `load_skill` when the host registers `createLoadSkillTool`. `toolResultFold` (off unless the host supplies `summarize`) optionally folds aged large tool results in provider input only. See [Context and skills](context-and-skills.md).
|
|
55
55
|
|
|
@@ -120,7 +120,7 @@ const agent = createAgent({
|
|
|
120
120
|
});
|
|
121
121
|
|
|
122
122
|
const session = agent.createSession({ id: "s1" });
|
|
123
|
-
const result = await session.run("Hi", { maxToolRounds: 1, compaction: { thresholdEntries: 20, keepRecentEntries: 6 }, retry: { maxAttempts: 3, baseDelayMs: 50 } });
|
|
123
|
+
const result = await session.run("Hi", { limits: { maxToolRounds: 1 }, compaction: { thresholdEntries: 20, keepRecentEntries: 6 }, retry: { maxAttempts: 3, baseDelayMs: 50 } });
|
|
124
124
|
console.log(result.text, result.usage?.totalTokens);
|
|
125
125
|
|
|
126
126
|
for await (const event of session.stream("Follow up")) console.log(event.type);
|
|
@@ -133,6 +133,8 @@ const clone = await session.clone({ id: "s2" });
|
|
|
133
133
|
|
|
134
134
|
## Extension and configuration notes
|
|
135
135
|
|
|
136
|
+
**Internal file structure (0.1.4).** Since 0.1.4 the runtime is spread across sibling modules behind the `src/agents.ts` barrel: `src/agent-session.ts` (the `RuntimeAgentSession` class, session factories, and shared session helpers), `src/agent-run-lifecycle.ts` (resume lifecycle: `resumeAgentRun`/`resumeAgentRunStream`), `src/agent-approval.ts` (pending-decision and approval helpers), `src/agent-tool-dispatch.ts` (elicitation and tool-policy helpers), `src/agent-run-state.ts` (run-state persistence + agent fingerprint), and `src/agent-loops.ts`/`src/compaction.ts`. The public import surface is unchanged — `createAgent`/`createAgentSession`/`resumeAgentRun`/`resumeAgentRunStream` still resolve from the package entry.
|
|
137
|
+
|
|
136
138
|
The runtime calls `assembleProviderInput()` on every turn and uses only runtime-consumed values supplied on `AgentConfig`: `instructions`, `systemPrompt`, `inputBuilder`, `promptBuilder`, `inputLayout`, `context`, selected `skills`, active `tools`, `middleware`, `resourceLoader`, metadata, `compaction`, `retry`, and `RunOptions.model`/`systemPrompt`/`inputLayout`/`compaction`/`retry`. Contributions remain inert until a host passes selected values into the agent config.
|
|
137
139
|
|
|
138
140
|
`AgentConfig` no longer accepts inert `extensions`, `settings`, or `credentials` fields. Load extensions with `createExtensionKernel()` before building config; read settings in the host before passing concrete runtime options; resolve credentials at the provider edge and pass exact secret values to redaction when needed.
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## What it does
|
|
4
4
|
|
|
5
|
-
`@arnilo/prism-browser` exposes
|
|
5
|
+
`@arnilo/prism-browser` exposes six exclusive model-facing tools—`browser_open`, `browser_snapshot`, `browser_act`, `browser_close`, `browser_evaluate`, and `browser_observe`—over a host-supplied Playwright `Browser`. Prism creates one non-persistent `BrowserContext` per run, serializes actions, returns bounded AI-mode accessibility snapshots with snapshot-scoped refs, enforces egress/side-effect/upload/download/screenshot policy, and closes context/pages/listeners/quarantined downloads on close, abort, or manager disposal. Since 0.1.4 the package also rides playwright-core's existing CDP transport for bounded page evaluation, console/network observation, and network/emulation control on Chromium hosts — zero new dependencies, Prism still never launches or downloads browsers.
|
|
6
6
|
|
|
7
7
|
## When to use it
|
|
8
8
|
|
|
9
|
-
Use when an agent must interact with JavaScript-heavy or authenticated pages that search/fetch cannot cover. Prefer `@arnilo/prism-web-tools` for ordinary public retrieval. Do not use this package as a browser launcher, MCP proxy, visual planner, CDP console, or persistent profile manager.
|
|
9
|
+
Use when an agent must interact with JavaScript-heavy or authenticated pages that search/fetch cannot cover, or needs bounded page-context evaluation and console/network observation on Chromium. Prefer `@arnilo/prism-web-tools` for ordinary public retrieval. Do not use this package as a browser launcher, MCP proxy, visual planner, general-purpose CDP console (domains are allowlisted), or persistent profile manager.
|
|
10
10
|
|
|
11
11
|
## Inputs / request
|
|
12
12
|
|
|
@@ -14,14 +14,16 @@ Use when an agent must interact with JavaScript-heavy or authenticated pages tha
|
|
|
14
14
|
| --- | --- | --- |
|
|
15
15
|
| `browser_open` | optional absolute `http(s)` `url` | host Playwright `Browser` or `BrowserManager`, limits, `ExecutionPolicy`, `networkPolicy`, uploads/downloads |
|
|
16
16
|
| `browser_snapshot` | optional `pageId` | same manager/context |
|
|
17
|
-
| `browser_act` | `action` plus action-specific fields (`target`, `snapshotId`, `url`, `text`, `values`, `paths`, `downloadId`, `dialogResponse`, `pageId`, `clip`, …) | policy checked before side effects |
|
|
17
|
+
| `browser_act` | `action` plus action-specific fields (`target`, `snapshotId`, `url`, `text`, `values`, `paths`, `downloadId`, `dialogResponse`, `pageId`, `clip`, `patterns`, `offline`, `latencyMs`, `downloadKbps`, `uploadKbps`, `reset`, `width`, `height`, `mobile`, `deviceScaleFactor`, `userAgent`, …) | policy checked before side effects |
|
|
18
|
+
| `browser_evaluate` | optional `pageId`, required `expression`, optional `awaitPromise`/`timeoutMs` | CDP `Runtime.evaluate` on a per-page session (Chromium hosts) |
|
|
19
|
+
| `browser_observe` | optional `pageId` | CDP Runtime/Network ring, drain-on-read |
|
|
18
20
|
| `browser_close` | none | closes only the run-owned context, never the host Browser process |
|
|
19
21
|
|
|
20
|
-
`createBrowserTools({ browser, executionPolicy?, limits?, networkPolicy?, uploads?, downloads?, beforeSideEffect? })` builds the
|
|
22
|
+
`createBrowserTools({ browser, executionPolicy?, limits?, networkPolicy?, uploads?, downloads?, beforeSideEffect?, cdp? })` builds the six tools. `createBrowserManager(...)` exposes host lifecycle helpers `closeRun(runId)` / `close()` and `listDownloads(runId)`.
|
|
21
23
|
|
|
22
|
-
Targets accepted by `browser_act`: snapshot `ref`, `role`(+`name`), `label`, `testId`,
|
|
24
|
+
Targets accepted by `browser_act`: snapshot `ref`, `role`(+`name`), `label`, `testId`, `text`, and (0.1.4) raw `{ css }` / `{ xpath }`. `selector` strings and `evaluate` target keys stay denied; page-context JS goes through the policy-gated `browser_evaluate`. Extensions and persistent/local profiles are unsupported.
|
|
23
25
|
|
|
24
|
-
`browser_act` actions: `navigate`, `click`, `type`, `fill`, `select`, `check`, `uncheck`, `scroll`, `wait`, `dialog`, `select_page`, `upload`, `screenshot`, `download_release`.
|
|
26
|
+
`browser_act` actions: `navigate`, `click`, `type`, `fill`, `select`, `check`, `uncheck`, `scroll`, `wait`, `dialog`, `select_page`, `upload`, `screenshot`, `download_release`, and (0.1.4, CDP) `block_urls`, `unblock_urls`, `throttle`, `emulate`.
|
|
25
27
|
|
|
26
28
|
## Outputs / response / events
|
|
27
29
|
|
|
@@ -105,16 +107,18 @@ await browser.close();
|
|
|
105
107
|
- Uploads require absolute paths under `uploads.roots` (realpath-contained; symlink escapes rejected). Downloads stream into `downloads.quarantine` with SHA-256/MIME/name metadata; `download_release` requires host `approveRelease`. Screenshots return bounded `ImageContent`.
|
|
106
108
|
- Observation (`snapshot`, `wait`, open-without-url, `close`) vs mutation/high-impact (`navigate`, click/form, dialog accept, upload, download release, popup select) is classified for `ExecutionPolicy` / `beforeSideEffect`.
|
|
107
109
|
- `createSharedSandboxBrowserOptions()` aligns browser uploads/downloads with Task 1 sandbox `/workspace` and `/downloads`. `assertBrowserSandboxNetwork()` in `@arnilo/prism-coding-security` fails closed for custom Docker networks without browser egress attestation.
|
|
108
|
-
- Raw CSS
|
|
110
|
+
- Raw CSS/XPath: since 0.1.4 `{ css }` / `{ xpath }` targets resolve via Playwright's selector engine (`locator(css)` / `locator("xpath=…")`); ref resolution keeps the built-in `aria-ref=` selector with a package-owned snapshot ref table for staleness checks.
|
|
111
|
+
- CDP capabilities (0.1.4): `browser_evaluate` (bounded `Runtime.evaluate`), `browser_observe` (Runtime console/exception + Network request/response/failed events in a bounded ring with drain-on-read), and `block_urls`/`unblock_urls` (`Network.setBlockedURLs`), `throttle` (`Network.emulateNetworkConditions`), `emulate` (`Emulation.setDeviceMetricsOverride` + optional `setUserAgentOverride`). All CDP sessions are per-page via `context.newCDPSession(page)` and are detached on run close — network/emulation changes are run-scoped and reset with `browser_close`. `BrowserCdpOptions.mode` (`auto` | `on` | `off`, default `auto`) gates the surface: non-Chromium hosts or mode `off` return `ERR_PRISM_BROWSER_CDP_UNAVAILABLE` without affecting Playwright-only tools. Domains are limited to the Runtime/Network/Emulation allowlist — cookies, tracing, performance profiles, IndexedDB, and worker debugging are not exposed. CDP is not an egress bypass: page network still routes through the run's routing/blocking and `networkPolicy`.
|
|
112
|
+
- CDP bounds (0.1.4): evaluate expression ≤ `maxActionInputBytes` (64 KiB default / 256 KiB hard) and result capped at `maxEvaluateResultBytes` (64 KiB / 256 KiB) with truncation marking; `browser_observe` rings capped at `maxConsoleEntries` (200/500) and `maxNetworkRequests`; `block_urls` patterns ≤ `maxBlockedUrlPatterns` (32/128); throttle latency ≤ 120 s and throughput ≤ 1 Gbps; emulate dimensions ≤ 16 384 and scale ≤ 10, user agent ≤ 2 KiB. Evaluate is classified high-impact (arbitrary page-context code execution): `ExecutionPolicy` approval and the `beforeSideEffect` hook are mandatory, it charges the action budget, and results are marked `untrusted_external`. `browser_observe` is observation-only (no side-effect hook, no action charge) and **never captures request/response bodies, cookies, or auth headers** — only bounded URL/method/status/error-text/arg previews.
|
|
109
113
|
- Verified-state checkpoints (0.0.14): `createBrowserCheckpointLedger()` records navigation state — URL, a domain-state hash, and host-owned data refs — never serialized browser internals (cookies/storage/contexts), which are fragile and secret-bearing. Frozen caps: URL 8 KiB/16 KiB, domain-state hash 256 B/1 KiB, host-data ref 2 KiB/8 KiB (refs only, never bodies), 16/64 checkpoints per run (oldest evicted). After any resume/interruption `markResumed(runId)` marks state stale; `assertVerifiedBeforeSideEffect(runId)` fails closed until the host reloads + `verify()`s, so side effects never replay on stale state. Checkpoints are run-scoped: a conversation thread composes through the run it owns, reusing the manager's sandbox/egress/approval/limit policy above.
|
|
110
114
|
|
|
111
115
|
Observation tools declare `kind: none`; mutations are `external_mutation`/`unsupported` and fail closed on stale checkpoint state. See [tool effects](tool-effects.md).
|
|
112
116
|
|
|
113
117
|
## Security and performance notes
|
|
114
118
|
|
|
115
|
-
Import is inert. Construction fails clearly when neither `browser` nor `manager` is supplied. Browser installation, launch, version, and control endpoint are host-owned. Prism never exposes
|
|
119
|
+
Import is inert. Construction fails clearly when neither `browser` nor `manager` is supplied. Browser installation, launch, version, and control endpoint are host-owned. Prism never exposes init scripts, extensions, persistent profiles, or model-supplied Playwright launch options; CDP exposure is limited to the allowlisted Runtime/Network/Emulation surface above (evaluate is policy-gated arbitrary code execution — treat results as untrusted). Secrets and storage state must not appear in snapshots, tool results, logs, or checkpoints. Finite caps charge before context/page/action/queue/snapshot/network/artifact retention; snapshots retain no unbounded DOM, console, request, response, or trace history. Unreleased downloads are deleted on context close.
|
|
116
120
|
|
|
117
|
-
Default tests use fake Playwright APIs only. Protected live gate: `PRISM_LIVE_PLAYWRIGHT=1` (or `PRISM_TEST_PLAYWRIGHT=1`) `npm run test:live -w @arnilo/prism-browser` exercises a local loopback hostile HTML fixture for snapshot refs, stale-ref rejection,
|
|
121
|
+
Default tests use fake Playwright APIs only. Protected live gate: `PRISM_LIVE_PLAYWRIGHT=1` (or `PRISM_TEST_PLAYWRIGHT=1`) `npm run test:live -w @arnilo/prism-browser` exercises a local loopback hostile HTML fixture for snapshot refs, stale-ref rejection, css/xpath targets, private/file deny, upload containment, screenshot bounds, download quarantine/release, and the CDP leg (real evaluate, observe, and emulate). Missing browser binaries fail closed when the gate is enabled. Adversarial network-free fixtures live in `eval-fixtures.test.ts`; see [Evaluations](evaluations.md) and `examples/coding-browser-evaluation.ts`.
|
|
118
122
|
|
|
119
123
|
## Related APIs
|
|
120
124
|
|
|
@@ -151,7 +151,6 @@ Read a text or image file.
|
|
|
151
151
|
| --- | --- | --- |
|
|
152
152
|
| `maxImageBytes` | `DEFAULT_MAX_IMAGE_BYTES` (10 MB) | Reject image reads larger than this many bytes. |
|
|
153
153
|
| `transformImage` | — | Host callback `( { buffer, mimeType } ) => Promise<Buffer>` run after read, before base64. |
|
|
154
|
-
| `autoResizeImages` | — | **Deprecated.** Ignored unless `transformImage` is also set (use `transformImage` instead). |
|
|
155
154
|
| `maxLines` / `maxBytes` | 2000 / 50 KiB | Text page display limits (hard: 100,000 / 1 MiB). |
|
|
156
155
|
| `maxScanBytes` | 64 MiB | Raw bytes scanned to reach one page (hard: 1 GiB). |
|
|
157
156
|
| `operations` | local fs | Pluggable bounded `ReadOperations` backend. |
|
|
@@ -173,7 +172,7 @@ const read = createReadTool(cwd, {
|
|
|
173
172
|
| `truncation` | text reads | `TruncationResult`. |
|
|
174
173
|
| `image` | image reads | `{ mimeType, resized, bytes }`. `resized` is `true` when `transformImage` ran. |
|
|
175
174
|
|
|
176
|
-
> `autoResizeImages`
|
|
175
|
+
> `autoResizeImages` was removed in 0.1.5; untyped callers now fail closed with a `TypeError` naming `transformImage` before any filesystem access.
|
|
177
176
|
|
|
178
177
|
### `write`
|
|
179
178
|
|
|
@@ -129,9 +129,8 @@ const evidence = recallObservationalMemory(entries, "aaaaaaaaaaaa");
|
|
|
129
129
|
const memory = createObservationalMemoryRuntime({
|
|
130
130
|
session,
|
|
131
131
|
appendEntry: (entry) => store.append(entry),
|
|
132
|
-
workerProvider,
|
|
133
|
-
sessionModel: agent.config.model, // fallback when
|
|
134
|
-
// workerModel: { provider: "mock", model: "memory" }, // optional override
|
|
132
|
+
observation: { provider: workerProvider, model: workerModel },
|
|
133
|
+
sessionModel: agent.config.model, // fallback when no worker model is configured
|
|
135
134
|
maxWorkerTurns: 8,
|
|
136
135
|
maxWorkerToolCalls: 64,
|
|
137
136
|
maxWorkerResultBytes: 64 * 1024,
|
|
@@ -151,13 +150,13 @@ await kernel.load([createObservationalMemoryExtension({ recallTool: { getEntries
|
|
|
151
150
|
|
|
152
151
|
Settings resolve to nested `observation` / `reflection` / `dropper` / `context` / `retrieval` groups via `resolveObservationalMemorySettings()`. Defaults: `observation.messageTokens: 10000`, `reflection.observationTokens: 20000`, `context.compactAfterTokens: 81000`, `context.recentMessages: 8`, `context.observationsPoolMaxTokens: 20000`, `dropper.targetTokens: 10000` (from `context.observationsPoolTargetTokens`), `retrieval.pageLimit: 20`, `agentMaxTurns: 16`, `passive: false`, `debugLog: false`. Optional `context.recentMessageMaxTokens` trims the recent-message context window (oldest first) after the count limit.
|
|
153
152
|
|
|
154
|
-
|
|
153
|
+
Pre-0.0.19 flat settings keys (`observeAfterTokens`, `reflectAfterTokens`, `compactAfterTokens`, `keepRecentEntries`, `recentMessageMaxTokens`, `observationsPoolMaxTokens`, `observationsPoolTargetTokens`, `workerModel`, `thinkingLevel`, `requireExplicitModel`) were removed in 0.1.5 — pass the nested replacements instead (see [migration](migration.md) for the key-by-key table). Settings-provider JSON or untyped option objects that still carry a removed key fail closed before any worker/provider call, compaction, or session append with a `TypeError` naming the key and its replacement.
|
|
155
154
|
|
|
156
|
-
Observer/reflector/dropper may use separate providers, models, instructions, thinking levels, credentials, and `requireExplicitModel`. `dropper.policy: "lowest-relevance"` drops deterministically without a model call; default is `"model"`.
|
|
155
|
+
Observer/reflector/dropper may use separate providers, models, instructions, thinking levels, credentials, and `requireExplicitModel`. `dropper.policy: "lowest-relevance"` drops deterministically without a model call; default is `"model"`. Workers resolve only from the nested `observation` / `reflection` / `dropper` configs plus `sessionModel` fallback — the top-level `workerProvider` / `workerModel` aliases were removed in 0.1.5.
|
|
157
156
|
|
|
158
157
|
Token counting uses `estimateEntryTokens()` / `estimateMessageTokens()`.
|
|
159
158
|
|
|
160
|
-
The runtime requires host-supplied `session`, an `appendEntry` callback bound to that session's owning store/branch, and at least one worker provider (`observation.provider`
|
|
159
|
+
The runtime requires host-supplied `session`, an `appendEntry` callback bound to that session's owning store/branch, and at least one worker provider (`observation.provider` / `reflection.provider` / `dropper.provider`). Model selection uses [use-case model selection](use-case-model-selection.md): pass per-worker `model` (or settings `observation.model` / `reflection.model` / `dropper.model`) to override, and `sessionModel: agent.config.model` so workers fall back to the session model when no worker model is configured. `requireExplicitModel: true` restores the historical `missing_model` skip when no explicit worker model is set. It no longer accepts a separate `store` option because mismatched session/store pairs can append memory entries outside the active branch. After each memory append, the runtime checks the appended entry is visible at the session leaf and fails closed/restores the previous checkout if the callback points elsewhere. Optional credential resolution is explicit; missing requested credentials skip worker execution. Default credential requests use the **resolved** model's provider id.
|
|
161
160
|
|
|
162
161
|
`createObservationalMemoryCompactionStrategy()` keeps recent message entries like the default compaction strategy, renders existing observations/reflections as the summary, and returns a standard Prism compaction entry. Its `data` includes `throughEntryId`, `keepEntryIds`, `strategy`, `trigger`, and `memory: { type: "om.folded", version: 1, fullFold, observations, reflections, droppedObservationIds }`. When active observations exceed `context.observationsPoolMaxTokens`, it performs a full fold and synchronously trims lowest-relevance observations until the folded payload fits hard byte/token caps (or throws a typed error).
|
|
163
162
|
|
package/docs/index.md
CHANGED
|
@@ -25,7 +25,7 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
|
|
|
25
25
|
## Compaction/session memory
|
|
26
26
|
- [Compaction and retry policies](compaction-and-retry.md): summarize branch history and retry transient provider failures with host-replaceable policies.
|
|
27
27
|
- [LLM compaction package](compaction-llm.md): optional provider-backed strategy with finite summary/reserve/error caps, bounded redacted streaming retention, mandatory finite post-policy `model.parameters.maxTokens`, and `createCodingCompactionStrategy()` for coding handoff focus.
|
|
28
|
-
- [Observational memory compaction package](compaction-observational-memory.md): optional source-backed memory with explicit `attach()` lifecycle — **Recent exact messages**, **Observation log**, **Reflections**, **Raw-source retrieval** (exact-id recall + cursor paging); dual coverage, nested settings
|
|
28
|
+
- [Observational memory compaction package](compaction-observational-memory.md): optional source-backed memory with explicit `attach()` lifecycle — **Recent exact messages**, **Observation log**, **Reflections**, **Raw-source retrieval** (exact-id recall + cursor paging); dual coverage, **nested-only settings** (pre-0.0.19 flat keys and top-level `workerProvider`/`workerModel` aliases removed in 0.1.5; removed keys fail closed naming the nested replacement), branch-isolated `appendEntry`, secrets redaction, and inert import/extension.
|
|
29
29
|
- [Working and semantic memory](working-and-semantic-memory.md): optional `@arnilo/prism-memory` working-memory store, semantic recall, finite Embedder/VectorStore contracts, PostgreSQL/pgvector path, consent lifecycle, identity-bound redacted export, and resumable bounded rebuild.
|
|
30
30
|
- [Session stores](session-stores.md): `SessionStore` contract, `SessionAppendOptions`, `SessionAppendConflictError`, branch handles, `readBranchPath`, optional bounded `searchSessions` / `SessionIndex` (memory linear|unsupported), and dev-vs-production branch reads — start here for session persistence.
|
|
31
31
|
- [Conversations](conversations.md): durable user-scoped conversation threads (create/list/continue/branch/archive/export/delete) on session + event-ledger seams, thread-bound reconnectable replay, frozen caps, and legal-hold-aware deletion.
|
|
@@ -35,7 +35,7 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
|
|
|
35
35
|
- [SQLite persistence](sqlite-persistence.md): optional `better-sqlite3` adapter with session/run storage, checkpoints/leases, feedback, FTS `searchSessions` (migration-v4), and transactionally verified/backfilled migration metadata.
|
|
36
36
|
- [PostgreSQL persistence](postgres-persistence.md): optional pooled `pg` adapter with session/run/checkpoint/lease/feedback storage, FTS `searchSessions` (migration-v4), advisory-locked checksummed/full-shape migrations, and opt-in live conformance.
|
|
37
37
|
- [Enterprise PostgreSQL state](enterprise-postgres-state.md): optional `@arnilo/prism-enterprise-postgres` composition for durable policy/evaluation/work-idempotency/model-router/`toolEffects` state, exact ownership, checksummed migrations, and explicit cleanup.
|
|
38
|
-
- [Migration guide](migration.md): **0.0.28 → 0.1.0** release-candidate hardening (no migration); **0.0.17 → 0.1.0 upgrade matrix** (store compatibility per release line: compatible / tested migration / tested refusal, plus breaking-default callouts); **0.0.27** ACP coding-host interop (capability advertise-when, session modes/config, MCP select, lifecycle events, elicitation); **0.0.26** coding intelligence, managed processes, forge, and safe egress; **0.0.25** durable custom loops and shared human-in-the-loop decisions; **0.0.24** distributed events and recoverable tool effects; **0.0.23** enterprise PostgreSQL state adapters (async router and work reconciliation); **0.0.22** third-party behavior integrations (Caveman, Ponytail); **0.0.21** coding-tool capability gaps (`outputMode`, glob, read-before-write, delete/move, aggregator 9/4); **0.0.20** progressive skill disclosure, empty registry default, `load_skill`, priority budget demotion, optional tool-result fold; **0.0.19** observational memory lifecycle; **0.0.15** OpenAI hosted tools/continuation/Realtime, exact AI SDK v4 matrix, RAG lifecycle/reranking/trust/status, and memory export/rebuild; **0.0.14** conversations, memory consent/lifecycle, artifact co-work review, AG-UI co-work events, scoped M365/GWS OAuth connectors, browser checkpoints, device contracts, and Alibaba/Ollama providers; plus prior release migrations.
|
|
38
|
+
- [Migration guide](migration.md): **0.1.4 → 0.1.5** documented breaking cut — deprecated-option removal (the inert provider request knobs, `maxToolRounds` alias, observational-memory flat keys/worker aliases, `autoResizeImages`, `INIT_PROVIDERS`) with exact replacement table, before/after examples, and fail-closed refusal behavior; **0.0.28 → 0.1.0** release-candidate hardening (no migration); **0.0.17 → 0.1.0 upgrade matrix** (store compatibility per release line: compatible / tested migration / tested refusal, plus breaking-default callouts); **0.0.27** ACP coding-host interop (capability advertise-when, session modes/config, MCP select, lifecycle events, elicitation); **0.0.26** coding intelligence, managed processes, forge, and safe egress; **0.0.25** durable custom loops and shared human-in-the-loop decisions; **0.0.24** distributed events and recoverable tool effects; **0.0.23** enterprise PostgreSQL state adapters (async router and work reconciliation); **0.0.22** third-party behavior integrations (Caveman, Ponytail); **0.0.21** coding-tool capability gaps (`outputMode`, glob, read-before-write, delete/move, aggregator 9/4); **0.0.20** progressive skill disclosure, empty registry default, `load_skill`, priority budget demotion, optional tool-result fold; **0.0.19** observational memory lifecycle; **0.0.15** OpenAI hosted tools/continuation/Realtime, exact AI SDK v4 matrix, RAG lifecycle/reranking/trust/status, and memory export/rebuild; **0.0.14** conversations, memory consent/lifecycle, artifact co-work review, AG-UI co-work events, scoped M365/GWS OAuth connectors, browser checkpoints, device contracts, and Alibaba/Ollama providers; plus prior release migrations.
|
|
39
39
|
- [Node JSONL session store](node-jsonl-session-store.md): development-only JSONL file adapter for single-process Node hosts; no cross-process safety; `searchSessions` throws `SessionSearchUnsupportedError`.
|
|
40
40
|
- [Persistence, credentials, and multimodality primitives](persistence-credentials-multimodality-primitives.md): Plan 056 inventory — session/run-ledger/persistence contracts, credential/OAuth seams, content/resource/model capabilities, package dependency matrix, conformance matrix, and threat model for production adapters.
|
|
41
41
|
|
|
@@ -72,7 +72,7 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
|
|
|
72
72
|
- [Web search, fetch, and extraction](web-tools.md): optional host-selected Brave/Exa discovery and Firecrawl Markdown/schema tools with native fetch, stable citations, late credentials, finite limits, and explicit untrusted-content boundaries.
|
|
73
73
|
- [Work tools](work-tools.md): optional `@arnilo/prism-work-tools` identity-scoped M365 + GWS connectors (hard-coded CLI argv, draft-then-approve, state-machine idempotency, shared result shapes); 0.0.14 adds a late-bound per-identity `tokenProvider` (env-only, fail-closed).
|
|
74
74
|
- [Work connectors](work-connectors.md): connector principles, capability gates, scoped OAuth establishment (0.0.14), and out-of-scope boundaries (Slack/Teams channels not shipped) for Microsoft 365 / Google Workspace.
|
|
75
|
-
- [Browser automation](browser-automation.md): optional `@arnilo/prism-browser` with host-supplied Playwright contexts, AI-mode snapshots/refs, ordered `browser_open`/`browser_snapshot`/`browser_act`/`browser_close
|
|
75
|
+
- [Browser automation](browser-automation.md): optional `@arnilo/prism-browser` with host-supplied Playwright contexts, AI-mode snapshots/refs, ordered `browser_open`/`browser_snapshot`/`browser_act`/`browser_close` plus (0.1.4) `browser_evaluate`/`browser_observe` and CDP `block_urls`/`unblock_urls`/`throttle`/`emulate` act actions on Chromium hosts, egress/side-effect/upload/download/screenshot policy, finite page/action/snapshot/network/artifact caps, and 0.0.14 verified-state checkpoints with reload/verify-before-side-effect.
|
|
76
76
|
- [Device adapters](device-adapters.md): deny-by-default realtime voice / desktop-control contract + conformance (0.0.14); no vendor package — admission fails closed without explicit consent+sandbox+approval, stream bounds, shared `RunLimits`, redacted telemetry.
|
|
77
77
|
- [Coding agent tools](coding-agent-tools.md): optional `shell`, `read`, `write`, `edit`, `repo_list`, `repo_search`, `glob`, `delete`, and `move` definitions plus opt-in `createGitTools()` / `coding_check`, opt-in `createAskUserDecisionTool` (single/multi/free-text + durable suspend glue), and `runCodingGoalVerify`; durable plan/todo Markdown helpers with workflow `state.coding` checkpoint metadata; streamed text pages, `repo_search` `outputMode`, bounded glob, optional read-before-write, optional Git-aware (`createGitAwareRepositoryOperations`) ignore-aware enumeration with native fallback, finite Git/check/plan/ask caps, bounded image/edit reads and write/edit payloads, finite shell wall/total-output limits, secure host-owned spill cleanup, pluggable bounded operation contracts, per-path mutation serialization, and optional `ExecutionPolicy`. No PDF/trash/PTY in the 0.0.21 baseline; Phase 9 adds optional language intelligence (separate page). Limits do not sandbox host access—gate with permission/trust policy and `@arnilo/prism-coding-security`.
|
|
78
78
|
- [Language intelligence](language-intelligence.md): optional host-activated `createLanguageIntelligence` — bounded in-package LSP 3.17 JSON-RPC client (Content-Length framing), host-selected server command/args per language, workspace symbols/definitions/references/diagnostics/hover/rename; lazy spawn; URI root confinement; rename gated by `ExecutionPolicy` + atomic write/mutation queue; frozen message/diagnostic/pending/result/timeout/server caps. No `vscode-languageserver-protocol` dependency.
|
|
@@ -129,7 +129,7 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
|
|
|
129
129
|
- [Ponytail behavior integration](ponytail.md): optional `@arnilo/prism-ponytail` — upstream Ponytail skills/commands, `ponytail-mode` injector, session `ponytail-mode` persistence; resolves peer `@dietrichgebert/ponytail` or `upstreamPath`; opt-in (not in code/sdk profiles).
|
|
130
130
|
|
|
131
131
|
## Release and install
|
|
132
|
-
- [Release and install](release-and-install.md): current **0.1.
|
|
132
|
+
- [Release and install](release-and-install.md): current **0.1.5** 49-package graph (root + 48 workspace packages; plan 017 the documented breaking cut — deprecated-option removal with `docs/migration.md` `0.1.4 → 0.1.5` section and reviewed compat-baseline regeneration via `--allow-break` then `--update-baseline`: the inert provider request knobs, `RunOptions.maxToolRounds`, observational-memory flat settings keys + top-level worker aliases, `ReadToolOptions.autoResizeImages`, `INIT_PROVIDERS`; all removals fail closed naming their replacement; plan 016 internal god-module split — `agents.ts`/`contracts.ts` reorganized behind barrel re-exports with a byte-identical public entry surface, measured tree-shaking improvement in `scripts/phase16-baseline.json`, and additive `@arnilo/prism-browser` Chrome DevTools Protocol capabilities — `browser_evaluate`/`browser_observe` and `block_urls`/`unblock_urls`/`throttle`/`emulate` act actions; plan 015 dead-code and deprecation hygiene on the frozen 0.1.x line — parameterized benchmark runner `scripts/benchmark.mjs` absorbing the per-version runners, archived review-coverage evidence in `docs/_evidence/`, non-blocking unused-code sweep `npm run sweep:unused`, opt-in checkpoint persistence for loaded-skill names and read-path sets; plan 014 Alibaba provider enrichment — embeddings, video input, verified compatible-mode surface decision table; plan 013 post-release hardening — build single-flight, MCP SSE relay test, combined coverage summary, canonical manifest-count narrative, ACP modes/config persistence guidance; Phase 12 release-candidate hardening; plan 012 — freeze manifest, compatibility matrix, upgrade matrix, packed-install e2e journeys, restart-recovery evidence, capacity envelopes, security policy), exact-peer/install/tarball rules, deterministic resumable publication and publish dry-run, frozen 0.1.x compatibility and support matrix (Node/PostgreSQL/platform/provider/protocol pins and unsupported combinations, machine-checked against `scripts/phase12-freeze-manifest.json`), protected PostgreSQL gate, pinned supply-chain gates, offline tests, the 0.0.15 provider/AI-SDK/RAG/memory protected live-canary matrix, and sandbox-browser Docker/Playwright gates.
|
|
133
133
|
- [0.1.0 / 1.0 readiness gates](0.1.0-readiness.md): command-per-gate 1.0 readiness table — frozen API surface + compat gate, migration/docs tripwires, budget table, live-suite matrix, security matrix, current-line status (**0.0.23** published target), signed-publication/live-canary prerequisites for 1.0, and Phase 12 demand-evidence entry criteria.
|
|
134
134
|
- [Review coverage archive](_evidence/): per-phase evidence freezes (plans 067–079, releases 0.0.4–0.0.16) — traceability matrices, provider validation, capability/primitive/limit matrices, benchmark budgets, and artifact-diet findings; tarball-excluded, kept in-repo for audit.
|
|
135
135
|
|
package/docs/migration.md
CHANGED
|
@@ -1,6 +1,119 @@
|
|
|
1
1
|
# Migration guide
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.4 → 0.1.5 deprecated-option removal (documented breaking cut)
|
|
4
|
+
|
|
5
|
+
Release **0.1.5** (plan 017) removes the deprecated compatibility surface that 0.1.x kept after 0.0.19: the inert provider timeout/retry knobs, the `maxToolRounds` run-option alias, the pre-0.0.19 observational-memory flat keys and worker aliases, the read-tool `autoResizeImages` flag, and the `INIT_PROVIDERS` constant. This is the **documented breaking cut** announced in the 0.1.4 migration section; every other 0.1.x release keeps the compat baseline green. Three roadmap labels from the original 0.1.5 task were corrected during planning and are honored here:
|
|
6
|
+
|
|
7
|
+
1. **`RunOptions.maxToolRounds`** (not `AgentConfig.maxToolRounds`) is the removed alias → use `RunOptions.limits.maxToolRounds`. `AgentConfig.limits.maxToolRounds` and `RunLimits.maxToolRounds` stay supported.
|
|
8
|
+
2. **`ReadToolOptions.autoResizeImages`** is the removed flag; `transformImage` is the supported replacement (the roadmap text had the direction reversed).
|
|
9
|
+
3. **`INIT_PROVIDERS`** is the removed constant; `listInitProviders()` is the supported replacement that remains (the roadmap said to remove `listInitProviders`).
|
|
10
|
+
|
|
11
|
+
### Removed symbols and replacements
|
|
12
|
+
|
|
13
|
+
| Removed | Replaced by | Fail-closed behavior |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `ProviderRequestOptions.timeoutMs` | `ProviderRequest.signal` / `RunOptions.signal` (host-side abort) | removed from the type; untyped callers are refused with a `TypeError` naming the replacement before any provider call |
|
|
16
|
+
| `ProviderRequestOptions.maxRetries` | `AgentConfig.retry` / `RunOptions.retry` | same |
|
|
17
|
+
| `ProviderRequestOptions.maxRetryDelayMs` | `AgentConfig.retry` / `RunOptions.retry` | same |
|
|
18
|
+
| `RunOptions.maxToolRounds` | `RunOptions.limits.maxToolRounds` | removed from the type; untyped `{ maxToolRounds }` run input is refused before the agent starts |
|
|
19
|
+
| `ObservationalMemorySettingsInput.observeAfterTokens` | `observation.messageTokens` | flat key removed from the type; settings-provider JSON or untyped overrides carrying it throw a `TypeError` naming the nested replacement before any worker/provider call, compaction, or session append |
|
|
20
|
+
| `ObservationalMemorySettingsInput.reflectAfterTokens` | `reflection.observationTokens` | same |
|
|
21
|
+
| `ObservationalMemorySettingsInput.compactAfterTokens` | `context.compactAfterTokens` | same |
|
|
22
|
+
| `ObservationalMemorySettingsInput.keepRecentEntries` | `context.recentMessages` | same |
|
|
23
|
+
| `ObservationalMemorySettingsInput.recentMessageMaxTokens` | `context.recentMessageMaxTokens` | same |
|
|
24
|
+
| `ObservationalMemorySettingsInput.observationsPoolMaxTokens` | `context.observationsPoolMaxTokens` | same |
|
|
25
|
+
| `ObservationalMemorySettingsInput.observationsPoolTargetTokens` | `context.observationsPoolTargetTokens` | same |
|
|
26
|
+
| `ObservationalMemorySettingsInput.workerModel` | `observation.model` / `reflection.model` / `dropper.model` | same |
|
|
27
|
+
| `ObservationalMemorySettingsInput.thinkingLevel` | `observation.thinkingLevel` / `reflection.thinkingLevel` / `dropper.thinkingLevel` | same |
|
|
28
|
+
| `ObservationalMemorySettingsInput.requireExplicitModel` | `observation.requireExplicitModel` / `reflection.requireExplicitModel` / `dropper.requireExplicitModel` | same |
|
|
29
|
+
| `CreateObservationalMemoryOptions.workerProvider` / `workerModel` | `observation.provider` / `observation.model` (and the `reflection` / `dropper` equivalents) | removed from the type; the factories throw synchronously naming the replacement |
|
|
30
|
+
| `ObservationalMemoryRuntimeOptions.workerProvider` / `workerModel` | `observation` / `reflection` / `dropper` worker configs | same |
|
|
31
|
+
| `ReadToolOptions.autoResizeImages` | `transformImage` | removed from the type; `createReadTool` throws naming `transformImage` before any path resolution or filesystem access |
|
|
32
|
+
| `INIT_PROVIDERS` (root export) | `listInitProviders()` | removed; init parsing, usage text, validation, and tests all use the function |
|
|
33
|
+
|
|
34
|
+
### Before / after
|
|
35
|
+
|
|
36
|
+
Provider knobs were inert in first-party providers (hosts were always expected to abort/retry at their own layer):
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
// 0.1.4
|
|
40
|
+
const session = await agent.createSession();
|
|
41
|
+
await session.run("Hi", {
|
|
42
|
+
provider: { timeoutMs: 30_000, maxRetries: 3, maxRetryDelayMs: 250 },
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// 0.1.5
|
|
46
|
+
const session = await agent.createSession();
|
|
47
|
+
await session.run("Hi", {
|
|
48
|
+
retry: { maxAttempts: 3, baseDelayMs: 250 },
|
|
49
|
+
signal: AbortSignal.timeout(30_000),
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`maxToolRounds` moves into the limits group (the CLI flag `--max-tool-rounds` is unchanged and maps to the nested limit):
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
// 0.1.4
|
|
57
|
+
await session.run("Hi", { maxToolRounds: 2 });
|
|
58
|
+
|
|
59
|
+
// 0.1.5
|
|
60
|
+
await session.run("Hi", { limits: { maxToolRounds: 2 } });
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Observational-memory settings and workers become nested-only (0.0.19 already introduced the nested groups; the flat keys were kept for pre-1.0 hosts):
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
// 0.1.4
|
|
67
|
+
createObservationalMemoryRuntime({
|
|
68
|
+
session,
|
|
69
|
+
appendEntry: (entry) => store.append(entry),
|
|
70
|
+
workerProvider,
|
|
71
|
+
sessionModel: agent.config.model,
|
|
72
|
+
overrides: { observeAfterTokens: 1, thinkingLevel: "low" },
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// 0.1.5
|
|
76
|
+
createObservationalMemoryRuntime({
|
|
77
|
+
session,
|
|
78
|
+
appendEntry: (entry) => store.append(entry),
|
|
79
|
+
observation: { provider: workerProvider, model: { provider: "neuralwatt", model: "glm-5.2-fast" } },
|
|
80
|
+
sessionModel: agent.config.model,
|
|
81
|
+
overrides: { observation: { messageTokens: 1, thinkingLevel: "low" } },
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The read tool keeps only the host-owned resize callback:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
// 0.1.4
|
|
89
|
+
createReadTool(cwd, { autoResizeImages: true });
|
|
90
|
+
|
|
91
|
+
// 0.1.5
|
|
92
|
+
createReadTool(cwd, {
|
|
93
|
+
transformImage: async ({ buffer, mimeType }) => resize(buffer, mimeType),
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Dynamic-config refusal behavior
|
|
98
|
+
|
|
99
|
+
Removed members are also removed from the runtime resolver paths, so **untyped** callers (plain JS, `as any`, settings-provider JSON, persisted run input) are caught before any side effect:
|
|
100
|
+
|
|
101
|
+
- Provider request knobs and `maxToolRounds`: refused at the top of the run entry point (`runInternal`) with a `TypeError` naming `RunOptions.limits.maxToolRounds` (or the abort/retry replacement) — before the agent starts, no tool/provider call happens.
|
|
102
|
+
- Observational-memory flat keys: `assertNoRemovedFlatKeys` runs before any worker/provider call, compaction, or session append; it names the first offending key and its nested replacement. The worker aliases are refused synchronously at both factory boundaries.
|
|
103
|
+
- `autoResizeImages`: refused at `createReadTool` construction, before path resolution or `access`/`statFile`/`readFile`.
|
|
104
|
+
- `INIT_PROVIDERS`: reads of the removed constant yield `undefined`; use `listInitProviders()`.
|
|
105
|
+
|
|
106
|
+
### Store compatibility
|
|
107
|
+
|
|
108
|
+
**Compatible — no persisted shape change.** None of the removals touch the session-store schema, run-state checkpoint shape, event schema, or default behavior: the removed options were inert aliases, and the nested replacements resolve to the same active values (e.g. `maxToolRounds` default 8 / hard cap 64 in `DEFAULT_RUN_LIMITS` / `HARD_RUN_LIMITS` are unchanged).
|
|
109
|
+
|
|
110
|
+
### Rollback
|
|
111
|
+
|
|
112
|
+
Restore the 0.1.4 manifests/tag (or revert this commit) — no data migration. Configs and code written against 0.1.5 nested forms also work on 0.1.4 (the nested members are not new in 0.1.5), but `@ts-expect-error`-free code must drop any removed-key usage first. Stores never change.
|
|
113
|
+
|
|
114
|
+
## 0.1.3 → 0.1.4 internal reorganization behind barrel re-exports (no migration)
|
|
115
|
+
|
|
116
|
+
Release **0.1.4** (plan 016) is an **internal file reorganization behind barrel re-exports**: the root `src/agents.ts` and `src/contracts.ts` god-modules were split by concern into sibling modules (`contracts-core` / `contracts-run-state` / `contracts-protocol` behind the `contracts.ts` barrel; `agent-session` / `agent-run-lifecycle` / `agent-approval` / `agent-tool-dispatch` / `agent-run-state` / `agent-loops` / `compaction` behind the `agents.ts` barrel). **Public declaration surface unchanged** — the root entry surface is byte-identical to 0.1.3 (zero added/removed/changed on the public entry; the only union-surface additions are 14 internal cross-module helper exports that are not consumer-importable, see `scripts/compat-baseline/arnilo__prism.txt`). The optional `@arnilo/prism-browser` package extends additively with Chrome DevTools Protocol capabilities (0.1.4): `browser_evaluate`, `browser_observe`, and the `block_urls`/`unblock_urls`/`throttle`/`emulate` act actions on Chromium hosts, plus raw `{ css }`/`{ xpath }` targets — new exports and two optional structural interface members only, zero removals. **Store compatibility: compatible** — no persisted shape, event schema, or default behavior changed (no runtime path changed; the split is declaration-level). no migration step; rollback = restore the 0.1.3 manifests/tag (stores never change). The next line, **0.1.5**, is the documented **breaking cut** (deprecated-option removal); its migration section will list the removed symbols (the public-but-unused export candidates from `scripts/dead-exports.mjs`).
|
|
4
117
|
|
|
5
118
|
Release **0.1.3** (plan 015) is the dead-code and deprecation hygiene patch on the frozen 0.1.x line: benchmark-runner consolidation (one parameterized `scripts/benchmark.mjs --scenario <name>` replaces the per-version runners; 16 orphaned `benchmark-0.0.{8..16}` runner/test files removed, all `benchmark-*.json` evidence kept), the 12 `docs/review-coverage-2026-07-*.md` evidence files archived to the tarball-excluded `docs/_evidence/`, a non-blocking unused-code sweep (`npm run sweep:unused`, always exits 0, report to `scripts/unused-sweep-report.txt`), and opt-in checkpoint persistence (`persistSessionState: true` on durable run/resume options persists the loaded-skill name catalog ≤64 names in the run-state checkpoint and restores it on resume — bodies re-resolve from the live registry; `createReadPathSetPersistence` in `@arnilo/prism-coding-agent` persists the read-before-write path set through the host `CheckpointStore`, ≤1024 paths, ownership-scoped). **Store compatibility: compatible** — the persisted run-state schema stays at version 1 (the optional `sessionState` field is absent by default, so 0.1.2 checkpoints parse unchanged and opt-out checkpoints are byte-identical); no upgrade or rollback step exists (rollback = restore the 0.1.2 manifests/tag; stores never change). Declaration surface is additive-only vs the frozen 0.1.x contract (`scripts/compat-baseline` regenerated at 0.1.3 with zero breaking deltas, enforced by `node scripts/release.mjs gate`). No breaking defaults.
|
|
6
119
|
|
package/docs/performance.md
CHANGED
|
@@ -28,6 +28,16 @@ node scripts/benchmark-0.1.0.mjs --out scripts/benchmark-0.1.0.json
|
|
|
28
28
|
PRISM_TEST_POSTGRES_URL="postgresql://…" node scripts/benchmark-0.1.0.mjs --out scripts/benchmark-0.1.0.json # adds protected legs
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
## 0.1.4 tree-shake measurement (static-reachability proxy)
|
|
32
|
+
|
|
33
|
+
The 0.1.4 god-module split (agents/contracts → per-concern modules behind barrels) is
|
|
34
|
+
measured by `scripts/phase16-tree-shake.mjs`: `dist/agents.js`/`dist/contracts.js` byte
|
|
35
|
+
sizes, `dist/*.js` module count, and a static-import reachability count from the minimal
|
|
36
|
+
entry proxy, recorded in `scripts/phase16-baseline.json` (task 3 of plan 016). Static
|
|
37
|
+
graph reachability is an upper-bound proxy, not a real bundle — a byte-accurate
|
|
38
|
+
tree-shake budget needs a bundler and actual consumer code, deferred behind a 0.1.7 DX
|
|
39
|
+
demand gate.
|
|
40
|
+
|
|
31
41
|
**Pass/fail thresholds.** Network-free rows fail above the frozen ceiling in
|
|
32
42
|
the table below; protected PostgreSQL rows fail above their per-phase
|
|
33
43
|
budgets.json ceilings (50/100 ms per the approved budget contract); startup
|