cascade-ai 0.13.2 → 0.15.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.
package/dist/index.d.cts CHANGED
@@ -387,6 +387,10 @@ interface CascadeConfig {
387
387
  enableToolCreation?: boolean;
388
388
  /** Persist runtime-generated tools and reload them on startup (untrusted). Default: true. */
389
389
  persistDynamicTools?: boolean;
390
+ /** Project knowledge (world state) settings. */
391
+ knowledge?: {
392
+ factsExtraction?: boolean;
393
+ };
390
394
  plugins?: string[];
391
395
  localConcurrency?: number;
392
396
  localInferenceTimeoutMs?: number;
@@ -478,6 +482,12 @@ interface ToolsConfig {
478
482
  mcpTrusted?: string[];
479
483
  /** Web search backends — at least one should be configured for best results */
480
484
  webSearch?: WebSearchConfig;
485
+ /**
486
+ * Sandbox runtime for LLM-authored dynamic tools. 'isolate' = hard V8 isolate
487
+ * (isolated-vm, capability-confined), 'worker' = node:worker_threads,
488
+ * 'auto' (default) = isolate when available else worker.
489
+ */
490
+ dynamicToolSandbox?: 'isolate' | 'worker' | 'auto';
481
491
  }
482
492
  interface HooksConfig {
483
493
  preToolUse?: HookDefinition[];
@@ -818,7 +828,9 @@ declare class TaskAnalyzer {
818
828
  * Scores tier-eligible models using cost efficiency + historical performance.
819
829
  * Falls back to the priority-list default when no candidates have history.
820
830
  */
821
- selectModel(prompt: string, tier: TierRole, selector: ModelSelector): Promise<ModelInfo | null>;
831
+ selectModel(prompt: string, tier: TierRole, selector: ModelSelector, opts?: {
832
+ requiresToolUse?: boolean;
833
+ }): Promise<ModelInfo | null>;
822
834
  /**
823
835
  * Record the outcome of a completed run across all tiers that were selected
824
836
  * during this session and persist stats to disk.
@@ -862,6 +874,20 @@ declare class MemoryStore {
862
874
  addMessage(message: StoredMessage): void;
863
875
  getSessionMessages(sessionId: string): StoredMessage[];
864
876
  searchMessages(query: string, limit?: number): StoredMessage[];
877
+ /** Full sessions (with messages) for the given ids, or ALL sessions. */
878
+ exportSessions(ids?: string[]): Session[];
879
+ /**
880
+ * Import sessions from a bundle. Returns the created `{id, title}` rows so
881
+ * the caller can mirror them into the runtime session list (the sidebar
882
+ * reads runtime_sessions, not sessions). Malformed entries are skipped.
883
+ */
884
+ importSessions(sessions: Session[]): Array<{
885
+ id: string;
886
+ title: string;
887
+ }>;
888
+ /** Import identities/personas, deduped by (case-insensitive) name — an
889
+ * existing name is skipped, never replaced. Imports are never the default. */
890
+ importIdentities(identities: Identity[]): number;
865
891
  createIdentity(identity: Identity): void;
866
892
  updateIdentity(id: string, updates: Partial<Identity>): void;
867
893
  getIdentity(id: string): Identity | null;
@@ -884,6 +910,15 @@ declare class MemoryStore {
884
910
  getCacheAge(): number;
885
911
  saveModelProfile(modelId: string, provider: ProviderType, specializations: string[]): void;
886
912
  getModelProfile(modelId: string, provider: ProviderType): ModelInfo | undefined;
913
+ /**
914
+ * Persist a probed capability verdict (currently: native tool support) into
915
+ * the model's cached profile, merging with whatever is already recorded.
916
+ * Read back via getModelProfile — so a model is probed once ever, not once
917
+ * per run.
918
+ */
919
+ saveModelCapability(modelId: string, provider: ProviderType, caps: {
920
+ supportsToolUse?: boolean;
921
+ }): void;
887
922
  getProfiledModelIds(): string[];
888
923
  private toolResultCache;
889
924
  private static CACHEABLE_TOOLS;
@@ -923,6 +958,16 @@ interface PriceEntry {
923
958
  input: number;
924
959
  output: number;
925
960
  }
961
+ /**
962
+ * Per-model capability facts from the same OpenRouter catalog the pricing fetch
963
+ * already downloads: context window, native tool support, and input modalities.
964
+ * Previously discarded — provider listModels() stubs guessed/hardcoded these.
965
+ */
966
+ interface CapabilityEntry {
967
+ contextWindow?: number;
968
+ supportsTools?: boolean;
969
+ inputModalities?: string[];
970
+ }
926
971
  interface LiveDataOptions {
927
972
  /** Master switch for live quality fetch. Default: true. */
928
973
  live?: boolean;
@@ -938,6 +983,7 @@ interface LiveDataOptions {
938
983
  declare class LiveDataProvider {
939
984
  private snapshot;
940
985
  private prices;
986
+ private capabilities;
941
987
  private source;
942
988
  private fetchedAt;
943
989
  private loaded;
@@ -953,7 +999,13 @@ declare class LiveDataProvider {
953
999
  refresh(force?: boolean): Promise<void>;
954
1000
  private doRefresh;
955
1001
  private fetchSnapshot;
956
- private fetchPrices;
1002
+ /**
1003
+ * One fetch of the OpenRouter catalog yields BOTH pricing and capability
1004
+ * facts (context window, native tool support, input modalities) — the
1005
+ * capability fields used to be discarded while providers guessed/hardcoded
1006
+ * them in their listModels() stubs.
1007
+ */
1008
+ private fetchCatalog;
957
1009
  private saveCache;
958
1010
  /** Quality profile for a model family, or null when we have no live/cached data. */
959
1011
  getQualityProfile(family: string): BenchmarkProfile | null;
@@ -964,10 +1016,20 @@ declare class LiveDataProvider {
964
1016
  * leaving the original untouched (so the shared catalog is never mutated).
965
1017
  */
966
1018
  applyLivePricing(models: ModelInfo[]): ModelInfo[];
1019
+ /** Current capability facts for a model id, or null when unknown. */
1020
+ getCapability(modelId: string): CapabilityEntry | null;
1021
+ /**
1022
+ * Returns capability-corrected copies of each model (originals untouched):
1023
+ * real context windows replace the providers' hardcoded guesses, native
1024
+ * tool support replaces the assume-by-provider default, and vision is set
1025
+ * from the declared input modalities.
1026
+ */
1027
+ applyLiveCapabilities(models: ModelInfo[]): ModelInfo[];
967
1028
  /** Where the active quality data came from — for /why and `cascade models`. */
968
1029
  getDataSource(): DataSource;
969
1030
  getGeneratedAt(): string | null;
970
1031
  hasLivePricing(): boolean;
1032
+ hasCapabilities(): boolean;
971
1033
  }
972
1034
 
973
1035
  interface WorldStateEntry {
@@ -976,6 +1038,19 @@ interface WorldStateEntry {
976
1038
  summary: string;
977
1039
  workerId: string;
978
1040
  }
1041
+ /**
1042
+ * A single queryable fact in the project knowledge graph (world-state v2):
1043
+ * an `(entity, relation) → value` triple with provenance. Facts are upserted on
1044
+ * `(entity, relation)`, so a newer observation supersedes an older one instead of
1045
+ * appending — T1/T2 query relevant facts by entity rather than replaying the log.
1046
+ */
1047
+ interface WorldFact {
1048
+ entity: string;
1049
+ relation: string;
1050
+ value: string;
1051
+ sourceWorker: string;
1052
+ timestamp: string;
1053
+ }
979
1054
  declare class WorldStateDB {
980
1055
  private workspacePath;
981
1056
  private debugMode;
@@ -990,6 +1065,58 @@ declare class WorldStateDB {
990
1065
  addEntry(workerId: string, summary: string): string;
991
1066
  getAllEntries(): WorldStateEntry[];
992
1067
  getFormattedState(): string;
1068
+ /**
1069
+ * Upsert a fact. `(entity, relation)` is normalized so casing/whitespace don't
1070
+ * fragment the key; an existing pair is superseded (value + provenance updated)
1071
+ * rather than duplicated. Empty entity/relation/value are ignored.
1072
+ */
1073
+ upsertFact(entity: string, relation: string, value: string, sourceWorker: string, timestamp?: string): void;
1074
+ private rowToFact;
1075
+ /** All facts whose entity matches one of the (normalized) query entities. */
1076
+ getFactsForEntities(entities: string[]): WorldFact[];
1077
+ /** Every fact (used for tests / debug / whole-graph views). */
1078
+ getAllFacts(): WorldFact[];
1079
+ /**
1080
+ * A compact, human/LLM-readable fact block for T1/T2 planning. When `entities`
1081
+ * is given, only facts about those entities are included; otherwise all facts.
1082
+ * Returns '' when there are none so callers can fall back to the linear log.
1083
+ */
1084
+ getFormattedFacts(entities?: string[]): string;
1085
+ /**
1086
+ * A compact knowledge block for T1/T2 planning. When `prompt` is given, facts
1087
+ * whose entity/relation/value mention a prompt token are preferred (relevance
1088
+ * filter); otherwise, or when nothing matches, all facts are used (capped at
1089
+ * `limit`). Returns '' when there are no facts, so the caller can fall back to
1090
+ * the raw linear log — this replaces replaying the whole log during planning.
1091
+ */
1092
+ getFormattedKnowledge(prompt?: string, limit?: number): string;
1093
+ exportKnowledge(): {
1094
+ facts: WorldFact[];
1095
+ worldLog: WorldStateEntry[];
1096
+ };
1097
+ /**
1098
+ * Merge imported knowledge. Facts upsert on (entity, relation) with
1099
+ * newer-timestamp-wins (a local fact newer than the imported one is kept);
1100
+ * log entries append with fresh ids, skipping exact duplicates
1101
+ * (worker + timestamp + summary). Returns counts of what actually landed.
1102
+ */
1103
+ importKnowledge(data: {
1104
+ facts?: Array<{
1105
+ entity?: string;
1106
+ relation?: string;
1107
+ value?: string;
1108
+ sourceWorker?: string;
1109
+ timestamp?: string;
1110
+ }>;
1111
+ worldLog?: Array<{
1112
+ workerId?: string;
1113
+ summary?: string;
1114
+ timestamp?: string;
1115
+ }>;
1116
+ }): {
1117
+ facts: number;
1118
+ logEntries: number;
1119
+ };
993
1120
  private dumpDebugIfNeeded;
994
1121
  close(): void;
995
1122
  }
@@ -1095,6 +1222,18 @@ declare class CascadeRouter extends EventEmitter {
1095
1222
  * No-op if store is not provided.
1096
1223
  */
1097
1224
  profileModels(store: MemoryStore): Promise<void>;
1225
+ /**
1226
+ * One-time native tool-call probe for local/compat models with NO capability
1227
+ * metadata. llama.cpp / LM Studio "/models" endpoints return ids only, so
1228
+ * `supportsToolUse` stays undefined and the T3 tool gate ASSUMES native
1229
+ * support — wrong for many local builds, which then fumble tool-format
1230
+ * output. Each unknown model is probed once with a trivial tool; the verdict
1231
+ * persists in the MemoryStore's model_cache (via the previously-dormant
1232
+ * getModelProfile read-back), so a model is probed once EVER, not per run.
1233
+ * Cloud providers are never probed — their metadata is authoritative.
1234
+ */
1235
+ probeLocalToolSupport(store: MemoryStore): Promise<void>;
1236
+ private probeNativeToolCall;
1098
1237
  /**
1099
1238
  * Cascade Auto live data: discover/validate real model ids from each cloud
1100
1239
  * provider, then fetch current public quality scores + per-token prices and
@@ -1111,8 +1250,10 @@ declare class CascadeRouter extends EventEmitter {
1111
1250
  */
1112
1251
  private discoverProviderModels;
1113
1252
  /**
1114
- * Replace available models with live-priced copies and refresh the already
1115
- * resolved tier models so shared-tier cost accounting uses current prices.
1253
+ * Replace available models with live-priced AND capability-corrected copies
1254
+ * (real context windows, native tool support, vision from modalities), then
1255
+ * refresh the already resolved tier models so shared-tier cost accounting and
1256
+ * the tool-use gate both see current data.
1116
1257
  */
1117
1258
  private applyLivePricing;
1118
1259
  generate(tier: TierRole, options: GenerateOptions, onChunk?: (chunk: StreamChunk) => void, requireVision?: boolean): Promise<GenerateResult>;
@@ -1127,6 +1268,10 @@ declare class CascadeRouter extends EventEmitter {
1127
1268
  enabled: boolean;
1128
1269
  maxPerSection: number;
1129
1270
  };
1271
+ /** Project-knowledge settings (config.knowledge). Facts extraction on by default. */
1272
+ getKnowledgeConfig(): {
1273
+ factsExtraction: boolean;
1274
+ };
1130
1275
  /**
1131
1276
  * Resolved T3 wave execution mode. 'auto' becomes 'sequential' when the T3
1132
1277
  * tier resolves to a LOCAL model (the single-GPU queue serializes anyway, so
@@ -1169,7 +1314,9 @@ declare class CascadeRouter extends EventEmitter {
1169
1314
  * null when Cascade Auto is off (callers then use the shared tier model).
1170
1315
  * Pure heuristic — no extra LLM call.
1171
1316
  */
1172
- selectModelForSubtask(tier: TierRole, text: string): Promise<ModelInfo | null>;
1317
+ selectModelForSubtask(tier: TierRole, text: string, opts?: {
1318
+ requiresToolUse?: boolean;
1319
+ }): Promise<ModelInfo | null>;
1173
1320
  getStats(): RouterStats;
1174
1321
  /**
1175
1322
  * What did delegation save? Compares actual spend against the
@@ -1468,6 +1615,7 @@ declare class PermissionEscalator extends EventEmitter {
1468
1615
  cancelAllPending(): void;
1469
1616
  }
1470
1617
 
1618
+ type SandboxMode = 'isolate' | 'worker' | 'auto';
1471
1619
  interface GeneratedToolSpec {
1472
1620
  name: string;
1473
1621
  description: string;
@@ -1489,12 +1637,14 @@ declare class ToolCreator {
1489
1637
  private workspacePath?;
1490
1638
  /** When false, persisted tools are neither loaded nor written. */
1491
1639
  private persistEnabled;
1640
+ /** Sandbox runtime for generated tools; passed to each DynamicTool. */
1641
+ private sandboxMode;
1492
1642
  private logger?;
1493
1643
  /** name → spec, for persistence, broadcast, and re-registration. */
1494
1644
  private specs;
1495
1645
  /** capability fingerprint → tool name, so the same need isn't re-generated. */
1496
1646
  private capabilityIndex;
1497
- constructor(router: CascadeRouter, registry: ToolRegistry, workspacePath?: string, persistEnabled?: boolean);
1647
+ constructor(router: CascadeRouter, registry: ToolRegistry, workspacePath?: string, persistEnabled?: boolean, sandboxMode?: SandboxMode);
1498
1648
  setPermissionEscalator(escalator: PermissionEscalator): void;
1499
1649
  /** Route diagnostics through the host (Cascade) so they survive the Ink TUI. */
1500
1650
  setLogger(fn: (msg: string) => void): void;
@@ -2011,6 +2161,14 @@ declare class T3Worker extends BaseTier {
2011
2161
  */
2012
2162
  private reflectAndImprove;
2013
2163
  private selfTest;
2164
+ /**
2165
+ * world-state v2: distill a completed subtask's output into durable
2166
+ * `(entity, relation, value)` facts and upsert them into the knowledge graph.
2167
+ * A bounded, cheap T3-tier call; entirely best-effort — any failure is swallowed
2168
+ * so it never blocks or fails the subtask. Respects the subtask's privacy tier
2169
+ * (a local-only subtask extracts on a local model too, never leaking to cloud).
2170
+ */
2171
+ private extractAndStoreFacts;
2014
2172
  private correctOutput;
2015
2173
  private buildSystemPrompt;
2016
2174
  private buildInitialPrompt;
package/dist/index.d.ts CHANGED
@@ -387,6 +387,10 @@ interface CascadeConfig {
387
387
  enableToolCreation?: boolean;
388
388
  /** Persist runtime-generated tools and reload them on startup (untrusted). Default: true. */
389
389
  persistDynamicTools?: boolean;
390
+ /** Project knowledge (world state) settings. */
391
+ knowledge?: {
392
+ factsExtraction?: boolean;
393
+ };
390
394
  plugins?: string[];
391
395
  localConcurrency?: number;
392
396
  localInferenceTimeoutMs?: number;
@@ -478,6 +482,12 @@ interface ToolsConfig {
478
482
  mcpTrusted?: string[];
479
483
  /** Web search backends — at least one should be configured for best results */
480
484
  webSearch?: WebSearchConfig;
485
+ /**
486
+ * Sandbox runtime for LLM-authored dynamic tools. 'isolate' = hard V8 isolate
487
+ * (isolated-vm, capability-confined), 'worker' = node:worker_threads,
488
+ * 'auto' (default) = isolate when available else worker.
489
+ */
490
+ dynamicToolSandbox?: 'isolate' | 'worker' | 'auto';
481
491
  }
482
492
  interface HooksConfig {
483
493
  preToolUse?: HookDefinition[];
@@ -818,7 +828,9 @@ declare class TaskAnalyzer {
818
828
  * Scores tier-eligible models using cost efficiency + historical performance.
819
829
  * Falls back to the priority-list default when no candidates have history.
820
830
  */
821
- selectModel(prompt: string, tier: TierRole, selector: ModelSelector): Promise<ModelInfo | null>;
831
+ selectModel(prompt: string, tier: TierRole, selector: ModelSelector, opts?: {
832
+ requiresToolUse?: boolean;
833
+ }): Promise<ModelInfo | null>;
822
834
  /**
823
835
  * Record the outcome of a completed run across all tiers that were selected
824
836
  * during this session and persist stats to disk.
@@ -862,6 +874,20 @@ declare class MemoryStore {
862
874
  addMessage(message: StoredMessage): void;
863
875
  getSessionMessages(sessionId: string): StoredMessage[];
864
876
  searchMessages(query: string, limit?: number): StoredMessage[];
877
+ /** Full sessions (with messages) for the given ids, or ALL sessions. */
878
+ exportSessions(ids?: string[]): Session[];
879
+ /**
880
+ * Import sessions from a bundle. Returns the created `{id, title}` rows so
881
+ * the caller can mirror them into the runtime session list (the sidebar
882
+ * reads runtime_sessions, not sessions). Malformed entries are skipped.
883
+ */
884
+ importSessions(sessions: Session[]): Array<{
885
+ id: string;
886
+ title: string;
887
+ }>;
888
+ /** Import identities/personas, deduped by (case-insensitive) name — an
889
+ * existing name is skipped, never replaced. Imports are never the default. */
890
+ importIdentities(identities: Identity[]): number;
865
891
  createIdentity(identity: Identity): void;
866
892
  updateIdentity(id: string, updates: Partial<Identity>): void;
867
893
  getIdentity(id: string): Identity | null;
@@ -884,6 +910,15 @@ declare class MemoryStore {
884
910
  getCacheAge(): number;
885
911
  saveModelProfile(modelId: string, provider: ProviderType, specializations: string[]): void;
886
912
  getModelProfile(modelId: string, provider: ProviderType): ModelInfo | undefined;
913
+ /**
914
+ * Persist a probed capability verdict (currently: native tool support) into
915
+ * the model's cached profile, merging with whatever is already recorded.
916
+ * Read back via getModelProfile — so a model is probed once ever, not once
917
+ * per run.
918
+ */
919
+ saveModelCapability(modelId: string, provider: ProviderType, caps: {
920
+ supportsToolUse?: boolean;
921
+ }): void;
887
922
  getProfiledModelIds(): string[];
888
923
  private toolResultCache;
889
924
  private static CACHEABLE_TOOLS;
@@ -923,6 +958,16 @@ interface PriceEntry {
923
958
  input: number;
924
959
  output: number;
925
960
  }
961
+ /**
962
+ * Per-model capability facts from the same OpenRouter catalog the pricing fetch
963
+ * already downloads: context window, native tool support, and input modalities.
964
+ * Previously discarded — provider listModels() stubs guessed/hardcoded these.
965
+ */
966
+ interface CapabilityEntry {
967
+ contextWindow?: number;
968
+ supportsTools?: boolean;
969
+ inputModalities?: string[];
970
+ }
926
971
  interface LiveDataOptions {
927
972
  /** Master switch for live quality fetch. Default: true. */
928
973
  live?: boolean;
@@ -938,6 +983,7 @@ interface LiveDataOptions {
938
983
  declare class LiveDataProvider {
939
984
  private snapshot;
940
985
  private prices;
986
+ private capabilities;
941
987
  private source;
942
988
  private fetchedAt;
943
989
  private loaded;
@@ -953,7 +999,13 @@ declare class LiveDataProvider {
953
999
  refresh(force?: boolean): Promise<void>;
954
1000
  private doRefresh;
955
1001
  private fetchSnapshot;
956
- private fetchPrices;
1002
+ /**
1003
+ * One fetch of the OpenRouter catalog yields BOTH pricing and capability
1004
+ * facts (context window, native tool support, input modalities) — the
1005
+ * capability fields used to be discarded while providers guessed/hardcoded
1006
+ * them in their listModels() stubs.
1007
+ */
1008
+ private fetchCatalog;
957
1009
  private saveCache;
958
1010
  /** Quality profile for a model family, or null when we have no live/cached data. */
959
1011
  getQualityProfile(family: string): BenchmarkProfile | null;
@@ -964,10 +1016,20 @@ declare class LiveDataProvider {
964
1016
  * leaving the original untouched (so the shared catalog is never mutated).
965
1017
  */
966
1018
  applyLivePricing(models: ModelInfo[]): ModelInfo[];
1019
+ /** Current capability facts for a model id, or null when unknown. */
1020
+ getCapability(modelId: string): CapabilityEntry | null;
1021
+ /**
1022
+ * Returns capability-corrected copies of each model (originals untouched):
1023
+ * real context windows replace the providers' hardcoded guesses, native
1024
+ * tool support replaces the assume-by-provider default, and vision is set
1025
+ * from the declared input modalities.
1026
+ */
1027
+ applyLiveCapabilities(models: ModelInfo[]): ModelInfo[];
967
1028
  /** Where the active quality data came from — for /why and `cascade models`. */
968
1029
  getDataSource(): DataSource;
969
1030
  getGeneratedAt(): string | null;
970
1031
  hasLivePricing(): boolean;
1032
+ hasCapabilities(): boolean;
971
1033
  }
972
1034
 
973
1035
  interface WorldStateEntry {
@@ -976,6 +1038,19 @@ interface WorldStateEntry {
976
1038
  summary: string;
977
1039
  workerId: string;
978
1040
  }
1041
+ /**
1042
+ * A single queryable fact in the project knowledge graph (world-state v2):
1043
+ * an `(entity, relation) → value` triple with provenance. Facts are upserted on
1044
+ * `(entity, relation)`, so a newer observation supersedes an older one instead of
1045
+ * appending — T1/T2 query relevant facts by entity rather than replaying the log.
1046
+ */
1047
+ interface WorldFact {
1048
+ entity: string;
1049
+ relation: string;
1050
+ value: string;
1051
+ sourceWorker: string;
1052
+ timestamp: string;
1053
+ }
979
1054
  declare class WorldStateDB {
980
1055
  private workspacePath;
981
1056
  private debugMode;
@@ -990,6 +1065,58 @@ declare class WorldStateDB {
990
1065
  addEntry(workerId: string, summary: string): string;
991
1066
  getAllEntries(): WorldStateEntry[];
992
1067
  getFormattedState(): string;
1068
+ /**
1069
+ * Upsert a fact. `(entity, relation)` is normalized so casing/whitespace don't
1070
+ * fragment the key; an existing pair is superseded (value + provenance updated)
1071
+ * rather than duplicated. Empty entity/relation/value are ignored.
1072
+ */
1073
+ upsertFact(entity: string, relation: string, value: string, sourceWorker: string, timestamp?: string): void;
1074
+ private rowToFact;
1075
+ /** All facts whose entity matches one of the (normalized) query entities. */
1076
+ getFactsForEntities(entities: string[]): WorldFact[];
1077
+ /** Every fact (used for tests / debug / whole-graph views). */
1078
+ getAllFacts(): WorldFact[];
1079
+ /**
1080
+ * A compact, human/LLM-readable fact block for T1/T2 planning. When `entities`
1081
+ * is given, only facts about those entities are included; otherwise all facts.
1082
+ * Returns '' when there are none so callers can fall back to the linear log.
1083
+ */
1084
+ getFormattedFacts(entities?: string[]): string;
1085
+ /**
1086
+ * A compact knowledge block for T1/T2 planning. When `prompt` is given, facts
1087
+ * whose entity/relation/value mention a prompt token are preferred (relevance
1088
+ * filter); otherwise, or when nothing matches, all facts are used (capped at
1089
+ * `limit`). Returns '' when there are no facts, so the caller can fall back to
1090
+ * the raw linear log — this replaces replaying the whole log during planning.
1091
+ */
1092
+ getFormattedKnowledge(prompt?: string, limit?: number): string;
1093
+ exportKnowledge(): {
1094
+ facts: WorldFact[];
1095
+ worldLog: WorldStateEntry[];
1096
+ };
1097
+ /**
1098
+ * Merge imported knowledge. Facts upsert on (entity, relation) with
1099
+ * newer-timestamp-wins (a local fact newer than the imported one is kept);
1100
+ * log entries append with fresh ids, skipping exact duplicates
1101
+ * (worker + timestamp + summary). Returns counts of what actually landed.
1102
+ */
1103
+ importKnowledge(data: {
1104
+ facts?: Array<{
1105
+ entity?: string;
1106
+ relation?: string;
1107
+ value?: string;
1108
+ sourceWorker?: string;
1109
+ timestamp?: string;
1110
+ }>;
1111
+ worldLog?: Array<{
1112
+ workerId?: string;
1113
+ summary?: string;
1114
+ timestamp?: string;
1115
+ }>;
1116
+ }): {
1117
+ facts: number;
1118
+ logEntries: number;
1119
+ };
993
1120
  private dumpDebugIfNeeded;
994
1121
  close(): void;
995
1122
  }
@@ -1095,6 +1222,18 @@ declare class CascadeRouter extends EventEmitter {
1095
1222
  * No-op if store is not provided.
1096
1223
  */
1097
1224
  profileModels(store: MemoryStore): Promise<void>;
1225
+ /**
1226
+ * One-time native tool-call probe for local/compat models with NO capability
1227
+ * metadata. llama.cpp / LM Studio "/models" endpoints return ids only, so
1228
+ * `supportsToolUse` stays undefined and the T3 tool gate ASSUMES native
1229
+ * support — wrong for many local builds, which then fumble tool-format
1230
+ * output. Each unknown model is probed once with a trivial tool; the verdict
1231
+ * persists in the MemoryStore's model_cache (via the previously-dormant
1232
+ * getModelProfile read-back), so a model is probed once EVER, not per run.
1233
+ * Cloud providers are never probed — their metadata is authoritative.
1234
+ */
1235
+ probeLocalToolSupport(store: MemoryStore): Promise<void>;
1236
+ private probeNativeToolCall;
1098
1237
  /**
1099
1238
  * Cascade Auto live data: discover/validate real model ids from each cloud
1100
1239
  * provider, then fetch current public quality scores + per-token prices and
@@ -1111,8 +1250,10 @@ declare class CascadeRouter extends EventEmitter {
1111
1250
  */
1112
1251
  private discoverProviderModels;
1113
1252
  /**
1114
- * Replace available models with live-priced copies and refresh the already
1115
- * resolved tier models so shared-tier cost accounting uses current prices.
1253
+ * Replace available models with live-priced AND capability-corrected copies
1254
+ * (real context windows, native tool support, vision from modalities), then
1255
+ * refresh the already resolved tier models so shared-tier cost accounting and
1256
+ * the tool-use gate both see current data.
1116
1257
  */
1117
1258
  private applyLivePricing;
1118
1259
  generate(tier: TierRole, options: GenerateOptions, onChunk?: (chunk: StreamChunk) => void, requireVision?: boolean): Promise<GenerateResult>;
@@ -1127,6 +1268,10 @@ declare class CascadeRouter extends EventEmitter {
1127
1268
  enabled: boolean;
1128
1269
  maxPerSection: number;
1129
1270
  };
1271
+ /** Project-knowledge settings (config.knowledge). Facts extraction on by default. */
1272
+ getKnowledgeConfig(): {
1273
+ factsExtraction: boolean;
1274
+ };
1130
1275
  /**
1131
1276
  * Resolved T3 wave execution mode. 'auto' becomes 'sequential' when the T3
1132
1277
  * tier resolves to a LOCAL model (the single-GPU queue serializes anyway, so
@@ -1169,7 +1314,9 @@ declare class CascadeRouter extends EventEmitter {
1169
1314
  * null when Cascade Auto is off (callers then use the shared tier model).
1170
1315
  * Pure heuristic — no extra LLM call.
1171
1316
  */
1172
- selectModelForSubtask(tier: TierRole, text: string): Promise<ModelInfo | null>;
1317
+ selectModelForSubtask(tier: TierRole, text: string, opts?: {
1318
+ requiresToolUse?: boolean;
1319
+ }): Promise<ModelInfo | null>;
1173
1320
  getStats(): RouterStats;
1174
1321
  /**
1175
1322
  * What did delegation save? Compares actual spend against the
@@ -1468,6 +1615,7 @@ declare class PermissionEscalator extends EventEmitter {
1468
1615
  cancelAllPending(): void;
1469
1616
  }
1470
1617
 
1618
+ type SandboxMode = 'isolate' | 'worker' | 'auto';
1471
1619
  interface GeneratedToolSpec {
1472
1620
  name: string;
1473
1621
  description: string;
@@ -1489,12 +1637,14 @@ declare class ToolCreator {
1489
1637
  private workspacePath?;
1490
1638
  /** When false, persisted tools are neither loaded nor written. */
1491
1639
  private persistEnabled;
1640
+ /** Sandbox runtime for generated tools; passed to each DynamicTool. */
1641
+ private sandboxMode;
1492
1642
  private logger?;
1493
1643
  /** name → spec, for persistence, broadcast, and re-registration. */
1494
1644
  private specs;
1495
1645
  /** capability fingerprint → tool name, so the same need isn't re-generated. */
1496
1646
  private capabilityIndex;
1497
- constructor(router: CascadeRouter, registry: ToolRegistry, workspacePath?: string, persistEnabled?: boolean);
1647
+ constructor(router: CascadeRouter, registry: ToolRegistry, workspacePath?: string, persistEnabled?: boolean, sandboxMode?: SandboxMode);
1498
1648
  setPermissionEscalator(escalator: PermissionEscalator): void;
1499
1649
  /** Route diagnostics through the host (Cascade) so they survive the Ink TUI. */
1500
1650
  setLogger(fn: (msg: string) => void): void;
@@ -2011,6 +2161,14 @@ declare class T3Worker extends BaseTier {
2011
2161
  */
2012
2162
  private reflectAndImprove;
2013
2163
  private selfTest;
2164
+ /**
2165
+ * world-state v2: distill a completed subtask's output into durable
2166
+ * `(entity, relation, value)` facts and upsert them into the knowledge graph.
2167
+ * A bounded, cheap T3-tier call; entirely best-effort — any failure is swallowed
2168
+ * so it never blocks or fails the subtask. Respects the subtask's privacy tier
2169
+ * (a local-only subtask extracts on a local model too, never leaking to cloud).
2170
+ */
2171
+ private extractAndStoreFacts;
2014
2172
  private correctOutput;
2015
2173
  private buildSystemPrompt;
2016
2174
  private buildInitialPrompt;