@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.
@@ -335,6 +335,15 @@ interface ModelConfig {
335
335
  modelOptions?: ModelSpecificOptions;
336
336
  }
337
337
 
338
+ /**
339
+ * What happened to `strict` on a request, recorded per call rather than inferred.
340
+ *
341
+ * `applied` and `notAttempted` are the two states that a refusal-only field cannot tell apart —
342
+ * both leave `strictRefusalReasons` empty. Recording the verdict positively is what makes "was
343
+ * this agent's output actually enforced?" answerable from an `ai_calls` row.
344
+ */
345
+ type StrictStatus = 'applied' | 'refused' | 'compileRejected' | 'notAttempted';
346
+
338
347
  declare const ResourceGovernanceStatusSchema: z.ZodEnum<{
339
348
  active: "active";
340
349
  deprecated: "deprecated";
@@ -862,6 +871,7 @@ interface WorkflowDefinition {
862
871
  * Generic LLM Types
863
872
  * Universal interfaces for LLM interaction across all resource types
864
873
  */
874
+
865
875
  /**
866
876
  * Standard chat message format
867
877
  * Compatible with OpenAI, Anthropic, and other providers
@@ -895,6 +905,35 @@ interface LLMGenerateResponse<T = unknown> {
895
905
  totalTokens: number;
896
906
  };
897
907
  cost?: number;
908
+ /**
909
+ * What actually happened to `strict` on the request that produced this response. Every server
910
+ * adapter sets it on every call, so the value is a statement rather than an inference:
911
+ *
912
+ * - `applied` — the request carried `strict: true` and the grammar was in effect
913
+ * - `refused` — `toStrictSchema` could not express the schema, so the request went out unstrict
914
+ * - `compileRejected` — the schema passed `toStrictSchema` but the provider's grammar compiler
915
+ * rejected it at request time, and the call was retried unstrict
916
+ * - `notAttempted` — this adapter does not send strict at all (OpenAI, Google, OpenRouter)
917
+ *
918
+ * This exists because `strictRefusalReasons` alone cannot answer the question. Its absence means
919
+ * "strict held" OR "nothing ever tried", and a prod run that recorded zero refusals while
920
+ * returning an array-typed field as a string is exactly the case where the difference matters.
921
+ *
922
+ * Internal-only, like `usage` — `UniversalLLMAdapter` lifts it onto the `ai_calls` row and
923
+ * strips it before the response reaches callers.
924
+ */
925
+ strictStatus?: StrictStatus;
926
+ /**
927
+ * Why this call went out WITHOUT `strict`, on an adapter that tried to send it with one.
928
+ *
929
+ * The detail behind a `refused` / `compileRejected` `strictStatus` — the short, stable reason
930
+ * strings `toStrictSchema` computes. Read `strictStatus` to answer "was it enforced"; read this
931
+ * to answer "why not".
932
+ *
933
+ * Internal-only, like `usage` — `UniversalLLMAdapter` lifts it onto the `ai_calls` row and
934
+ * strips it before the response reaches callers.
935
+ */
936
+ strictRefusalReasons?: string[];
898
937
  }
899
938
  /**
900
939
  * LLM Adapter interface
@@ -926,6 +965,14 @@ interface LLMAdapter {
926
965
  * Memory types mirror action types for clarity and filtering
927
966
  */
928
967
  type MemoryEntryType = 'context' | 'input' | 'reasoning' | 'tool-result' | 'delegation-result' | 'error';
968
+ /**
969
+ * Who authored an entry's content.
970
+ *
971
+ * This is what lets the assembled prompt tell framework-authored text apart from text that
972
+ * originated outside the trust boundary. `'framework'` content is ours; the other three are not
973
+ * and are rendered inside the JSON data envelope (see `MemoryManager.toContextParts`).
974
+ */
975
+ type MemoryEntrySource = 'framework' | 'user' | 'tool' | 'model';
929
976
  /**
930
977
  * Memory entry - represents a single entry in agent memory
931
978
  * Stored in agent memory, translated by adapters to vendor-specific formats
@@ -936,6 +983,16 @@ interface MemoryEntry {
936
983
  timestamp: number;
937
984
  turnNumber: number | null;
938
985
  iterationNumber: number | null;
986
+ /**
987
+ * Provenance. **Optional on purpose** — `undefined` means unknown, which is what every
988
+ * pre-existing snapshot and every not-yet-redeployed tenant bundle produces. Read sites MUST
989
+ * test `== null`, never `=== undefined`: the `inTurnScope` predicate in `manager.ts` is the
990
+ * cautionary precedent, where a `=== undefined` check silently dropped every `null`-stamped
991
+ * entry. `isMemoryEntry` is deliberately NOT tightened to require this field; doing so would
992
+ * make every stored snapshot fail validation, and `restoreSessionMemory` fails open by
993
+ * starting the agent with empty memory rather than throwing.
994
+ */
995
+ source?: MemoryEntrySource;
939
996
  }
940
997
  /**
941
998
  * Agent memory - Self-orchestrated memory with session + working storage
@@ -963,8 +1020,17 @@ interface MemoryStatus {
963
1020
  sessionMemoryKeys: number;
964
1021
  sessionMemoryLimit: number;
965
1022
  currentKeys: string[];
1023
+ sessionMemoryTokens: number;
1024
+ sessionMemoryTokenLimit: number;
1025
+ /**
1026
+ * History tokens as a percentage of `historyBudget` — history ALONE, not history plus session
1027
+ * memory. It previously reported the combined total under this name, so session memory growth
1028
+ * read as history pressure and triggered history compaction that could not relieve it.
1029
+ */
966
1030
  historyPercent: number;
967
1031
  historyTokens: number;
1032
+ historyBudget: number;
1033
+ totalTokens: number;
968
1034
  tokenBudget: number;
969
1035
  }
970
1036
  /**
@@ -981,6 +1047,20 @@ interface MemoryConstraints {
981
1047
  * Agent provides strings, framework handles wrapping and auto-compaction
982
1048
  */
983
1049
 
1050
+ /**
1051
+ * The framework's own framing message and the untrusted data envelope, as separate strings.
1052
+ *
1053
+ * They are separate because the model must be able to tell them apart, and so must the input
1054
+ * sanitizer: the framing is framework-authored and trusted, the envelope is not. Concatenating
1055
+ * them — which is what this replaced — made that distinction undecidable at the adapter and left
1056
+ * the framework's own section headers inside the region scanned for delimiter injection.
1057
+ */
1058
+ interface MemoryContextParts {
1059
+ /** Framework-authored. Memory status and a description of the envelope. Carries NO stored content. */
1060
+ framing: string;
1061
+ /** Every stored fragment, JSON-encoded and source-tagged. Untrusted. */
1062
+ dataEnvelope: string;
1063
+ }
984
1064
  /**
985
1065
  * Memory Manager - Agent memory orchestration
986
1066
  * Provides ultra-simple API for agents (strings only)
@@ -997,7 +1077,7 @@ declare class MemoryManager {
997
1077
  * @param key - Session memory key
998
1078
  * @param content - String content from agent
999
1079
  */
1000
- set(key: string, content: string): void;
1080
+ set(key: string, content: string, source?: MemoryEntrySource): void;
1001
1081
  /**
1002
1082
  * Get session memory entry content
1003
1083
  * @param key - Session memory key
@@ -1026,6 +1106,15 @@ declare class MemoryManager {
1026
1106
  * Emergency fallback if agent exceeds limits
1027
1107
  */
1028
1108
  enforceHardLimits(): void;
1109
+ /**
1110
+ * Evict oldest session memory entries until the pool fits its token limit.
1111
+ *
1112
+ * Key count and token count are different constraints: 25 short keys are fine, 25 large ones
1113
+ * are not. Eviction is oldest-first by timestamp, matching the key-count path, and always
1114
+ * leaves at least one entry so a single oversized key degrades to "one key" rather than to
1115
+ * "memory silently emptied".
1116
+ */
1117
+ private enforceSessionMemoryTokenLimit;
1029
1118
  /**
1030
1119
  * Get history length (for logging and introspection)
1031
1120
  * @returns Number of entries in history
@@ -1049,14 +1138,29 @@ declare class MemoryManager {
1049
1138
  */
1050
1139
  getSnapshot(): AgentMemory | undefined;
1051
1140
  /**
1052
- * Build context string for LLM
1053
- * Serializes sessionmemory + history memory with clear sections
1054
- * Shows current iteration entries FIRST (reverse chronological) for LLM attention
1141
+ * Build the framework framing and the untrusted data envelope for an LLM call.
1142
+ *
1143
+ * These are two separate strings because they are two different trust levels, and they used to
1144
+ * be one. Concatenated, the framework's own `=== ... ===` section headers sat in the same string
1145
+ * as stored tool output and user text, so the input sanitizer matched its own scaffolding on
1146
+ * every call and nothing downstream could tell which half a match came from. Splitting them
1147
+ * makes that distinction structural: the framing is ours, the envelope is not.
1148
+ *
1149
+ * The envelope is JSON, which additionally neutralizes the anchored-delimiter attack class —
1150
+ * `JSON.stringify` escapes newlines, so a stored fragment cannot produce a line that starts
1151
+ * with `===` no matter what it contains.
1152
+ *
1153
+ * The current turn's own input is deliberately NOT in either string. It travels as its own
1154
+ * `role:'user'` message (see `buildAgentMessages`), which is the whole point: a model asked to
1155
+ * treat "everything in this block" as data was also being handed the live question inside that
1156
+ * block.
1157
+ *
1158
+ * Shows current iteration entries FIRST (reverse chronological) for LLM attention.
1159
+ *
1055
1160
  * @param currentIteration - Current iteration number (0 = pre-iteration)
1056
1161
  * @param currentTurn - Current turn number (optional, for session context filtering)
1057
- * @returns Formatted memory context for LLM prompt
1058
1162
  */
1059
- toContext(currentIteration: number, currentTurn?: number): string;
1163
+ toContextParts(currentIteration: number, currentTurn?: number): MemoryContextParts;
1060
1164
  }
1061
1165
 
1062
1166
  /**
@@ -1255,6 +1359,12 @@ interface IterationContext {
1255
1359
  modelConfig: ModelConfig;
1256
1360
  adapterFactory: LLMAdapterFactory;
1257
1361
  knowledgeMap?: KnowledgeMap;
1362
+ /**
1363
+ * The validated input for this execution, serialized. It travels here because the model gets
1364
+ * it as its own `role:'user'` message; nothing else in this context carried it, so the input
1365
+ * had to be read back out of memory history and shipped inside the memory block.
1366
+ */
1367
+ currentInput: string;
1258
1368
  }
1259
1369
 
1260
1370
  type Json = string | number | boolean | null | {
@@ -4001,6 +4111,7 @@ type Database = {
4001
4111
  deleted_at: string | null;
4002
4112
  ended_at: string | null;
4003
4113
  memory_snapshot: Json;
4114
+ memory_version: number;
4004
4115
  metadata: Json | null;
4005
4116
  organization_id: string;
4006
4117
  resource_id: string;
@@ -4017,6 +4128,7 @@ type Database = {
4017
4128
  deleted_at?: string | null;
4018
4129
  ended_at?: string | null;
4019
4130
  memory_snapshot: Json;
4131
+ memory_version?: number;
4020
4132
  metadata?: Json | null;
4021
4133
  organization_id: string;
4022
4134
  resource_id: string;
@@ -4033,6 +4145,7 @@ type Database = {
4033
4145
  deleted_at?: string | null;
4034
4146
  ended_at?: string | null;
4035
4147
  memory_snapshot?: Json;
4148
+ memory_version?: number;
4036
4149
  metadata?: Json | null;
4037
4150
  organization_id?: string;
4038
4151
  resource_id?: string;
@@ -4370,9 +4483,14 @@ type Database = {
4370
4483
  p_session_id: string;
4371
4484
  };
4372
4485
  Returns: {
4486
+ context_window_size: number;
4373
4487
  created_at: string;
4488
+ cumulative_input_tokens: number;
4489
+ cumulative_output_tokens: number;
4490
+ deleted_at: string;
4374
4491
  ended_at: string;
4375
4492
  memory_snapshot: Json;
4493
+ memory_version: number;
4376
4494
  metadata: Json;
4377
4495
  organization_id: string;
4378
4496
  resource_id: string;
@@ -4414,6 +4532,18 @@ type Database = {
4414
4532
  };
4415
4533
  Returns: boolean;
4416
4534
  };
4535
+ increment_session_tokens: {
4536
+ Args: {
4537
+ p_input_tokens: number;
4538
+ p_output_tokens: number;
4539
+ p_session_id: string;
4540
+ };
4541
+ Returns: {
4542
+ context_window_size: number;
4543
+ cumulative_input_tokens: number;
4544
+ cumulative_output_tokens: number;
4545
+ }[];
4546
+ };
4417
4547
  is_org_member: {
4418
4548
  Args: {
4419
4549
  org_id: string;
@@ -10146,6 +10276,25 @@ declare class MetricsCollector {
10146
10276
  buildExecutionMetrics(metricsConfig?: ResourceMetricsConfig): ExecutionMetricsSummary;
10147
10277
  }
10148
10278
 
10279
+ /**
10280
+ * Which `role:'user'` message slot a sanitizer warning came from, derived from the request's
10281
+ * message array (no new plumbing from callers):
10282
+ * - `'memory-context'` — the framework's framing message, identified by the `=== MEMORY STATUS ===`
10283
+ * banner. Framework-authored and trusted; it is no longer scanned at all, so this source should
10284
+ * not appear for agent calls. Retained because stale tenant bundles still emit the old combined
10285
+ * block, and their rows must stay readable.
10286
+ * - `'data-envelope'` — the JSON envelope carrying every stored fragment. Untrusted, and the slot
10287
+ * where a match is genuine signal.
10288
+ * - `'input'` — the turn's own input, on its own message. Also untrusted.
10289
+ * - `'history'` — replayed prior turns. Untrusted, but already screened at their own front door,
10290
+ * so warnings here are recorded and never block (see `screenInput`).
10291
+ */
10292
+ type InputWarningSource = 'memory-context' | 'data-envelope' | 'history' | 'input';
10293
+ /** Per-source breakdown of sanitizer warnings, so a memory-context echo is distinguishable from a genuine hit. */
10294
+ interface SourcedInputWarnings {
10295
+ source: InputWarningSource;
10296
+ warnings: string[];
10297
+ }
10149
10298
  interface BaseAICall {
10150
10299
  callSequence: number;
10151
10300
  callType: 'agent-reasoning' | 'agent-completion' | 'workflow-step' | 'tool' | 'other';
@@ -10155,6 +10304,71 @@ interface BaseAICall {
10155
10304
  costUsd: number;
10156
10305
  latencyMs: number;
10157
10306
  context?: AICallContext;
10307
+ /**
10308
+ * Distinct prompt-injection pattern types detected in the request's user-role messages.
10309
+ * Present only when the input sanitizer matched something. Non-blocking matches ride along on
10310
+ * the successful call's row; a blocked call records a row of its own (see `inputBlocked`).
10311
+ *
10312
+ * Flat union across all `role:'user'` messages — unchanged shape, kept for existing readers.
10313
+ * See `inputWarningsBySource` for the per-slot breakdown.
10314
+ */
10315
+ inputWarnings?: string[];
10316
+ /**
10317
+ * Additive breakdown of `inputWarnings` by message slot (see `InputWarningSource`). Present only
10318
+ * when at least one source produced a warning. Existing readers that only look at the flat
10319
+ * `inputWarnings` array are unaffected.
10320
+ */
10321
+ inputWarningsBySource?: SourcedInputWarnings[];
10322
+ /**
10323
+ * True when the sanitizer blocked the request and no provider call was made.
10324
+ * Such a row carries zero tokens, zero cost, and zero latency — it exists so a hard,
10325
+ * user-visible failure is observable at all. Before this, a blocked call produced no row.
10326
+ */
10327
+ inputBlocked?: boolean;
10328
+ /**
10329
+ * The validator's message when the provider responded but its output failed `responseSchema`
10330
+ * validation (e.g. `missing required field 'nextActions'`). Present only on such a row.
10331
+ *
10332
+ * This is NOT the blocked-input case: the provider DID respond and tokens WERE spent, so the row
10333
+ * carries real `inputTokens`, `outputTokens`, cost and latency. It is a paid call that produced
10334
+ * nothing usable, and before this it produced no row at all.
10335
+ *
10336
+ * One row per failed attempt — the adapter retries a validation failure up to `LLM_MAX_ATTEMPTS`,
10337
+ * so a turn that exhausts its retries records three. Reading `inputTokens` across these rows is
10338
+ * what measures malformed-output rate against context size rather than inferring it.
10339
+ *
10340
+ * Existing readers that only look at the fields above are unaffected.
10341
+ */
10342
+ outputValidationError?: string;
10343
+ /**
10344
+ * The raw model output that failed validation, JSON-stringified and truncated to a bounded
10345
+ * length. Truncation is visible in the value itself (a trailing `…[truncated: N chars total]`),
10346
+ * never silent. Present only alongside `outputValidationError`.
10347
+ */
10348
+ unvalidatedOutput?: string;
10349
+ /**
10350
+ * What happened to `strict` structured output on this call — `applied`, `refused`,
10351
+ * `compileRejected`, or `notAttempted`. Every server adapter sets it, so this is the field that
10352
+ * answers "is this agent's output actually being enforced?" without reading source.
10353
+ *
10354
+ * It replaces an inference that turned out to be unsound. `strictRefusalReasons` alone records
10355
+ * only refusals, so an empty row meant "strict held" OR "this adapter never tries" — and a prod
10356
+ * run recorded zero refusals while a call returned an array-typed field as a string, which a
10357
+ * grammar makes impossible. The absence proved nothing, because the deployed API had no way to
10358
+ * write the field at all.
10359
+ *
10360
+ * Absent on rows written before this field existed; that absence is itself diagnostic (the API
10361
+ * predates the change). Existing readers that only look at the fields above are unaffected.
10362
+ */
10363
+ strictStatus?: StrictStatus;
10364
+ /**
10365
+ * Why this call went out without `strict`, when a strict-capable adapter refused the schema. The
10366
+ * detail behind `strictStatus: 'refused' | 'compileRejected'` — read `strictStatus` for whether
10367
+ * it was enforced, this for why not.
10368
+ *
10369
+ * Existing readers that only look at the fields above are unaffected.
10370
+ */
10371
+ strictRefusalReasons?: string[];
10158
10372
  }
10159
10373
  type AICallContext = AgentReasoningContext | AgentCompletionContext | WorkflowStepContext | ToolCallContext | OtherCallContext;
10160
10374
  interface AgentReasoningContext {
@@ -10200,6 +10414,20 @@ interface LLMUsageData {
10200
10414
  latencyMs: number;
10201
10415
  /** Actual cost from provider in USD (when available, e.g., OpenRouter) */
10202
10416
  cost?: number;
10417
+ /** Distinct prompt-injection pattern types detected in the request's user-role messages */
10418
+ inputWarnings?: string[];
10419
+ /** Additive per-source breakdown of `inputWarnings` — see `SourcedInputWarnings` */
10420
+ inputWarningsBySource?: SourcedInputWarnings[];
10421
+ /** True when the sanitizer blocked the request and no provider call was made */
10422
+ inputBlocked?: boolean;
10423
+ /** Validator message when the provider responded but the output failed `responseSchema` validation */
10424
+ outputValidationError?: string;
10425
+ /** Raw model output that failed validation — JSON-stringified, truncated, truncation marked inline */
10426
+ unvalidatedOutput?: string;
10427
+ /** What happened to `strict` on this call — set by every server adapter, refusal or not */
10428
+ strictStatus?: StrictStatus;
10429
+ /** Why the call went out unstrict, when a strict-capable adapter refused the schema */
10430
+ strictRefusalReasons?: string[];
10203
10431
  }
10204
10432
  interface AIUsageSummary {
10205
10433
  model: LLMModel;