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