@arnilo/prism 0.0.10 → 0.0.11

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.
Files changed (45) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/agent-loops.d.ts +1 -0
  3. package/dist/agent-loops.js +37 -2
  4. package/dist/agents.js +125 -6
  5. package/dist/context-budget.d.ts +63 -0
  6. package/dist/context-budget.js +235 -0
  7. package/dist/contracts.d.ts +105 -0
  8. package/dist/contracts.js +77 -0
  9. package/dist/index.d.ts +6 -4
  10. package/dist/index.js +4 -3
  11. package/dist/input.d.ts +3 -0
  12. package/dist/input.js +71 -28
  13. package/dist/node/session-store-jsonl.js +4 -1
  14. package/dist/rpc.js +13 -2
  15. package/dist/session-stores.d.ts +7 -2
  16. package/dist/session-stores.js +174 -4
  17. package/dist/structured-output.d.ts +5 -1
  18. package/dist/structured-output.js +18 -0
  19. package/dist/testing/persistence-schema.d.ts +1 -1
  20. package/dist/testing/persistence-schema.js +8 -2
  21. package/dist/testing/session-store-conformance.d.ts +6 -0
  22. package/dist/testing/session-store-conformance.js +36 -1
  23. package/docs/agent-loops.md +8 -1
  24. package/docs/agent-session-runtime.md +5 -1
  25. package/docs/cli-rpc.md +2 -1
  26. package/docs/coding-agent-tools.md +68 -1
  27. package/docs/evaluations.md +1 -1
  28. package/docs/index.md +12 -11
  29. package/docs/input-and-prompt-assembly.md +4 -1
  30. package/docs/migration.md +16 -0
  31. package/docs/node-jsonl-session-store.md +1 -1
  32. package/docs/performance.md +17 -0
  33. package/docs/postgres-persistence.md +3 -3
  34. package/docs/provider-packages.md +1 -1
  35. package/docs/providers/anthropic.md +92 -0
  36. package/docs/providers/google.md +87 -0
  37. package/docs/public-contracts.md +4 -0
  38. package/docs/release-and-install.md +159 -70
  39. package/docs/review-coverage-2026-07-22-phase-6.md +209 -0
  40. package/docs/session-store-conformance.md +2 -0
  41. package/docs/session-stores.md +40 -1
  42. package/docs/sqlite-persistence.md +3 -3
  43. package/docs/structured-output.md +7 -1
  44. package/docs/workflows.md +2 -0
  45. package/package.json +2 -2
@@ -493,6 +493,22 @@ export declare class AgentRunError extends Error {
493
493
  readonly cause?: unknown;
494
494
  });
495
495
  }
496
+ /** Mid-run steer queue: default pending message count (fail closed at this cap). */
497
+ export declare const DEFAULT_MAX_PENDING_STEERS = 8;
498
+ /** Absolute pending steer count ceiling if hosts later expose overrides. */
499
+ export declare const HARD_MAX_PENDING_STEERS = 32;
500
+ /** Mid-run steer queue: default total UTF-8 byte budget across pending messages. */
501
+ export declare const DEFAULT_MAX_PENDING_STEER_BYTES: number;
502
+ /** Absolute pending steer byte ceiling if hosts later expose overrides. */
503
+ export declare const HARD_MAX_PENDING_STEER_BYTES: number;
504
+ export interface SteerOptions {
505
+ /**
506
+ * When true, abort the in-flight provider stream and continue the same run after
507
+ * injecting steered user text. Default false: inject before the next provider turn
508
+ * (after the current tool batch completes).
509
+ */
510
+ readonly softInterrupt?: boolean;
511
+ }
496
512
  export interface AgentSession {
497
513
  readonly id: string;
498
514
  /** Current branch leaf entry id; advances on every append/run and is re-pointed by `checkout`.
@@ -500,6 +516,12 @@ export interface AgentSession {
500
516
  readonly leafId: string | undefined;
501
517
  run(input: string | Message | readonly Message[], options?: RunOptions): Promise<AgentRunResult>;
502
518
  prompt(input: string, options?: RunOptions): Promise<AgentRunResult>;
519
+ /**
520
+ * Enqueue user text into an active run. Default injects before the next provider turn.
521
+ * `softInterrupt: true` aborts the current provider stream, then continues the same run.
522
+ * Fails closed when no run is active or the pending queue exceeds caps.
523
+ */
524
+ steer(input: string | Message | readonly Message[], options?: SteerOptions): void;
503
525
  /** Subscribe first, then start exactly one run and yield only that run's events until it terminates. */
504
526
  stream(input: string | Message | readonly Message[], options?: RunOptions & SubscribeOptions): AsyncIterable<AgentEvent>;
505
527
  compact(options?: CompactionOptions): Promise<CompactionResult>;
@@ -1022,7 +1044,80 @@ export interface SessionStore {
1022
1044
  * avoid `list(sessionId)` (full-session scan) + in-memory rebuild. Optional — the
1023
1045
  * built-in memory/JSONL stores omit it and the runtime falls back to `list()`. */
1024
1046
  readBranchPath?(query: SessionBranchRead): Promise<PersistencePage<SessionEntry>>;
1047
+ /**
1048
+ * Optional bounded session search. Prefer implementing this **or** returning a companion
1049
+ * `SessionIndex` from the adapter factory — hosts must not need both. Call
1050
+ * `resolveSessionSearchQuery` before scan/query. Memory defaults to capped linear
1051
+ * search (`sessionSearchMode: "unsupported"` throws). JSONL throws unsupported.
1052
+ */
1053
+ searchSessions?(query: SessionSearchQuery): Promise<PersistencePage<SessionSearchHit>>;
1054
+ }
1055
+ /** Host-written `SessionRecord.metadata` / session metadata key for workspace filtering. */
1056
+ export declare const SESSION_SEARCH_WORKSPACE_METADATA_KEY: "workspaceRoot";
1057
+ export declare const DEFAULT_SESSION_SEARCH_LIMIT = 20;
1058
+ export declare const HARD_MAX_SESSION_SEARCH_LIMIT = 100;
1059
+ export declare const DEFAULT_MAX_SESSION_SEARCH_QUERY_BYTES: number;
1060
+ export declare const HARD_MAX_SESSION_SEARCH_QUERY_BYTES: number;
1061
+ export declare const DEFAULT_MAX_SESSION_SEARCH_SNIPPET_BYTES = 512;
1062
+ export declare const HARD_MAX_SESSION_SEARCH_SNIPPET_BYTES: number;
1063
+ export declare const DEFAULT_MAX_SESSION_SEARCH_CURSOR_BYTES: number;
1064
+ export declare const HARD_MAX_SESSION_SEARCH_CURSOR_BYTES: number;
1065
+ export declare const DEFAULT_MAX_SESSION_SEARCH_LINEAR_SESSIONS = 1000;
1066
+ export declare const HARD_MAX_SESSION_SEARCH_LINEAR_SESSIONS = 5000;
1067
+ export declare const DEFAULT_MAX_SESSION_SEARCH_LINEAR_ENTRIES = 10000;
1068
+ export declare const HARD_MAX_SESSION_SEARCH_LINEAR_ENTRIES = 50000;
1069
+ export declare const DEFAULT_MAX_SESSION_SEARCH_LINEAR_BYTES: number;
1070
+ export declare const HARD_MAX_SESSION_SEARCH_LINEAR_BYTES: number;
1071
+ export declare const DEFAULT_MAX_SESSION_SEARCH_FTS_CANDIDATES = 1000;
1072
+ export declare const HARD_MAX_SESSION_SEARCH_FTS_CANDIDATES = 5000;
1073
+ /** Bounded session search filters. Workspace matches host-written `metadata.workspaceRoot`. */
1074
+ export interface SessionSearchQuery extends PersistenceQuery, OwnershipScope {
1075
+ readonly workspaceRoot?: string;
1076
+ /** Optional full-text / message+summary query (adapter-defined matching). */
1077
+ readonly query?: string;
1078
+ readonly provider?: string;
1079
+ readonly model?: string;
1080
+ readonly label?: string;
1081
+ readonly summary?: string;
1082
+ readonly fromUpdatedAt?: string;
1083
+ readonly toUpdatedAt?: string;
1084
+ readonly signal?: AbortSignal;
1025
1085
  }
1086
+ /**
1087
+ * Safe search hit for resume/checkout. Never includes credentials or raw full transcripts.
1088
+ * `leafId` is the branch tip for `session.checkout` when known.
1089
+ */
1090
+ export interface SessionSearchHit {
1091
+ readonly sessionId: string;
1092
+ readonly leafId?: string;
1093
+ readonly updatedAt?: string;
1094
+ readonly label?: string;
1095
+ readonly summary?: string;
1096
+ readonly snippet?: string;
1097
+ /** Safe display fields only (e.g. workspaceRoot); never credentials. */
1098
+ readonly metadata?: Readonly<Record<string, unknown>>;
1099
+ }
1100
+ /** Narrow search seam; adapters may implement this instead of `SessionStore.searchSessions`. */
1101
+ export interface SessionIndex {
1102
+ search(query: SessionSearchQuery): Promise<PersistencePage<SessionSearchHit>>;
1103
+ }
1104
+ /** Validated search query with finite `limit` / `order` filled in. */
1105
+ export interface ResolvedSessionSearchQuery extends SessionSearchQuery {
1106
+ readonly limit: number;
1107
+ readonly order: "asc" | "desc";
1108
+ }
1109
+ /**
1110
+ * O(1) validation before any scan/query. Applies default page limit; rejects NaN,
1111
+ * non-positive limits, oversize query/cursor/filter strings, and invalid order.
1112
+ */
1113
+ export declare function resolveSessionSearchQuery(query: SessionSearchQuery): ResolvedSessionSearchQuery;
1114
+ export declare const SESSION_SEARCH_UNSUPPORTED_CODE: "session_search_unsupported";
1115
+ /** Thrown when a store opts out of `searchSessions` (memory `unsupported`, JSONL). */
1116
+ export declare class SessionSearchUnsupportedError extends Error {
1117
+ readonly code: "session_search_unsupported";
1118
+ constructor(message?: string);
1119
+ }
1120
+ export declare function isSessionSearchUnsupported(error: unknown): error is SessionSearchUnsupportedError;
1026
1121
  /** Query for a single branch's ancestor chain (DB-friendly: one recursive/ancestor query
1027
1122
  * instead of a full-session scan). Honored by `SessionStore.readBranchPath` and the pure
1028
1123
  * branch helpers' reader overload. `leafId` is optional (omit for the latest leaf). */
@@ -1642,6 +1737,10 @@ export interface LoopContext {
1642
1737
  isToolCallExclusive?(call: ToolCallContent): boolean;
1643
1738
  appendMessage(message: Message): Promise<void>;
1644
1739
  emit(event: AgentEvent): void;
1740
+ /** True when mid-run steers are queued for the next provider turn. */
1741
+ hasPendingSteers?(): boolean;
1742
+ /** Drain pending steers into history/session. Returns true when any were applied. */
1743
+ applyPendingSteers?(): Promise<boolean>;
1645
1744
  }
1646
1745
  export interface AgentLoopStrategy {
1647
1746
  readonly name: string;
@@ -1663,6 +1762,12 @@ export type AgentLoopOptions = {
1663
1762
  readonly structuredOutput?: StructuredOutputOptions;
1664
1763
  /** `native` maps schema to capable providers; `artifact-loop` keeps repair turns only. */
1665
1764
  readonly structuredOutputMode?: "native" | "artifact-loop";
1765
+ /**
1766
+ * When to attach native `structuredOutput` under `toolCalls: "bounded"`.
1767
+ * `every-turn` (default): schema on every provider request (legacy).
1768
+ * `final-turn-only`: tool-eligible turns omit schema; artifact/revision turns send schema and withdraw tools.
1769
+ */
1770
+ readonly structuredOutputTiming?: "every-turn" | "final-turn-only";
1666
1771
  };
1667
1772
  export interface ArtifactValidation {
1668
1773
  readonly ok: boolean;
package/dist/contracts.js CHANGED
@@ -10,6 +10,14 @@ export class AgentRunError extends Error {
10
10
  this.result = result;
11
11
  }
12
12
  }
13
+ /** Mid-run steer queue: default pending message count (fail closed at this cap). */
14
+ export const DEFAULT_MAX_PENDING_STEERS = 8;
15
+ /** Absolute pending steer count ceiling if hosts later expose overrides. */
16
+ export const HARD_MAX_PENDING_STEERS = 32;
17
+ /** Mid-run steer queue: default total UTF-8 byte budget across pending messages. */
18
+ export const DEFAULT_MAX_PENDING_STEER_BYTES = 64 * 1024;
19
+ /** Absolute pending steer byte ceiling if hosts later expose overrides. */
20
+ export const HARD_MAX_PENDING_STEER_BYTES = 256 * 1024;
13
21
  export const SESSION_ENTRY_KINDS = [
14
22
  "message",
15
23
  "event",
@@ -25,6 +33,75 @@ export const SESSION_ENTRY_SCHEMA_VERSION = 1;
25
33
  export function isSessionEntryKind(value) {
26
34
  return typeof value === "string" && SESSION_ENTRY_KIND_SET.has(value);
27
35
  }
36
+ /** Host-written `SessionRecord.metadata` / session metadata key for workspace filtering. */
37
+ export const SESSION_SEARCH_WORKSPACE_METADATA_KEY = "workspaceRoot";
38
+ export const DEFAULT_SESSION_SEARCH_LIMIT = 20;
39
+ export const HARD_MAX_SESSION_SEARCH_LIMIT = 100;
40
+ export const DEFAULT_MAX_SESSION_SEARCH_QUERY_BYTES = 4 * 1024;
41
+ export const HARD_MAX_SESSION_SEARCH_QUERY_BYTES = 16 * 1024;
42
+ export const DEFAULT_MAX_SESSION_SEARCH_SNIPPET_BYTES = 512;
43
+ export const HARD_MAX_SESSION_SEARCH_SNIPPET_BYTES = 4 * 1024;
44
+ export const DEFAULT_MAX_SESSION_SEARCH_CURSOR_BYTES = 1 * 1024;
45
+ export const HARD_MAX_SESSION_SEARCH_CURSOR_BYTES = 4 * 1024;
46
+ export const DEFAULT_MAX_SESSION_SEARCH_LINEAR_SESSIONS = 1_000;
47
+ export const HARD_MAX_SESSION_SEARCH_LINEAR_SESSIONS = 5_000;
48
+ export const DEFAULT_MAX_SESSION_SEARCH_LINEAR_ENTRIES = 10_000;
49
+ export const HARD_MAX_SESSION_SEARCH_LINEAR_ENTRIES = 50_000;
50
+ export const DEFAULT_MAX_SESSION_SEARCH_LINEAR_BYTES = 8 * 1024 * 1024;
51
+ export const HARD_MAX_SESSION_SEARCH_LINEAR_BYTES = 64 * 1024 * 1024;
52
+ export const DEFAULT_MAX_SESSION_SEARCH_FTS_CANDIDATES = 1_000;
53
+ export const HARD_MAX_SESSION_SEARCH_FTS_CANDIDATES = 5_000;
54
+ /**
55
+ * O(1) validation before any scan/query. Applies default page limit; rejects NaN,
56
+ * non-positive limits, oversize query/cursor/filter strings, and invalid order.
57
+ */
58
+ export function resolveSessionSearchQuery(query) {
59
+ const limit = query.limit === undefined ? DEFAULT_SESSION_SEARCH_LIMIT : query.limit;
60
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > HARD_MAX_SESSION_SEARCH_LIMIT) {
61
+ throw new TypeError(`SessionSearchQuery.limit must be a safe integer from 1 to ${HARD_MAX_SESSION_SEARCH_LIMIT}`);
62
+ }
63
+ const order = query.order ?? "desc";
64
+ if (order !== "asc" && order !== "desc") {
65
+ throw new TypeError('SessionSearchQuery.order must be "asc" or "desc"');
66
+ }
67
+ assertSearchStringBytes(query.query, "query", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
68
+ assertSearchStringBytes(query.cursor, "cursor", HARD_MAX_SESSION_SEARCH_CURSOR_BYTES);
69
+ assertSearchStringBytes(query.workspaceRoot, "workspaceRoot", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
70
+ assertSearchStringBytes(query.provider, "provider", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
71
+ assertSearchStringBytes(query.model, "model", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
72
+ assertSearchStringBytes(query.label, "label", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
73
+ assertSearchStringBytes(query.summary, "summary", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
74
+ assertSearchStringBytes(query.tenantId, "tenantId", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
75
+ assertSearchStringBytes(query.accountId, "accountId", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
76
+ assertSearchStringBytes(query.userId, "userId", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
77
+ assertSearchStringBytes(query.fromUpdatedAt, "fromUpdatedAt", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
78
+ assertSearchStringBytes(query.toUpdatedAt, "toUpdatedAt", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
79
+ return { ...query, limit, order };
80
+ }
81
+ function assertSearchStringBytes(value, name, hardMax) {
82
+ if (value === undefined)
83
+ return;
84
+ if (typeof value !== "string") {
85
+ throw new TypeError(`SessionSearchQuery.${name} must be a string`);
86
+ }
87
+ // ponytail: UTF-8 byte length via TextEncoder; upgrade only if a non-Unicode host appears.
88
+ const bytes = new TextEncoder().encode(value).byteLength;
89
+ if (bytes > hardMax) {
90
+ throw new TypeError(`SessionSearchQuery.${name} exceeds ${hardMax} bytes`);
91
+ }
92
+ }
93
+ export const SESSION_SEARCH_UNSUPPORTED_CODE = "session_search_unsupported";
94
+ /** Thrown when a store opts out of `searchSessions` (memory `unsupported`, JSONL). */
95
+ export class SessionSearchUnsupportedError extends Error {
96
+ code = SESSION_SEARCH_UNSUPPORTED_CODE;
97
+ constructor(message = "session search is unsupported by this store") {
98
+ super(message);
99
+ this.name = "SessionSearchUnsupportedError";
100
+ }
101
+ }
102
+ export function isSessionSearchUnsupported(error) {
103
+ return error instanceof Error && error.code === SESSION_SEARCH_UNSUPPORTED_CODE;
104
+ }
28
105
  /** Stable error code carried by `SessionAppendConflictError`. */
29
106
  export const SESSION_APPEND_CONFLICT_CODE = "session_append_conflict";
30
107
  /**
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export type * from "./contracts.js";
2
2
  export type { RunLimitCounters, RunLimitName, SecureAgentOptions } from "./contracts.js";
3
- export { isSessionEntryKind, SESSION_APPEND_CONFLICT_CODE, SESSION_ENTRY_KINDS, SESSION_ENTRY_SCHEMA_VERSION, SessionAppendConflictError, isSessionAppendConflict, AgentRunError, AgentRunStateError } from "./contracts.js";
3
+ export { isSessionEntryKind, SESSION_APPEND_CONFLICT_CODE, SESSION_ENTRY_KINDS, SESSION_ENTRY_SCHEMA_VERSION, SessionAppendConflictError, isSessionAppendConflict, AgentRunError, AgentRunStateError, SESSION_SEARCH_WORKSPACE_METADATA_KEY, SESSION_SEARCH_UNSUPPORTED_CODE, SessionSearchUnsupportedError, isSessionSearchUnsupported, DEFAULT_SESSION_SEARCH_LIMIT, HARD_MAX_SESSION_SEARCH_LIMIT, DEFAULT_MAX_SESSION_SEARCH_QUERY_BYTES, HARD_MAX_SESSION_SEARCH_QUERY_BYTES, DEFAULT_MAX_SESSION_SEARCH_SNIPPET_BYTES, HARD_MAX_SESSION_SEARCH_SNIPPET_BYTES, DEFAULT_MAX_SESSION_SEARCH_CURSOR_BYTES, HARD_MAX_SESSION_SEARCH_CURSOR_BYTES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_SESSIONS, HARD_MAX_SESSION_SEARCH_LINEAR_SESSIONS, DEFAULT_MAX_SESSION_SEARCH_LINEAR_ENTRIES, HARD_MAX_SESSION_SEARCH_LINEAR_ENTRIES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_BYTES, HARD_MAX_SESSION_SEARCH_LINEAR_BYTES, DEFAULT_MAX_SESSION_SEARCH_FTS_CANDIDATES, HARD_MAX_SESSION_SEARCH_FTS_CANDIDATES, resolveSessionSearchQuery, DEFAULT_MAX_PENDING_STEERS, HARD_MAX_PENDING_STEERS, DEFAULT_MAX_PENDING_STEER_BYTES, HARD_MAX_PENDING_STEER_BYTES } from "./contracts.js";
4
4
  export { createAgent, createAgentSession, resumeAgentRun } from "./agents.js";
5
5
  export { createBatchedRunLedger, isFlushableRunLedger, DEFAULT_LEDGER_BATCH_ENTRIES, HARD_LEDGER_BATCH_ENTRIES, DEFAULT_LEDGER_BATCH_BYTES, HARD_LEDGER_BATCH_BYTES, DEFAULT_LEDGER_BATCH_DELAY_MS, HARD_LEDGER_BATCH_DELAY_MS, } from "./run-ledger.js";
6
6
  export type { BatchedRunLedgerOptions } from "./run-ledger.js";
@@ -34,7 +34,7 @@ export type { ExtensionErrorPolicy, ExtensionEventBus, ExtensionEventHandler, Ex
34
34
  export type { CredentialRecord, CredentialValueSource, MemoryCredentialStore } from "./credentials.js";
35
35
  export { createModelRegistry } from "./models.js";
36
36
  export { authMethodKey, defineProviderPackage, systemPromptContributionKey } from "./provider-packages.js";
37
- export { assertStructuredOutputRequestSupported, DEFAULT_MAX_STRUCTURED_OUTPUT_NAME_LENGTH, DEFAULT_MAX_STRUCTURED_OUTPUT_SCHEMA_BYTES, modelSupportsStructuredOutput, resolveRunProviderOptions, StructuredOutputError, validateStructuredOutputOptions, } from "./structured-output.js";
37
+ export { assertStructuredOutputRequestSupported, artifactStructuredOutputRequest, DEFAULT_MAX_STRUCTURED_OUTPUT_NAME_LENGTH, DEFAULT_MAX_STRUCTURED_OUTPUT_SCHEMA_BYTES, modelSupportsStructuredOutput, resolveRunProviderOptions, StructuredOutputError, validateStructuredOutputOptions, withoutStructuredOutput, } from "./structured-output.js";
38
38
  export { createProviderTurnMetadata, readProviderHttpStatus } from "./observability.js";
39
39
  export { createProviderRequestPolicyChain, createSessionCachePolicy, mergeProviderRequestOptions } from "./provider-request-policy.js";
40
40
  export type { SessionCachePolicyOptions } from "./provider-request-policy.js";
@@ -55,10 +55,12 @@ export { createChainedSettingsProvider, createStaticSettingsProvider } from "./s
55
55
  export { createMiddlewareRegistry } from "./middleware.js";
56
56
  export type { Middleware, MiddlewareHookName, MiddlewareNext, MiddlewareRegistry, MiddlewareRegistryOptions } from "./middleware.js";
57
57
  export { assembleProviderInput, createDefaultInputBuilder, createDefaultPromptBuilder, renderPromptTemplate, resolveContextProviders } from "./input.js";
58
+ export { applyContextBudget, CONTEXT_BUDGET_ERROR_CODE, CONTEXT_BUDGET_REPORT_METADATA_KEY, ContextBudgetError, DEFAULT_MAX_CONTEXT_BUDGET_OMISSIONS, estimateAssemblyTokens, estimateMessageBytes, estimateMessageTokens, estimateTextBytes, estimateTextTokens, getContextBudgetReport, HARD_MAX_CONTEXT_BUDGET_BYTES, HARD_MAX_CONTEXT_BUDGET_OMISSIONS, HARD_MAX_CONTEXT_BUDGET_TOKENS, isContextBudgetError, resolveContextBudget, } from "./context-budget.js";
59
+ export type { ContextBudget, ContextBudgetMessageGroups, ContextBudgetOmission, ContextBudgetOmissionKind, ContextBudgetReport, } from "./context-budget.js";
58
60
  export type { AgentInput, AssembleProviderInputOptions, DefaultInputBuilder, DefaultInputBuildContext, DefaultPromptBuilder, InputAttachment, PromptInstruction, PromptTemplateOptions, ResolveContextOptions } from "./input.js";
59
61
  export { createMockProvider } from "./mock-provider.js";
60
62
  export { createMemorySessionStore, createSessionEntry, getSessionBranchEntries, listSessionBranches, rebuildSessionContext } from "./session-stores.js";
61
- export type { CreateSessionEntryOptions, SessionBranch, SessionBranchOptions, SessionContextSnapshot } from "./session-stores.js";
63
+ export type { CreateMemorySessionStoreOptions, CreateSessionEntryOptions, MemorySessionSearchMode, SessionBranch, SessionBranchOptions, SessionContextSnapshot } from "./session-stores.js";
62
64
  export type { MockProviderOptions } from "./mock-provider.js";
63
65
  export { providerContentDelta, providerDone, providerError, providerTextDelta, providerThinkingDelta, providerToolCall, providerToolCallDelta, providerUsage, toolCallContent, toolCallFromArgumentsText, } from "./provider-events.js";
64
66
  export type { ProviderResolver } from "./contracts.js";
@@ -83,5 +85,5 @@ export type { DispatchToolCallOptions, ToolArgumentValidationError, ToolArgument
83
85
  export type { DuplicateRegistrationOptions, DuplicateRegistrationPolicy } from "./registry-options.js";
84
86
  export { dispatchToolCallsInOrder, generateValidateReviseLoop, isAgentLoopOptions, resolveLoop, resolveToolConcurrency, singleShotLoop } from "./agent-loops.js";
85
87
  export declare const name = "prism";
86
- export declare const version = "0.0.10";
88
+ export declare const version = "0.0.11";
87
89
  export declare const description = "Agent harness for AI providers, agents, sessions, and tools.";
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { isSessionEntryKind, SESSION_APPEND_CONFLICT_CODE, SESSION_ENTRY_KINDS, SESSION_ENTRY_SCHEMA_VERSION, SessionAppendConflictError, isSessionAppendConflict, AgentRunError, AgentRunStateError } from "./contracts.js";
1
+ export { isSessionEntryKind, SESSION_APPEND_CONFLICT_CODE, SESSION_ENTRY_KINDS, SESSION_ENTRY_SCHEMA_VERSION, SessionAppendConflictError, isSessionAppendConflict, AgentRunError, AgentRunStateError, SESSION_SEARCH_WORKSPACE_METADATA_KEY, SESSION_SEARCH_UNSUPPORTED_CODE, SessionSearchUnsupportedError, isSessionSearchUnsupported, DEFAULT_SESSION_SEARCH_LIMIT, HARD_MAX_SESSION_SEARCH_LIMIT, DEFAULT_MAX_SESSION_SEARCH_QUERY_BYTES, HARD_MAX_SESSION_SEARCH_QUERY_BYTES, DEFAULT_MAX_SESSION_SEARCH_SNIPPET_BYTES, HARD_MAX_SESSION_SEARCH_SNIPPET_BYTES, DEFAULT_MAX_SESSION_SEARCH_CURSOR_BYTES, HARD_MAX_SESSION_SEARCH_CURSOR_BYTES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_SESSIONS, HARD_MAX_SESSION_SEARCH_LINEAR_SESSIONS, DEFAULT_MAX_SESSION_SEARCH_LINEAR_ENTRIES, HARD_MAX_SESSION_SEARCH_LINEAR_ENTRIES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_BYTES, HARD_MAX_SESSION_SEARCH_LINEAR_BYTES, DEFAULT_MAX_SESSION_SEARCH_FTS_CANDIDATES, HARD_MAX_SESSION_SEARCH_FTS_CANDIDATES, resolveSessionSearchQuery, DEFAULT_MAX_PENDING_STEERS, HARD_MAX_PENDING_STEERS, DEFAULT_MAX_PENDING_STEER_BYTES, HARD_MAX_PENDING_STEER_BYTES } from "./contracts.js";
2
2
  export { createAgent, createAgentSession, resumeAgentRun } from "./agents.js";
3
3
  export { createBatchedRunLedger, isFlushableRunLedger, DEFAULT_LEDGER_BATCH_ENTRIES, HARD_LEDGER_BATCH_ENTRIES, DEFAULT_LEDGER_BATCH_BYTES, HARD_LEDGER_BATCH_BYTES, DEFAULT_LEDGER_BATCH_DELAY_MS, HARD_LEDGER_BATCH_DELAY_MS, } from "./run-ledger.js";
4
4
  export { createSecureAgent } from "./secure-agent.js";
@@ -19,7 +19,7 @@ export { createChainedCredentialResolver, createEnvCredentialResolver, createExp
19
19
  export { createExtensionEventBus, createExtensionKernel } from "./extensions.js";
20
20
  export { createModelRegistry } from "./models.js";
21
21
  export { authMethodKey, defineProviderPackage, systemPromptContributionKey } from "./provider-packages.js";
22
- export { assertStructuredOutputRequestSupported, DEFAULT_MAX_STRUCTURED_OUTPUT_NAME_LENGTH, DEFAULT_MAX_STRUCTURED_OUTPUT_SCHEMA_BYTES, modelSupportsStructuredOutput, resolveRunProviderOptions, StructuredOutputError, validateStructuredOutputOptions, } from "./structured-output.js";
22
+ export { assertStructuredOutputRequestSupported, artifactStructuredOutputRequest, DEFAULT_MAX_STRUCTURED_OUTPUT_NAME_LENGTH, DEFAULT_MAX_STRUCTURED_OUTPUT_SCHEMA_BYTES, modelSupportsStructuredOutput, resolveRunProviderOptions, StructuredOutputError, validateStructuredOutputOptions, withoutStructuredOutput, } from "./structured-output.js";
23
23
  export { createProviderTurnMetadata, readProviderHttpStatus } from "./observability.js";
24
24
  export { createProviderRequestPolicyChain, createSessionCachePolicy, mergeProviderRequestOptions } from "./provider-request-policy.js";
25
25
  export { THINKING_LEVELS, applyThinkingLevel, isThinkingLevel, normalizeThinkingLevel, thinkingCompatFor, thinkingFamilyForModel, } from "./thinking.js";
@@ -31,6 +31,7 @@ export { loadBinaryResource, loadJsonResource, loadManifestResource, loadTextRes
31
31
  export { createChainedSettingsProvider, createStaticSettingsProvider } from "./settings.js";
32
32
  export { createMiddlewareRegistry } from "./middleware.js";
33
33
  export { assembleProviderInput, createDefaultInputBuilder, createDefaultPromptBuilder, renderPromptTemplate, resolveContextProviders } from "./input.js";
34
+ export { applyContextBudget, CONTEXT_BUDGET_ERROR_CODE, CONTEXT_BUDGET_REPORT_METADATA_KEY, ContextBudgetError, DEFAULT_MAX_CONTEXT_BUDGET_OMISSIONS, estimateAssemblyTokens, estimateMessageBytes, estimateMessageTokens, estimateTextBytes, estimateTextTokens, getContextBudgetReport, HARD_MAX_CONTEXT_BUDGET_BYTES, HARD_MAX_CONTEXT_BUDGET_OMISSIONS, HARD_MAX_CONTEXT_BUDGET_TOKENS, isContextBudgetError, resolveContextBudget, } from "./context-budget.js";
34
35
  export { createMockProvider } from "./mock-provider.js";
35
36
  export { createMemorySessionStore, createSessionEntry, getSessionBranchEntries, listSessionBranches, rebuildSessionContext } from "./session-stores.js";
36
37
  export { providerContentDelta, providerDone, providerError, providerTextDelta, providerThinkingDelta, providerToolCall, providerToolCallDelta, providerUsage, toolCallContent, toolCallFromArgumentsText, } from "./provider-events.js";
@@ -45,6 +46,6 @@ export { assertGuardrailsAllowed, GuardrailError, MAX_GUARDRAIL_CONCURRENCY, run
45
46
  export { createRunLimitTracker, DEFAULT_RUN_LIMITS, HARD_MAX_RUN_COST, HARD_RUN_LIMITS, RunLimitError, RunLimitTracker, resolveRunLimits } from "./run-limits.js";
46
47
  export { dispatchToolCallsInOrder, generateValidateReviseLoop, isAgentLoopOptions, resolveLoop, resolveToolConcurrency, singleShotLoop } from "./agent-loops.js";
47
48
  export const name = "prism";
48
- export const version = "0.0.10";
49
+ export const version = "0.0.11";
49
50
  export const description = "Agent harness for AI providers, agents, sessions, and tools.";
50
51
  //# sourceMappingURL=index.js.map
package/dist/input.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { ContentBlock, ContextBlock, ContextProvider, ContextResolutionContext, InputBuilder, InstructionInjector, InputBuildContext, JsonObject, Message, ModelConfig, PromptBuilder, PromptBuildRequest, ProviderRequest, ProviderRequestOptions, ResourceLoader, Skill, ToolDefinition, ToolResult } from "./contracts.js";
2
2
  import type { MiddlewareRegistry } from "./middleware.js";
3
3
  import type { SecretRedactor } from "./redaction.js";
4
+ import { type ContextBudget } from "./context-budget.js";
4
5
  export type AgentInput = string | Message | readonly Message[];
5
6
  export interface PromptInstruction {
6
7
  readonly text: string;
@@ -54,6 +55,8 @@ export interface AssembleProviderInputOptions extends DefaultInputBuildContext {
54
55
  readonly skills?: readonly Skill[];
55
56
  readonly tools?: readonly ToolDefinition[];
56
57
  readonly providerOptions?: ProviderRequestOptions;
58
+ /** Assembler-time eviction; does not delete session store history. */
59
+ readonly contextBudget?: ContextBudget;
57
60
  }
58
61
  export declare function createDefaultInputBuilder(): DefaultInputBuilder;
59
62
  export declare function resolveContextProviders(options: ResolveContextOptions): Promise<readonly ContextBlock[]>;
package/dist/input.js CHANGED
@@ -3,22 +3,12 @@ import { assertMessagesSupportModelCapabilities } from "./content.js";
3
3
  import { loadTextResource } from "./resources.js";
4
4
  import { composeSystemPrompt } from "./system-prompts.js";
5
5
  import { runInstructionInjectors } from "./instruction-injection.js";
6
+ import { applyContextBudget, CONTEXT_BUDGET_REPORT_METADATA_KEY, } from "./context-budget.js";
6
7
  export function createDefaultInputBuilder() {
7
8
  return {
8
9
  name: "default-input",
9
10
  async build(input, context = {}) {
10
- const groups = {
11
- instructions: [
12
- ...instructionMessages(context.systemInstructions, "System instruction"),
13
- ...instructionMessages(context.developerInstructions, "Developer instruction"),
14
- ...customInstructionMessages(context.instructions),
15
- ],
16
- summaries: summaryMessages(context.summaries),
17
- history: [...(context.history ?? [])],
18
- input: inputMessages(input),
19
- attachments: await attachmentMessages(context),
20
- toolResults: toolResultMessages(context.toolResults),
21
- };
11
+ const groups = await buildDefaultInputMessageGroups(input, context);
22
12
  const messages = flattenInputGroups(groups, context.inputLayout ?? "legacy");
23
13
  return context.middleware ? context.middleware.run("input_assembly", messages) : messages;
24
14
  },
@@ -65,7 +55,6 @@ export function renderPromptTemplate(template, variables, options = {}) {
65
55
  });
66
56
  }
67
57
  export async function assembleProviderInput(options) {
68
- const inputBuilder = options.inputBuilder ?? createDefaultInputBuilder();
69
58
  const baseContext = {
70
59
  sessionId: options.sessionId,
71
60
  runId: options.runId,
@@ -92,44 +81,98 @@ export async function assembleProviderInput(options) {
92
81
  ? composeSystemPrompt(injectorContribs.instructions, { base: options.systemInstructions })
93
82
  : options.systemInstructions;
94
83
  const buildContext = { ...options, ...baseContext, systemInstructions };
95
- const messages = await inputBuilder.build(options.input, buildContext);
96
- const context = await resolveContextProviders({
97
- providers: options.contextProviders,
98
- messages,
99
- injectedBlocks: injectorContribs.contextBlocks.length ? injectorContribs.contextBlocks : undefined,
100
- middleware: options.middleware,
101
- ...baseContext,
102
- });
84
+ const layout = buildContext.inputLayout ?? "legacy";
85
+ let messages;
86
+ let context;
87
+ let skills = options.skills;
88
+ let tools = options.tools;
89
+ let budgetReport;
90
+ if (options.contextBudget) {
91
+ // ponytail: budget path always uses default groups so eviction kinds stay deterministic;
92
+ // custom inputBuilder still honored when contextBudget is absent.
93
+ const groups = await buildDefaultInputMessageGroups(options.input, buildContext);
94
+ context = await resolveContextProviders({
95
+ providers: options.contextProviders,
96
+ messages: flattenInputGroups(groups, layout),
97
+ injectedBlocks: injectorContribs.contextBlocks.length ? injectorContribs.contextBlocks : undefined,
98
+ middleware: options.middleware,
99
+ ...baseContext,
100
+ });
101
+ const applied = applyContextBudget({
102
+ groups,
103
+ context,
104
+ skills: skills ?? [],
105
+ tools,
106
+ budget: options.contextBudget,
107
+ layout,
108
+ });
109
+ messages = flattenInputGroups(applied.groups, layout);
110
+ if (buildContext.middleware)
111
+ messages = await buildContext.middleware.run("input_assembly", messages);
112
+ context = applied.context;
113
+ skills = applied.skills;
114
+ tools = applied.tools;
115
+ if (options.contextBudget.reportOmissions)
116
+ budgetReport = applied.report;
117
+ }
118
+ else {
119
+ const inputBuilder = options.inputBuilder ?? createDefaultInputBuilder();
120
+ messages = await inputBuilder.build(options.input, buildContext);
121
+ context = await resolveContextProviders({
122
+ providers: options.contextProviders,
123
+ messages,
124
+ injectedBlocks: injectorContribs.contextBlocks.length ? injectorContribs.contextBlocks : undefined,
125
+ middleware: options.middleware,
126
+ ...baseContext,
127
+ });
128
+ }
103
129
  const promptBuilder = options.promptBuilder ?? createDefaultPromptBuilder();
104
130
  const promptRequest = options.middleware
105
131
  ? await options.middleware.run("prompt_build", {
106
132
  messages,
107
133
  context,
108
- skills: options.skills,
109
- tools: options.tools,
134
+ skills,
135
+ tools,
110
136
  metadata: options.metadata,
111
137
  signal: options.signal,
112
138
  })
113
139
  : {
114
140
  messages,
115
141
  context,
116
- skills: options.skills,
117
- tools: options.tools,
142
+ skills,
143
+ tools,
118
144
  metadata: options.metadata,
119
145
  signal: options.signal,
120
146
  };
121
- const providerMessages = await promptBuilder.build({ ...promptRequest, tools: options.tools });
147
+ const providerMessages = await promptBuilder.build({ ...promptRequest, tools });
122
148
  assertMessagesSupportModelCapabilities(options.model, providerMessages);
149
+ const metadata = budgetReport
150
+ ? { ...options.metadata, [CONTEXT_BUDGET_REPORT_METADATA_KEY]: budgetReport }
151
+ : options.metadata;
123
152
  return {
124
153
  model: options.model,
125
154
  messages: providerMessages,
126
- tools: options.tools,
155
+ tools,
127
156
  context,
128
157
  options: options.providerOptions,
129
- metadata: options.metadata,
158
+ metadata,
130
159
  signal: options.signal,
131
160
  };
132
161
  }
162
+ async function buildDefaultInputMessageGroups(input, context) {
163
+ return {
164
+ instructions: [
165
+ ...instructionMessages(context.systemInstructions, "System instruction"),
166
+ ...instructionMessages(context.developerInstructions, "Developer instruction"),
167
+ ...customInstructionMessages(context.instructions),
168
+ ],
169
+ summaries: summaryMessages(context.summaries),
170
+ history: [...(context.history ?? [])],
171
+ input: inputMessages(input),
172
+ attachments: await attachmentMessages(context),
173
+ toolResults: toolResultMessages(context.toolResults),
174
+ };
175
+ }
133
176
  function flattenInputGroups(groups, layout) {
134
177
  switch (layout) {
135
178
  case "cache_aware":
@@ -1,6 +1,6 @@
1
1
  import { mkdir, readFile, appendFile } from "node:fs/promises";
2
2
  import { dirname } from "node:path";
3
- import { SESSION_APPEND_CONFLICT_CODE, SessionAppendConflictError, isSessionEntryKind, SESSION_ENTRY_SCHEMA_VERSION } from "../contracts.js";
3
+ import { SESSION_APPEND_CONFLICT_CODE, SessionAppendConflictError, SessionSearchUnsupportedError, isSessionEntryKind, SESSION_ENTRY_SCHEMA_VERSION, } from "../contracts.js";
4
4
  import { isNodeErrorCode } from "./config.js";
5
5
  export function createJsonlSessionStore(pathOrOptions) {
6
6
  const options = typeof pathOrOptions === "string" ? { path: pathOrOptions, createDirectory: true } : pathOrOptions;
@@ -46,6 +46,9 @@ export function createJsonlSessionStore(pathOrOptions) {
46
46
  async get(id) {
47
47
  return findEntry(path, id);
48
48
  },
49
+ async searchSessions() {
50
+ throw new SessionSearchUnsupportedError("JSONL session store does not support searchSessions");
51
+ },
49
52
  };
50
53
  }
51
54
  /** Read a JSONL session file and return both valid entries and per-line parse errors. */
package/dist/rpc.js CHANGED
@@ -82,8 +82,19 @@ async function handleRequest(request, state, stdout, activeRuns) {
82
82
  activeRuns.set(session.id, { requestId: request.id, promise });
83
83
  break;
84
84
  }
85
- case "steer":
86
- throw new Error("steer is not supported by the current AgentSession runtime");
85
+ case "steer": {
86
+ const input = stringParam(request.params, "input") ?? stringParam(request.params, "prompt");
87
+ if (!input)
88
+ throw new Error("steer requires params.input");
89
+ const softInterrupt = request.params?.softInterrupt === true;
90
+ state.current.steer(input, softInterrupt ? { softInterrupt: true } : undefined);
91
+ write(stdout, {
92
+ id: request.id,
93
+ ok: true,
94
+ result: { sessionId: state.current.id },
95
+ });
96
+ break;
97
+ }
87
98
  case "abort":
88
99
  state.current.abort(stringParam(request.params, "reason"));
89
100
  write(stdout, { id: request.id, ok: true, result: { sessionId: state.current.id } });
@@ -1,4 +1,4 @@
1
- import type { Message, SessionBranchRead, BranchReader, SessionEntry, SessionStore } from "./contracts.js";
1
+ import { type Message, type SessionBranchRead, type BranchReader, type SessionEntry, type SessionStore } from "./contracts.js";
2
2
  export interface CreateSessionEntryOptions extends Omit<SessionEntry, "id" | "timestamp"> {
3
3
  readonly id?: string;
4
4
  readonly timestamp?: string;
@@ -24,4 +24,9 @@ export declare function getSessionBranchEntries(entries: readonly SessionEntry[]
24
24
  export declare function listSessionBranches(entries: readonly SessionEntry[]): readonly SessionBranch[];
25
25
  export declare function rebuildSessionContext(reader: BranchReader, query: SessionBranchRead): Promise<SessionContextSnapshot>;
26
26
  export declare function rebuildSessionContext(entries: readonly SessionEntry[], options?: SessionBranchOptions): SessionContextSnapshot;
27
- export declare function createMemorySessionStore(initialEntries?: readonly SessionEntry[]): SessionStore;
27
+ export type MemorySessionSearchMode = "linear" | "unsupported";
28
+ export interface CreateMemorySessionStoreOptions {
29
+ /** Default `"linear"`: capped in-process scan. `"unsupported"`: typed throw. */
30
+ readonly sessionSearchMode?: MemorySessionSearchMode;
31
+ }
32
+ export declare function createMemorySessionStore(initialEntries?: readonly SessionEntry[], options?: CreateMemorySessionStoreOptions): SessionStore;