@elevasis/sdk 1.40.0 → 1.41.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -372,6 +372,15 @@ interface ModelConfig {
372
372
  modelOptions?: ModelSpecificOptions;
373
373
  }
374
374
 
375
+ /**
376
+ * What happened to `strict` on a request, recorded per call rather than inferred.
377
+ *
378
+ * `applied` and `notAttempted` are the two states that a refusal-only field cannot tell apart —
379
+ * both leave `strictRefusalReasons` empty. Recording the verdict positively is what makes "was
380
+ * this agent's output actually enforced?" answerable from an `ai_calls` row.
381
+ */
382
+ type StrictStatus = 'applied' | 'refused' | 'compileRejected' | 'notAttempted';
383
+
375
384
  declare const ResourceGovernanceStatusSchema: z.ZodEnum<{
376
385
  active: "active";
377
386
  deprecated: "deprecated";
@@ -899,6 +908,7 @@ interface WorkflowDefinition {
899
908
  * Generic LLM Types
900
909
  * Universal interfaces for LLM interaction across all resource types
901
910
  */
911
+
902
912
  /**
903
913
  * Standard chat message format
904
914
  * Compatible with OpenAI, Anthropic, and other providers
@@ -932,6 +942,35 @@ interface LLMGenerateResponse<T = unknown> {
932
942
  totalTokens: number;
933
943
  };
934
944
  cost?: number;
945
+ /**
946
+ * What actually happened to `strict` on the request that produced this response. Every server
947
+ * adapter sets it on every call, so the value is a statement rather than an inference:
948
+ *
949
+ * - `applied` — the request carried `strict: true` and the grammar was in effect
950
+ * - `refused` — `toStrictSchema` could not express the schema, so the request went out unstrict
951
+ * - `compileRejected` — the schema passed `toStrictSchema` but the provider's grammar compiler
952
+ * rejected it at request time, and the call was retried unstrict
953
+ * - `notAttempted` — this adapter does not send strict at all (OpenAI, Google, OpenRouter)
954
+ *
955
+ * This exists because `strictRefusalReasons` alone cannot answer the question. Its absence means
956
+ * "strict held" OR "nothing ever tried", and a prod run that recorded zero refusals while
957
+ * returning an array-typed field as a string is exactly the case where the difference matters.
958
+ *
959
+ * Internal-only, like `usage` — `UniversalLLMAdapter` lifts it onto the `ai_calls` row and
960
+ * strips it before the response reaches callers.
961
+ */
962
+ strictStatus?: StrictStatus;
963
+ /**
964
+ * Why this call went out WITHOUT `strict`, on an adapter that tried to send it with one.
965
+ *
966
+ * The detail behind a `refused` / `compileRejected` `strictStatus` — the short, stable reason
967
+ * strings `toStrictSchema` computes. Read `strictStatus` to answer "was it enforced"; read this
968
+ * to answer "why not".
969
+ *
970
+ * Internal-only, like `usage` — `UniversalLLMAdapter` lifts it onto the `ai_calls` row and
971
+ * strips it before the response reaches callers.
972
+ */
973
+ strictRefusalReasons?: string[];
935
974
  }
936
975
  /**
937
976
  * LLM Adapter interface
@@ -963,6 +1002,14 @@ interface LLMAdapter {
963
1002
  * Memory types mirror action types for clarity and filtering
964
1003
  */
965
1004
  type MemoryEntryType = 'context' | 'input' | 'reasoning' | 'tool-result' | 'delegation-result' | 'error';
1005
+ /**
1006
+ * Who authored an entry's content.
1007
+ *
1008
+ * This is what lets the assembled prompt tell framework-authored text apart from text that
1009
+ * originated outside the trust boundary. `'framework'` content is ours; the other three are not
1010
+ * and are rendered inside the JSON data envelope (see `MemoryManager.toContextParts`).
1011
+ */
1012
+ type MemoryEntrySource = 'framework' | 'user' | 'tool' | 'model';
966
1013
  /**
967
1014
  * Memory entry - represents a single entry in agent memory
968
1015
  * Stored in agent memory, translated by adapters to vendor-specific formats
@@ -973,6 +1020,16 @@ interface MemoryEntry {
973
1020
  timestamp: number;
974
1021
  turnNumber: number | null;
975
1022
  iterationNumber: number | null;
1023
+ /**
1024
+ * Provenance. **Optional on purpose** — `undefined` means unknown, which is what every
1025
+ * pre-existing snapshot and every not-yet-redeployed tenant bundle produces. Read sites MUST
1026
+ * test `== null`, never `=== undefined`: the `inTurnScope` predicate in `manager.ts` is the
1027
+ * cautionary precedent, where a `=== undefined` check silently dropped every `null`-stamped
1028
+ * entry. `isMemoryEntry` is deliberately NOT tightened to require this field; doing so would
1029
+ * make every stored snapshot fail validation, and `restoreSessionMemory` fails open by
1030
+ * starting the agent with empty memory rather than throwing.
1031
+ */
1032
+ source?: MemoryEntrySource;
976
1033
  }
977
1034
  /**
978
1035
  * Agent memory - Self-orchestrated memory with session + working storage
@@ -1000,8 +1057,17 @@ interface MemoryStatus {
1000
1057
  sessionMemoryKeys: number;
1001
1058
  sessionMemoryLimit: number;
1002
1059
  currentKeys: string[];
1060
+ sessionMemoryTokens: number;
1061
+ sessionMemoryTokenLimit: number;
1062
+ /**
1063
+ * History tokens as a percentage of `historyBudget` — history ALONE, not history plus session
1064
+ * memory. It previously reported the combined total under this name, so session memory growth
1065
+ * read as history pressure and triggered history compaction that could not relieve it.
1066
+ */
1003
1067
  historyPercent: number;
1004
1068
  historyTokens: number;
1069
+ historyBudget: number;
1070
+ totalTokens: number;
1005
1071
  tokenBudget: number;
1006
1072
  }
1007
1073
  /**
@@ -1018,6 +1084,20 @@ interface MemoryConstraints {
1018
1084
  * Agent provides strings, framework handles wrapping and auto-compaction
1019
1085
  */
1020
1086
 
1087
+ /**
1088
+ * The framework's own framing message and the untrusted data envelope, as separate strings.
1089
+ *
1090
+ * They are separate because the model must be able to tell them apart, and so must the input
1091
+ * sanitizer: the framing is framework-authored and trusted, the envelope is not. Concatenating
1092
+ * them — which is what this replaced — made that distinction undecidable at the adapter and left
1093
+ * the framework's own section headers inside the region scanned for delimiter injection.
1094
+ */
1095
+ interface MemoryContextParts {
1096
+ /** Framework-authored. Memory status and a description of the envelope. Carries NO stored content. */
1097
+ framing: string;
1098
+ /** Every stored fragment, JSON-encoded and source-tagged. Untrusted. */
1099
+ dataEnvelope: string;
1100
+ }
1021
1101
  /**
1022
1102
  * Memory Manager - Agent memory orchestration
1023
1103
  * Provides ultra-simple API for agents (strings only)
@@ -1034,7 +1114,7 @@ declare class MemoryManager {
1034
1114
  * @param key - Session memory key
1035
1115
  * @param content - String content from agent
1036
1116
  */
1037
- set(key: string, content: string): void;
1117
+ set(key: string, content: string, source?: MemoryEntrySource): void;
1038
1118
  /**
1039
1119
  * Get session memory entry content
1040
1120
  * @param key - Session memory key
@@ -1063,6 +1143,15 @@ declare class MemoryManager {
1063
1143
  * Emergency fallback if agent exceeds limits
1064
1144
  */
1065
1145
  enforceHardLimits(): void;
1146
+ /**
1147
+ * Evict oldest session memory entries until the pool fits its token limit.
1148
+ *
1149
+ * Key count and token count are different constraints: 25 short keys are fine, 25 large ones
1150
+ * are not. Eviction is oldest-first by timestamp, matching the key-count path, and always
1151
+ * leaves at least one entry so a single oversized key degrades to "one key" rather than to
1152
+ * "memory silently emptied".
1153
+ */
1154
+ private enforceSessionMemoryTokenLimit;
1066
1155
  /**
1067
1156
  * Get history length (for logging and introspection)
1068
1157
  * @returns Number of entries in history
@@ -1086,14 +1175,29 @@ declare class MemoryManager {
1086
1175
  */
1087
1176
  getSnapshot(): AgentMemory | undefined;
1088
1177
  /**
1089
- * Build context string for LLM
1090
- * Serializes sessionmemory + history memory with clear sections
1091
- * Shows current iteration entries FIRST (reverse chronological) for LLM attention
1178
+ * Build the framework framing and the untrusted data envelope for an LLM call.
1179
+ *
1180
+ * These are two separate strings because they are two different trust levels, and they used to
1181
+ * be one. Concatenated, the framework's own `=== ... ===` section headers sat in the same string
1182
+ * as stored tool output and user text, so the input sanitizer matched its own scaffolding on
1183
+ * every call and nothing downstream could tell which half a match came from. Splitting them
1184
+ * makes that distinction structural: the framing is ours, the envelope is not.
1185
+ *
1186
+ * The envelope is JSON, which additionally neutralizes the anchored-delimiter attack class —
1187
+ * `JSON.stringify` escapes newlines, so a stored fragment cannot produce a line that starts
1188
+ * with `===` no matter what it contains.
1189
+ *
1190
+ * The current turn's own input is deliberately NOT in either string. It travels as its own
1191
+ * `role:'user'` message (see `buildAgentMessages`), which is the whole point: a model asked to
1192
+ * treat "everything in this block" as data was also being handed the live question inside that
1193
+ * block.
1194
+ *
1195
+ * Shows current iteration entries FIRST (reverse chronological) for LLM attention.
1196
+ *
1092
1197
  * @param currentIteration - Current iteration number (0 = pre-iteration)
1093
1198
  * @param currentTurn - Current turn number (optional, for session context filtering)
1094
- * @returns Formatted memory context for LLM prompt
1095
1199
  */
1096
- toContext(currentIteration: number, currentTurn?: number): string;
1200
+ toContextParts(currentIteration: number, currentTurn?: number): MemoryContextParts;
1097
1201
  }
1098
1202
 
1099
1203
  /**
@@ -1292,6 +1396,12 @@ interface IterationContext {
1292
1396
  modelConfig: ModelConfig;
1293
1397
  adapterFactory: LLMAdapterFactory;
1294
1398
  knowledgeMap?: KnowledgeMap;
1399
+ /**
1400
+ * The validated input for this execution, serialized. It travels here because the model gets
1401
+ * it as its own `role:'user'` message; nothing else in this context carried it, so the input
1402
+ * had to be read back out of memory history and shipped inside the memory block.
1403
+ */
1404
+ currentInput: string;
1295
1405
  }
1296
1406
 
1297
1407
  declare const OntologyScopeSchema: z.ZodDefault<z.ZodObject<{
@@ -2593,6 +2703,25 @@ declare class MetricsCollector {
2593
2703
  buildExecutionMetrics(metricsConfig?: ResourceMetricsConfig): ExecutionMetricsSummary;
2594
2704
  }
2595
2705
 
2706
+ /**
2707
+ * Which `role:'user'` message slot a sanitizer warning came from, derived from the request's
2708
+ * message array (no new plumbing from callers):
2709
+ * - `'memory-context'` — the framework's framing message, identified by the `=== MEMORY STATUS ===`
2710
+ * banner. Framework-authored and trusted; it is no longer scanned at all, so this source should
2711
+ * not appear for agent calls. Retained because stale tenant bundles still emit the old combined
2712
+ * block, and their rows must stay readable.
2713
+ * - `'data-envelope'` — the JSON envelope carrying every stored fragment. Untrusted, and the slot
2714
+ * where a match is genuine signal.
2715
+ * - `'input'` — the turn's own input, on its own message. Also untrusted.
2716
+ * - `'history'` — replayed prior turns. Untrusted, but already screened at their own front door,
2717
+ * so warnings here are recorded and never block (see `screenInput`).
2718
+ */
2719
+ type InputWarningSource = 'memory-context' | 'data-envelope' | 'history' | 'input';
2720
+ /** Per-source breakdown of sanitizer warnings, so a memory-context echo is distinguishable from a genuine hit. */
2721
+ interface SourcedInputWarnings {
2722
+ source: InputWarningSource;
2723
+ warnings: string[];
2724
+ }
2596
2725
  interface BaseAICall {
2597
2726
  callSequence: number;
2598
2727
  callType: 'agent-reasoning' | 'agent-completion' | 'workflow-step' | 'tool' | 'other';
@@ -2602,6 +2731,71 @@ interface BaseAICall {
2602
2731
  costUsd: number;
2603
2732
  latencyMs: number;
2604
2733
  context?: AICallContext;
2734
+ /**
2735
+ * Distinct prompt-injection pattern types detected in the request's user-role messages.
2736
+ * Present only when the input sanitizer matched something. Non-blocking matches ride along on
2737
+ * the successful call's row; a blocked call records a row of its own (see `inputBlocked`).
2738
+ *
2739
+ * Flat union across all `role:'user'` messages — unchanged shape, kept for existing readers.
2740
+ * See `inputWarningsBySource` for the per-slot breakdown.
2741
+ */
2742
+ inputWarnings?: string[];
2743
+ /**
2744
+ * Additive breakdown of `inputWarnings` by message slot (see `InputWarningSource`). Present only
2745
+ * when at least one source produced a warning. Existing readers that only look at the flat
2746
+ * `inputWarnings` array are unaffected.
2747
+ */
2748
+ inputWarningsBySource?: SourcedInputWarnings[];
2749
+ /**
2750
+ * True when the sanitizer blocked the request and no provider call was made.
2751
+ * Such a row carries zero tokens, zero cost, and zero latency — it exists so a hard,
2752
+ * user-visible failure is observable at all. Before this, a blocked call produced no row.
2753
+ */
2754
+ inputBlocked?: boolean;
2755
+ /**
2756
+ * The validator's message when the provider responded but its output failed `responseSchema`
2757
+ * validation (e.g. `missing required field 'nextActions'`). Present only on such a row.
2758
+ *
2759
+ * This is NOT the blocked-input case: the provider DID respond and tokens WERE spent, so the row
2760
+ * carries real `inputTokens`, `outputTokens`, cost and latency. It is a paid call that produced
2761
+ * nothing usable, and before this it produced no row at all.
2762
+ *
2763
+ * One row per failed attempt — the adapter retries a validation failure up to `LLM_MAX_ATTEMPTS`,
2764
+ * so a turn that exhausts its retries records three. Reading `inputTokens` across these rows is
2765
+ * what measures malformed-output rate against context size rather than inferring it.
2766
+ *
2767
+ * Existing readers that only look at the fields above are unaffected.
2768
+ */
2769
+ outputValidationError?: string;
2770
+ /**
2771
+ * The raw model output that failed validation, JSON-stringified and truncated to a bounded
2772
+ * length. Truncation is visible in the value itself (a trailing `…[truncated: N chars total]`),
2773
+ * never silent. Present only alongside `outputValidationError`.
2774
+ */
2775
+ unvalidatedOutput?: string;
2776
+ /**
2777
+ * What happened to `strict` structured output on this call — `applied`, `refused`,
2778
+ * `compileRejected`, or `notAttempted`. Every server adapter sets it, so this is the field that
2779
+ * answers "is this agent's output actually being enforced?" without reading source.
2780
+ *
2781
+ * It replaces an inference that turned out to be unsound. `strictRefusalReasons` alone records
2782
+ * only refusals, so an empty row meant "strict held" OR "this adapter never tries" — and a prod
2783
+ * run recorded zero refusals while a call returned an array-typed field as a string, which a
2784
+ * grammar makes impossible. The absence proved nothing, because the deployed API had no way to
2785
+ * write the field at all.
2786
+ *
2787
+ * Absent on rows written before this field existed; that absence is itself diagnostic (the API
2788
+ * predates the change). Existing readers that only look at the fields above are unaffected.
2789
+ */
2790
+ strictStatus?: StrictStatus;
2791
+ /**
2792
+ * Why this call went out without `strict`, when a strict-capable adapter refused the schema. The
2793
+ * detail behind `strictStatus: 'refused' | 'compileRejected'` — read `strictStatus` for whether
2794
+ * it was enforced, this for why not.
2795
+ *
2796
+ * Existing readers that only look at the fields above are unaffected.
2797
+ */
2798
+ strictRefusalReasons?: string[];
2605
2799
  }
2606
2800
  type AICallContext = AgentReasoningContext | AgentCompletionContext | WorkflowStepContext | ToolCallContext | OtherCallContext;
2607
2801
  interface AgentReasoningContext {
@@ -2647,6 +2841,20 @@ interface LLMUsageData {
2647
2841
  latencyMs: number;
2648
2842
  /** Actual cost from provider in USD (when available, e.g., OpenRouter) */
2649
2843
  cost?: number;
2844
+ /** Distinct prompt-injection pattern types detected in the request's user-role messages */
2845
+ inputWarnings?: string[];
2846
+ /** Additive per-source breakdown of `inputWarnings` — see `SourcedInputWarnings` */
2847
+ inputWarningsBySource?: SourcedInputWarnings[];
2848
+ /** True when the sanitizer blocked the request and no provider call was made */
2849
+ inputBlocked?: boolean;
2850
+ /** Validator message when the provider responded but the output failed `responseSchema` validation */
2851
+ outputValidationError?: string;
2852
+ /** Raw model output that failed validation — JSON-stringified, truncated, truncation marked inline */
2853
+ unvalidatedOutput?: string;
2854
+ /** What happened to `strict` on this call — set by every server adapter, refusal or not */
2855
+ strictStatus?: StrictStatus;
2856
+ /** Why the call went out unstrict, when a strict-capable adapter refused the schema */
2857
+ strictRefusalReasons?: string[];
2650
2858
  }
2651
2859
  interface AIUsageSummary {
2652
2860
  model: LLMModel;