@theokit/sdk 4.22.0 → 4.23.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.
Files changed (58) hide show
  1. package/dist/compaction.cjs +31 -0
  2. package/dist/compaction.cjs.map +1 -1
  3. package/dist/compaction.d.cts +79 -0
  4. package/dist/compaction.d.ts +79 -0
  5. package/dist/compaction.js +28 -1
  6. package/dist/compaction.js.map +1 -1
  7. package/dist/{cron-Bhdyjl0B.d.ts → cron-B_NkE_VM.d.ts} +15 -1
  8. package/dist/{cron-M2Xz7lq2.d.cts → cron-x3muPNgg.d.cts} +15 -1
  9. package/dist/cron.cjs +461 -332
  10. package/dist/cron.cjs.map +1 -1
  11. package/dist/cron.d.cts +2 -2
  12. package/dist/cron.d.ts +2 -2
  13. package/dist/cron.js +461 -332
  14. package/dist/cron.js.map +1 -1
  15. package/dist/{errors-gE8612p9.d.cts → errors-BdL-buYn.d.cts} +1 -1
  16. package/dist/{errors-CG2RpeW-.d.ts → errors-DHZtSNnj.d.ts} +1 -1
  17. package/dist/errors.d.cts +2 -2
  18. package/dist/eval.cjs +461 -332
  19. package/dist/eval.cjs.map +1 -1
  20. package/dist/eval.js +461 -332
  21. package/dist/eval.js.map +1 -1
  22. package/dist/index.cjs +634 -503
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +21 -12
  25. package/dist/index.d.ts +21 -12
  26. package/dist/index.js +634 -503
  27. package/dist/index.js.map +1 -1
  28. package/dist/internal/global-singleton.d.ts +13 -0
  29. package/dist/internal/llm/openai-messages.d.ts +2 -0
  30. package/dist/internal/local-agent/mcp-pool.d.ts +41 -0
  31. package/dist/internal/local-agent/real-local-run.d.ts +2 -0
  32. package/dist/internal/persistence/index.cjs +3 -1
  33. package/dist/internal/persistence/index.cjs.map +1 -1
  34. package/dist/internal/persistence/index.js +3 -1
  35. package/dist/internal/persistence/index.js.map +1 -1
  36. package/dist/internal/providers/registry.d.ts +1 -0
  37. package/dist/internal/runtime/lifecycle/context-budget-event.d.ts +25 -0
  38. package/dist/internal/runtime/lifecycle/goal-marker.d.ts +13 -0
  39. package/dist/internal/runtime/lifecycle/run-until.d.ts +0 -1
  40. package/dist/internal/session/agent-session.d.ts +1 -1
  41. package/dist/internal/session/session-cache.d.ts +20 -0
  42. package/dist/models.cjs +11 -15
  43. package/dist/models.cjs.map +1 -1
  44. package/dist/models.js +11 -15
  45. package/dist/models.js.map +1 -1
  46. package/dist/persistence.cjs +3 -1
  47. package/dist/persistence.cjs.map +1 -1
  48. package/dist/persistence.js +3 -1
  49. package/dist/persistence.js.map +1 -1
  50. package/dist/provider-catalog.json +144 -469
  51. package/dist/{run-DFM1H2jW.d.cts → run-OJbGyweZ.d.cts} +20 -1
  52. package/dist/{run-DFM1H2jW.d.ts → run-OJbGyweZ.d.ts} +20 -1
  53. package/dist/types/agent.d.ts +14 -0
  54. package/dist/types/run-events.d.ts +20 -1
  55. package/dist/workflow.cjs.map +1 -1
  56. package/dist/workflow.js.map +1 -1
  57. package/package.json +2 -2
  58. package/dist/goal-loop.d.ts +0 -35
package/dist/index.js CHANGED
@@ -1704,6 +1704,44 @@ var init_plugin_guards = __esm({
1704
1704
  }
1705
1705
  });
1706
1706
 
1707
+ // src/compaction.ts
1708
+ function resolveEffectiveContextWindow(input) {
1709
+ if (!(input.margin > 0) || input.margin > 1) {
1710
+ throw new ContextWindowMarginError(input.margin);
1711
+ }
1712
+ const withMargin = (raw) => Math.floor(raw * input.margin);
1713
+ if (input.override !== void 0) {
1714
+ const clamped = input.catalog !== void 0 && input.override > input.catalog;
1715
+ const raw = clamped ? input.catalog : input.override;
1716
+ return { window: withMargin(raw), source: "override", clamped };
1717
+ }
1718
+ if (input.catalog !== void 0) {
1719
+ return { window: withMargin(input.catalog), source: "catalog", clamped: false };
1720
+ }
1721
+ return { window: withMargin(input.floor ?? 0), source: "fallback", clamped: false };
1722
+ }
1723
+ function estimateTokens(text) {
1724
+ return Math.ceil(text.length / 4);
1725
+ }
1726
+ var ContextWindowMarginError, CONTEXT_WINDOW_MARGIN, CONTEXT_WINDOW_FLOOR;
1727
+ var init_compaction = __esm({
1728
+ "src/compaction.ts"() {
1729
+ init_errors();
1730
+ ContextWindowMarginError = class extends TheokitAgentError {
1731
+ constructor(margin) {
1732
+ super(
1733
+ `context-window margin must be in (0, 1], got ${String(margin)}. A margin above 1 grows the assumed window and delays compaction past the real limit.`,
1734
+ { code: "invalid_context_window_margin" }
1735
+ );
1736
+ this.margin = margin;
1737
+ }
1738
+ margin;
1739
+ };
1740
+ CONTEXT_WINDOW_MARGIN = 0.95;
1741
+ CONTEXT_WINDOW_FLOOR = 128e3;
1742
+ }
1743
+ });
1744
+
1707
1745
  // src/types/run-events.ts
1708
1746
  function emitRunEvent(sink, event) {
1709
1747
  if (sink === void 0) return;
@@ -1762,6 +1800,18 @@ var init_session_summary_writer = __esm({
1762
1800
  MAX_TURN_CHARS = 2e3;
1763
1801
  }
1764
1802
  });
1803
+
1804
+ // src/internal/global-singleton.ts
1805
+ function globalSingleton(key2, create) {
1806
+ const g = globalThis;
1807
+ const sym = Symbol.for(key2);
1808
+ if (g[sym] === void 0) g[sym] = create();
1809
+ return g[sym];
1810
+ }
1811
+ var init_global_singleton = __esm({
1812
+ "src/internal/global-singleton.ts"() {
1813
+ }
1814
+ });
1765
1815
  var MODALITIES, costSchema, limitSchema, modalitiesSchema, catalogModelSchema;
1766
1816
  var init_catalog_schema = __esm({
1767
1817
  "src/internal/providers/catalog-schema.ts"() {
@@ -1802,12 +1852,6 @@ var init_catalog_schema = __esm({
1802
1852
  });
1803
1853
 
1804
1854
  // src/internal/providers/registry.ts
1805
- function globalSingleton(key2, create) {
1806
- const g = globalThis;
1807
- const sym = Symbol.for(key2);
1808
- if (g[sym] === void 0) g[sym] = create();
1809
- return g[sym];
1810
- }
1811
1855
  function registerProvider(profile) {
1812
1856
  if (REGISTRY.has(profile.name)) {
1813
1857
  process.stderr.write(`[theokit-sdk] Provider "${profile.name}" overridden by user plugin.
@@ -1835,6 +1879,7 @@ function listProviders() {
1835
1879
  var REGISTRY, ALIASES;
1836
1880
  var init_registry = __esm({
1837
1881
  "src/internal/providers/registry.ts"() {
1882
+ init_global_singleton();
1838
1883
  REGISTRY = globalSingleton(
1839
1884
  "theokit-sdk.providers.registry",
1840
1885
  () => /* @__PURE__ */ new Map()
@@ -1842,12 +1887,6 @@ var init_registry = __esm({
1842
1887
  ALIASES = globalSingleton("theokit-sdk.providers.aliases", () => /* @__PURE__ */ new Map());
1843
1888
  }
1844
1889
  });
1845
- function globalSingleton2(key2, create) {
1846
- const g = globalThis;
1847
- const sym = Symbol.for(key2);
1848
- if (g[sym] === void 0) g[sym] = create();
1849
- return g[sym];
1850
- }
1851
1890
  function getCatalogModelInfo(key2) {
1852
1891
  ensureModelIndexLoaded();
1853
1892
  return modelInfoIndex.get(key2);
@@ -1949,33 +1988,25 @@ function registerCatalogProviders(opts) {
1949
1988
  var __dirname_resolved, modelInfoIndex, patchedModelKeys, indexState, _capabilitiesCache;
1950
1989
  var init_catalog_loader = __esm({
1951
1990
  "src/internal/providers/catalog-loader.ts"() {
1991
+ init_global_singleton();
1952
1992
  init_catalog_schema();
1953
1993
  init_registry();
1954
1994
  __dirname_resolved = dirname(fileURLToPath(import.meta.url));
1955
- modelInfoIndex = globalSingleton2(
1995
+ modelInfoIndex = globalSingleton(
1956
1996
  "theokit-sdk.providers.model-info-index",
1957
1997
  () => /* @__PURE__ */ new Map()
1958
1998
  );
1959
- patchedModelKeys = globalSingleton2(
1999
+ patchedModelKeys = globalSingleton(
1960
2000
  "theokit-sdk.providers.model-info-patched",
1961
2001
  () => /* @__PURE__ */ new Set()
1962
2002
  );
1963
- indexState = globalSingleton2("theokit-sdk.providers.model-info-loaded", () => ({
2003
+ indexState = globalSingleton("theokit-sdk.providers.model-info-loaded", () => ({
1964
2004
  loaded: false
1965
2005
  }));
1966
2006
  _capabilitiesCache = null;
1967
2007
  }
1968
2008
  });
1969
2009
 
1970
- // src/compaction.ts
1971
- function estimateTokens(text) {
1972
- return Math.ceil(text.length / 4);
1973
- }
1974
- var init_compaction = __esm({
1975
- "src/compaction.ts"() {
1976
- }
1977
- });
1978
-
1979
2010
  // src/internal/providers/builtin/anthropic.ts
1980
2011
  var ANTHROPIC;
1981
2012
  var init_anthropic = __esm({
@@ -2771,12 +2802,6 @@ var init_builtin = __esm({
2771
2802
  })();
2772
2803
  }
2773
2804
  });
2774
- function globalSingleton3(key2, create) {
2775
- const g = globalThis;
2776
- const sym = Symbol.for(key2);
2777
- if (g[sym] === void 0) g[sym] = create();
2778
- return g[sym];
2779
- }
2780
2805
  function pluginsRoot() {
2781
2806
  return join(homedir(), ".theokit", "plugins", "model-providers");
2782
2807
  }
@@ -2868,8 +2893,9 @@ async function loadOne(dir, entryName) {
2868
2893
  var discoveryState;
2869
2894
  var init_discovery = __esm({
2870
2895
  "src/internal/providers/discovery.ts"() {
2896
+ init_global_singleton();
2871
2897
  init_registry();
2872
- discoveryState = globalSingleton3("theokit-sdk.providers.discovered", () => ({
2898
+ discoveryState = globalSingleton("theokit-sdk.providers.discovered", () => ({
2873
2899
  done: false
2874
2900
  }));
2875
2901
  }
@@ -4546,6 +4572,68 @@ var init_hermes_tool_extract = __esm({
4546
4572
  }
4547
4573
  });
4548
4574
 
4575
+ // src/internal/llm/openai-messages.ts
4576
+ function toOpenAIMessages(message) {
4577
+ if (message.role === "system") return [systemMessage(message)];
4578
+ if (message.role === "user") return userOrToolMessages(message);
4579
+ return [assistantMessage(message)];
4580
+ }
4581
+ function systemMessage(message) {
4582
+ return { role: "system", content: joinTextParts2(message) };
4583
+ }
4584
+ function joinTextParts2(message) {
4585
+ return message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
4586
+ }
4587
+ function userOrToolMessages(message) {
4588
+ const out = [];
4589
+ for (const part of message.content) {
4590
+ if (part.type === "tool_result") {
4591
+ out.push({
4592
+ role: "tool",
4593
+ tool_call_id: part.toolUseId,
4594
+ // SE7 — this wire's tool role is string-only: text blocks flatten; an
4595
+ // image block fails fast (ConfigurationError).
4596
+ content: toStringToolResultContent(part.content, "openai")
4597
+ });
4598
+ }
4599
+ }
4600
+ const userText = joinTextParts2(message);
4601
+ const imageParts = message.content.filter(
4602
+ (p) => p.type === "image"
4603
+ );
4604
+ if (imageParts.length > 0) {
4605
+ const content = [];
4606
+ if (userText.length > 0) content.push({ type: "text", text: userText });
4607
+ for (const img of imageParts) {
4608
+ const url = img.source.type === "base64" ? `data:${img.source.media_type};base64,${img.source.data}` : img.source.url;
4609
+ content.push({ type: "image_url", image_url: { url } });
4610
+ }
4611
+ out.push({ role: "user", content });
4612
+ } else if (userText.length > 0) {
4613
+ out.push({ role: "user", content: userText });
4614
+ }
4615
+ return out;
4616
+ }
4617
+ function assistantMessage(message) {
4618
+ const text = joinTextParts2(message);
4619
+ const toolCalls = message.content.filter((part) => part.type === "tool_use").map((part) => {
4620
+ const tc = part;
4621
+ return {
4622
+ id: tc.id,
4623
+ type: "function",
4624
+ function: { name: tc.name, arguments: JSON.stringify(tc.input) }
4625
+ };
4626
+ });
4627
+ const result = { role: "assistant", content: text };
4628
+ if (toolCalls.length > 0) result.tool_calls = toolCalls;
4629
+ return result;
4630
+ }
4631
+ var init_openai_messages = __esm({
4632
+ "src/internal/llm/openai-messages.ts"() {
4633
+ init_tool_result_content();
4634
+ }
4635
+ });
4636
+
4549
4637
  // src/internal/llm/openai.ts
4550
4638
  function deriveChatPath(baseUrl) {
4551
4639
  try {
@@ -4607,61 +4695,6 @@ function encodeOpenAIResponseFormat(rf) {
4607
4695
  }
4608
4696
  };
4609
4697
  }
4610
- function toOpenAIMessages(message) {
4611
- if (message.role === "system") return [systemMessage(message)];
4612
- if (message.role === "user") return userOrToolMessages(message);
4613
- return [assistantMessage(message)];
4614
- }
4615
- function systemMessage(message) {
4616
- return { role: "system", content: joinTextParts2(message) };
4617
- }
4618
- function joinTextParts2(message) {
4619
- return message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
4620
- }
4621
- function userOrToolMessages(message) {
4622
- const out = [];
4623
- for (const part of message.content) {
4624
- if (part.type === "tool_result") {
4625
- out.push({
4626
- role: "tool",
4627
- tool_call_id: part.toolUseId,
4628
- // SE7 — this wire's tool role is string-only: text blocks flatten; an
4629
- // image block fails fast (ConfigurationError).
4630
- content: toStringToolResultContent(part.content, "openai")
4631
- });
4632
- }
4633
- }
4634
- const userText = joinTextParts2(message);
4635
- const imageParts = message.content.filter(
4636
- (p) => p.type === "image"
4637
- );
4638
- if (imageParts.length > 0) {
4639
- const content = [];
4640
- if (userText.length > 0) content.push({ type: "text", text: userText });
4641
- for (const img of imageParts) {
4642
- const url = img.source.type === "base64" ? `data:${img.source.media_type};base64,${img.source.data}` : img.source.url;
4643
- content.push({ type: "image_url", image_url: { url } });
4644
- }
4645
- out.push({ role: "user", content });
4646
- } else if (userText.length > 0) {
4647
- out.push({ role: "user", content: userText });
4648
- }
4649
- return out;
4650
- }
4651
- function assistantMessage(message) {
4652
- const text = joinTextParts2(message);
4653
- const toolCalls = message.content.filter((part) => part.type === "tool_use").map((part) => {
4654
- const tc = part;
4655
- return {
4656
- id: tc.id,
4657
- type: "function",
4658
- function: { name: tc.name, arguments: JSON.stringify(tc.input) }
4659
- };
4660
- });
4661
- const result = { role: "assistant", content: text };
4662
- if (toolCalls.length > 0) result.tool_calls = toolCalls;
4663
- return result;
4664
- }
4665
4698
  var OpenAIClient, OpenAIStreamAccumulator, openAISystemText;
4666
4699
  var init_openai2 = __esm({
4667
4700
  "src/internal/llm/openai.ts"() {
@@ -4670,8 +4703,8 @@ var init_openai2 = __esm({
4670
4703
  init_openai_compatible2();
4671
4704
  init_finish();
4672
4705
  init_hermes_tool_extract();
4706
+ init_openai_messages();
4673
4707
  init_sse();
4674
- init_tool_result_content();
4675
4708
  OpenAIClient = class {
4676
4709
  constructor(options) {
4677
4710
  this.options = options;
@@ -5760,6 +5793,14 @@ function selectTransport(profile, apiKey) {
5760
5793
  const ctx = { apiKey };
5761
5794
  return { fetch: profile.transform.fetch?.(ctx), headers: profile.transform.headers?.(ctx) };
5762
5795
  };
5796
+ const comTransform = (opts, criar) => {
5797
+ const t = applyTransform();
5798
+ assertOAuthResolved(t);
5799
+ if (t.fetch !== void 0) opts.fetch = t.fetch;
5800
+ const merged = profile.extraHeaders !== void 0 || t.headers !== void 0 ? { ...profile.extraHeaders, ...t.headers } : void 0;
5801
+ if (merged !== void 0) opts.extraHeaders = merged;
5802
+ return criar(opts);
5803
+ };
5763
5804
  const assertOAuthResolved = (t) => {
5764
5805
  if (apiKey !== "__oauth_lazy_token__") return;
5765
5806
  const auth = t.headers?.authorization ?? t.headers?.Authorization;
@@ -5788,12 +5829,7 @@ function selectTransport(profile, apiKey) {
5788
5829
  }
5789
5830
  const envOverride = resolveBaseUrlEnvOverride(profile.name);
5790
5831
  if (envOverride !== void 0) opts.baseUrl = envOverride;
5791
- const t = applyTransform();
5792
- assertOAuthResolved(t);
5793
- if (t.fetch !== void 0) opts.fetch = t.fetch;
5794
- const merged = profile.extraHeaders !== void 0 || t.headers !== void 0 ? { ...profile.extraHeaders, ...t.headers } : void 0;
5795
- if (merged !== void 0) opts.extraHeaders = merged;
5796
- return new OpenAIClient(opts);
5832
+ return comTransform(opts, (o) => new OpenAIClient(o));
5797
5833
  }
5798
5834
  if (profile.apiMode === "anthropic_messages") {
5799
5835
  if (profile.name === "vertex") {
@@ -5802,12 +5838,7 @@ function selectTransport(profile, apiKey) {
5802
5838
  }
5803
5839
  const opts = { apiKey };
5804
5840
  opts.baseUrl = process.env.ANTHROPIC_API_BASE_URL ?? profile.baseUrl;
5805
- const t = applyTransform();
5806
- assertOAuthResolved(t);
5807
- if (t.fetch !== void 0) opts.fetch = t.fetch;
5808
- const merged = profile.extraHeaders !== void 0 || t.headers !== void 0 ? { ...profile.extraHeaders, ...t.headers } : void 0;
5809
- if (merged !== void 0) opts.extraHeaders = merged;
5810
- return new AnthropicClient(opts);
5841
+ return comTransform(opts, (o) => new AnthropicClient(o));
5811
5842
  }
5812
5843
  if (profile.apiMode === "bedrock_anthropic") {
5813
5844
  const realKey = apiKey === "__bedrock_lazy_token__" ? void 0 : apiKey;
@@ -5997,194 +6028,44 @@ var init_compression_summarizer = __esm({
5997
6028
  }
5998
6029
  });
5999
6030
 
6000
- // src/internal/session/agent-session-store.ts
6001
- function seedTranscript(prior, opts) {
6002
- return SessionTranscript.fromRecords(prior, opts);
6003
- }
6004
- function mapAgentTurn(steps) {
6005
- const assistant = {};
6006
- const toolResults = [];
6007
- const toolCalls = [];
6008
- for (const step of steps) {
6009
- if (step.type === "thinkingMessage") assistant.thinking = step.message.text;
6010
- else if (step.type === "assistantMessage") assistant.text = step.message.text;
6011
- else if (step.type === "toolCall")
6012
- toolCalls.push({
6013
- id: step.message.callId,
6014
- name: step.message.name,
6015
- input: step.message.args ?? {}
6016
- });
6017
- else
6018
- toolResults.push({
6019
- toolUseId: step.message.callId,
6020
- content: step.message.result,
6021
- isError: step.message.isError
6022
- });
6023
- }
6024
- if (toolCalls.length > 0) assistant.toolCalls = toolCalls;
6025
- return { assistant, toolResults };
6031
+ // src/internal/session/session-cache.ts
6032
+ function transcriptKey(cwd, agentId) {
6033
+ return `${cwd}::${agentId}`;
6026
6034
  }
6027
- function hasAssistantContent(a) {
6028
- return a.text !== void 0 || a.thinking !== void 0 || (a.toolCalls?.length ?? 0) > 0;
6035
+ function invalidateSessionCache(cwd, agentId) {
6036
+ sessions.delete(agentId);
6037
+ hydratedKeys.delete(transcriptKey(cwd, agentId));
6029
6038
  }
6030
- function appendConversation(transcript, conversation) {
6031
- for (const ct of conversation) {
6032
- if (ct.type !== "agentConversationTurn") continue;
6033
- const { assistant, toolResults } = mapAgentTurn(ct.turn.steps);
6034
- if (hasAssistantContent(assistant)) transcript.appendAssistantTurn(assistant);
6035
- if (toolResults.length > 0) transcript.appendToolResults(toolResults);
6039
+ var sessions, hydratedKeys;
6040
+ var init_session_cache = __esm({
6041
+ "src/internal/session/session-cache.ts"() {
6042
+ sessions = /* @__PURE__ */ new Map();
6043
+ hydratedKeys = /* @__PURE__ */ new Set();
6036
6044
  }
6045
+ });
6046
+
6047
+ // src/internal/session/compact-session.ts
6048
+ var compact_session_exports = {};
6049
+ __export(compact_session_exports, {
6050
+ COMPACT_SUMMARY_MARKER: () => COMPACT_SUMMARY_MARKER,
6051
+ COMPACT_USER_MESSAGE_MAX_TOKENS: () => COMPACT_USER_MESSAGE_MAX_TOKENS,
6052
+ autoCompactIfNeeded: () => autoCompactIfNeeded,
6053
+ buildDefaultSummarizer: () => buildDefaultSummarizer,
6054
+ compactSessionTranscript: () => compactSessionTranscript,
6055
+ isCompactSummary: () => isCompactSummary,
6056
+ resolveSummarizerRoute: () => resolveSummarizerRoute,
6057
+ shouldAutoCompact: () => shouldAutoCompact
6058
+ });
6059
+ function isCompactSummary(content) {
6060
+ return content.startsWith(COMPACT_SUMMARY_MARKER) || content.startsWith("[[theokit:goal-continuation]]");
6037
6061
  }
6038
- async function readSessionMessages(store, agentId) {
6039
- const records = await store.readRecords(agentId);
6040
- return reconstructMessages(records).map(narrowToSessionMessage);
6041
- }
6042
- function partToText(p) {
6043
- if (p.type === "text") return p.text ?? "";
6044
- if (p.type === "tool_use") return `[tool call] ${p.name ?? ""}`;
6045
- if (p.type === "tool_result") {
6046
- const body = typeof p.content === "string" ? p.content : JSON.stringify(p.content);
6047
- return `[tool result] ${body}`;
6048
- }
6049
- return "";
6050
- }
6051
- function narrowToSessionMessage(m) {
6052
- const role = m.role === "user" ? "user" : "assistant";
6053
- const text = m.content.map(partToText).filter((s) => s.length > 0).join("\n");
6054
- return { role, text };
6055
- }
6056
- function deltaRecords(transcript, priorLength) {
6057
- return transcript.records().slice(priorLength);
6058
- }
6059
- async function persistTurn(store, loc, sessionId, turn) {
6060
- const prior = await store.readRecords(loc.agentId);
6061
- const transcript = seedTranscript(prior, { cwd: loc.cwd, sessionId, model: loc.model });
6062
- transcript.appendUserTurn(turn.userText);
6063
- appendConversation(transcript, turn.conversation);
6064
- await store.appendRecords(loc.agentId, deltaRecords(transcript, prior.length));
6065
- }
6066
- var init_agent_session_store = __esm({
6067
- "src/internal/session/agent-session-store.ts"() {
6068
- init_session_transcript();
6069
- }
6070
- });
6071
-
6072
- // src/internal/session/agent-session.ts
6073
- function transcriptKey(cwd, agentId) {
6074
- return `${cwd}::${agentId}`;
6075
- }
6076
- function appendSessionMessage(agentId, message) {
6077
- const existing = sessions.get(agentId) ?? [];
6078
- existing.push(message);
6079
- sessions.set(agentId, existing);
6080
- }
6081
- function getSessionMessages(agentId) {
6082
- return sessions.get(agentId) ?? [];
6083
- }
6084
- function persistTurnToTranscript(store, loc, sessionId, turn, onCompact) {
6085
- const key2 = transcriptKey(loc.cwd, loc.agentId);
6086
- const chained = (pendingWrites.get(key2) ?? Promise.resolve()).then(async () => {
6087
- try {
6088
- await persistTurn(store, loc, sessionId, turn);
6089
- const count = (recordCounts.get(key2) ?? 0) + 1;
6090
- recordCounts.set(key2, count);
6091
- if (turn.autoCompact !== void 0) {
6092
- const { autoCompactIfNeeded: autoCompactIfNeeded2 } = await Promise.resolve().then(() => (init_compact_session(), compact_session_exports));
6093
- const fired = await autoCompactIfNeeded2({
6094
- store,
6095
- loc,
6096
- sessionId,
6097
- usageTotal: turn.autoCompact.usageTotal,
6098
- contextWindow: turn.autoCompact.contextWindow,
6099
- turnCount: count,
6100
- summarize: turn.autoCompact.summarize
6101
- });
6102
- if (fired) onCompact?.();
6103
- }
6104
- } catch (cause) {
6105
- const msg = cause instanceof Error ? cause.message : String(cause);
6106
- process.stderr.write(
6107
- `[theokit-sdk] session transcript write failed (${loc.agentId}): ${msg}
6108
- `
6109
- );
6110
- }
6111
- });
6112
- pendingWrites.set(
6113
- key2,
6114
- chained.then(
6115
- () => void 0,
6116
- () => void 0
6117
- )
6118
- );
6119
- }
6120
- async function hydrateSession(agentId, loc) {
6121
- const key2 = transcriptKey(loc.cwd, agentId);
6122
- if (hydratedKeys.has(key2)) return;
6123
- hydratedKeys.add(key2);
6124
- const persisted = await readSessionMessages(loc.store, agentId);
6125
- if (persisted.length === 0) return;
6126
- sessions.set(agentId, persisted);
6127
- }
6128
- async function flushSessionWrites() {
6129
- while (pendingWrites.size > 0) {
6130
- const all = Array.from(pendingWrites.values());
6131
- pendingWrites.clear();
6132
- await Promise.all(all);
6133
- }
6134
- }
6135
- function clearSession(agentId) {
6136
- sessions.delete(agentId);
6137
- }
6138
- function invalidateSessionCache(cwd, agentId) {
6139
- sessions.delete(agentId);
6140
- hydratedKeys.delete(transcriptKey(cwd, agentId));
6141
- }
6142
- function enqueueSessionWrite(cwd, agentId, fn) {
6143
- const key2 = transcriptKey(cwd, agentId);
6144
- const prior = pendingWrites.get(key2) ?? Promise.resolve();
6145
- const result = prior.then(fn);
6146
- pendingWrites.set(
6147
- key2,
6148
- result.then(
6149
- () => void 0,
6150
- () => void 0
6151
- )
6152
- );
6153
- return result;
6154
- }
6155
- var sessions, hydratedKeys, pendingWrites, recordCounts;
6156
- var init_agent_session = __esm({
6157
- "src/internal/session/agent-session.ts"() {
6158
- init_agent_session_store();
6159
- sessions = /* @__PURE__ */ new Map();
6160
- hydratedKeys = /* @__PURE__ */ new Set();
6161
- pendingWrites = /* @__PURE__ */ new Map();
6162
- recordCounts = /* @__PURE__ */ new Map();
6163
- }
6164
- });
6165
-
6166
- // src/internal/session/compact-session.ts
6167
- var compact_session_exports = {};
6168
- __export(compact_session_exports, {
6169
- COMPACT_SUMMARY_MARKER: () => COMPACT_SUMMARY_MARKER,
6170
- COMPACT_USER_MESSAGE_MAX_TOKENS: () => COMPACT_USER_MESSAGE_MAX_TOKENS,
6171
- autoCompactIfNeeded: () => autoCompactIfNeeded,
6172
- buildDefaultSummarizer: () => buildDefaultSummarizer,
6173
- compactSessionTranscript: () => compactSessionTranscript,
6174
- isCompactSummary: () => isCompactSummary,
6175
- resolveSummarizerRoute: () => resolveSummarizerRoute,
6176
- shouldAutoCompact: () => shouldAutoCompact
6177
- });
6178
- function isCompactSummary(content) {
6179
- return content.startsWith(COMPACT_SUMMARY_MARKER) || content.startsWith("[[theokit:goal-continuation]]");
6180
- }
6181
- function plainText(content) {
6182
- if (typeof content === "string") return content;
6183
- if (!Array.isArray(content)) return void 0;
6184
- const texts = content.filter(
6185
- (p) => p !== null && typeof p === "object" && p.type === "text" && typeof p.text === "string"
6186
- ).map((p) => p.text);
6187
- return texts.length > 0 ? texts.join("\n") : void 0;
6062
+ function plainText(content) {
6063
+ if (typeof content === "string") return content;
6064
+ if (!Array.isArray(content)) return void 0;
6065
+ const texts = content.filter(
6066
+ (p) => p !== null && typeof p === "object" && p.type === "text" && typeof p.text === "string"
6067
+ ).map((p) => p.text);
6068
+ return texts.length > 0 ? texts.join("\n") : void 0;
6188
6069
  }
6189
6070
  async function compactSessionTranscript(opts) {
6190
6071
  const prior = await opts.store.readRecords(opts.loc.agentId);
@@ -6321,7 +6202,7 @@ var init_compact_session = __esm({
6321
6202
  init_providers();
6322
6203
  init_compression_model_registry();
6323
6204
  init_compression_summarizer();
6324
- init_agent_session();
6205
+ init_session_cache();
6325
6206
  COMPACT_SUMMARY_MARKER = "[[theokit:compact-summary]]";
6326
6207
  COMPACT_USER_MESSAGE_MAX_TOKENS = 2e4;
6327
6208
  autoCompactAttempts = (() => {
@@ -6332,6 +6213,165 @@ var init_compact_session = __esm({
6332
6213
  })();
6333
6214
  }
6334
6215
  });
6216
+
6217
+ // src/internal/session/agent-session-store.ts
6218
+ function seedTranscript(prior, opts) {
6219
+ return SessionTranscript.fromRecords(prior, opts);
6220
+ }
6221
+ function mapAgentTurn(steps) {
6222
+ const assistant = {};
6223
+ const toolResults = [];
6224
+ const toolCalls = [];
6225
+ for (const step of steps) {
6226
+ if (step.type === "thinkingMessage") assistant.thinking = step.message.text;
6227
+ else if (step.type === "assistantMessage") assistant.text = step.message.text;
6228
+ else if (step.type === "toolCall")
6229
+ toolCalls.push({
6230
+ id: step.message.callId,
6231
+ name: step.message.name,
6232
+ input: step.message.args ?? {}
6233
+ });
6234
+ else
6235
+ toolResults.push({
6236
+ toolUseId: step.message.callId,
6237
+ content: step.message.result,
6238
+ isError: step.message.isError
6239
+ });
6240
+ }
6241
+ if (toolCalls.length > 0) assistant.toolCalls = toolCalls;
6242
+ return { assistant, toolResults };
6243
+ }
6244
+ function hasAssistantContent(a) {
6245
+ return a.text !== void 0 || a.thinking !== void 0 || (a.toolCalls?.length ?? 0) > 0;
6246
+ }
6247
+ function appendConversation(transcript, conversation) {
6248
+ for (const ct of conversation) {
6249
+ if (ct.type !== "agentConversationTurn") continue;
6250
+ const { assistant, toolResults } = mapAgentTurn(ct.turn.steps);
6251
+ if (hasAssistantContent(assistant)) transcript.appendAssistantTurn(assistant);
6252
+ if (toolResults.length > 0) transcript.appendToolResults(toolResults);
6253
+ }
6254
+ }
6255
+ async function readSessionMessages(store, agentId) {
6256
+ const records = await store.readRecords(agentId);
6257
+ return reconstructMessages(records).map(narrowToSessionMessage);
6258
+ }
6259
+ function partToText(p) {
6260
+ if (p.type === "text") return p.text ?? "";
6261
+ if (p.type === "tool_use") return `[tool call] ${p.name ?? ""}`;
6262
+ if (p.type === "tool_result") {
6263
+ const body = typeof p.content === "string" ? p.content : JSON.stringify(p.content);
6264
+ return `[tool result] ${body}`;
6265
+ }
6266
+ return "";
6267
+ }
6268
+ function narrowToSessionMessage(m) {
6269
+ const role = m.role === "user" ? "user" : "assistant";
6270
+ const text = m.content.map(partToText).filter((s) => s.length > 0).join("\n");
6271
+ return { role, text };
6272
+ }
6273
+ function deltaRecords(transcript, priorLength) {
6274
+ return transcript.records().slice(priorLength);
6275
+ }
6276
+ async function persistTurn(store, loc, sessionId, turn) {
6277
+ const prior = await store.readRecords(loc.agentId);
6278
+ const transcript = seedTranscript(prior, { cwd: loc.cwd, sessionId, model: loc.model });
6279
+ transcript.appendUserTurn(turn.userText);
6280
+ appendConversation(transcript, turn.conversation);
6281
+ await store.appendRecords(loc.agentId, deltaRecords(transcript, prior.length));
6282
+ }
6283
+ var init_agent_session_store = __esm({
6284
+ "src/internal/session/agent-session-store.ts"() {
6285
+ init_session_transcript();
6286
+ }
6287
+ });
6288
+
6289
+ // src/internal/session/agent-session.ts
6290
+ function appendSessionMessage(agentId, message) {
6291
+ const existing = sessions.get(agentId) ?? [];
6292
+ existing.push(message);
6293
+ sessions.set(agentId, existing);
6294
+ }
6295
+ function getSessionMessages(agentId) {
6296
+ return sessions.get(agentId) ?? [];
6297
+ }
6298
+ function persistTurnToTranscript(store, loc, sessionId, turn, onCompact) {
6299
+ const key2 = transcriptKey(loc.cwd, loc.agentId);
6300
+ const chained = (pendingWrites.get(key2) ?? Promise.resolve()).then(async () => {
6301
+ try {
6302
+ await persistTurn(store, loc, sessionId, turn);
6303
+ const count = (recordCounts.get(key2) ?? 0) + 1;
6304
+ recordCounts.set(key2, count);
6305
+ if (turn.autoCompact !== void 0) {
6306
+ const { autoCompactIfNeeded: autoCompactIfNeeded2 } = await Promise.resolve().then(() => (init_compact_session(), compact_session_exports));
6307
+ const fired = await autoCompactIfNeeded2({
6308
+ store,
6309
+ loc,
6310
+ sessionId,
6311
+ usageTotal: turn.autoCompact.usageTotal,
6312
+ contextWindow: turn.autoCompact.contextWindow,
6313
+ turnCount: count,
6314
+ summarize: turn.autoCompact.summarize
6315
+ });
6316
+ if (fired) onCompact?.();
6317
+ }
6318
+ } catch (cause) {
6319
+ const msg = cause instanceof Error ? cause.message : String(cause);
6320
+ process.stderr.write(
6321
+ `[theokit-sdk] session transcript write failed (${loc.agentId}): ${msg}
6322
+ `
6323
+ );
6324
+ }
6325
+ });
6326
+ pendingWrites.set(
6327
+ key2,
6328
+ chained.then(
6329
+ () => void 0,
6330
+ () => void 0
6331
+ )
6332
+ );
6333
+ }
6334
+ async function hydrateSession(agentId, loc) {
6335
+ const key2 = transcriptKey(loc.cwd, agentId);
6336
+ if (hydratedKeys.has(key2)) return;
6337
+ hydratedKeys.add(key2);
6338
+ const persisted = await readSessionMessages(loc.store, agentId);
6339
+ if (persisted.length === 0) return;
6340
+ sessions.set(agentId, persisted);
6341
+ }
6342
+ async function flushSessionWrites() {
6343
+ while (pendingWrites.size > 0) {
6344
+ const all = Array.from(pendingWrites.values());
6345
+ pendingWrites.clear();
6346
+ await Promise.all(all);
6347
+ }
6348
+ }
6349
+ function clearSession(agentId) {
6350
+ sessions.delete(agentId);
6351
+ }
6352
+ function enqueueSessionWrite(cwd, agentId, fn) {
6353
+ const key2 = transcriptKey(cwd, agentId);
6354
+ const prior = pendingWrites.get(key2) ?? Promise.resolve();
6355
+ const result = prior.then(fn);
6356
+ pendingWrites.set(
6357
+ key2,
6358
+ result.then(
6359
+ () => void 0,
6360
+ () => void 0
6361
+ )
6362
+ );
6363
+ return result;
6364
+ }
6365
+ var pendingWrites, recordCounts;
6366
+ var init_agent_session = __esm({
6367
+ "src/internal/session/agent-session.ts"() {
6368
+ init_agent_session_store();
6369
+ init_session_cache();
6370
+ init_session_cache();
6371
+ pendingWrites = /* @__PURE__ */ new Map();
6372
+ recordCounts = /* @__PURE__ */ new Map();
6373
+ }
6374
+ });
6335
6375
  async function withToolWhitelist(whitelist, fn) {
6336
6376
  return toolWhitelistStore.run(whitelist, fn);
6337
6377
  }
@@ -6543,7 +6583,9 @@ async function loadDriver(filePath) {
6543
6583
  }
6544
6584
  try {
6545
6585
  const mod = await (driverLoaderOverrides?.nodeSqlite?.() ?? Promise.resolve(
6546
- process.getBuiltinModule?.("node:sqlite") ?? (() => {
6586
+ process.getBuiltinModule?.(
6587
+ "node:sqlite"
6588
+ ) ?? (() => {
6547
6589
  throw new Error("node:sqlite built-in unavailable (Node < 22.3)");
6548
6590
  })()
6549
6591
  ));
@@ -7449,190 +7491,71 @@ var init_index_manager = __esm({
7449
7491
  score: 0,
7450
7492
  textScore: 0,
7451
7493
  snippet: truncateSnippet(String(row.text ?? "")),
7452
- source: String(row.source),
7453
- citation: `${path}:${startLine}-${endLine}`
7454
- };
7455
- });
7456
- }
7457
- // ───── persistence helpers ─────────────────────────────────────────
7458
- loadFilesIndex() {
7459
- const rows = this.db.prepare("SELECT id, path, hash FROM files").all();
7460
- return new Map(rows.map((row) => [row.path, { id: row.id, hash: row.hash }]));
7461
- }
7462
- upsertFile(absPath, relPath, hash, mtimeMs, source = "memory") {
7463
- const stmt = this.db.prepare(
7464
- `INSERT INTO files (path, rel_path, mtime, hash, source) VALUES (?, ?, ?, ?, ?)
7465
- ON CONFLICT(path) DO UPDATE SET hash = excluded.hash, mtime = excluded.mtime, source = excluded.source
7466
- RETURNING id`
7467
- );
7468
- const row = stmt.get(absPath, relPath, Math.floor(mtimeMs), hash, source);
7469
- return row.id;
7470
- }
7471
- deleteChunksForFile(fileId) {
7472
- this.db.prepare("DELETE FROM chunks WHERE file_id = ?").run(fileId);
7473
- }
7474
- insertChunk(fileId, startLine, endLine, text, hash) {
7475
- this.db.prepare(
7476
- "INSERT INTO chunks (file_id, start_line, end_line, text, hash) VALUES (?, ?, ?, ?, ?)"
7477
- ).run(fileId, startLine, endLine, text, hash);
7478
- }
7479
- close() {
7480
- this.db.close();
7481
- }
7482
- };
7483
- }
7484
- });
7485
-
7486
- // src/internal/personality/context.ts
7487
- var context_exports = {};
7488
- __export(context_exports, {
7489
- currentPersonalityContext: () => currentPersonalityContext,
7490
- warnPersonalitySwitchInsideFork: () => warnPersonalitySwitchInsideFork,
7491
- withPersonalityContext: () => withPersonalityContext
7492
- });
7493
- function withPersonalityContext(ctx, fn) {
7494
- return storage.run(ctx, fn);
7495
- }
7496
- function currentPersonalityContext() {
7497
- return storage.getStore();
7498
- }
7499
- function warnPersonalitySwitchInsideFork(agentId) {
7500
- warnOnce(
7501
- `personality-switch-in-fork-${agentId}`,
7502
- `[theokit-sdk] usePersonality is a no-op inside a fork (D168). Subagents inherit the parent's active personality at fork-construction time.`
7503
- );
7504
- }
7505
- var storage;
7506
- var init_context = __esm({
7507
- "src/internal/personality/context.ts"() {
7508
- init_hooks_source();
7509
- storage = new AsyncLocalStorage();
7510
- }
7511
- });
7512
-
7513
- // src/internal/judge/parse-verdict.ts
7514
- function parseVerdict(text) {
7515
- const trimmed = text.trim();
7516
- if (trimmed.startsWith(DONE_PREFIX)) {
7517
- return {
7518
- verdict: "done",
7519
- reason: trimmed.slice(DONE_PREFIX.length).trim(),
7520
- parseFailed: false
7521
- };
7522
- }
7523
- if (trimmed.startsWith(CONTINUE_PREFIX)) {
7524
- return {
7525
- verdict: "continue",
7526
- reason: trimmed.slice(CONTINUE_PREFIX.length).trim(),
7527
- parseFailed: false
7528
- };
7529
- }
7530
- if (trimmed.startsWith(SKIPPED_PREFIX)) {
7531
- return {
7532
- verdict: "skipped",
7533
- reason: trimmed.slice(SKIPPED_PREFIX.length).trim(),
7534
- parseFailed: false
7535
- };
7536
- }
7537
- return {
7538
- verdict: "continue",
7539
- reason: `judge response malformed: "${trimmed.slice(0, 100)}"`,
7540
- parseFailed: true
7541
- };
7542
- }
7543
- var DONE_PREFIX, CONTINUE_PREFIX, SKIPPED_PREFIX;
7544
- var init_parse_verdict = __esm({
7545
- "src/internal/judge/parse-verdict.ts"() {
7546
- DONE_PREFIX = "DONE:";
7547
- CONTINUE_PREFIX = "CONTINUE:";
7548
- SKIPPED_PREFIX = "SKIPPED:";
7549
- }
7550
- });
7551
-
7552
- // src/internal/judge/judge-call.ts
7553
- var judge_call_exports = {};
7554
- __export(judge_call_exports, {
7555
- composeJudgePrompt: () => composeJudgePrompt,
7556
- judgeCallImpl: () => judgeCallImpl
7557
- });
7558
- async function judgeCallImpl(ctx, options, deps) {
7559
- const prompt = composeJudgePrompt(ctx);
7560
- const apiKey = options?.apiKey ?? process.env.OPENROUTER_API_KEY;
7561
- if (apiKey === void 0) {
7562
- return {
7563
- verdict: "continue",
7564
- reason: "judge unavailable: OPENROUTER_API_KEY missing and no override passed via options.apiKey",
7565
- parseFailed: true
7566
- };
7567
- }
7568
- const judgeModel = options?.judgeModel ?? "openai/gpt-4o-mini";
7569
- let auxAgent;
7570
- try {
7571
- auxAgent = await deps.create({
7572
- apiKey,
7573
- model: { id: judgeModel },
7574
- tools: [],
7575
- local: {},
7576
- metadata: { forkOrigin: "judge" }
7577
- });
7578
- const run = await auxAgent.send(prompt);
7579
- const result = await run.wait();
7580
- return parseVerdict(result.result ?? "");
7581
- } catch (err) {
7582
- return {
7583
- verdict: "continue",
7584
- reason: `judge call failed: ${err instanceof Error ? err.message : String(err)}`,
7585
- parseFailed: true
7586
- };
7587
- } finally {
7588
- if (auxAgent !== void 0) {
7589
- try {
7590
- await auxAgent.dispose();
7591
- } catch {
7494
+ source: String(row.source),
7495
+ citation: `${path}:${startLine}-${endLine}`
7496
+ };
7497
+ });
7592
7498
  }
7593
- }
7499
+ // ───── persistence helpers ─────────────────────────────────────────
7500
+ loadFilesIndex() {
7501
+ const rows = this.db.prepare("SELECT id, path, hash FROM files").all();
7502
+ return new Map(rows.map((row) => [row.path, { id: row.id, hash: row.hash }]));
7503
+ }
7504
+ upsertFile(absPath, relPath, hash, mtimeMs, source = "memory") {
7505
+ const stmt = this.db.prepare(
7506
+ `INSERT INTO files (path, rel_path, mtime, hash, source) VALUES (?, ?, ?, ?, ?)
7507
+ ON CONFLICT(path) DO UPDATE SET hash = excluded.hash, mtime = excluded.mtime, source = excluded.source
7508
+ RETURNING id`
7509
+ );
7510
+ const row = stmt.get(absPath, relPath, Math.floor(mtimeMs), hash, source);
7511
+ return row.id;
7512
+ }
7513
+ deleteChunksForFile(fileId) {
7514
+ this.db.prepare("DELETE FROM chunks WHERE file_id = ?").run(fileId);
7515
+ }
7516
+ insertChunk(fileId, startLine, endLine, text, hash) {
7517
+ this.db.prepare(
7518
+ "INSERT INTO chunks (file_id, start_line, end_line, text, hash) VALUES (?, ?, ?, ?, ?)"
7519
+ ).run(fileId, startLine, endLine, text, hash);
7520
+ }
7521
+ close() {
7522
+ this.db.close();
7523
+ }
7524
+ };
7594
7525
  }
7595
- }
7596
- function composeJudgePrompt(ctx) {
7597
- const subgoals = ctx.subgoals !== void 0 && ctx.subgoals.length > 0 ? ctx.subgoals.join(", ") : "(none)";
7598
- return `You are a goal judge. Determine if this goal is satisfied.
7599
-
7600
- Goal: ${ctx.goal}
7601
- Subgoals: ${subgoals}
7602
- Last agent response: ${ctx.lastResponse}
7603
-
7604
- Respond with EXACTLY one of:
7605
- - DONE: <reason>
7606
- - CONTINUE: <what's left>
7607
- - SKIPPED: <why not applicable>
7526
+ });
7608
7527
 
7609
- Be strict. If unclear, prefer CONTINUE.`;
7528
+ // src/internal/personality/context.ts
7529
+ var context_exports = {};
7530
+ __export(context_exports, {
7531
+ currentPersonalityContext: () => currentPersonalityContext,
7532
+ warnPersonalitySwitchInsideFork: () => warnPersonalitySwitchInsideFork,
7533
+ withPersonalityContext: () => withPersonalityContext
7534
+ });
7535
+ function withPersonalityContext(ctx, fn) {
7536
+ return storage.run(ctx, fn);
7610
7537
  }
7611
- var init_judge_call = __esm({
7612
- "src/internal/judge/judge-call.ts"() {
7613
- init_parse_verdict();
7538
+ function currentPersonalityContext() {
7539
+ return storage.getStore();
7540
+ }
7541
+ function warnPersonalitySwitchInsideFork(agentId) {
7542
+ warnOnce(
7543
+ `personality-switch-in-fork-${agentId}`,
7544
+ `[theokit-sdk] usePersonality is a no-op inside a fork (D168). Subagents inherit the parent's active personality at fork-construction time.`
7545
+ );
7546
+ }
7547
+ var storage;
7548
+ var init_context = __esm({
7549
+ "src/internal/personality/context.ts"() {
7550
+ init_hooks_source();
7551
+ storage = new AsyncLocalStorage();
7614
7552
  }
7615
7553
  });
7616
7554
 
7617
- // src/goal-loop.ts
7618
- function runGoalLoop(agent, goal, options, depsOverride) {
7619
- async function* wrap() {
7620
- const { runUntilImpl: runUntilImpl2 } = await Promise.resolve().then(() => (init_run_until(), run_until_exports));
7621
- const deps = depsOverride ?? await (async () => {
7622
- const { judgeCallImpl: judgeCallImpl2 } = await Promise.resolve().then(() => (init_judge_call(), judge_call_exports));
7623
- const { getAgentFacade: getAgentFacade2 } = await Promise.resolve().then(() => (init_agent_factory_registry(), agent_factory_registry_exports));
7624
- const create = getAgentFacade2().create;
7625
- return {
7626
- judge: (ctx, opts) => judgeCallImpl2(ctx, opts, { create })
7627
- };
7628
- })();
7629
- return yield* runUntilImpl2(agent, goal, options, deps);
7630
- }
7631
- return wrap();
7632
- }
7555
+ // src/internal/runtime/lifecycle/goal-marker.ts
7633
7556
  var GOAL_CONTINUATION_MARKER;
7634
- var init_goal_loop = __esm({
7635
- "src/goal-loop.ts"() {
7557
+ var init_goal_marker = __esm({
7558
+ "src/internal/runtime/lifecycle/goal-marker.ts"() {
7636
7559
  GOAL_CONTINUATION_MARKER = "[[theokit:goal-continuation]]";
7637
7560
  }
7638
7561
  });
@@ -7806,7 +7729,111 @@ ${lastResponse.slice(-1e3)}`
7806
7729
  }
7807
7730
  var init_run_until = __esm({
7808
7731
  "src/internal/runtime/lifecycle/run-until.ts"() {
7809
- init_goal_loop();
7732
+ init_goal_marker();
7733
+ }
7734
+ });
7735
+
7736
+ // src/internal/judge/parse-verdict.ts
7737
+ function parseVerdict(text) {
7738
+ const trimmed = text.trim();
7739
+ if (trimmed.startsWith(DONE_PREFIX)) {
7740
+ return {
7741
+ verdict: "done",
7742
+ reason: trimmed.slice(DONE_PREFIX.length).trim(),
7743
+ parseFailed: false
7744
+ };
7745
+ }
7746
+ if (trimmed.startsWith(CONTINUE_PREFIX)) {
7747
+ return {
7748
+ verdict: "continue",
7749
+ reason: trimmed.slice(CONTINUE_PREFIX.length).trim(),
7750
+ parseFailed: false
7751
+ };
7752
+ }
7753
+ if (trimmed.startsWith(SKIPPED_PREFIX)) {
7754
+ return {
7755
+ verdict: "skipped",
7756
+ reason: trimmed.slice(SKIPPED_PREFIX.length).trim(),
7757
+ parseFailed: false
7758
+ };
7759
+ }
7760
+ return {
7761
+ verdict: "continue",
7762
+ reason: `judge response malformed: "${trimmed.slice(0, 100)}"`,
7763
+ parseFailed: true
7764
+ };
7765
+ }
7766
+ var DONE_PREFIX, CONTINUE_PREFIX, SKIPPED_PREFIX;
7767
+ var init_parse_verdict = __esm({
7768
+ "src/internal/judge/parse-verdict.ts"() {
7769
+ DONE_PREFIX = "DONE:";
7770
+ CONTINUE_PREFIX = "CONTINUE:";
7771
+ SKIPPED_PREFIX = "SKIPPED:";
7772
+ }
7773
+ });
7774
+
7775
+ // src/internal/judge/judge-call.ts
7776
+ var judge_call_exports = {};
7777
+ __export(judge_call_exports, {
7778
+ composeJudgePrompt: () => composeJudgePrompt,
7779
+ judgeCallImpl: () => judgeCallImpl
7780
+ });
7781
+ async function judgeCallImpl(ctx, options, deps) {
7782
+ const prompt = composeJudgePrompt(ctx);
7783
+ const apiKey = options?.apiKey ?? process.env.OPENROUTER_API_KEY;
7784
+ if (apiKey === void 0) {
7785
+ return {
7786
+ verdict: "continue",
7787
+ reason: "judge unavailable: OPENROUTER_API_KEY missing and no override passed via options.apiKey",
7788
+ parseFailed: true
7789
+ };
7790
+ }
7791
+ const judgeModel = options?.judgeModel ?? "openai/gpt-4o-mini";
7792
+ let auxAgent;
7793
+ try {
7794
+ auxAgent = await deps.create({
7795
+ apiKey,
7796
+ model: { id: judgeModel },
7797
+ tools: [],
7798
+ local: {},
7799
+ metadata: { forkOrigin: "judge" }
7800
+ });
7801
+ const run = await auxAgent.send(prompt);
7802
+ const result = await run.wait();
7803
+ return parseVerdict(result.result ?? "");
7804
+ } catch (err) {
7805
+ return {
7806
+ verdict: "continue",
7807
+ reason: `judge call failed: ${err instanceof Error ? err.message : String(err)}`,
7808
+ parseFailed: true
7809
+ };
7810
+ } finally {
7811
+ if (auxAgent !== void 0) {
7812
+ try {
7813
+ await auxAgent.dispose();
7814
+ } catch {
7815
+ }
7816
+ }
7817
+ }
7818
+ }
7819
+ function composeJudgePrompt(ctx) {
7820
+ const subgoals = ctx.subgoals !== void 0 && ctx.subgoals.length > 0 ? ctx.subgoals.join(", ") : "(none)";
7821
+ return `You are a goal judge. Determine if this goal is satisfied.
7822
+
7823
+ Goal: ${ctx.goal}
7824
+ Subgoals: ${subgoals}
7825
+ Last agent response: ${ctx.lastResponse}
7826
+
7827
+ Respond with EXACTLY one of:
7828
+ - DONE: <reason>
7829
+ - CONTINUE: <what's left>
7830
+ - SKIPPED: <why not applicable>
7831
+
7832
+ Be strict. If unclear, prefer CONTINUE.`;
7833
+ }
7834
+ var init_judge_call = __esm({
7835
+ "src/internal/judge/judge-call.ts"() {
7836
+ init_parse_verdict();
7810
7837
  }
7811
7838
  });
7812
7839
 
@@ -13274,6 +13301,7 @@ function parseDecisionFromStdout(stdout) {
13274
13301
  }
13275
13302
 
13276
13303
  // src/internal/runtime/lifecycle/post-run-lifecycle.ts
13304
+ init_compaction();
13277
13305
  init_run_events();
13278
13306
  init_session_summary_writer();
13279
13307
  init_catalog_loader();
@@ -13301,6 +13329,12 @@ function resolveActiveMemorySummaryForSend(legacySummary, portPathEnabled) {
13301
13329
  return legacySummary;
13302
13330
  }
13303
13331
 
13332
+ // src/internal/runtime/lifecycle/context-budget-event.ts
13333
+ function buildContextBudgetEvent(model, resolved) {
13334
+ if (resolved.source !== "fallback") return void 0;
13335
+ return { type: "compaction_fallback", model, window: resolved.window };
13336
+ }
13337
+
13304
13338
  // src/internal/runtime/lifecycle/post-run-lifecycle.ts
13305
13339
  async function runPostRunLifecycle(inputs) {
13306
13340
  const {
@@ -13326,18 +13360,15 @@ async function runPostRunLifecycle(inputs) {
13326
13360
  appendSessionMessage(agentId, { role: "assistant", text: result.result });
13327
13361
  }
13328
13362
  const conversation = await safeConversation(run);
13329
- const contextWindow = getCatalogModelInfo(model)?.limit?.context;
13330
- if (contextWindow === void 0) {
13331
- const g = globalThis;
13332
- const sym = /* @__PURE__ */ Symbol.for("theokit-sdk.compact.no-cw-warned");
13333
- const warned3 = g[sym] ??= /* @__PURE__ */ new Set();
13334
- if (!warned3.has(model)) {
13335
- warned3.add(model);
13336
- process.stderr.write(
13337
- `[theokit-sdk] auto-compaction disabled: model "${model}" has no context-window entry in the catalog
13338
- `
13339
- );
13340
- }
13363
+ const resolvedWindow = resolveEffectiveContextWindow({
13364
+ catalog: getCatalogModelInfo(model)?.limit?.context,
13365
+ margin: CONTEXT_WINDOW_MARGIN,
13366
+ floor: CONTEXT_WINDOW_FLOOR
13367
+ });
13368
+ const contextWindow = resolvedWindow.window;
13369
+ const budgetEvent = buildContextBudgetEvent(model, resolvedWindow);
13370
+ if (budgetEvent !== void 0 && onRunEvent !== void 0) {
13371
+ emitRunEvent(onRunEvent, budgetEvent);
13341
13372
  }
13342
13373
  const lastRequestUsage = result.usage?.requests?.at(-1)?.totalTokens;
13343
13374
  const usageForTrigger = lastRequestUsage ?? result.usage?.totalTokens;
@@ -18309,6 +18340,71 @@ function registerPluginProviderProfiles(entries) {
18309
18340
 
18310
18341
  // src/internal/local-agent/real-local-run.ts
18311
18342
  init_async_local_storage();
18343
+
18344
+ // src/internal/local-agent/mcp-pool.ts
18345
+ var DEFAULT_IDLE_TTL_MS = 6e5;
18346
+ function configKey(config) {
18347
+ return JSON.stringify(
18348
+ config,
18349
+ (_k, v) => v !== null && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(
18350
+ Object.entries(v).sort(([a], [b]) => a < b ? -1 : 1)
18351
+ ) : v
18352
+ );
18353
+ }
18354
+ var McpClientPool = class {
18355
+ entries = /* @__PURE__ */ new Map();
18356
+ idleTtlMs;
18357
+ now;
18358
+ constructor(options = {}) {
18359
+ this.idleTtlMs = options.idleTtlMs ?? DEFAULT_IDLE_TTL_MS;
18360
+ this.now = options.now ?? Date.now;
18361
+ }
18362
+ /**
18363
+ * Return the pooled client for `(sessionId, serverName, config)`, creating it via `factory` on
18364
+ * first use. Every call refreshes idleness — the TTL measures time SINCE LAST USE, not age.
18365
+ *
18366
+ * Synchronous by design: `createMcpClient` is itself synchronous (the handshake happens later, on
18367
+ * `initialize`), so there is no `await` between the lookup and the insert and two concurrent runs
18368
+ * in the same session cannot both miss the cache.
18369
+ */
18370
+ acquire(sessionId, serverName, config, factory) {
18371
+ const key2 = `${sessionId}\0${serverName}\0${configKey(config)}`;
18372
+ const existing = this.entries.get(key2);
18373
+ if (existing !== void 0) {
18374
+ existing.lastUsedAt = this.now();
18375
+ return existing.client;
18376
+ }
18377
+ const client = factory();
18378
+ this.entries.set(key2, { client, sessionId, lastUsedAt: this.now() });
18379
+ return client;
18380
+ }
18381
+ /**
18382
+ * Close and forget every client of ONE session. Scoped deliberately: clearing the whole map would
18383
+ * tear down the servers of every concurrent conversation.
18384
+ */
18385
+ disposeSession(sessionId, close) {
18386
+ for (const [key2, entry] of this.entries) {
18387
+ if (entry.sessionId !== sessionId) continue;
18388
+ close(entry.client);
18389
+ this.entries.delete(key2);
18390
+ }
18391
+ }
18392
+ /** Close and forget every client idle for longer than the TTL. */
18393
+ reapIdle(close) {
18394
+ const cutoff = this.now() - this.idleTtlMs;
18395
+ for (const [key2, entry] of this.entries) {
18396
+ if (entry.lastUsedAt > cutoff) continue;
18397
+ close(entry.client);
18398
+ this.entries.delete(key2);
18399
+ }
18400
+ }
18401
+ /** Live pooled-client count — for observability and tests. */
18402
+ size() {
18403
+ return this.entries.size;
18404
+ }
18405
+ };
18406
+
18407
+ // src/internal/local-agent/real-local-run.ts
18312
18408
  init_real_local_run_provider();
18313
18409
 
18314
18410
  // src/a2a/subagent.ts
@@ -18727,12 +18823,23 @@ function buildLoopInputs(options, runId, userText, userImages) {
18727
18823
  ...options.agentOptions.memoryProvider !== void 0 ? { memoryProvider: options.agentOptions.memoryProvider } : {}
18728
18824
  };
18729
18825
  }
18826
+ var sessionMcpPool = new McpClientPool();
18827
+ function disposeSessionMcpClients(agentId) {
18828
+ sessionMcpPool.disposeSession(agentId, (client) => {
18829
+ void client.close();
18830
+ });
18831
+ }
18730
18832
  function buildMcpMap(options) {
18731
18833
  const map = /* @__PURE__ */ new Map();
18732
18834
  const inline = options.sendOptions.mcpServers ?? options.agentOptions.mcpServers;
18733
18835
  if (inline === void 0) return map;
18836
+ const pooled = options.agentOptions.mcpLifecycle === "session";
18837
+ if (pooled) sessionMcpPool.reapIdle((c) => void c.close());
18734
18838
  for (const [name, config] of Object.entries(inline)) {
18735
- map.set(name, createMcpClient(name, config));
18839
+ map.set(
18840
+ name,
18841
+ pooled ? sessionMcpPool.acquire(options.agentId, name, config, () => createMcpClient(name, config)) : createMcpClient(name, config)
18842
+ );
18736
18843
  }
18737
18844
  return map;
18738
18845
  }
@@ -19861,7 +19968,8 @@ var LocalAgentMemory = class {
19861
19968
  const message = cause instanceof Error ? cause.message : String(cause);
19862
19969
  const g = globalThis;
19863
19970
  const sym = /* @__PURE__ */ Symbol.for("theokit-sdk.memory.warned");
19864
- const warned3 = g[sym] ??= /* @__PURE__ */ new Set();
19971
+ g[sym] ??= /* @__PURE__ */ new Set();
19972
+ const warned3 = g[sym];
19865
19973
  if (!warned3.has(message)) {
19866
19974
  warned3.add(message);
19867
19975
  process.stderr.write(`[theokit-sdk] memory tools unavailable: ${message}
@@ -21046,6 +21154,7 @@ var LocalAgent = class {
21046
21154
  liveAgentRegistry.forget(this.agentId);
21047
21155
  this.lifecycleAbortController.abort();
21048
21156
  await withCwdMutex(`agent-send:${this.agentId}`, () => Promise.resolve());
21157
+ disposeSessionMcpClients(this.agentId);
21049
21158
  await flushSessionWrites();
21050
21159
  await flushRegistrySaves(this.workspaceCwd);
21051
21160
  }
@@ -21346,8 +21455,8 @@ async function getRegisteredAgentOrThrow(agentId) {
21346
21455
  // src/agent.ts
21347
21456
  init_errors();
21348
21457
  init_discovery();
21349
- init_agent_session();
21350
21458
  init_agent_factory_registry();
21459
+ init_agent_session();
21351
21460
  var streamObjectImport;
21352
21461
  var Agent = class _Agent {
21353
21462
  constructor() {
@@ -21639,26 +21748,26 @@ var Agent = class _Agent {
21639
21748
  reg = getRegisteredAgent(agentId);
21640
21749
  }
21641
21750
  if (reg === void 0 || reg.runtime !== "local") {
21642
- throw new UnknownAgentError(`No local agent "${agentId}" registered \u2014 compact targets local sessions.`);
21751
+ throw new UnknownAgentError(
21752
+ `No local agent "${agentId}" registered \u2014 compact targets local sessions.`
21753
+ );
21643
21754
  }
21644
- const cwd = reg.cwd ?? process.cwd();
21645
- const optModel = reg.options.model;
21646
- const model = reg.model?.id ?? (typeof optModel === "string" ? optModel : optModel?.id) ?? "unknown";
21647
21755
  const { compactSessionTranscript: compactSessionTranscript2, buildDefaultSummarizer: buildDefaultSummarizer2 } = await Promise.resolve().then(() => (init_compact_session(), compact_session_exports));
21648
- const { FsSessionStore: FsSessionStore2 } = await Promise.resolve().then(() => (init_fs_session_store(), fs_session_store_exports));
21649
- const { defaultBaseDir: defaultBaseDir2, expandTilde: expandTilde2 } = await Promise.resolve().then(() => (init_session_transcript(), session_transcript_exports));
21650
- const baseDir = reg.options.local?.baseDir !== void 0 ? expandTilde2(reg.options.local.baseDir) : defaultBaseDir2();
21651
- const store = new FsSessionStore2({ baseDir, cwd });
21652
- return enqueueSessionWrite(cwd, agentId, () => compactSessionTranscript2({
21653
- store,
21654
- loc: { cwd, agentId, model },
21655
- sessionId: agentId,
21656
- trigger: options.trigger ?? "manual",
21657
- summarize: options.summarize ?? buildDefaultSummarizer2({
21658
- agentModel: model,
21659
- ...reg.options.apiKey !== void 0 ? { apiKey: reg.options.apiKey } : {}
21756
+ const { cwd, model, store } = await abrirStoreLocal(reg);
21757
+ return enqueueSessionWrite(
21758
+ cwd,
21759
+ agentId,
21760
+ () => compactSessionTranscript2({
21761
+ store,
21762
+ loc: { cwd, agentId, model },
21763
+ sessionId: agentId,
21764
+ trigger: options.trigger ?? "manual",
21765
+ summarize: options.summarize ?? buildDefaultSummarizer2({
21766
+ agentModel: model,
21767
+ ...reg.options.apiKey !== void 0 ? { apiKey: reg.options.apiKey } : {}
21768
+ })
21660
21769
  })
21661
- }));
21770
+ );
21662
21771
  }
21663
21772
  /**
21664
21773
  * M51 — inject a SYNTHETIC user+assistant pair into a LOCAL session's persisted transcript WITHOUT
@@ -21675,16 +21784,12 @@ var Agent = class _Agent {
21675
21784
  reg = getRegisteredAgent(agentId);
21676
21785
  }
21677
21786
  if (reg === void 0 || reg.runtime !== "local") {
21678
- throw new UnknownAgentError(`No local agent "${agentId}" registered \u2014 injectSessionTurn targets local sessions.`);
21787
+ throw new UnknownAgentError(
21788
+ `No local agent "${agentId}" registered \u2014 injectSessionTurn targets local sessions.`
21789
+ );
21679
21790
  }
21680
- const cwd = reg.cwd ?? process.cwd();
21681
- const optModel = reg.options.model;
21682
- const model = reg.model?.id ?? (typeof optModel === "string" ? optModel : optModel?.id) ?? "unknown";
21683
21791
  const { injectSessionTurn: injectSessionTurn2 } = await Promise.resolve().then(() => (init_inject_session(), inject_session_exports));
21684
- const { FsSessionStore: FsSessionStore2 } = await Promise.resolve().then(() => (init_fs_session_store(), fs_session_store_exports));
21685
- const { defaultBaseDir: defaultBaseDir2, expandTilde: expandTilde2 } = await Promise.resolve().then(() => (init_session_transcript(), session_transcript_exports));
21686
- const baseDir = reg.options.local?.baseDir !== void 0 ? expandTilde2(reg.options.local.baseDir) : defaultBaseDir2();
21687
- const store = new FsSessionStore2({ baseDir, cwd });
21792
+ const { cwd, model, store } = await abrirStoreLocal(reg);
21688
21793
  await injectSessionTurn2({
21689
21794
  store,
21690
21795
  loc: { cwd, agentId, model },
@@ -21726,6 +21831,15 @@ setAgentFacade({
21726
21831
  resume: (agentId, options) => Agent.resume(agentId, options),
21727
21832
  batch: (prompts, options) => Agent.batch(prompts, options)
21728
21833
  });
21834
+ async function abrirStoreLocal(reg) {
21835
+ const cwd = reg.cwd ?? process.cwd();
21836
+ const optModel = reg.options.model;
21837
+ const model = reg.model?.id ?? (typeof optModel === "string" ? optModel : optModel?.id) ?? "unknown";
21838
+ const { FsSessionStore: FsSessionStore2 } = await Promise.resolve().then(() => (init_fs_session_store(), fs_session_store_exports));
21839
+ const { defaultBaseDir: defaultBaseDir2, expandTilde: expandTilde2 } = await Promise.resolve().then(() => (init_session_transcript(), session_transcript_exports));
21840
+ const baseDir = reg.options.local?.baseDir !== void 0 ? expandTilde2(reg.options.local.baseDir) : defaultBaseDir2();
21841
+ return { cwd, model, store: new FsSessionStore2({ baseDir, cwd }) };
21842
+ }
21729
21843
 
21730
21844
  // src/agent-factory.ts
21731
21845
  function createAgentFactory(common) {
@@ -22840,7 +22954,24 @@ var EventBus = class {
22840
22954
 
22841
22955
  // src/index.ts
22842
22956
  init_generate_object();
22843
- init_goal_loop();
22957
+
22958
+ // src/goal-loop.ts
22959
+ init_goal_marker();
22960
+ function runGoalLoop(agent, goal, options, depsOverride) {
22961
+ async function* wrap() {
22962
+ const { runUntilImpl: runUntilImpl2 } = await Promise.resolve().then(() => (init_run_until(), run_until_exports));
22963
+ const deps = depsOverride ?? await (async () => {
22964
+ const { judgeCallImpl: judgeCallImpl2 } = await Promise.resolve().then(() => (init_judge_call(), judge_call_exports));
22965
+ const { getAgentFacade: getAgentFacade2 } = await Promise.resolve().then(() => (init_agent_factory_registry(), agent_factory_registry_exports));
22966
+ const create = getAgentFacade2().create;
22967
+ return {
22968
+ judge: (ctx, opts) => judgeCallImpl2(ctx, opts, { create })
22969
+ };
22970
+ })();
22971
+ return yield* runUntilImpl2(agent, goal, options, deps);
22972
+ }
22973
+ return wrap();
22974
+ }
22844
22975
 
22845
22976
  // src/internal/budget/tracker/budget-tracker-counter.ts
22846
22977
  function createCounterBudgetTracker(options = {}) {