@theokit/sdk 4.22.0 → 4.24.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 (63) hide show
  1. package/dist/auth/index.cjs +29 -5
  2. package/dist/auth/index.cjs.map +1 -1
  3. package/dist/auth/index.js +29 -5
  4. package/dist/auth/index.js.map +1 -1
  5. package/dist/compaction.cjs +31 -0
  6. package/dist/compaction.cjs.map +1 -1
  7. package/dist/compaction.d.cts +79 -0
  8. package/dist/compaction.d.ts +79 -0
  9. package/dist/compaction.js +28 -1
  10. package/dist/compaction.js.map +1 -1
  11. package/dist/{cron-Bhdyjl0B.d.ts → cron-B_NkE_VM.d.ts} +15 -1
  12. package/dist/{cron-M2Xz7lq2.d.cts → cron-x3muPNgg.d.cts} +15 -1
  13. package/dist/cron.cjs +467 -337
  14. package/dist/cron.cjs.map +1 -1
  15. package/dist/cron.d.cts +2 -2
  16. package/dist/cron.d.ts +2 -2
  17. package/dist/cron.js +467 -337
  18. package/dist/cron.js.map +1 -1
  19. package/dist/{errors-gE8612p9.d.cts → errors-BdL-buYn.d.cts} +1 -1
  20. package/dist/{errors-CG2RpeW-.d.ts → errors-DHZtSNnj.d.ts} +1 -1
  21. package/dist/errors.d.cts +2 -2
  22. package/dist/eval.cjs +467 -337
  23. package/dist/eval.cjs.map +1 -1
  24. package/dist/eval.js +467 -337
  25. package/dist/eval.js.map +1 -1
  26. package/dist/index.cjs +640 -508
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +21 -12
  29. package/dist/index.d.ts +21 -12
  30. package/dist/index.js +640 -508
  31. package/dist/index.js.map +1 -1
  32. package/dist/internal/auth/credential-store.d.ts +20 -3
  33. package/dist/internal/global-singleton.d.ts +13 -0
  34. package/dist/internal/llm/openai-messages.d.ts +2 -0
  35. package/dist/internal/local-agent/mcp-pool.d.ts +41 -0
  36. package/dist/internal/local-agent/real-local-run.d.ts +2 -0
  37. package/dist/internal/persistence/index.cjs +3 -1
  38. package/dist/internal/persistence/index.cjs.map +1 -1
  39. package/dist/internal/persistence/index.js +3 -1
  40. package/dist/internal/persistence/index.js.map +1 -1
  41. package/dist/internal/providers/registry.d.ts +1 -0
  42. package/dist/internal/runtime/lifecycle/context-budget-event.d.ts +25 -0
  43. package/dist/internal/runtime/lifecycle/goal-marker.d.ts +13 -0
  44. package/dist/internal/runtime/lifecycle/run-until.d.ts +0 -1
  45. package/dist/internal/session/agent-session.d.ts +1 -1
  46. package/dist/internal/session/session-cache.d.ts +20 -0
  47. package/dist/models.cjs +22 -20
  48. package/dist/models.cjs.map +1 -1
  49. package/dist/models.js +22 -20
  50. package/dist/models.js.map +1 -1
  51. package/dist/persistence.cjs +3 -1
  52. package/dist/persistence.cjs.map +1 -1
  53. package/dist/persistence.js +3 -1
  54. package/dist/persistence.js.map +1 -1
  55. package/dist/provider-catalog.json +144 -469
  56. package/dist/{run-DFM1H2jW.d.cts → run-OJbGyweZ.d.cts} +20 -1
  57. package/dist/{run-DFM1H2jW.d.ts → run-OJbGyweZ.d.ts} +20 -1
  58. package/dist/types/agent.d.ts +14 -0
  59. package/dist/types/run-events.d.ts +20 -1
  60. package/dist/workflow.cjs.map +1 -1
  61. package/dist/workflow.js.map +1 -1
  62. package/package.json +2 -2
  63. 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({
@@ -2348,11 +2379,12 @@ function writeCredential(cred, config, env = {}) {
2348
2379
  var CredentialError, apiFileSchema, oauthFileSchema, fileSchema;
2349
2380
  var init_credential_store = __esm({
2350
2381
  "src/internal/auth/credential-store.ts"() {
2351
- CredentialError = class extends Error {
2352
- constructor(message) {
2353
- super(message);
2354
- this.name = "CredentialError";
2355
- }
2382
+ init_errors();
2383
+ CredentialError = class extends exports.AuthenticationError {
2384
+ // Field, not an assignment in the constructor: `AuthenticationError.name` is `override readonly`
2385
+ // (`errors.ts:174`), so `this.name = …` does not compile. Caught by `tsc`, not by vitest — the
2386
+ // suite was green with the broken assignment because the transpiler strips the type.
2387
+ name = "CredentialError";
2356
2388
  };
2357
2389
  apiFileSchema = zod.z.object({
2358
2390
  type: zod.z.literal("api").optional(),
@@ -2774,12 +2806,6 @@ var init_builtin = __esm({
2774
2806
  })();
2775
2807
  }
2776
2808
  });
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
2809
  function pluginsRoot() {
2784
2810
  return path.join(os.homedir(), ".theokit", "plugins", "model-providers");
2785
2811
  }
@@ -2871,8 +2897,9 @@ async function loadOne(dir, entryName) {
2871
2897
  var discoveryState;
2872
2898
  var init_discovery = __esm({
2873
2899
  "src/internal/providers/discovery.ts"() {
2900
+ init_global_singleton();
2874
2901
  init_registry();
2875
- discoveryState = globalSingleton3("theokit-sdk.providers.discovered", () => ({
2902
+ discoveryState = globalSingleton("theokit-sdk.providers.discovered", () => ({
2876
2903
  done: false
2877
2904
  }));
2878
2905
  }
@@ -4549,6 +4576,68 @@ var init_hermes_tool_extract = __esm({
4549
4576
  }
4550
4577
  });
4551
4578
 
4579
+ // src/internal/llm/openai-messages.ts
4580
+ function toOpenAIMessages(message) {
4581
+ if (message.role === "system") return [systemMessage(message)];
4582
+ if (message.role === "user") return userOrToolMessages(message);
4583
+ return [assistantMessage(message)];
4584
+ }
4585
+ function systemMessage(message) {
4586
+ return { role: "system", content: joinTextParts2(message) };
4587
+ }
4588
+ function joinTextParts2(message) {
4589
+ return message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
4590
+ }
4591
+ function userOrToolMessages(message) {
4592
+ const out = [];
4593
+ for (const part of message.content) {
4594
+ if (part.type === "tool_result") {
4595
+ out.push({
4596
+ role: "tool",
4597
+ tool_call_id: part.toolUseId,
4598
+ // SE7 — this wire's tool role is string-only: text blocks flatten; an
4599
+ // image block fails fast (ConfigurationError).
4600
+ content: toStringToolResultContent(part.content, "openai")
4601
+ });
4602
+ }
4603
+ }
4604
+ const userText = joinTextParts2(message);
4605
+ const imageParts = message.content.filter(
4606
+ (p) => p.type === "image"
4607
+ );
4608
+ if (imageParts.length > 0) {
4609
+ const content = [];
4610
+ if (userText.length > 0) content.push({ type: "text", text: userText });
4611
+ for (const img of imageParts) {
4612
+ const url = img.source.type === "base64" ? `data:${img.source.media_type};base64,${img.source.data}` : img.source.url;
4613
+ content.push({ type: "image_url", image_url: { url } });
4614
+ }
4615
+ out.push({ role: "user", content });
4616
+ } else if (userText.length > 0) {
4617
+ out.push({ role: "user", content: userText });
4618
+ }
4619
+ return out;
4620
+ }
4621
+ function assistantMessage(message) {
4622
+ const text = joinTextParts2(message);
4623
+ const toolCalls = message.content.filter((part) => part.type === "tool_use").map((part) => {
4624
+ const tc = part;
4625
+ return {
4626
+ id: tc.id,
4627
+ type: "function",
4628
+ function: { name: tc.name, arguments: JSON.stringify(tc.input) }
4629
+ };
4630
+ });
4631
+ const result = { role: "assistant", content: text };
4632
+ if (toolCalls.length > 0) result.tool_calls = toolCalls;
4633
+ return result;
4634
+ }
4635
+ var init_openai_messages = __esm({
4636
+ "src/internal/llm/openai-messages.ts"() {
4637
+ init_tool_result_content();
4638
+ }
4639
+ });
4640
+
4552
4641
  // src/internal/llm/openai.ts
4553
4642
  function deriveChatPath(baseUrl) {
4554
4643
  try {
@@ -4610,61 +4699,6 @@ function encodeOpenAIResponseFormat(rf) {
4610
4699
  }
4611
4700
  };
4612
4701
  }
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
4702
  var OpenAIClient, OpenAIStreamAccumulator, openAISystemText;
4669
4703
  var init_openai2 = __esm({
4670
4704
  "src/internal/llm/openai.ts"() {
@@ -4673,8 +4707,8 @@ var init_openai2 = __esm({
4673
4707
  init_openai_compatible2();
4674
4708
  init_finish();
4675
4709
  init_hermes_tool_extract();
4710
+ init_openai_messages();
4676
4711
  init_sse();
4677
- init_tool_result_content();
4678
4712
  OpenAIClient = class {
4679
4713
  constructor(options) {
4680
4714
  this.options = options;
@@ -5763,6 +5797,14 @@ function selectTransport(profile, apiKey) {
5763
5797
  const ctx = { apiKey };
5764
5798
  return { fetch: profile.transform.fetch?.(ctx), headers: profile.transform.headers?.(ctx) };
5765
5799
  };
5800
+ const comTransform = (opts, criar) => {
5801
+ const t = applyTransform();
5802
+ assertOAuthResolved(t);
5803
+ if (t.fetch !== void 0) opts.fetch = t.fetch;
5804
+ const merged = profile.extraHeaders !== void 0 || t.headers !== void 0 ? { ...profile.extraHeaders, ...t.headers } : void 0;
5805
+ if (merged !== void 0) opts.extraHeaders = merged;
5806
+ return criar(opts);
5807
+ };
5766
5808
  const assertOAuthResolved = (t) => {
5767
5809
  if (apiKey !== "__oauth_lazy_token__") return;
5768
5810
  const auth = t.headers?.authorization ?? t.headers?.Authorization;
@@ -5791,12 +5833,7 @@ function selectTransport(profile, apiKey) {
5791
5833
  }
5792
5834
  const envOverride = resolveBaseUrlEnvOverride(profile.name);
5793
5835
  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);
5836
+ return comTransform(opts, (o) => new OpenAIClient(o));
5800
5837
  }
5801
5838
  if (profile.apiMode === "anthropic_messages") {
5802
5839
  if (profile.name === "vertex") {
@@ -5805,12 +5842,7 @@ function selectTransport(profile, apiKey) {
5805
5842
  }
5806
5843
  const opts = { apiKey };
5807
5844
  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);
5845
+ return comTransform(opts, (o) => new AnthropicClient(o));
5814
5846
  }
5815
5847
  if (profile.apiMode === "bedrock_anthropic") {
5816
5848
  const realKey = apiKey === "__bedrock_lazy_token__" ? void 0 : apiKey;
@@ -6000,194 +6032,44 @@ var init_compression_summarizer = __esm({
6000
6032
  }
6001
6033
  });
6002
6034
 
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 };
6035
+ // src/internal/session/session-cache.ts
6036
+ function transcriptKey(cwd, agentId) {
6037
+ return `${cwd}::${agentId}`;
6029
6038
  }
6030
- function hasAssistantContent(a) {
6031
- return a.text !== void 0 || a.thinking !== void 0 || (a.toolCalls?.length ?? 0) > 0;
6039
+ function invalidateSessionCache(cwd, agentId) {
6040
+ sessions.delete(agentId);
6041
+ hydratedKeys.delete(transcriptKey(cwd, agentId));
6032
6042
  }
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);
6043
+ var sessions, hydratedKeys;
6044
+ var init_session_cache = __esm({
6045
+ "src/internal/session/session-cache.ts"() {
6046
+ sessions = /* @__PURE__ */ new Map();
6047
+ hydratedKeys = /* @__PURE__ */ new Set();
6039
6048
  }
6049
+ });
6050
+
6051
+ // src/internal/session/compact-session.ts
6052
+ var compact_session_exports = {};
6053
+ __export(compact_session_exports, {
6054
+ COMPACT_SUMMARY_MARKER: () => COMPACT_SUMMARY_MARKER,
6055
+ COMPACT_USER_MESSAGE_MAX_TOKENS: () => COMPACT_USER_MESSAGE_MAX_TOKENS,
6056
+ autoCompactIfNeeded: () => autoCompactIfNeeded,
6057
+ buildDefaultSummarizer: () => buildDefaultSummarizer,
6058
+ compactSessionTranscript: () => compactSessionTranscript,
6059
+ isCompactSummary: () => isCompactSummary,
6060
+ resolveSummarizerRoute: () => resolveSummarizerRoute,
6061
+ shouldAutoCompact: () => shouldAutoCompact
6062
+ });
6063
+ function isCompactSummary(content) {
6064
+ return content.startsWith(COMPACT_SUMMARY_MARKER) || content.startsWith("[[theokit:goal-continuation]]");
6040
6065
  }
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;
6066
+ function plainText(content) {
6067
+ if (typeof content === "string") return content;
6068
+ if (!Array.isArray(content)) return void 0;
6069
+ const texts = content.filter(
6070
+ (p) => p !== null && typeof p === "object" && p.type === "text" && typeof p.text === "string"
6071
+ ).map((p) => p.text);
6072
+ return texts.length > 0 ? texts.join("\n") : void 0;
6191
6073
  }
6192
6074
  async function compactSessionTranscript(opts) {
6193
6075
  const prior = await opts.store.readRecords(opts.loc.agentId);
@@ -6324,7 +6206,7 @@ var init_compact_session = __esm({
6324
6206
  init_providers();
6325
6207
  init_compression_model_registry();
6326
6208
  init_compression_summarizer();
6327
- init_agent_session();
6209
+ init_session_cache();
6328
6210
  COMPACT_SUMMARY_MARKER = "[[theokit:compact-summary]]";
6329
6211
  COMPACT_USER_MESSAGE_MAX_TOKENS = 2e4;
6330
6212
  autoCompactAttempts = (() => {
@@ -6335,6 +6217,165 @@ var init_compact_session = __esm({
6335
6217
  })();
6336
6218
  }
6337
6219
  });
6220
+
6221
+ // src/internal/session/agent-session-store.ts
6222
+ function seedTranscript(prior, opts) {
6223
+ return SessionTranscript.fromRecords(prior, opts);
6224
+ }
6225
+ function mapAgentTurn(steps) {
6226
+ const assistant = {};
6227
+ const toolResults = [];
6228
+ const toolCalls = [];
6229
+ for (const step of steps) {
6230
+ if (step.type === "thinkingMessage") assistant.thinking = step.message.text;
6231
+ else if (step.type === "assistantMessage") assistant.text = step.message.text;
6232
+ else if (step.type === "toolCall")
6233
+ toolCalls.push({
6234
+ id: step.message.callId,
6235
+ name: step.message.name,
6236
+ input: step.message.args ?? {}
6237
+ });
6238
+ else
6239
+ toolResults.push({
6240
+ toolUseId: step.message.callId,
6241
+ content: step.message.result,
6242
+ isError: step.message.isError
6243
+ });
6244
+ }
6245
+ if (toolCalls.length > 0) assistant.toolCalls = toolCalls;
6246
+ return { assistant, toolResults };
6247
+ }
6248
+ function hasAssistantContent(a) {
6249
+ return a.text !== void 0 || a.thinking !== void 0 || (a.toolCalls?.length ?? 0) > 0;
6250
+ }
6251
+ function appendConversation(transcript, conversation) {
6252
+ for (const ct of conversation) {
6253
+ if (ct.type !== "agentConversationTurn") continue;
6254
+ const { assistant, toolResults } = mapAgentTurn(ct.turn.steps);
6255
+ if (hasAssistantContent(assistant)) transcript.appendAssistantTurn(assistant);
6256
+ if (toolResults.length > 0) transcript.appendToolResults(toolResults);
6257
+ }
6258
+ }
6259
+ async function readSessionMessages(store, agentId) {
6260
+ const records = await store.readRecords(agentId);
6261
+ return reconstructMessages(records).map(narrowToSessionMessage);
6262
+ }
6263
+ function partToText(p) {
6264
+ if (p.type === "text") return p.text ?? "";
6265
+ if (p.type === "tool_use") return `[tool call] ${p.name ?? ""}`;
6266
+ if (p.type === "tool_result") {
6267
+ const body = typeof p.content === "string" ? p.content : JSON.stringify(p.content);
6268
+ return `[tool result] ${body}`;
6269
+ }
6270
+ return "";
6271
+ }
6272
+ function narrowToSessionMessage(m) {
6273
+ const role = m.role === "user" ? "user" : "assistant";
6274
+ const text = m.content.map(partToText).filter((s) => s.length > 0).join("\n");
6275
+ return { role, text };
6276
+ }
6277
+ function deltaRecords(transcript, priorLength) {
6278
+ return transcript.records().slice(priorLength);
6279
+ }
6280
+ async function persistTurn(store, loc, sessionId, turn) {
6281
+ const prior = await store.readRecords(loc.agentId);
6282
+ const transcript = seedTranscript(prior, { cwd: loc.cwd, sessionId, model: loc.model });
6283
+ transcript.appendUserTurn(turn.userText);
6284
+ appendConversation(transcript, turn.conversation);
6285
+ await store.appendRecords(loc.agentId, deltaRecords(transcript, prior.length));
6286
+ }
6287
+ var init_agent_session_store = __esm({
6288
+ "src/internal/session/agent-session-store.ts"() {
6289
+ init_session_transcript();
6290
+ }
6291
+ });
6292
+
6293
+ // src/internal/session/agent-session.ts
6294
+ function appendSessionMessage(agentId, message) {
6295
+ const existing = sessions.get(agentId) ?? [];
6296
+ existing.push(message);
6297
+ sessions.set(agentId, existing);
6298
+ }
6299
+ function getSessionMessages(agentId) {
6300
+ return sessions.get(agentId) ?? [];
6301
+ }
6302
+ function persistTurnToTranscript(store, loc, sessionId, turn, onCompact) {
6303
+ const key2 = transcriptKey(loc.cwd, loc.agentId);
6304
+ const chained = (pendingWrites.get(key2) ?? Promise.resolve()).then(async () => {
6305
+ try {
6306
+ await persistTurn(store, loc, sessionId, turn);
6307
+ const count = (recordCounts.get(key2) ?? 0) + 1;
6308
+ recordCounts.set(key2, count);
6309
+ if (turn.autoCompact !== void 0) {
6310
+ const { autoCompactIfNeeded: autoCompactIfNeeded2 } = await Promise.resolve().then(() => (init_compact_session(), compact_session_exports));
6311
+ const fired = await autoCompactIfNeeded2({
6312
+ store,
6313
+ loc,
6314
+ sessionId,
6315
+ usageTotal: turn.autoCompact.usageTotal,
6316
+ contextWindow: turn.autoCompact.contextWindow,
6317
+ turnCount: count,
6318
+ summarize: turn.autoCompact.summarize
6319
+ });
6320
+ if (fired) onCompact?.();
6321
+ }
6322
+ } catch (cause) {
6323
+ const msg = cause instanceof Error ? cause.message : String(cause);
6324
+ process.stderr.write(
6325
+ `[theokit-sdk] session transcript write failed (${loc.agentId}): ${msg}
6326
+ `
6327
+ );
6328
+ }
6329
+ });
6330
+ pendingWrites.set(
6331
+ key2,
6332
+ chained.then(
6333
+ () => void 0,
6334
+ () => void 0
6335
+ )
6336
+ );
6337
+ }
6338
+ async function hydrateSession(agentId, loc) {
6339
+ const key2 = transcriptKey(loc.cwd, agentId);
6340
+ if (hydratedKeys.has(key2)) return;
6341
+ hydratedKeys.add(key2);
6342
+ const persisted = await readSessionMessages(loc.store, agentId);
6343
+ if (persisted.length === 0) return;
6344
+ sessions.set(agentId, persisted);
6345
+ }
6346
+ async function flushSessionWrites() {
6347
+ while (pendingWrites.size > 0) {
6348
+ const all = Array.from(pendingWrites.values());
6349
+ pendingWrites.clear();
6350
+ await Promise.all(all);
6351
+ }
6352
+ }
6353
+ function clearSession(agentId) {
6354
+ sessions.delete(agentId);
6355
+ }
6356
+ function enqueueSessionWrite(cwd, agentId, fn) {
6357
+ const key2 = transcriptKey(cwd, agentId);
6358
+ const prior = pendingWrites.get(key2) ?? Promise.resolve();
6359
+ const result = prior.then(fn);
6360
+ pendingWrites.set(
6361
+ key2,
6362
+ result.then(
6363
+ () => void 0,
6364
+ () => void 0
6365
+ )
6366
+ );
6367
+ return result;
6368
+ }
6369
+ var pendingWrites, recordCounts;
6370
+ var init_agent_session = __esm({
6371
+ "src/internal/session/agent-session.ts"() {
6372
+ init_agent_session_store();
6373
+ init_session_cache();
6374
+ init_session_cache();
6375
+ pendingWrites = /* @__PURE__ */ new Map();
6376
+ recordCounts = /* @__PURE__ */ new Map();
6377
+ }
6378
+ });
6338
6379
  async function withToolWhitelist(whitelist, fn) {
6339
6380
  return toolWhitelistStore.run(whitelist, fn);
6340
6381
  }
@@ -6546,7 +6587,9 @@ async function loadDriver(filePath) {
6546
6587
  }
6547
6588
  try {
6548
6589
  const mod = await (driverLoaderOverrides?.nodeSqlite?.() ?? Promise.resolve(
6549
- process.getBuiltinModule?.("node:sqlite") ?? (() => {
6590
+ process.getBuiltinModule?.(
6591
+ "node:sqlite"
6592
+ ) ?? (() => {
6550
6593
  throw new Error("node:sqlite built-in unavailable (Node < 22.3)");
6551
6594
  })()
6552
6595
  ));
@@ -7452,190 +7495,71 @@ var init_index_manager = __esm({
7452
7495
  score: 0,
7453
7496
  textScore: 0,
7454
7497
  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 {
7498
+ source: String(row.source),
7499
+ citation: `${path}:${startLine}-${endLine}`
7500
+ };
7501
+ });
7595
7502
  }
7596
- }
7503
+ // ───── persistence helpers ─────────────────────────────────────────
7504
+ loadFilesIndex() {
7505
+ const rows = this.db.prepare("SELECT id, path, hash FROM files").all();
7506
+ return new Map(rows.map((row) => [row.path, { id: row.id, hash: row.hash }]));
7507
+ }
7508
+ upsertFile(absPath, relPath, hash, mtimeMs, source = "memory") {
7509
+ const stmt = this.db.prepare(
7510
+ `INSERT INTO files (path, rel_path, mtime, hash, source) VALUES (?, ?, ?, ?, ?)
7511
+ ON CONFLICT(path) DO UPDATE SET hash = excluded.hash, mtime = excluded.mtime, source = excluded.source
7512
+ RETURNING id`
7513
+ );
7514
+ const row = stmt.get(absPath, relPath, Math.floor(mtimeMs), hash, source);
7515
+ return row.id;
7516
+ }
7517
+ deleteChunksForFile(fileId) {
7518
+ this.db.prepare("DELETE FROM chunks WHERE file_id = ?").run(fileId);
7519
+ }
7520
+ insertChunk(fileId, startLine, endLine, text, hash) {
7521
+ this.db.prepare(
7522
+ "INSERT INTO chunks (file_id, start_line, end_line, text, hash) VALUES (?, ?, ?, ?, ?)"
7523
+ ).run(fileId, startLine, endLine, text, hash);
7524
+ }
7525
+ close() {
7526
+ this.db.close();
7527
+ }
7528
+ };
7597
7529
  }
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>
7530
+ });
7611
7531
 
7612
- Be strict. If unclear, prefer CONTINUE.`;
7532
+ // src/internal/personality/context.ts
7533
+ var context_exports = {};
7534
+ __export(context_exports, {
7535
+ currentPersonalityContext: () => currentPersonalityContext,
7536
+ warnPersonalitySwitchInsideFork: () => warnPersonalitySwitchInsideFork,
7537
+ withPersonalityContext: () => withPersonalityContext
7538
+ });
7539
+ function withPersonalityContext(ctx, fn) {
7540
+ return storage.run(ctx, fn);
7613
7541
  }
7614
- var init_judge_call = __esm({
7615
- "src/internal/judge/judge-call.ts"() {
7616
- init_parse_verdict();
7542
+ function currentPersonalityContext() {
7543
+ return storage.getStore();
7544
+ }
7545
+ function warnPersonalitySwitchInsideFork(agentId) {
7546
+ warnOnce(
7547
+ `personality-switch-in-fork-${agentId}`,
7548
+ `[theokit-sdk] usePersonality is a no-op inside a fork (D168). Subagents inherit the parent's active personality at fork-construction time.`
7549
+ );
7550
+ }
7551
+ var storage;
7552
+ var init_context = __esm({
7553
+ "src/internal/personality/context.ts"() {
7554
+ init_hooks_source();
7555
+ storage = new async_hooks.AsyncLocalStorage();
7617
7556
  }
7618
7557
  });
7619
7558
 
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
- }
7559
+ // src/internal/runtime/lifecycle/goal-marker.ts
7636
7560
  exports.GOAL_CONTINUATION_MARKER = void 0;
7637
- var init_goal_loop = __esm({
7638
- "src/goal-loop.ts"() {
7561
+ var init_goal_marker = __esm({
7562
+ "src/internal/runtime/lifecycle/goal-marker.ts"() {
7639
7563
  exports.GOAL_CONTINUATION_MARKER = "[[theokit:goal-continuation]]";
7640
7564
  }
7641
7565
  });
@@ -7809,7 +7733,111 @@ ${lastResponse.slice(-1e3)}`
7809
7733
  }
7810
7734
  var init_run_until = __esm({
7811
7735
  "src/internal/runtime/lifecycle/run-until.ts"() {
7812
- init_goal_loop();
7736
+ init_goal_marker();
7737
+ }
7738
+ });
7739
+
7740
+ // src/internal/judge/parse-verdict.ts
7741
+ function parseVerdict(text) {
7742
+ const trimmed = text.trim();
7743
+ if (trimmed.startsWith(DONE_PREFIX)) {
7744
+ return {
7745
+ verdict: "done",
7746
+ reason: trimmed.slice(DONE_PREFIX.length).trim(),
7747
+ parseFailed: false
7748
+ };
7749
+ }
7750
+ if (trimmed.startsWith(CONTINUE_PREFIX)) {
7751
+ return {
7752
+ verdict: "continue",
7753
+ reason: trimmed.slice(CONTINUE_PREFIX.length).trim(),
7754
+ parseFailed: false
7755
+ };
7756
+ }
7757
+ if (trimmed.startsWith(SKIPPED_PREFIX)) {
7758
+ return {
7759
+ verdict: "skipped",
7760
+ reason: trimmed.slice(SKIPPED_PREFIX.length).trim(),
7761
+ parseFailed: false
7762
+ };
7763
+ }
7764
+ return {
7765
+ verdict: "continue",
7766
+ reason: `judge response malformed: "${trimmed.slice(0, 100)}"`,
7767
+ parseFailed: true
7768
+ };
7769
+ }
7770
+ var DONE_PREFIX, CONTINUE_PREFIX, SKIPPED_PREFIX;
7771
+ var init_parse_verdict = __esm({
7772
+ "src/internal/judge/parse-verdict.ts"() {
7773
+ DONE_PREFIX = "DONE:";
7774
+ CONTINUE_PREFIX = "CONTINUE:";
7775
+ SKIPPED_PREFIX = "SKIPPED:";
7776
+ }
7777
+ });
7778
+
7779
+ // src/internal/judge/judge-call.ts
7780
+ var judge_call_exports = {};
7781
+ __export(judge_call_exports, {
7782
+ composeJudgePrompt: () => composeJudgePrompt,
7783
+ judgeCallImpl: () => judgeCallImpl
7784
+ });
7785
+ async function judgeCallImpl(ctx, options, deps) {
7786
+ const prompt = composeJudgePrompt(ctx);
7787
+ const apiKey = options?.apiKey ?? process.env.OPENROUTER_API_KEY;
7788
+ if (apiKey === void 0) {
7789
+ return {
7790
+ verdict: "continue",
7791
+ reason: "judge unavailable: OPENROUTER_API_KEY missing and no override passed via options.apiKey",
7792
+ parseFailed: true
7793
+ };
7794
+ }
7795
+ const judgeModel = options?.judgeModel ?? "openai/gpt-4o-mini";
7796
+ let auxAgent;
7797
+ try {
7798
+ auxAgent = await deps.create({
7799
+ apiKey,
7800
+ model: { id: judgeModel },
7801
+ tools: [],
7802
+ local: {},
7803
+ metadata: { forkOrigin: "judge" }
7804
+ });
7805
+ const run = await auxAgent.send(prompt);
7806
+ const result = await run.wait();
7807
+ return parseVerdict(result.result ?? "");
7808
+ } catch (err) {
7809
+ return {
7810
+ verdict: "continue",
7811
+ reason: `judge call failed: ${err instanceof Error ? err.message : String(err)}`,
7812
+ parseFailed: true
7813
+ };
7814
+ } finally {
7815
+ if (auxAgent !== void 0) {
7816
+ try {
7817
+ await auxAgent.dispose();
7818
+ } catch {
7819
+ }
7820
+ }
7821
+ }
7822
+ }
7823
+ function composeJudgePrompt(ctx) {
7824
+ const subgoals = ctx.subgoals !== void 0 && ctx.subgoals.length > 0 ? ctx.subgoals.join(", ") : "(none)";
7825
+ return `You are a goal judge. Determine if this goal is satisfied.
7826
+
7827
+ Goal: ${ctx.goal}
7828
+ Subgoals: ${subgoals}
7829
+ Last agent response: ${ctx.lastResponse}
7830
+
7831
+ Respond with EXACTLY one of:
7832
+ - DONE: <reason>
7833
+ - CONTINUE: <what's left>
7834
+ - SKIPPED: <why not applicable>
7835
+
7836
+ Be strict. If unclear, prefer CONTINUE.`;
7837
+ }
7838
+ var init_judge_call = __esm({
7839
+ "src/internal/judge/judge-call.ts"() {
7840
+ init_parse_verdict();
7813
7841
  }
7814
7842
  });
7815
7843
 
@@ -13277,6 +13305,7 @@ function parseDecisionFromStdout(stdout) {
13277
13305
  }
13278
13306
 
13279
13307
  // src/internal/runtime/lifecycle/post-run-lifecycle.ts
13308
+ init_compaction();
13280
13309
  init_run_events();
13281
13310
  init_session_summary_writer();
13282
13311
  init_catalog_loader();
@@ -13304,6 +13333,12 @@ function resolveActiveMemorySummaryForSend(legacySummary, portPathEnabled) {
13304
13333
  return legacySummary;
13305
13334
  }
13306
13335
 
13336
+ // src/internal/runtime/lifecycle/context-budget-event.ts
13337
+ function buildContextBudgetEvent(model, resolved) {
13338
+ if (resolved.source !== "fallback") return void 0;
13339
+ return { type: "compaction_fallback", model, window: resolved.window };
13340
+ }
13341
+
13307
13342
  // src/internal/runtime/lifecycle/post-run-lifecycle.ts
13308
13343
  async function runPostRunLifecycle(inputs) {
13309
13344
  const {
@@ -13329,18 +13364,15 @@ async function runPostRunLifecycle(inputs) {
13329
13364
  appendSessionMessage(agentId, { role: "assistant", text: result.result });
13330
13365
  }
13331
13366
  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
- }
13367
+ const resolvedWindow = resolveEffectiveContextWindow({
13368
+ catalog: getCatalogModelInfo(model)?.limit?.context,
13369
+ margin: CONTEXT_WINDOW_MARGIN,
13370
+ floor: CONTEXT_WINDOW_FLOOR
13371
+ });
13372
+ const contextWindow = resolvedWindow.window;
13373
+ const budgetEvent = buildContextBudgetEvent(model, resolvedWindow);
13374
+ if (budgetEvent !== void 0 && onRunEvent !== void 0) {
13375
+ emitRunEvent(onRunEvent, budgetEvent);
13344
13376
  }
13345
13377
  const lastRequestUsage = result.usage?.requests?.at(-1)?.totalTokens;
13346
13378
  const usageForTrigger = lastRequestUsage ?? result.usage?.totalTokens;
@@ -18312,6 +18344,71 @@ function registerPluginProviderProfiles(entries) {
18312
18344
 
18313
18345
  // src/internal/local-agent/real-local-run.ts
18314
18346
  init_async_local_storage();
18347
+
18348
+ // src/internal/local-agent/mcp-pool.ts
18349
+ var DEFAULT_IDLE_TTL_MS = 6e5;
18350
+ function configKey(config) {
18351
+ return JSON.stringify(
18352
+ config,
18353
+ (_k, v) => v !== null && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(
18354
+ Object.entries(v).sort(([a], [b]) => a < b ? -1 : 1)
18355
+ ) : v
18356
+ );
18357
+ }
18358
+ var McpClientPool = class {
18359
+ entries = /* @__PURE__ */ new Map();
18360
+ idleTtlMs;
18361
+ now;
18362
+ constructor(options = {}) {
18363
+ this.idleTtlMs = options.idleTtlMs ?? DEFAULT_IDLE_TTL_MS;
18364
+ this.now = options.now ?? Date.now;
18365
+ }
18366
+ /**
18367
+ * Return the pooled client for `(sessionId, serverName, config)`, creating it via `factory` on
18368
+ * first use. Every call refreshes idleness — the TTL measures time SINCE LAST USE, not age.
18369
+ *
18370
+ * Synchronous by design: `createMcpClient` is itself synchronous (the handshake happens later, on
18371
+ * `initialize`), so there is no `await` between the lookup and the insert and two concurrent runs
18372
+ * in the same session cannot both miss the cache.
18373
+ */
18374
+ acquire(sessionId, serverName, config, factory) {
18375
+ const key2 = `${sessionId}\0${serverName}\0${configKey(config)}`;
18376
+ const existing = this.entries.get(key2);
18377
+ if (existing !== void 0) {
18378
+ existing.lastUsedAt = this.now();
18379
+ return existing.client;
18380
+ }
18381
+ const client = factory();
18382
+ this.entries.set(key2, { client, sessionId, lastUsedAt: this.now() });
18383
+ return client;
18384
+ }
18385
+ /**
18386
+ * Close and forget every client of ONE session. Scoped deliberately: clearing the whole map would
18387
+ * tear down the servers of every concurrent conversation.
18388
+ */
18389
+ disposeSession(sessionId, close) {
18390
+ for (const [key2, entry] of this.entries) {
18391
+ if (entry.sessionId !== sessionId) continue;
18392
+ close(entry.client);
18393
+ this.entries.delete(key2);
18394
+ }
18395
+ }
18396
+ /** Close and forget every client idle for longer than the TTL. */
18397
+ reapIdle(close) {
18398
+ const cutoff = this.now() - this.idleTtlMs;
18399
+ for (const [key2, entry] of this.entries) {
18400
+ if (entry.lastUsedAt > cutoff) continue;
18401
+ close(entry.client);
18402
+ this.entries.delete(key2);
18403
+ }
18404
+ }
18405
+ /** Live pooled-client count — for observability and tests. */
18406
+ size() {
18407
+ return this.entries.size;
18408
+ }
18409
+ };
18410
+
18411
+ // src/internal/local-agent/real-local-run.ts
18315
18412
  init_real_local_run_provider();
18316
18413
 
18317
18414
  // src/a2a/subagent.ts
@@ -18730,12 +18827,23 @@ function buildLoopInputs(options, runId, userText, userImages) {
18730
18827
  ...options.agentOptions.memoryProvider !== void 0 ? { memoryProvider: options.agentOptions.memoryProvider } : {}
18731
18828
  };
18732
18829
  }
18830
+ var sessionMcpPool = new McpClientPool();
18831
+ function disposeSessionMcpClients(agentId) {
18832
+ sessionMcpPool.disposeSession(agentId, (client) => {
18833
+ void client.close();
18834
+ });
18835
+ }
18733
18836
  function buildMcpMap(options) {
18734
18837
  const map = /* @__PURE__ */ new Map();
18735
18838
  const inline = options.sendOptions.mcpServers ?? options.agentOptions.mcpServers;
18736
18839
  if (inline === void 0) return map;
18840
+ const pooled = options.agentOptions.mcpLifecycle === "session";
18841
+ if (pooled) sessionMcpPool.reapIdle((c) => void c.close());
18737
18842
  for (const [name, config] of Object.entries(inline)) {
18738
- map.set(name, createMcpClient(name, config));
18843
+ map.set(
18844
+ name,
18845
+ pooled ? sessionMcpPool.acquire(options.agentId, name, config, () => createMcpClient(name, config)) : createMcpClient(name, config)
18846
+ );
18739
18847
  }
18740
18848
  return map;
18741
18849
  }
@@ -19864,7 +19972,8 @@ var LocalAgentMemory = class {
19864
19972
  const message = cause instanceof Error ? cause.message : String(cause);
19865
19973
  const g = globalThis;
19866
19974
  const sym = /* @__PURE__ */ Symbol.for("theokit-sdk.memory.warned");
19867
- const warned3 = g[sym] ??= /* @__PURE__ */ new Set();
19975
+ g[sym] ??= /* @__PURE__ */ new Set();
19976
+ const warned3 = g[sym];
19868
19977
  if (!warned3.has(message)) {
19869
19978
  warned3.add(message);
19870
19979
  process.stderr.write(`[theokit-sdk] memory tools unavailable: ${message}
@@ -21049,6 +21158,7 @@ var LocalAgent = class {
21049
21158
  liveAgentRegistry.forget(this.agentId);
21050
21159
  this.lifecycleAbortController.abort();
21051
21160
  await withCwdMutex(`agent-send:${this.agentId}`, () => Promise.resolve());
21161
+ disposeSessionMcpClients(this.agentId);
21052
21162
  await flushSessionWrites();
21053
21163
  await flushRegistrySaves(this.workspaceCwd);
21054
21164
  }
@@ -21349,8 +21459,8 @@ async function getRegisteredAgentOrThrow(agentId) {
21349
21459
  // src/agent.ts
21350
21460
  init_errors();
21351
21461
  init_discovery();
21352
- init_agent_session();
21353
21462
  init_agent_factory_registry();
21463
+ init_agent_session();
21354
21464
  var streamObjectImport;
21355
21465
  var Agent = class _Agent {
21356
21466
  constructor() {
@@ -21642,26 +21752,26 @@ var Agent = class _Agent {
21642
21752
  reg = getRegisteredAgent(agentId);
21643
21753
  }
21644
21754
  if (reg === void 0 || reg.runtime !== "local") {
21645
- throw new exports.UnknownAgentError(`No local agent "${agentId}" registered \u2014 compact targets local sessions.`);
21755
+ throw new exports.UnknownAgentError(
21756
+ `No local agent "${agentId}" registered \u2014 compact targets local sessions.`
21757
+ );
21646
21758
  }
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
21759
  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 } : {}
21760
+ const { cwd, model, store } = await abrirStoreLocal(reg);
21761
+ return enqueueSessionWrite(
21762
+ cwd,
21763
+ agentId,
21764
+ () => compactSessionTranscript2({
21765
+ store,
21766
+ loc: { cwd, agentId, model },
21767
+ sessionId: agentId,
21768
+ trigger: options.trigger ?? "manual",
21769
+ summarize: options.summarize ?? buildDefaultSummarizer2({
21770
+ agentModel: model,
21771
+ ...reg.options.apiKey !== void 0 ? { apiKey: reg.options.apiKey } : {}
21772
+ })
21663
21773
  })
21664
- }));
21774
+ );
21665
21775
  }
21666
21776
  /**
21667
21777
  * M51 — inject a SYNTHETIC user+assistant pair into a LOCAL session's persisted transcript WITHOUT
@@ -21678,16 +21788,12 @@ var Agent = class _Agent {
21678
21788
  reg = getRegisteredAgent(agentId);
21679
21789
  }
21680
21790
  if (reg === void 0 || reg.runtime !== "local") {
21681
- throw new exports.UnknownAgentError(`No local agent "${agentId}" registered \u2014 injectSessionTurn targets local sessions.`);
21791
+ throw new exports.UnknownAgentError(
21792
+ `No local agent "${agentId}" registered \u2014 injectSessionTurn targets local sessions.`
21793
+ );
21682
21794
  }
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
21795
  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 });
21796
+ const { cwd, model, store } = await abrirStoreLocal(reg);
21691
21797
  await injectSessionTurn2({
21692
21798
  store,
21693
21799
  loc: { cwd, agentId, model },
@@ -21729,6 +21835,15 @@ setAgentFacade({
21729
21835
  resume: (agentId, options) => Agent.resume(agentId, options),
21730
21836
  batch: (prompts, options) => Agent.batch(prompts, options)
21731
21837
  });
21838
+ async function abrirStoreLocal(reg) {
21839
+ const cwd = reg.cwd ?? process.cwd();
21840
+ const optModel = reg.options.model;
21841
+ const model = reg.model?.id ?? (typeof optModel === "string" ? optModel : optModel?.id) ?? "unknown";
21842
+ const { FsSessionStore: FsSessionStore2 } = await Promise.resolve().then(() => (init_fs_session_store(), fs_session_store_exports));
21843
+ const { defaultBaseDir: defaultBaseDir2, expandTilde: expandTilde2 } = await Promise.resolve().then(() => (init_session_transcript(), session_transcript_exports));
21844
+ const baseDir = reg.options.local?.baseDir !== void 0 ? expandTilde2(reg.options.local.baseDir) : defaultBaseDir2();
21845
+ return { cwd, model, store: new FsSessionStore2({ baseDir, cwd }) };
21846
+ }
21732
21847
 
21733
21848
  // src/agent-factory.ts
21734
21849
  function createAgentFactory(common) {
@@ -22843,7 +22958,24 @@ var EventBus = class {
22843
22958
 
22844
22959
  // src/index.ts
22845
22960
  init_generate_object();
22846
- init_goal_loop();
22961
+
22962
+ // src/goal-loop.ts
22963
+ init_goal_marker();
22964
+ function runGoalLoop(agent, goal, options, depsOverride) {
22965
+ async function* wrap() {
22966
+ const { runUntilImpl: runUntilImpl2 } = await Promise.resolve().then(() => (init_run_until(), run_until_exports));
22967
+ const deps = depsOverride ?? await (async () => {
22968
+ const { judgeCallImpl: judgeCallImpl2 } = await Promise.resolve().then(() => (init_judge_call(), judge_call_exports));
22969
+ const { getAgentFacade: getAgentFacade2 } = await Promise.resolve().then(() => (init_agent_factory_registry(), agent_factory_registry_exports));
22970
+ const create = getAgentFacade2().create;
22971
+ return {
22972
+ judge: (ctx, opts) => judgeCallImpl2(ctx, opts, { create })
22973
+ };
22974
+ })();
22975
+ return yield* runUntilImpl2(agent, goal, options, deps);
22976
+ }
22977
+ return wrap();
22978
+ }
22847
22979
 
22848
22980
  // src/internal/budget/tracker/budget-tracker-counter.ts
22849
22981
  function createCounterBudgetTracker(options = {}) {