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