@elevasis/sdk 1.42.0 → 1.44.0

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.
@@ -274,113 +274,6 @@ interface IExecutionLogger {
274
274
  error(message: string, context?: LogContext): void;
275
275
  }
276
276
 
277
- /**
278
- * Model Configuration
279
- * Centralized model information, configuration, options, constraints, and validation
280
- * Single source of truth for all model-related definitions
281
- * Update manually when pricing changes or new models are added
282
- */
283
-
284
- /**
285
- * Supported Open AI models (direct SDK access)
286
- */
287
- type OpenAIModel = 'gpt-5' | 'gpt-5.4-mini' | 'gpt-5.4-nano';
288
- /**
289
- * Supported OpenRouter models (explicit union for type safety)
290
- */
291
- type OpenRouterModel = 'openrouter/z-ai/glm-5';
292
- /**
293
- * Supported Google models (direct SDK access)
294
- */
295
- type GoogleModel = 'gemini-3-flash-preview' | 'gemini-3.1-flash-lite-preview';
296
- /**
297
- * Supported Anthropic models (direct SDK access via @anthropic-ai/sdk)
298
- */
299
- type AnthropicModel = 'claude-opus-5' | 'claude-sonnet-5' | 'claude-haiku-4-5-20251001' | 'claude-haiku-4-5';
300
- /** Supported LLM models */
301
- type LLMModel = OpenAIModel | OpenRouterModel | GoogleModel | AnthropicModel | 'mock';
302
- /**
303
- * GPT-5 model options schema
304
- */
305
- declare const GPT5OptionsSchema: z.ZodObject<{
306
- reasoning_effort: z.ZodOptional<z.ZodEnum<{
307
- minimal: "minimal";
308
- low: "low";
309
- medium: "medium";
310
- high: "high";
311
- }>>;
312
- verbosity: z.ZodOptional<z.ZodEnum<{
313
- low: "low";
314
- medium: "medium";
315
- high: "high";
316
- }>>;
317
- }, z.core.$strip>;
318
- /**
319
- * OpenRouter model options schema
320
- * OpenRouter-specific options for routing and transforms
321
- */
322
- declare const OpenRouterOptionsSchema: z.ZodObject<{
323
- transforms: z.ZodOptional<z.ZodArray<z.ZodString>>;
324
- route: z.ZodOptional<z.ZodEnum<{
325
- fallback: "fallback";
326
- }>>;
327
- }, z.core.$strip>;
328
- /**
329
- * Google model options schema
330
- * Gemini 3 specific options for thinking depth control
331
- */
332
- declare const GoogleOptionsSchema: z.ZodObject<{
333
- thinkingLevel: z.ZodOptional<z.ZodEnum<{
334
- minimal: "minimal";
335
- low: "low";
336
- medium: "medium";
337
- high: "high";
338
- }>>;
339
- }, z.core.$strip>;
340
- /**
341
- * Anthropic model options schema
342
- * Currently empty - future options must be added per supported model family
343
- */
344
- declare const AnthropicOptionsSchema: z.ZodObject<{}, z.core.$strict>;
345
- /**
346
- * Infer TypeScript types from schemas
347
- */
348
- type GPT5Options = z.infer<typeof GPT5OptionsSchema>;
349
- type MockOptions = Record<string, never>;
350
- type OpenRouterOptions = z.infer<typeof OpenRouterOptionsSchema>;
351
- type GoogleOptions = z.infer<typeof GoogleOptionsSchema>;
352
- type AnthropicOptions = z.infer<typeof AnthropicOptionsSchema>;
353
- type ModelSpecificOptions = GPT5Options | MockOptions | OpenRouterOptions | GoogleOptions | AnthropicOptions;
354
- /**
355
- * Model configuration for LLM execution
356
- * Belongs in resource definition (AgentDefinition, WorkflowDefinition, etc.)
357
- */
358
- interface ModelConfig {
359
- model: LLMModel;
360
- provider: 'openai' | 'anthropic' | 'openrouter' | 'google' | 'mock';
361
- apiKey: string;
362
- temperature?: number;
363
- /** Maximum output tokens per LLM call. NOT the model's context window — see ModelInfo.maxTokens for that. */
364
- maxOutputTokens?: number;
365
- topP?: number;
366
- /**
367
- * Model-specific options (flat structure)
368
- * Options are model-specific, not vendor-specific
369
- * Available options defined in MODEL_INFO per model
370
- * Validated at build time via validateModelOptions()
371
- */
372
- modelOptions?: ModelSpecificOptions;
373
- }
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
-
384
277
  declare const ResourceGovernanceStatusSchema: z.ZodEnum<{
385
278
  active: "active";
386
279
  deprecated: "deprecated";
@@ -743,165 +636,194 @@ type AgentResourceEntry = z.infer<typeof AgentResourceEntrySchema$1>;
743
636
  type ResourceEntry = z.infer<typeof ResourceEntrySchema>;
744
637
 
745
638
  /**
746
- * Shared form field types for dynamic form generation
747
- * Used by: Command Queue, Execution Runner UI, future form-based features
639
+ * Memory type definitions
640
+ * Types for agent memory management with semantic entry types
748
641
  */
749
642
  /**
750
- * Supported form field types for action payloads
751
- * Maps to Mantine form components
643
+ * Semantic memory entry types
644
+ * Use-case agnostic types that describe the purpose of each entry
645
+ * Memory types mirror action types for clarity and filtering
752
646
  */
753
- type FormFieldType = 'text' | 'textarea' | 'number' | 'select' | 'checkbox' | 'radio' | 'richtext';
647
+ type MemoryEntryType = 'context' | 'input' | 'reasoning' | 'tool-result' | 'error';
754
648
  /**
755
- * Form field definition
649
+ * Who authored an entry's content.
650
+ *
651
+ * This is what lets the assembled prompt tell framework-authored text apart from text that
652
+ * originated outside the trust boundary. `'framework'` content is ours; the other three are not
653
+ * and are rendered inside the JSON data envelope (see `MemoryManager.toContextParts`).
756
654
  */
757
- interface FormField {
758
- /** Field key in payload object */
759
- name: string;
760
- /** Field label for UI */
761
- label: string;
762
- /** Field type (determines UI component) */
763
- type: FormFieldType;
764
- /** Default value */
765
- defaultValue?: unknown;
766
- /** Required field */
767
- required?: boolean;
768
- /** Placeholder text */
769
- placeholder?: string;
770
- /** Help text */
771
- description?: string;
772
- /** Options for select/radio */
773
- options?: Array<{
774
- label: string;
775
- value: string | number;
776
- }>;
777
- /** Min/max for number */
778
- min?: number;
779
- max?: number;
780
- /** Path to context value for pre-filling (dot notation, e.g., 'proposal.summary') */
781
- defaultValueFromContext?: string;
782
- }
655
+ type MemoryEntrySource = 'framework' | 'user' | 'tool' | 'model';
783
656
  /**
784
- * Form schema for action payload collection
657
+ * Memory entry - represents a single entry in agent memory
658
+ * Stored in agent memory, translated by adapters to vendor-specific formats
785
659
  */
786
- interface FormSchema {
787
- /** Form title */
788
- title?: string;
789
- /** Form description */
790
- description?: string;
791
- /** Form fields */
792
- fields: FormField[];
660
+ interface MemoryEntry {
661
+ type: MemoryEntryType;
662
+ content: string;
663
+ timestamp: number;
664
+ turnNumber: number | null;
665
+ iterationNumber: number | null;
666
+ /**
667
+ * Provenance. **Optional on purpose** — `undefined` means unknown, which is what every
668
+ * pre-existing snapshot and every not-yet-redeployed tenant bundle produces. Read sites MUST
669
+ * test `== null`, never `=== undefined`: the `inTurnScope` predicate in `manager.ts` is the
670
+ * cautionary precedent, where a `=== undefined` check silently dropped every `null`-stamped
671
+ * entry. `isMemoryEntry` is deliberately NOT tightened to require this field; doing so would
672
+ * make every stored snapshot fail validation, and `restoreSessionMemory` fails open by
673
+ * starting the agent with empty memory rather than throwing.
674
+ */
675
+ source?: MemoryEntrySource;
676
+ /**
677
+ * Which tool produced this entry. Set on `tool-result` entries so the model can tell N parallel
678
+ * results apart -- the framework instructs batching independent tool calls in one iteration, and
679
+ * an anonymous result is unattributable the moment two land in the same iteration. `addToolError`
680
+ * already carries this (folded into its `content` JSON); this is the same fact for the success
681
+ * path, carried as a real field instead of prose the caller has to parse back out.
682
+ */
683
+ toolName?: string;
684
+ /**
685
+ * Present when `truncateContent` cut this entry's `content` to fit its token budget. A sibling
686
+ * field, never text appended into `content` -- the notice used to be spliced into the string
687
+ * itself, which could (and did) land inside a JSON string literal `truncateContent` had just cut
688
+ * open, breaking `JSON.parse` on the far end. Absent means never truncated.
689
+ */
690
+ truncated?: {
691
+ omittedTokens: number;
692
+ };
693
+ /**
694
+ * Prompt-injection warning types found in `content`, screened once here -- when the entry is
695
+ * written -- instead of by re-scanning the whole accumulated envelope on every iteration it gets
696
+ * re-sent for (`screenRequest`'s `data-envelope` slot used to do exactly that). Empty array means
697
+ * screened and clean; `undefined` means never screened (entries that bypass `addToHistory`/`set`,
698
+ * or pre-existing snapshots from before this field existed).
699
+ */
700
+ warnings?: string[];
793
701
  }
794
-
795
702
  /**
796
- * Execution interface configuration
797
- * Defines how a resource is executed via the UI (forms, scheduling, webhooks)
798
- * Applies to both agents and workflows
703
+ * Agent memory - Self-orchestrated memory with session + working storage
704
+ * Agent has full control over what persists, framework handles auto-compaction
799
705
  */
800
- interface ExecutionInterface {
801
- /** Form configuration for execution inputs */
802
- form: ExecutionFormSchema;
803
- /** Optional: Schedule configuration */
804
- schedule?: ScheduleConfig;
805
- /** Optional: Webhook trigger configuration */
806
- webhook?: WebhookConfig;
706
+ interface AgentMemory {
707
+ /**
708
+ * Session memory - Persists for session/conversation duration
709
+ * Never auto-trimmed by framework
710
+ * Agent-managed key-value store for critical information
711
+ * Agent provides strings, framework wraps in MemoryEntry
712
+ */
713
+ sessionMemory: Record<string, MemoryEntry>;
714
+ /**
715
+ * Working memory - Execution history
716
+ * Automatically compacted by framework when needed
717
+ * Agent doesn't control compaction
718
+ */
719
+ history: MemoryEntry[];
807
720
  }
808
721
  /**
809
- * Execution form schema
810
- * Extends FormSchema with execution-specific fields
722
+ * Memory status for agent awareness
811
723
  */
812
- interface ExecutionFormSchema extends FormSchema {
724
+ interface MemoryStatus {
725
+ sessionMemoryKeys: number;
726
+ sessionMemoryLimit: number;
727
+ sessionMemoryTokens: number;
728
+ sessionMemoryTokenLimit: number;
813
729
  /**
814
- * Field mappings to resource input schema
815
- * Maps form field names to contract input paths
816
- * If omitted, field names must match contract input keys exactly
730
+ * History tokens as a percentage of `historyBudget` — history ALONE, not history plus session
731
+ * memory. It previously reported the combined total under this name, so session memory growth
732
+ * read as history pressure and triggered history compaction that could not relieve it.
817
733
  */
818
- fieldMappings?: Record<string, string>;
734
+ historyPercent: number;
819
735
  /**
820
- * Submit button configuration
821
- * Default: { label: 'Run', loadingLabel: 'Running...' }
736
+ * Tokens the history entries **in scope for the requested turn** occupy — the same set
737
+ * `toContextParts` puts in the envelope. Equal to `storedHistoryTokens` when `getStatus` is
738
+ * called without a turn.
739
+ *
740
+ * This is the number the model is shown, and it is scoped because the model is handed a scoped
741
+ * set. Counting the whole cross-turn array here meant the framing quoted the size of a store
742
+ * while the envelope beside it carried one turn's worth of it.
822
743
  */
823
- submitButton?: {
824
- label?: string;
825
- loadingLabel?: string;
826
- confirmMessage?: string;
827
- };
744
+ historyTokens: number;
745
+ /**
746
+ * Tokens the **entire** history array occupies, across every turn the session snapshot restored.
747
+ *
748
+ * This is what compaction measures, because compaction trims that array. Scoping it to a turn
749
+ * would let the store grow without bound whenever the current turn happened to be small.
750
+ */
751
+ storedHistoryTokens: number;
752
+ /** `storedHistoryTokens` as a percentage of `historyBudget`. The auto-compaction trigger. */
753
+ storedHistoryPercent: number;
754
+ historyBudget: number;
828
755
  }
829
756
  /**
830
- * Schedule configuration for automated execution
757
+ * Memory constraints (optional limits)
831
758
  */
832
- interface ScheduleConfig {
833
- /** Whether scheduling is enabled for this resource */
834
- enabled: boolean;
835
- /** Default schedule (cron expression) */
836
- defaultSchedule?: string;
837
- /** Allowed schedule patterns (if restricted) */
838
- allowedPatterns?: string[];
759
+ interface MemoryConstraints {
760
+ maxSessionMemoryKeys?: number;
761
+ maxMemoryTokens?: number;
839
762
  }
763
+
840
764
  /**
841
- * Webhook configuration for external triggers
765
+ * Types for the schema compiler. `compile.ts` walks a `JsonSchema` once, driven entirely by a
766
+ * `ProviderDialect`, and every server adapter compiles through it.
842
767
  */
843
- interface WebhookConfig {
844
- /** Whether webhook trigger is enabled */
845
- enabled: boolean;
846
- /** Expected payload schema (for documentation) */
847
- payloadSchema?: unknown;
848
- }
849
-
850
- interface WorkflowConfig extends ResourceDefinition {
851
- type: 'workflow';
852
- /** OM descriptor backing canonical identity and governance metadata. */
853
- resource?: WorkflowResourceEntry;
854
- }
855
- interface WorkflowStepDefinition {
856
- id: string;
857
- name: string;
858
- description: string;
859
- }
860
- type StepHandler = (input: unknown, context: ExecutionContext) => Promise<unknown>;
861
- interface LinearNext {
862
- type: 'linear';
863
- target: string;
864
- }
865
- interface ConditionalNext {
866
- type: 'conditional';
867
- routes: Array<{
868
- condition: (data: unknown) => boolean;
869
- target: string;
870
- }>;
871
- default: string;
872
- }
873
- type NextConfig = LinearNext | ConditionalNext | null;
874
- interface WorkflowStep extends WorkflowStepDefinition {
875
- handler: StepHandler;
876
- inputSchema: z.ZodSchema;
877
- outputSchema: z.ZodSchema;
878
- next: NextConfig;
879
- }
880
- interface WorkflowDefinition {
881
- config: WorkflowConfig;
882
- contract: Contract;
883
- steps: Record<string, WorkflowStep>;
884
- entryPoint: string;
885
- /**
886
- * Metrics configuration for ROI calculations
887
- * Optional: Only needed if tracking automation savings
888
- */
889
- metricsConfig?: ResourceMetricsConfig;
768
+ /**
769
+ * What happened to `strict` on a request, recorded per call rather than inferred.
770
+ *
771
+ * `applied` and `notAttempted` are the two states that a refusal-only field cannot tell apart --
772
+ * both leave `strictRefusalReasons` empty. Recording the verdict positively is what makes "was
773
+ * this agent's output actually enforced?" answerable from an `ai_calls` row.
774
+ */
775
+ type StrictStatus = 'applied' | 'refused' | 'compileRejected' | 'notAttempted';
776
+ /**
777
+ * A JSON Schema node, typed enough to be useful without pretending to validate the spec.
778
+ *
779
+ * The compiler has to accept schemas that arrive OUTSIDE the strict subset (that is the whole
780
+ * point of a dialect that can refuse or rewrite them) as well as the `$ref`/`$defs`/`const`/
781
+ * `$schema` shapes the strict subset has no vocabulary for at all. The index signature exists
782
+ * because tenant schemas carry keywords (`minLength`, `pattern`, `minimum`, ...) this compiler
783
+ * drops or refuses on, and they still need somewhere to type-check while they pass through
784
+ * `Object.entries`.
785
+ */
786
+ interface JsonSchema {
787
+ type?: string | string[];
890
788
  /**
891
- * Execution interface configuration (optional)
892
- * If provided, workflow appears in Execution Runner UI
789
+ * The value is `JsonSchema | undefined`, not `JsonSchema`, because a property really can be
790
+ * declared with nothing describing it. `buildIterationResponseSchema` emits one per tool as
791
+ * `input: tool.inputSchema`, and `ToolDefinition.inputSchema` is typed `unknown` -- a tool
792
+ * deployed without one puts `undefined` under a key that exists.
793
+ *
794
+ * Both readers already handle it: `compileSchema` passes each value through `convertNode`, which
795
+ * takes `unknown`, and `collectErrors` opens with `if (!schema || typeof schema !== 'object')`
796
+ * above a comment naming this exact case. Declaring the value non-optional only hid that they
797
+ * were right to.
893
798
  */
894
- interface?: ExecutionInterface;
799
+ properties?: Record<string, JsonSchema | undefined>;
800
+ items?: JsonSchema;
801
+ anyOf?: JsonSchema[];
802
+ oneOf?: JsonSchema[];
803
+ allOf?: JsonSchema[];
804
+ required?: string[];
805
+ additionalProperties?: boolean | JsonSchema;
806
+ minItems?: number;
807
+ maxItems?: number;
808
+ format?: string;
809
+ enum?: unknown[];
810
+ const?: unknown;
811
+ description?: string;
812
+ default?: unknown;
813
+ $ref?: string;
814
+ $defs?: Record<string, JsonSchema>;
815
+ definitions?: Record<string, JsonSchema>;
816
+ $schema?: string;
817
+ $id?: string;
818
+ $anchor?: string;
895
819
  /**
896
- * Lead-gen processing stage this workflow implements (optional).
897
- * Must match a key in the platform lead-gen stage catalog.
898
- * Used by org-os graph derivation to surface workflow→stage edges and
899
- * by pipeline_config validation to confirm each catalog stage has an
900
- * implementing workflow before a list is activated.
901
- *
902
- * Example: stageImplemented: 'verified' on the email-verification workflow.
820
+ * OpenAPI's nullability spelling, which is not JSON Schema's. It is declared because
821
+ * `response-schema-validator.ts` READS it (`schemaPermitsNull`) -- Google's schema dialect is
822
+ * OpenAPI-derived, so a schema that reaches the validator can carry it. No dialect in `compile.ts`
823
+ * writes or rewrites it; the canonical spelling this compiler emits is `type: ['x', 'null']`.
903
824
  */
904
- stageImplemented?: string;
825
+ nullable?: boolean;
826
+ [key: string]: unknown;
905
827
  }
906
828
 
907
829
  /**
@@ -916,6 +838,31 @@ interface WorkflowDefinition {
916
838
  interface LLMMessage {
917
839
  role: 'system' | 'user' | 'assistant';
918
840
  content: string;
841
+ /**
842
+ * Marks this message as the end of a byte-stable prefix worth an Anthropic cache breakpoint,
843
+ * beyond the one the system prompt already gets. Anthropic's rule is "everything up to and
844
+ * including the marked block is cached", so this only ever needs to sit on ONE message -- the
845
+ * last one before content that changes.
846
+ *
847
+ * `buildAgentMessages` sets it on the last replayed prior-turn message: conversation history is
848
+ * fixed for the whole turn (only the framing/envelope after it grow per iteration), so it is the
849
+ * only part of a session agent's messages, besides the system prompt, that is ever byte-identical
850
+ * call to call. A hint rather than a mechanism deliberately -- an adapter that does not read it
851
+ * (OpenAI, OpenRouter, any test stub) just ignores the extra property; only the Anthropic adapter
852
+ * turns it into a wire `cache_control` block.
853
+ */
854
+ cacheBreakpoint?: boolean;
855
+ /**
856
+ * Prompt-injection warning types already found in this message's content, when the caller has
857
+ * already screened it and wants `screenRequest` to use that verdict instead of re-scanning.
858
+ *
859
+ * Set only on the data-envelope message by `buildAgentMessages`, sourced from
860
+ * `MemoryContextParts.envelopeWarnings` -- itself an aggregate of `MemoryEntry.warnings` stamped
861
+ * once per fragment when it entered memory. `undefined` means "not pre-screened"; `screenRequest`
862
+ * falls back to scanning the content directly, which is what every other message role/slot still
863
+ * does and what a hand-built message (tests, other callers) gets by default.
864
+ */
865
+ envelopeWarnings?: string[];
919
866
  }
920
867
  /**
921
868
  * Generic LLM generation request
@@ -923,16 +870,86 @@ interface LLMMessage {
923
870
  */
924
871
  interface LLMGenerateRequest {
925
872
  messages: LLMMessage[];
926
- responseSchema: unknown;
873
+ /**
874
+ * JSON Schema for structured output. Omit it for an unstructured call.
875
+ *
876
+ * Absence is what turns validation off: `runGeneratePipeline` skips `validateResponseSchema`
877
+ * entirely when this is missing, whatever `validationSchema` holds.
878
+ *
879
+ * This was declared `responseSchema: unknown` -- required, and typed as nothing. `unknown` admits
880
+ * `undefined`, so "required" only ever forced the KEY to be written, and `createLLMCallTool`
881
+ * writes it as `undefined` on every call where the model supplies no usable schema. There was no
882
+ * type error available for that, and three separate layers re-derived the same nullability at
883
+ * runtime under three different rules -- truthiness in the pipeline, an object check in the
884
+ * validator, and a `'type'`-key check in the tool. Because the pipeline's was truthiness, `null`,
885
+ * `0` and `''` all quietly meant "no structured output" while the type insisted a schema was
886
+ * mandatory. Optional-and-typed is what those three were compensating for.
887
+ */
888
+ responseSchema?: JsonSchema;
927
889
  /** Maximum output tokens per LLM call. NOT the model's context window — see ModelInfo.maxTokens for that. */
928
890
  maxOutputTokens?: number;
929
891
  temperature?: number;
930
892
  topP?: number;
931
893
  signal?: AbortSignal;
894
+ /**
895
+ * Caller-supplied acceptance step (Wave D2b / decision A15). A pipeline-aware adapter
896
+ * (`UniversalLLMAdapter`, via `runGeneratePipeline`) runs this once per retry attempt, right
897
+ * after the response has passed `responseSchema` validation. Throw to reject the attempt --
898
+ * rejection is classified exactly like a thrown `LLMResponseParseError` from
899
+ * `validateResponseSchema`: retryable, no circuit-breaker verdict, and the attempt is recorded as
900
+ * a failure (`ai_calls` validation-failure row) rather than a clean success. Returning normally
901
+ * (including `undefined`) accepts the response.
902
+ *
903
+ * Optional, and a HINT rather than a dependency -- an adapter that does not read this field
904
+ * simply ignores it, so a caller must not assume it ran:
905
+ * - A bare test-stub `LLMAdapter` (many exist in this codebase) does not invoke it.
906
+ * - `PostMessageLLMAdapter` (`packages/sdk/src/worker/llm-adapter.ts`) cannot forward it at all --
907
+ * functions cannot be structured-cloned across the worker `postMessage` boundary, so its
908
+ * `params` object is built from an explicit allowlist that omits `accept`. The field is dropped
909
+ * before `postMessage` is ever called (no `DataCloneError`), and the parent-side handler that
910
+ * fulfils the call (`tool-dispatcher.ts`'s `case 'llm'`) rebuilds its own `LLMGenerateRequest`
911
+ * from that allowlisted payload, so there is nothing to forward even in principle. This is the
912
+ * path every deployed org-bundle agent and the `command-center-assistant` static module run
913
+ * through today -- `accept` does not reach their retry loop.
914
+ *
915
+ * This is not a validation mechanism on its own: it does not decide whether output is acceptable,
916
+ * the caller's function does, by throwing or not. `callLLMForAgentIteration`
917
+ * (`execution/engine/agent/reasoning/adapters/agent-adapter-helpers.ts`) passes its Zod parse of
918
+ * the iteration response as this field, so a malformed-but-schema-valid iteration is re-sampled
919
+ * inside the retry loop instead of losing the turn -- for the in-process callers that can see it.
920
+ */
921
+ accept?: (output: unknown) => void;
922
+ /**
923
+ * The schema the RESPONSE is validated against, when that must differ from the schema the
924
+ * provider was asked to sample against. Defaults to `responseSchema` when omitted.
925
+ *
926
+ * **This does not affect what is sent to the provider.** `responseSchema` remains the only schema
927
+ * an adapter puts on the wire; this one is read solely by `runGeneratePipeline`'s validation step.
928
+ * Whether validation happens at all is still decided by `responseSchema` -- a request with no
929
+ * `responseSchema` is unstructured and stays unvalidated, whatever this field holds.
930
+ *
931
+ * A caller may legitimately ACCEPT A SUPERSET of what it ASKS FOR -- a document that validates a
932
+ * response more leniently than the one the provider was asked to sample against. No caller in this
933
+ * codebase supplies one today (agent iterations validate with a single Zod parse instead, see
934
+ * `agent-adapter-helpers.ts`), but the mechanism stays: `validateResponseSchema` does not descend
935
+ * into `anyOf`/`oneOf` regardless of which document is supplied here, so this field only ever
936
+ * changes which top-level/required/type keywords are checked, never which acceptance contract a
937
+ * union is read as.
938
+ *
939
+ * Unlike `accept` above, this is DATA. It is structured-cloneable, so it survives the worker
940
+ * `postMessage` boundary that drops `accept`: `PostMessageLLMAdapter` forwards it in its params
941
+ * allowlist and `tool-dispatcher.ts`'s `case 'llm'` puts it back on the `LLMGenerateRequest` it
942
+ * rebuilds parent-side. That is why a divergence expressible as a schema belongs here rather than
943
+ * in a callback -- deployed org-bundle agents run on the far side of that boundary.
944
+ */
945
+ validationSchema?: JsonSchema;
932
946
  }
933
947
  /**
934
948
  * Generic LLM generation response
935
- * Usage field is internal-only (stripped by UniversalLLMAdapter wrapper)
949
+ * `usage`, `cost`, `strictStatus` and `strictRefusalReasons` are observability fields. They are
950
+ * **read** by `UniversalLLMAdapter` and lifted onto the `ai_calls` row; they are **not removed**.
951
+ * The wrapper returns the base adapter's response object as-is, so a caller can observe all four.
952
+ * Earlier revisions of this file claimed they were stripped — they never were.
936
953
  */
937
954
  interface LLMGenerateResponse<T = unknown> {
938
955
  output: T;
@@ -940,35 +957,53 @@ interface LLMGenerateResponse<T = unknown> {
940
957
  inputTokens: number;
941
958
  outputTokens: number;
942
959
  totalTokens: number;
960
+ /**
961
+ * Anthropic-only: input tokens served from the prompt cache (`cache_read_input_tokens`), billed
962
+ * at 0.1x the base input rate. Optional so OpenAI/OpenRouter usage objects, which never report
963
+ * this, stay valid -- absent means "this provider doesn't report it," not "zero were read."
964
+ */
965
+ cacheReadInputTokens?: number;
966
+ /**
967
+ * Anthropic-only: input tokens written to the prompt cache this call
968
+ * (`cache_creation_input_tokens`), billed at 1.25x the base input rate. Same optionality
969
+ * rationale as `cacheReadInputTokens`.
970
+ */
971
+ cacheCreationInputTokens?: number;
943
972
  };
944
973
  cost?: number;
945
974
  /**
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:
975
+ * What actually happened to `strict` on the request that produced this response.
948
976
  *
949
977
  * - `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
978
+ * - `refused` — `compileSchema` could not express the schema, so the request went out unstrict
979
+ * - `compileRejected` — the schema passed `compileSchema` but the provider's grammar compiler
952
980
  * rejected it at request time, and the call was retried unstrict
953
- * - `notAttempted` — this adapter does not send strict at all (OpenAI, Google, OpenRouter)
981
+ * - `notAttempted` — the adapter did not send `strict` on this call
954
982
  *
955
983
  * This exists because `strictRefusalReasons` alone cannot answer the question. Its absence means
956
984
  * "strict held" OR "nothing ever tried", and a prod run that recorded zero refusals while
957
985
  * returning an array-typed field as a string is exactly the case where the difference matters.
958
986
  *
959
- * Internal-only, like `usage` `UniversalLLMAdapter` lifts it onto the `ai_calls` row and
960
- * strips it before the response reaches callers.
987
+ * **Do not read this as "provider X never sends strict."** It describes one call, not an adapter.
988
+ * A previous revision of this comment enumerated OpenAI, Google and OpenRouter as adapters that
989
+ * never send `strict`, which was false for OpenRouter — it sends `strict: true` whenever the
990
+ * schema compiles, and separately reports `notAttempted`. That producer bug is still live; the
991
+ * fix is to make the value a return of schema compilation rather than a per-adapter literal.
992
+ * `MockAdapter` sets no value at all, so absence does not imply `notAttempted` either.
993
+ *
994
+ * Observability only — `UniversalLLMAdapter` lifts it onto the `ai_calls` row. It is not removed
995
+ * from the response.
961
996
  */
962
997
  strictStatus?: StrictStatus;
963
998
  /**
964
999
  * Why this call went out WITHOUT `strict`, on an adapter that tried to send it with one.
965
1000
  *
966
1001
  * The detail behind a `refused` / `compileRejected` `strictStatus` — the short, stable reason
967
- * strings `toStrictSchema` computes. Read `strictStatus` to answer "was it enforced"; read this
1002
+ * strings `compileSchema` computes. Read `strictStatus` to answer "was it enforced"; read this
968
1003
  * to answer "why not".
969
1004
  *
970
- * Internal-only, like `usage` — `UniversalLLMAdapter` lifts it onto the `ai_calls` row and
971
- * strips it before the response reaches callers.
1005
+ * Observability only — `UniversalLLMAdapter` lifts it onto the `ai_calls` row. It is not removed
1006
+ * from the response.
972
1007
  */
973
1008
  strictRefusalReasons?: string[];
974
1009
  }
@@ -993,89 +1028,84 @@ interface LLMAdapter {
993
1028
  }
994
1029
 
995
1030
  /**
996
- * Memory type definitions
997
- * Types for agent memory management with semantic entry types
1031
+ * Model Configuration
1032
+ * Centralized model information, configuration, options, constraints, and validation
1033
+ * Single source of truth for all model-related definitions
1034
+ * Update manually when pricing changes or new models are added
998
1035
  */
1036
+
999
1037
  /**
1000
- * Semantic memory entry types
1001
- * Use-case agnostic types that describe the purpose of each entry
1002
- * Memory types mirror action types for clarity and filtering
1038
+ * Supported Open AI models (direct SDK access)
1003
1039
  */
1004
- type MemoryEntryType = 'context' | 'input' | 'reasoning' | 'tool-result' | 'delegation-result' | 'error';
1040
+ type OpenAIModel = 'gpt-5' | 'gpt-5.4-mini' | 'gpt-5.4-nano';
1005
1041
  /**
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`).
1042
+ * Supported OpenRouter models (explicit union for type safety)
1011
1043
  */
1012
- type MemoryEntrySource = 'framework' | 'user' | 'tool' | 'model';
1044
+ type OpenRouterModel = 'openrouter/z-ai/glm-5';
1013
1045
  /**
1014
- * Memory entry - represents a single entry in agent memory
1015
- * Stored in agent memory, translated by adapters to vendor-specific formats
1046
+ * Supported Anthropic models (direct SDK access via @anthropic-ai/sdk)
1016
1047
  */
1017
- interface MemoryEntry {
1018
- type: MemoryEntryType;
1019
- content: string;
1020
- timestamp: number;
1021
- turnNumber: number | null;
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;
1033
- }
1048
+ type AnthropicModel = 'claude-opus-5' | 'claude-sonnet-5' | 'claude-haiku-4-5-20251001' | 'claude-haiku-4-5';
1049
+ /** Supported LLM models */
1050
+ type LLMModel = OpenAIModel | OpenRouterModel | AnthropicModel | 'mock';
1034
1051
  /**
1035
- * Agent memory - Self-orchestrated memory with session + working storage
1036
- * Agent has full control over what persists, framework handles auto-compaction
1052
+ * GPT-5 model options schema
1037
1053
  */
1038
- interface AgentMemory {
1039
- /**
1040
- * Session memory - Persists for session/conversation duration
1041
- * Never auto-trimmed by framework
1042
- * Agent-managed key-value store for critical information
1043
- * Agent provides strings, framework wraps in MemoryEntry
1044
- */
1045
- sessionMemory: Record<string, MemoryEntry>;
1046
- /**
1047
- * Working memory - Execution history
1048
- * Automatically compacted by framework when needed
1049
- * Agent doesn't control compaction
1050
- */
1051
- history: MemoryEntry[];
1052
- }
1054
+ declare const GPT5OptionsSchema: z.ZodObject<{
1055
+ reasoning_effort: z.ZodOptional<z.ZodEnum<{
1056
+ minimal: "minimal";
1057
+ low: "low";
1058
+ medium: "medium";
1059
+ high: "high";
1060
+ }>>;
1061
+ verbosity: z.ZodOptional<z.ZodEnum<{
1062
+ low: "low";
1063
+ medium: "medium";
1064
+ high: "high";
1065
+ }>>;
1066
+ }, z.core.$strip>;
1053
1067
  /**
1054
- * Memory status for agent awareness
1068
+ * OpenRouter model options schema
1069
+ * OpenRouter-specific options for routing and transforms
1055
1070
  */
1056
- interface MemoryStatus {
1057
- sessionMemoryKeys: number;
1058
- sessionMemoryLimit: number;
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
- */
1067
- historyPercent: number;
1068
- historyTokens: number;
1069
- historyBudget: number;
1070
- totalTokens: number;
1071
- tokenBudget: number;
1072
- }
1071
+ declare const OpenRouterOptionsSchema: z.ZodObject<{
1072
+ transforms: z.ZodOptional<z.ZodArray<z.ZodString>>;
1073
+ route: z.ZodOptional<z.ZodEnum<{
1074
+ fallback: "fallback";
1075
+ }>>;
1076
+ }, z.core.$strip>;
1073
1077
  /**
1074
- * Memory constraints (optional limits)
1078
+ * Anthropic model options schema
1079
+ * Currently empty - future options must be added per supported model family
1075
1080
  */
1076
- interface MemoryConstraints {
1077
- maxSessionMemoryKeys?: number;
1078
- maxMemoryTokens?: number;
1081
+ declare const AnthropicOptionsSchema: z.ZodObject<{}, z.core.$strict>;
1082
+ /**
1083
+ * Infer TypeScript types from schemas
1084
+ */
1085
+ type GPT5Options = z.infer<typeof GPT5OptionsSchema>;
1086
+ type MockOptions = Record<string, never>;
1087
+ type OpenRouterOptions = z.infer<typeof OpenRouterOptionsSchema>;
1088
+ type AnthropicOptions = z.infer<typeof AnthropicOptionsSchema>;
1089
+ type ModelSpecificOptions = GPT5Options | MockOptions | OpenRouterOptions | AnthropicOptions;
1090
+ /**
1091
+ * Model configuration for LLM execution
1092
+ * Belongs in resource definition (AgentDefinition, WorkflowDefinition, etc.)
1093
+ */
1094
+ interface ModelConfig {
1095
+ model: LLMModel;
1096
+ provider: 'openai' | 'anthropic' | 'openrouter' | 'mock';
1097
+ apiKey: string;
1098
+ temperature?: number;
1099
+ /** Maximum output tokens per LLM call. NOT the model's context window — see ModelInfo.maxTokens for that. */
1100
+ maxOutputTokens?: number;
1101
+ topP?: number;
1102
+ /**
1103
+ * Model-specific options (flat structure)
1104
+ * Options are model-specific, not vendor-specific
1105
+ * Available options defined in MODEL_INFO per model
1106
+ * Validated at build time via validateModelOptions()
1107
+ */
1108
+ modelOptions?: ModelSpecificOptions;
1079
1109
  }
1080
1110
 
1081
1111
  /**
@@ -1095,8 +1125,27 @@ interface MemoryConstraints {
1095
1125
  interface MemoryContextParts {
1096
1126
  /** Framework-authored. Memory status and a description of the envelope. Carries NO stored content. */
1097
1127
  framing: string;
1098
- /** Every stored fragment, JSON-encoded and source-tagged. Untrusted. */
1128
+ /** Every stored fragment, JSON-encoded. Untrusted. */
1099
1129
  dataEnvelope: string;
1130
+ /**
1131
+ * Union of prompt-injection warning types already found across every fragment `dataEnvelope`
1132
+ * actually carries this call, aggregated from verdicts stamped once when each fragment entered
1133
+ * memory (see `MemoryEntry.warnings`) rather than by re-scanning `dataEnvelope`'s text on every
1134
+ * iteration it gets rebuilt for. Elided fragments (see `ENVELOPE_FULL_RESULT_WINDOW`) contribute
1135
+ * nothing here — their original content isn't what gets sent once they're stubbed.
1136
+ *
1137
+ * This is metadata about the envelope, not part of it: folding a detector's own finding into the
1138
+ * model-visible JSON would hand a would-be attacker — plausibly the same person on the other end
1139
+ * of a session conversation — direct feedback on which pattern tripped. A caller wiring this up
1140
+ * (`screenRequest`'s `data-envelope` slot is the one that currently re-scans instead of reading
1141
+ * this) should treat it exactly the way `screenRequest` already treats cross-turn history: it
1142
+ * warns, but whether it blocks is that caller's decision to make, not this one's.
1143
+ *
1144
+ * Optional (not just possibly-empty): the fixture literals in `agent/reasoning/**` tests build
1145
+ * `MemoryContextParts` by hand without it, and requiring it would make this signature's landing
1146
+ * a forced edit across files this change does not otherwise touch.
1147
+ */
1148
+ envelopeWarnings?: string[];
1100
1149
  }
1101
1150
  /**
1102
1151
  * Memory Manager - Agent memory orchestration
@@ -1108,7 +1157,41 @@ declare class MemoryManager {
1108
1157
  private constraints;
1109
1158
  private logger?;
1110
1159
  private cachedSnapshot?;
1160
+ /**
1161
+ * Rolling correction for `estimateTokens`'s bias, learned from real provider usage.
1162
+ * `undefined` until the first `recordActualUsage` call -- the cold-start state, where
1163
+ * `estimate()` returns the raw `estimateTokens` output unscaled. See `recordActualUsage`.
1164
+ */
1165
+ private tokenCorrectionFactor?;
1111
1166
  constructor(memory: AgentMemory, constraints?: MemoryConstraints, logger?: AgentScopedLogger | undefined);
1167
+ /**
1168
+ * Record how far `estimateTokens` was from reality on a real provider call, and roll it into a
1169
+ * correction applied to every estimate this instance makes from here on -- `getStatus`'s three
1170
+ * token fields and `enforceSessionMemoryTokenLimit`'s eviction check, which is what
1171
+ * `autoCompact`/`enforceHardLimits` actually decide compaction from (C3 / Wave M3).
1172
+ *
1173
+ * `estimateTokens` is `chars / 3.5` -- a constant-ratio guess with no knowledge of JSON escaping,
1174
+ * key overhead, or real tokenizer behaviour. Every provider call already returns an EXACT count
1175
+ * (`usage.inputTokens`) that reaches `ai_calls` and is then dropped; this is where it stops being
1176
+ * dropped, without replacing the estimator outright -- a cold session still needs SOME number
1177
+ * before its first real call completes, so the estimator stays the prior and this only corrects
1178
+ * it once real data exists.
1179
+ *
1180
+ * `estimatedRequestTokens` must be `estimateTokens` applied to the SAME text `actualInputTokens`
1181
+ * was billed for -- the whole assembled request (system prompt, tools, conversation history, the
1182
+ * envelope, everything), not just what this class itself emits. `estimateTokens`'s bias is a
1183
+ * property of the heuristic, not of which slice of the request it is pointed at, so measuring it
1184
+ * against the full request (visible to the caller, not to this class) and applying the result to
1185
+ * this class's own estimates (which can only ever see its own slice) is a fair trade -- one ratio,
1186
+ * calibrated on real data, standing in for a per-segment breakdown nothing needs.
1187
+ *
1188
+ * Exponential moving average, not a straight replace: a single call's ratio is noisy, and a
1189
+ * straight replace lets one outlier swing every compaction decision made afterward. Each new
1190
+ * observation gets 30% weight, converging within a handful of calls without chasing one spike.
1191
+ */
1192
+ recordActualUsage(estimatedRequestTokens: number, actualInputTokens: number): void;
1193
+ /** `estimateTokens`, scaled by the learned correction once one exists. See `recordActualUsage`. */
1194
+ private estimate;
1112
1195
  /**
1113
1196
  * Set session memory entry (agent provides string, framework wraps it)
1114
1197
  * @param key - Session memory key
@@ -1150,6 +1233,14 @@ declare class MemoryManager {
1150
1233
  * are not. Eviction is oldest-first by timestamp, matching the key-count path, and always
1151
1234
  * leaves at least one entry so a single oversized key degrades to "one key" rather than to
1152
1235
  * "memory silently emptied".
1236
+ *
1237
+ * The running total is **recomputed** from the survivors rather than decremented per entry.
1238
+ * `getStatus` estimates the pool as a ceiling of the joined sum, and a per-entry decrement is a
1239
+ * sum of ceilings — the larger of the two by up to one token per key. The running total therefore
1240
+ * fell faster than the pool did, and the loop could exit reporting a fit while the very next
1241
+ * `getStatus` still read over the limit. Recomputing makes the loop's exit condition and the
1242
+ * number it is judged by the same expression. The pool is capped at `MAX_SESSION_MEMORY_KEYS`
1243
+ * entries, so the extra passes are bounded and cheap.
1153
1244
  */
1154
1245
  private enforceSessionMemoryTokenLimit;
1155
1246
  /**
@@ -1159,9 +1250,13 @@ declare class MemoryManager {
1159
1250
  getHistoryLength(): number;
1160
1251
  /**
1161
1252
  * Get memory status for agent awareness
1253
+ *
1254
+ * @param currentTurn - Turn to scope `historyTokens` / `historyPercent` to. Omit to measure the
1255
+ * whole store, which is what the compaction paths want. Callers building something the model
1256
+ * reads should pass it, so the count describes the set the model is actually handed.
1162
1257
  * @returns Memory status with token usage and key counts
1163
1258
  */
1164
- getStatus(): MemoryStatus;
1259
+ getStatus(currentTurn?: number): MemoryStatus;
1165
1260
  /**
1166
1261
  * Create memory snapshot for persistence
1167
1262
  * Caches snapshot internally for later retrieval
@@ -1192,7 +1287,15 @@ declare class MemoryManager {
1192
1287
  * treat "everything in this block" as data was also being handed the live question inside that
1193
1288
  * block.
1194
1289
  *
1195
- * Shows current iteration entries FIRST (reverse chronological) for LLM attention.
1290
+ * History entries stay chronological. They used to be split into a "current iteration" slot
1291
+ * (reverse chronological, for LLM positional bias) and an "earlier" slot -- but the LLM call
1292
+ * always happens BEFORE `addToHistory` writes that iteration's own entries, so the
1293
+ * current-iteration slot held nothing on any call that mattered. One chronological list replaces
1294
+ * both.
1295
+ *
1296
+ * Tool results (and tool errors) older than `ENVELOPE_FULL_RESULT_WINDOW` iterations are carried
1297
+ * as a short stub instead of their full content -- see `ENVELOPE_FULL_RESULT_WINDOW`. The STORE
1298
+ * (`this.memory.history`) is untouched; only what this call carries is capped.
1196
1299
  *
1197
1300
  * @param currentIteration - Current iteration number (0 = pre-iteration)
1198
1301
  * @param currentTurn - Current turn number (optional, for session context filtering)
@@ -1201,175 +1304,145 @@ declare class MemoryManager {
1201
1304
  }
1202
1305
 
1203
1306
  /**
1204
- * Knowledge Map Types
1205
- *
1206
- * Enables agents to navigate organizational knowledge through a lightweight
1207
- * graph that lazy-loads capabilities on-demand.
1208
- *
1209
- * @module agent/knowledge-map
1307
+ * Shared form field types for dynamic form generation
1308
+ * Used by: Command Queue, Execution Runner UI, future form-based features
1210
1309
  */
1211
-
1212
1310
  /**
1213
- * Lightweight knowledge map (passed as agent property)
1214
- *
1215
- * Contains metadata about available knowledge nodes without loading
1216
- * the full content upfront. Total size: ~300-500 tokens.
1217
- *
1218
- * Multi-tenancy is enforced via:
1219
- * - File-scoped maps (organizations/{org-name}/knowledge/)
1220
- * - ExecutionContext.organizationId passed to node.load()
1311
+ * Supported form field types for action payloads
1312
+ * Maps to Mantine form components
1221
1313
  */
1222
- interface KnowledgeMap {
1223
- /** Available knowledge nodes indexed by ID */
1224
- nodes: Record<string, KnowledgeNode>;
1225
- }
1314
+ type FormFieldType = 'text' | 'textarea' | 'number' | 'select' | 'checkbox' | 'radio' | 'richtext';
1226
1315
  /**
1227
- * Single knowledge source
1228
- *
1229
- * Represents a domain knowledge area (CRM, brand guidelines, Excel tools)
1230
- * that can be lazy-loaded to provide instructions and tools to agents.
1316
+ * Form field definition
1231
1317
  */
1232
- interface KnowledgeNode {
1233
- /** Unique identifier for this node (e.g., "crm", "brand-guidelines") */
1234
- id: string;
1235
- /**
1236
- * Description of when to use this knowledge
1237
- * Used for semantic matching against user intent
1238
- */
1239
- description: string;
1240
- /**
1241
- * Load knowledge content on-demand
1242
- *
1243
- * @param context - Execution context with organizationId for multi-tenancy
1244
- * @returns Promise resolving to knowledge content (prompt + optional tools)
1245
- */
1246
- load(context: ExecutionContext): Promise<KnowledgeContent>;
1247
- /**
1248
- * Loaded state flag
1249
- * Set to true after load() is called
1250
- */
1251
- loaded?: boolean;
1252
- /**
1253
- * Cached prompt (for system prompt serialization)
1254
- * Only the prompt is cached - tools go to toolRegistry, children flattened to nodes
1255
- */
1256
- prompt?: string;
1318
+ interface FormField {
1319
+ /** Field key in payload object */
1320
+ name: string;
1321
+ /** Field label for UI */
1322
+ label: string;
1323
+ /** Field type (determines UI component) */
1324
+ type: FormFieldType;
1325
+ /** Default value */
1326
+ defaultValue?: unknown;
1327
+ /** Required field */
1328
+ required?: boolean;
1329
+ /** Placeholder text */
1330
+ placeholder?: string;
1331
+ /** Help text */
1332
+ description?: string;
1333
+ /** Options for select/radio */
1334
+ options?: Array<{
1335
+ label: string;
1336
+ value: string | number;
1337
+ }>;
1338
+ /** Min/max for number */
1339
+ min?: number;
1340
+ max?: number;
1341
+ /** Path to context value for pre-filling (dot notation, e.g., 'proposal.summary') */
1342
+ defaultValueFromContext?: string;
1257
1343
  }
1258
1344
  /**
1259
- * Content returned by knowledge node
1260
- *
1261
- * Separates instructions (prompt) from capabilities (tools).
1262
- * Tools are optional - some nodes only provide context.
1263
- *
1264
- * Supports recursive navigation - nodes can contain child nodes
1265
- * that are discovered when the parent node is loaded.
1345
+ * Form schema for action payload collection
1266
1346
  */
1267
- interface KnowledgeContent {
1268
- /** Instructions and context (markdown format) */
1269
- prompt: string;
1270
- /** Tool implementations (optional) */
1271
- tools?: Tool[];
1272
- /**
1273
- * Child knowledge nodes (optional, recursive)
1274
- *
1275
- * Enables hierarchical navigation: base → specialized → deep expertise.
1276
- * Child nodes are flattened into the main knowledge map when parent loads,
1277
- * making them available for subsequent navigate-knowledge actions.
1278
- *
1279
- * Example: CRM base node returns crm-customers and crm-deals as children
1280
- */
1281
- nodes?: Record<string, KnowledgeNode>;
1347
+ interface FormSchema {
1348
+ /** Form title */
1349
+ title?: string;
1350
+ /** Form description */
1351
+ description?: string;
1352
+ /** Form fields */
1353
+ fields: FormField[];
1282
1354
  }
1283
1355
 
1284
1356
  /**
1285
- * Agent-specific type definitions
1286
- * Types for autonomous agents with tools, memory, and constraints
1357
+ * Execution interface configuration
1358
+ * Defines how a resource is executed via the UI (forms, scheduling, webhooks)
1359
+ * Applies to both agents and workflows
1287
1360
  */
1288
-
1361
+ interface ExecutionInterface {
1362
+ /** Form configuration for execution inputs */
1363
+ form: ExecutionFormSchema;
1364
+ /** Optional: Schedule configuration */
1365
+ schedule?: ScheduleConfig;
1366
+ /** Optional: Webhook trigger configuration */
1367
+ webhook?: WebhookConfig;
1368
+ }
1289
1369
  /**
1290
- * Factory function for creating LLM adapters.
1291
- * Injected into the Agent class to decouple the engine from server-only provider SDKs.
1292
- * - API process: provides createLLMAdapter (real SDKs + process.env API keys)
1293
- * - SDK worker: provides PostMessageLLMAdapter (proxies via platform.call)
1294
- *
1295
- * Uses `any` for optional params so both the real createLLMAdapter (with typed
1296
- * AIUsageCollector/AICallContext) and the worker proxy (which ignores them) satisfy the type.
1370
+ * Execution form schema
1371
+ * Extends FormSchema with execution-specific fields
1297
1372
  */
1298
- type LLMAdapterFactory = (config: ModelConfig, ...args: any[]) => LLMAdapter;
1299
- type AgentKind = 'orchestrator' | 'specialist' | 'utility' | 'platform';
1300
- interface AgentConfig extends ResourceDefinition {
1301
- type: 'agent';
1302
- /** OM descriptor backing canonical identity and governance metadata. */
1303
- resource?: AgentResourceEntry;
1304
- kind: AgentKind;
1305
- systemPrompt: string;
1306
- constraints?: AgentConstraints;
1307
- /**
1308
- * Session capability declaration (opt-in)
1309
- * If true, agent is designed for multi-turn session interactions
1310
- * Controls whether agent can use message action and appears in Sessions UI
1311
- *
1312
- * Use for:
1313
- * - Conversational agents with multi-turn interactions
1314
- * - Agents requiring persistent context across turns
1315
- * - Agents that need human-in-the-loop communication
1316
- */
1317
- sessionCapable?: boolean;
1373
+ interface ExecutionFormSchema extends FormSchema {
1318
1374
  /**
1319
- * Security level for system prompt hardening (auto-derived if omitted)
1320
- *
1321
- * - 'standard': Lightweight defense (3 rules) - default for non-session agents
1322
- * - 'hardened': Comprehensive defense (5 rules) - default for session-capable agents
1323
- * - 'none': No security prompt - for pure internal agents with no external input
1324
- *
1325
- * If omitted, derived from sessionCapable:
1326
- * sessionCapable: true -> 'hardened'
1327
- * sessionCapable: false -> 'standard'
1375
+ * Field mappings to resource input schema
1376
+ * Maps form field names to contract input paths
1377
+ * If omitted, field names must match contract input keys exactly
1328
1378
  */
1329
- securityLevel?: 'standard' | 'hardened' | 'none';
1379
+ fieldMappings?: Record<string, string>;
1330
1380
  /**
1331
- * Memory management preferences (opt-in)
1332
- * If provided, agent can use memoryOps to manage session memory
1333
- * If omitted, agent has no memory management capabilities
1334
- *
1335
- * Agent-specific guidance on what to preserve, when to persist, and what to clean up.
1336
- * This guidance is injected into the system prompt when memory management is enabled.
1337
- *
1338
- * Use for:
1339
- * - Conversational agents needing cross-turn context
1340
- * - Agents managing complex user preferences
1341
- * - Agents tracking decisions over multiple iterations
1381
+ * Submit button configuration
1382
+ * Default: { label: 'Run', loadingLabel: 'Running...' }
1342
1383
  */
1343
- memoryPreferences?: string;
1384
+ submitButton?: {
1385
+ label?: string;
1386
+ loadingLabel?: string;
1387
+ confirmMessage?: string;
1388
+ };
1389
+ }
1390
+ /**
1391
+ * Schedule configuration for automated execution
1392
+ */
1393
+ interface ScheduleConfig {
1394
+ /** Whether scheduling is enabled for this resource */
1395
+ enabled: boolean;
1396
+ /** Default schedule (cron expression) */
1397
+ defaultSchedule?: string;
1398
+ /** Allowed schedule patterns (if restricted) */
1399
+ allowedPatterns?: string[];
1400
+ }
1401
+ /**
1402
+ * Webhook configuration for external triggers
1403
+ */
1404
+ interface WebhookConfig {
1405
+ /** Whether webhook trigger is enabled */
1406
+ enabled: boolean;
1407
+ /** Expected payload schema (for documentation) */
1408
+ payloadSchema?: unknown;
1409
+ }
1410
+
1411
+ interface WorkflowConfig extends ResourceDefinition {
1412
+ type: 'workflow';
1413
+ /** OM descriptor backing canonical identity and governance metadata. */
1414
+ resource?: WorkflowResourceEntry;
1344
1415
  }
1345
- interface AgentConstraints {
1346
- maxIterations?: number;
1347
- timeout?: number;
1348
- maxSessionMemoryKeys?: number;
1349
- maxMemoryTokens?: number;
1416
+ interface WorkflowStepDefinition {
1417
+ id: string;
1418
+ name: string;
1419
+ description: string;
1350
1420
  }
1351
- interface AgentDefinition {
1352
- config: AgentConfig;
1421
+ type StepHandler = (input: unknown, context: ExecutionContext) => Promise<unknown>;
1422
+ interface LinearNext {
1423
+ type: 'linear';
1424
+ target: string;
1425
+ }
1426
+ interface ConditionalNext {
1427
+ type: 'conditional';
1428
+ routes: Array<{
1429
+ condition: (data: unknown) => boolean;
1430
+ target: string;
1431
+ }>;
1432
+ default: string;
1433
+ }
1434
+ type NextConfig = LinearNext | ConditionalNext | null;
1435
+ interface WorkflowStep extends WorkflowStepDefinition {
1436
+ handler: StepHandler;
1437
+ inputSchema: z.ZodSchema;
1438
+ outputSchema: z.ZodSchema;
1439
+ next: NextConfig;
1440
+ }
1441
+ interface WorkflowDefinition {
1442
+ config: WorkflowConfig;
1353
1443
  contract: Contract;
1354
- tools: Tool[];
1355
- /**
1356
- * Model configuration for LLM execution
1357
- * Specifies provider, API key, and model-specific options
1358
- */
1359
- modelConfig: ModelConfig;
1360
- /**
1361
- * Optional knowledge map for lazy-loading capabilities
1362
- * Enables agents to navigate organizational knowledge on-demand
1363
- */
1364
- knowledgeMap?: KnowledgeMap;
1365
- /**
1366
- * Preload memory before execution starts
1367
- * Handles BOTH context loading AND session restoration
1368
- *
1369
- * @param context - Execution context (includes sessionId if session turn)
1370
- * @returns Initial AgentMemory state (sessionMemory entries + optionally history)
1371
- */
1372
- preloadMemory?: (context: ExecutionContext) => Promise<AgentMemory> | AgentMemory;
1444
+ steps: Record<string, WorkflowStep>;
1445
+ entryPoint: string;
1373
1446
  /**
1374
1447
  * Metrics configuration for ROI calculations
1375
1448
  * Optional: Only needed if tracking automation savings
@@ -1377,31 +1450,19 @@ interface AgentDefinition {
1377
1450
  metricsConfig?: ResourceMetricsConfig;
1378
1451
  /**
1379
1452
  * Execution interface configuration (optional)
1380
- * If provided, agent appears in Execution Runner UI
1453
+ * If provided, workflow appears in Execution Runner UI
1381
1454
  */
1382
1455
  interface?: ExecutionInterface;
1383
- }
1384
- /**
1385
- * Agent execution context
1386
- * Groups all state needed for agent execution phases
1387
- */
1388
- interface IterationContext {
1389
- config: AgentConfig;
1390
- contract: Contract;
1391
- toolRegistry: Map<string, Tool>;
1392
- memoryManager: MemoryManager;
1393
- executionContext: ExecutionContext;
1394
- iteration: number;
1395
- logger: AgentScopedLogger;
1396
- modelConfig: ModelConfig;
1397
- adapterFactory: LLMAdapterFactory;
1398
- knowledgeMap?: KnowledgeMap;
1399
1456
  /**
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.
1457
+ * Lead-gen processing stage this workflow implements (optional).
1458
+ * Must match a key in the platform lead-gen stage catalog.
1459
+ * Used by org-os graph derivation to surface workflow→stage edges and
1460
+ * by pipeline_config validation to confirm each catalog stage has an
1461
+ * implementing workflow before a list is activated.
1462
+ *
1463
+ * Example: stageImplemented: 'verified' on the email-verification workflow.
1403
1464
  */
1404
- currentInput: string;
1465
+ stageImplemented?: string;
1405
1466
  }
1406
1467
 
1407
1468
  declare const OntologyScopeSchema: z.ZodDefault<z.ZodObject<{
@@ -2681,6 +2742,32 @@ declare const OrganizationModelSchema$1: z.ZodObject<{
2681
2742
 
2682
2743
  type OrganizationModel$1 = z.infer<typeof OrganizationModelSchema$1>;
2683
2744
 
2745
+ /**
2746
+ * AIUsageCollector
2747
+ * Centralized token tracking that aggregates usage across all LLM calls in an execution
2748
+ */
2749
+ declare class AIUsageCollector {
2750
+ private model;
2751
+ private calls;
2752
+ private callSequence;
2753
+ /**
2754
+ * Record a single AI call with usage metrics
2755
+ *
2756
+ * @param usage - Token usage and latency data from LLM adapter
2757
+ * @param callType - Type discriminator (agent-reasoning, tool, etc.)
2758
+ * @param context - Optional typed context specific to callType
2759
+ */
2760
+ record(usage: LLMUsageData, callType?: BaseAICall['callType'], context?: AICallContext): void;
2761
+ /**
2762
+ * Get aggregated summary of all AI calls
2763
+ */
2764
+ getSummary(): AIUsageSummary;
2765
+ /**
2766
+ * Check if any usage has been recorded
2767
+ */
2768
+ hasUsage(): boolean;
2769
+ }
2770
+
2684
2771
  /**
2685
2772
  * MetricsCollector
2686
2773
  * Tracks execution timing and ROI metrics
@@ -2731,6 +2818,17 @@ interface BaseAICall {
2731
2818
  costUsd: number;
2732
2819
  latencyMs: number;
2733
2820
  context?: AICallContext;
2821
+ /**
2822
+ * Anthropic-only: input tokens served from the prompt cache this call (`cache_read_input_tokens`),
2823
+ * billed at 0.1x the base input rate. Already folded into `inputTokens`/`totalInputTokens` so
2824
+ * aggregate totals reflect real usage -- present here as the raw breakdown, not additive on top.
2825
+ */
2826
+ cacheReadInputTokens?: number;
2827
+ /**
2828
+ * Anthropic-only: input tokens written to the prompt cache this call (`cache_creation_input_tokens`),
2829
+ * billed at 1.25x the base input rate. Same folding rationale as `cacheReadInputTokens`.
2830
+ */
2831
+ cacheCreationInputTokens?: number;
2734
2832
  /**
2735
2833
  * Distinct prompt-injection pattern types detected in the request's user-role messages.
2736
2834
  * Present only when the input sanitizer matched something. Non-blocking matches ride along on
@@ -2796,6 +2894,41 @@ interface BaseAICall {
2796
2894
  * Existing readers that only look at the fields above are unaffected.
2797
2895
  */
2798
2896
  strictRefusalReasons?: string[];
2897
+ /**
2898
+ * Time spent in the base adapter's `generate()` call alone, excluding `responseSchema`
2899
+ * validation. On a success or validation-failure row, `providerMs + validateMs === latencyMs`
2900
+ * (modulo rounding) -- `latencyMs` keeps its existing meaning unchanged; this and `validateMs`
2901
+ * are the same window split into its two components.
2902
+ *
2903
+ * Present on success and validation-failure rows. Absent on a blocked row (no provider call was
2904
+ * made) and on rows written before this field existed.
2905
+ */
2906
+ providerMs?: number;
2907
+ /**
2908
+ * Time spent in `validateResponseSchema` alone. Omitted when the call carried no `responseSchema`
2909
+ * (nothing to validate); `0` is a legitimate value meaning a schema was supplied and validation
2910
+ * was effectively instant. See `providerMs` for how the two relate to `latencyMs`.
2911
+ *
2912
+ * Present on success and validation-failure rows that supplied a `responseSchema`. Absent on a
2913
+ * blocked row and on rows written before this field existed.
2914
+ */
2915
+ validateMs?: number;
2916
+ /**
2917
+ * Total elapsed time for the WHOLE `generate()` call -- every retry attempt plus every backoff
2918
+ * sleep between them. Unlike `latencyMs` (which is per-attempt and never includes backoff, by
2919
+ * design -- see `runWithRetry`), this is the one number that answers "how long did the caller
2920
+ * actually wait". On a call that never retried, `wallClockMs === latencyMs`. On a retried call,
2921
+ * `wallClockMs` is strictly greater than any individual row's `latencyMs` from that same call, by
2922
+ * at least the backoff time actually slept.
2923
+ *
2924
+ * The same value is attached to every row produced by one `generate()` call (a validation-failure
2925
+ * row from an earlier attempt included), because it describes the call, not the attempt.
2926
+ *
2927
+ * Present on success and validation-failure rows. Absent on a blocked row -- a blocked call never
2928
+ * reaches the retry loop, so `wallClockMs` would just restate `latencyMs` (0). Absent on rows
2929
+ * written before this field existed.
2930
+ */
2931
+ wallClockMs?: number;
2799
2932
  }
2800
2933
  type AICallContext = AgentReasoningContext | AgentCompletionContext | WorkflowStepContext | ToolCallContext | OtherCallContext;
2801
2934
  interface AgentReasoningContext {
@@ -2841,6 +2974,16 @@ interface LLMUsageData {
2841
2974
  latencyMs: number;
2842
2975
  /** Actual cost from provider in USD (when available, e.g., OpenRouter) */
2843
2976
  cost?: number;
2977
+ /**
2978
+ * Anthropic-only: input tokens served from the prompt cache (`cache_read_input_tokens`), billed at
2979
+ * 0.1x the base input rate. Absent for providers that never report it (OpenAI, OpenRouter).
2980
+ */
2981
+ cacheReadInputTokens?: number;
2982
+ /**
2983
+ * Anthropic-only: input tokens written to the prompt cache this call
2984
+ * (`cache_creation_input_tokens`), billed at 1.25x the base input rate. Same absence rationale.
2985
+ */
2986
+ cacheCreationInputTokens?: number;
2844
2987
  /** Distinct prompt-injection pattern types detected in the request's user-role messages */
2845
2988
  inputWarnings?: string[];
2846
2989
  /** Additive per-source breakdown of `inputWarnings` — see `SourcedInputWarnings` */
@@ -2855,6 +2998,12 @@ interface LLMUsageData {
2855
2998
  strictStatus?: StrictStatus;
2856
2999
  /** Why the call went out unstrict, when a strict-capable adapter refused the schema */
2857
3000
  strictRefusalReasons?: string[];
3001
+ /** Time in the base adapter's `generate()` alone, excluding `responseSchema` validation. See `BaseAICall.providerMs`. */
3002
+ providerMs?: number;
3003
+ /** Time in `validateResponseSchema` alone. Omitted when no `responseSchema` was supplied. See `BaseAICall.validateMs`. */
3004
+ validateMs?: number;
3005
+ /** Total elapsed for the whole `generate()` call, including every retry and every backoff sleep. See `BaseAICall.wallClockMs`. */
3006
+ wallClockMs?: number;
2858
3007
  }
2859
3008
  interface AIUsageSummary {
2860
3009
  model: LLMModel;
@@ -2877,29 +3026,147 @@ interface ResourceMetricsConfig {
2877
3026
  }
2878
3027
 
2879
3028
  /**
2880
- * AIUsageCollector
2881
- * Centralized token tracking that aggregates usage across all LLM calls in an execution
3029
+ * Agent-specific type definitions
3030
+ * Types for autonomous agents with tools, memory, and constraints
2882
3031
  */
2883
- declare class AIUsageCollector {
2884
- private model;
2885
- private calls;
2886
- private callSequence;
3032
+
3033
+ /**
3034
+ * Factory function for creating LLM adapters.
3035
+ * Injected into the Agent class to decouple the engine from server-only provider SDKs.
3036
+ * - API process: provides createLLMAdapter (real SDKs + process.env API keys)
3037
+ * - SDK worker: provides PostMessageLLMAdapter (proxies via platform.call)
3038
+ *
3039
+ * Uses `any` for optional params so both the real createLLMAdapter (with typed
3040
+ * AIUsageCollector/AICallContext) and the worker proxy (which ignores them) satisfy the type.
3041
+ */
3042
+ type LLMAdapterFactory = (config: ModelConfig, ...args: any[]) => LLMAdapter;
3043
+ type AgentKind = 'orchestrator' | 'specialist' | 'utility' | 'platform';
3044
+ interface AgentConfig extends ResourceDefinition {
3045
+ type: 'agent';
3046
+ /** OM descriptor backing canonical identity and governance metadata. */
3047
+ resource?: AgentResourceEntry;
3048
+ kind: AgentKind;
3049
+ systemPrompt: string;
3050
+ constraints?: AgentConstraints;
2887
3051
  /**
2888
- * Record a single AI call with usage metrics
3052
+ * Session capability declaration (opt-in)
3053
+ * If true, agent is designed for multi-turn session interactions
3054
+ * Controls whether agent can use message action and appears in Sessions UI
2889
3055
  *
2890
- * @param usage - Token usage and latency data from LLM adapter
2891
- * @param callType - Type discriminator (agent-reasoning, tool, etc.)
2892
- * @param context - Optional typed context specific to callType
3056
+ * Use for:
3057
+ * - Conversational agents with multi-turn interactions
3058
+ * - Agents requiring persistent context across turns
3059
+ * - Agents that need human-in-the-loop communication
2893
3060
  */
2894
- record(usage: LLMUsageData, callType?: BaseAICall['callType'], context?: AICallContext): void;
3061
+ sessionCapable?: boolean;
2895
3062
  /**
2896
- * Get aggregated summary of all AI calls
3063
+ * Overrides the default `message` requiredness for a session-capable agent (ignored for
3064
+ * non-session agents, which always get `AgentCapabilities.message: 'off'`). Defaults to
3065
+ * `'required'` -- see `AgentCapabilities.message`'s doc comment for why. Set `'optional'` only
3066
+ * when the agent legitimately needs tool-only turns with no reply, and the deploy target can
3067
+ * tolerate the blind-retry risk `validateResponseSchema` carries on any path where the schema is
3068
+ * not compiled into a sampling grammar.
2897
3069
  */
2898
- getSummary(): AIUsageSummary;
3070
+ messagePolicy?: 'optional' | 'required';
2899
3071
  /**
2900
- * Check if any usage has been recorded
3072
+ * Explicit opt-in to skip the iteration loop and produce `contract.outputSchema`-shaped output in
3073
+ * a single LLM call (round 3 decision B6: explicit opt-in, never inferred from `kind`,
3074
+ * `sessionCapable`, or tool count -- so no existing agent changes shape by default). Structurally
3075
+ * the normal path pays two calls minimum: `iterate()` always runs at least one, and `complete()`
3076
+ * runs a second whose prompt re-derives the answer from history rather than reading what the
3077
+ * iteration already decided. A single-shot classifier -- one input in, one structured output out,
3078
+ * no multi-step reasoning needed -- does not need that second derivation; `complete()` already
3079
+ * makes exactly the one call it needs, from `currentInput` directly.
3080
+ *
3081
+ * Requires `sessionCapable` to be falsy and `contract.outputSchema` to be present. `Agent`
3082
+ * validates both during initialization and throws `AgentInitializationError` if either is missing,
3083
+ * rather than silently falling back to the normal two-call path on a misconfigured opt-in. Tools
3084
+ * registered on the agent are never invoked in this path -- there is no iteration loop to call
3085
+ * them from, so an agent that needs tool calls before it can answer is not eligible regardless of
3086
+ * this flag.
2901
3087
  */
2902
- hasUsage(): boolean;
3088
+ singleShot?: boolean;
3089
+ /**
3090
+ * Security level for system prompt hardening (auto-derived if omitted)
3091
+ *
3092
+ * - 'standard': Lightweight defense (3 rules) - default for non-session agents
3093
+ * - 'hardened': Comprehensive defense (5 rules) - default for session-capable agents
3094
+ * - 'none': No security prompt - for pure internal agents with no external input
3095
+ *
3096
+ * If omitted, derived from sessionCapable:
3097
+ * sessionCapable: true -> 'hardened'
3098
+ * sessionCapable: false -> 'standard'
3099
+ */
3100
+ securityLevel?: 'standard' | 'hardened' | 'none';
3101
+ /**
3102
+ * Memory management preferences (opt-in)
3103
+ * If provided, agent can use memoryOps to manage session memory
3104
+ * If omitted, agent has no memory management capabilities
3105
+ *
3106
+ * Agent-specific guidance on what to preserve, when to persist, and what to clean up.
3107
+ * This guidance is injected into the system prompt when memory management is enabled.
3108
+ *
3109
+ * Use for:
3110
+ * - Conversational agents needing cross-turn context
3111
+ * - Agents managing complex user preferences
3112
+ * - Agents tracking decisions over multiple iterations
3113
+ */
3114
+ memoryPreferences?: string;
3115
+ }
3116
+ interface AgentConstraints {
3117
+ maxIterations?: number;
3118
+ timeout?: number;
3119
+ maxSessionMemoryKeys?: number;
3120
+ maxMemoryTokens?: number;
3121
+ }
3122
+ interface AgentDefinition {
3123
+ config: AgentConfig;
3124
+ contract: Contract;
3125
+ tools: Tool[];
3126
+ /**
3127
+ * Model configuration for LLM execution
3128
+ * Specifies provider, API key, and model-specific options
3129
+ */
3130
+ modelConfig: ModelConfig;
3131
+ /**
3132
+ * Preload memory before execution starts
3133
+ * Handles BOTH context loading AND session restoration
3134
+ *
3135
+ * @param context - Execution context (includes sessionId if session turn)
3136
+ * @returns Initial AgentMemory state (sessionMemory entries + optionally history)
3137
+ */
3138
+ preloadMemory?: (context: ExecutionContext) => Promise<AgentMemory> | AgentMemory;
3139
+ /**
3140
+ * Metrics configuration for ROI calculations
3141
+ * Optional: Only needed if tracking automation savings
3142
+ */
3143
+ metricsConfig?: ResourceMetricsConfig;
3144
+ /**
3145
+ * Execution interface configuration (optional)
3146
+ * If provided, agent appears in Execution Runner UI
3147
+ */
3148
+ interface?: ExecutionInterface;
3149
+ }
3150
+ /**
3151
+ * Agent execution context
3152
+ * Groups all state needed for agent execution phases
3153
+ */
3154
+ interface IterationContext {
3155
+ config: AgentConfig;
3156
+ contract: Contract;
3157
+ toolRegistry: Map<string, Tool>;
3158
+ memoryManager: MemoryManager;
3159
+ executionContext: ExecutionContext;
3160
+ iteration: number;
3161
+ logger: AgentScopedLogger;
3162
+ modelConfig: ModelConfig;
3163
+ adapterFactory: LLMAdapterFactory;
3164
+ /**
3165
+ * The validated input for this execution, serialized. It travels here because the model gets
3166
+ * it as its own `role:'user'` message; nothing else in this context carried it, so the input
3167
+ * had to be read back out of memory history and shipped inside the memory block.
3168
+ */
3169
+ currentInput: string;
2903
3170
  }
2904
3171
 
2905
3172
  /**
@@ -3047,6 +3314,19 @@ interface Tool {
3047
3314
  outputSchema: z.ZodSchema;
3048
3315
  execute: (options: ToolExecutionOptions) => Promise<unknown>;
3049
3316
  timeout?: number;
3317
+ /**
3318
+ * Optional per-tool output size bound, in approximate tokens. Today the ONLY size bound on tool
3319
+ * output is a 4,000-token truncation applied post-hoc at memory insert -- after the payload is
3320
+ * already fully materialized, validated against `outputSchema`, emitted as a session message, and
3321
+ * logged. This field exists so a tool can declare its own bound up front instead.
3322
+ *
3323
+ * Enforcement is NOT here. `executor.ts:executeToolCall` is the single call site that produces the
3324
+ * value handed to all four sinks (memory, the `agent:tool_result` event, the session message, and
3325
+ * the log line) -- enforcing there, before that value is emitted, is what makes the four sinks agree
3326
+ * instead of three of them seeing the untruncated payload. See that file's own comment for the
3327
+ * exact insertion point.
3328
+ */
3329
+ maxOutputTokens?: number;
3050
3330
  }
3051
3331
 
3052
3332
  /**