@truefoundry/trueforge 0.1.3 → 0.1.4-rc.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 (159) hide show
  1. package/dist/_frontend/assets/{ConnectorSettings-EYOQ72OS-DdSqeM08.js → ConnectorSettings-ODC3LVHN-jhiXpbVc.js} +1 -1
  2. package/dist/_frontend/assets/ConnectorSettings-ODC3LVHN-jhiXpbVc.js.br +0 -0
  3. package/dist/_frontend/assets/ConnectorSettings-ODC3LVHN-jhiXpbVc.js.gz +0 -0
  4. package/dist/_frontend/assets/{DockLayout-XIOJZ2YM-htVfvSLo.js → DockLayout-2RWDSUKE-DY3y3Is3.js} +1 -1
  5. package/dist/_frontend/assets/{DrawerLayout-CNKNNVI7-CQ7wZgB6.js → DrawerLayout-CZLJYMIN-BYncSNoi.js} +2 -2
  6. package/dist/_frontend/assets/DrawerLayout-CZLJYMIN-BYncSNoi.js.br +0 -0
  7. package/dist/_frontend/assets/DrawerLayout-CZLJYMIN-BYncSNoi.js.gz +0 -0
  8. package/dist/_frontend/assets/{ModelSettings-WBDPLI6O-CYfqXYXo.js → ModelSettings-CKQ5CVAC-DxQtYKup.js} +1 -1
  9. package/dist/_frontend/assets/ModelSettings-CKQ5CVAC-DxQtYKup.js.br +0 -0
  10. package/dist/_frontend/assets/ModelSettings-CKQ5CVAC-DxQtYKup.js.gz +0 -0
  11. package/dist/_frontend/assets/{SandboxSettings-4GHBXULN-BPT3vPku.js → SandboxSettings-HSCPYUOC-DMFf-U04.js} +1 -1
  12. package/dist/_frontend/assets/SandboxSettings-HSCPYUOC-DMFf-U04.js.br +0 -0
  13. package/dist/_frontend/assets/SandboxSettings-HSCPYUOC-DMFf-U04.js.gz +0 -0
  14. package/dist/_frontend/assets/SettingsBuilder-MYWRFXRI-C4p8uaXT.js +2 -0
  15. package/dist/_frontend/assets/SettingsBuilder-MYWRFXRI-C4p8uaXT.js.br +0 -0
  16. package/dist/_frontend/assets/SettingsBuilder-MYWRFXRI-C4p8uaXT.js.gz +0 -0
  17. package/dist/_frontend/assets/{ShellRouteSync-T4WLY57Q-BKbLAZEv.js → ShellRouteSync-T4WLY57Q-BC8a0ojU.js} +1 -1
  18. package/dist/_frontend/assets/ShellRouteSync-T4WLY57Q-BC8a0ojU.js.br +0 -0
  19. package/dist/_frontend/assets/ShellRouteSync-T4WLY57Q-BC8a0ojU.js.gz +0 -0
  20. package/dist/_frontend/assets/{SidebarLayout-JGLTENB3-BiZz45he.js → SidebarLayout-WVY54TGZ-Ds7NVk-f.js} +2 -2
  21. package/dist/_frontend/assets/SidebarLayout-WVY54TGZ-Ds7NVk-f.js.br +0 -0
  22. package/dist/_frontend/assets/SidebarLayout-WVY54TGZ-Ds7NVk-f.js.gz +0 -0
  23. package/dist/_frontend/assets/{SkillSettings-UG6USX4N-DhQTOueg.js → SkillSettings-ECH6SHZ7-BtdCmc6B.js} +1 -1
  24. package/dist/_frontend/assets/SkillSettings-ECH6SHZ7-BtdCmc6B.js.br +0 -0
  25. package/dist/_frontend/assets/SkillSettings-ECH6SHZ7-BtdCmc6B.js.gz +0 -0
  26. package/dist/_frontend/assets/{TrueForgeUIWithRouter-CTC5XRSY-BnWelV-R.js → TrueForgeUIWithRouter-RKZ2E7PV-vF7U7cGf.js} +1 -1
  27. package/dist/_frontend/assets/{WidgetLayout-6P6OUUTG-B1G5uZ4_.js → WidgetLayout-NWWB2SXI-DJtuM33Y.js} +1 -1
  28. package/dist/_frontend/assets/WidgetLayout-NWWB2SXI-DJtuM33Y.js.br +0 -0
  29. package/dist/_frontend/assets/WidgetLayout-NWWB2SXI-DJtuM33Y.js.gz +0 -0
  30. package/dist/_frontend/assets/{chunk-62JRHF6Z-yJ2twjOf.js → chunk-62JRHF6Z-TG4aaXok.js} +1 -1
  31. package/dist/_frontend/assets/chunk-62JRHF6Z-TG4aaXok.js.br +0 -0
  32. package/dist/_frontend/assets/chunk-62JRHF6Z-TG4aaXok.js.gz +0 -0
  33. package/dist/_frontend/assets/{chunk-27MKI3HF-CVw53aeu.js → chunk-OZTNBS6V-axorP9qO.js} +1 -1
  34. package/dist/_frontend/assets/chunk-OZTNBS6V-axorP9qO.js.br +0 -0
  35. package/dist/_frontend/assets/{chunk-27MKI3HF-CVw53aeu.js.gz → chunk-OZTNBS6V-axorP9qO.js.gz} +0 -0
  36. package/dist/_frontend/assets/{chunk-H75C5ETA-CX-fQN0e.js → chunk-TCAVLWYL-BXtNAtCy.js} +2 -2
  37. package/dist/_frontend/assets/chunk-TCAVLWYL-BXtNAtCy.js.br +0 -0
  38. package/dist/_frontend/assets/chunk-TCAVLWYL-BXtNAtCy.js.gz +0 -0
  39. package/dist/_frontend/assets/{cssMode-CEpMXY8Z.js → cssMode-yEIs6GIw.js} +1 -1
  40. package/dist/_frontend/assets/cssMode-yEIs6GIw.js.br +0 -0
  41. package/dist/_frontend/assets/cssMode-yEIs6GIw.js.gz +0 -0
  42. package/dist/_frontend/assets/{freemarker2-cQSgojlC.js → freemarker2-aDGnYU8i.js} +1 -1
  43. package/dist/_frontend/assets/freemarker2-aDGnYU8i.js.br +0 -0
  44. package/dist/_frontend/assets/freemarker2-aDGnYU8i.js.gz +0 -0
  45. package/dist/_frontend/assets/{handlebars-BQ-clQ7_.js → handlebars-B6ny31dN.js} +1 -1
  46. package/dist/_frontend/assets/handlebars-B6ny31dN.js.br +0 -0
  47. package/dist/_frontend/assets/handlebars-B6ny31dN.js.gz +0 -0
  48. package/dist/_frontend/assets/{html-ChfWgJt-.js → html-D7u-MYoP.js} +1 -1
  49. package/dist/_frontend/assets/html-D7u-MYoP.js.br +0 -0
  50. package/dist/_frontend/assets/html-D7u-MYoP.js.gz +0 -0
  51. package/dist/_frontend/assets/{htmlMode-D1PmpoAx.js → htmlMode-kNcFDXKs.js} +1 -1
  52. package/dist/_frontend/assets/htmlMode-kNcFDXKs.js.br +0 -0
  53. package/dist/_frontend/assets/htmlMode-kNcFDXKs.js.gz +0 -0
  54. package/dist/_frontend/assets/{index-aEojVaVa.js → index-CLJuaUhx.js} +3 -3
  55. package/dist/_frontend/assets/index-CLJuaUhx.js.br +0 -0
  56. package/dist/_frontend/assets/index-CLJuaUhx.js.gz +0 -0
  57. package/dist/_frontend/assets/{index-BRS0kvLQ.js → index-D1GDH6ia.js} +122 -122
  58. package/dist/_frontend/assets/index-D1GDH6ia.js.br +0 -0
  59. package/dist/_frontend/assets/index-D1GDH6ia.js.gz +0 -0
  60. package/dist/_frontend/assets/{javascript-DFHGvkjD.js → javascript-BLSK7s1P.js} +1 -1
  61. package/dist/_frontend/assets/{jsonMode-DWo0prLv.js → jsonMode-XHJKWC_S.js} +1 -1
  62. package/dist/_frontend/assets/jsonMode-XHJKWC_S.js.br +0 -0
  63. package/dist/_frontend/assets/jsonMode-XHJKWC_S.js.gz +0 -0
  64. package/dist/_frontend/assets/{liquid-Bs2__P9v.js → liquid-Db1WZdZB.js} +1 -1
  65. package/dist/_frontend/assets/liquid-Db1WZdZB.js.br +0 -0
  66. package/dist/_frontend/assets/liquid-Db1WZdZB.js.gz +0 -0
  67. package/dist/_frontend/assets/{lspLanguageFeatures-zZ0stYf4.js → lspLanguageFeatures-BFyBtl8M.js} +1 -1
  68. package/dist/_frontend/assets/lspLanguageFeatures-BFyBtl8M.js.br +0 -0
  69. package/dist/_frontend/assets/lspLanguageFeatures-BFyBtl8M.js.gz +0 -0
  70. package/dist/_frontend/assets/{mdx-C7c8MDI_.js → mdx-DtFWu4ex.js} +1 -1
  71. package/dist/_frontend/assets/mdx-DtFWu4ex.js.br +0 -0
  72. package/dist/_frontend/assets/mdx-DtFWu4ex.js.gz +0 -0
  73. package/dist/_frontend/assets/{python-CLO39XAV.js → python-2R7BOup6.js} +1 -1
  74. package/dist/_frontend/assets/python-2R7BOup6.js.br +0 -0
  75. package/dist/_frontend/assets/python-2R7BOup6.js.gz +0 -0
  76. package/dist/_frontend/assets/{razor-efddsYhk.js → razor-DQLxdj72.js} +1 -1
  77. package/dist/_frontend/assets/razor-DQLxdj72.js.br +0 -0
  78. package/dist/_frontend/assets/razor-DQLxdj72.js.gz +0 -0
  79. package/dist/_frontend/assets/{tsMode-xfchPgTX.js → tsMode-Ctn4PuNt.js} +1 -1
  80. package/dist/_frontend/assets/tsMode-Ctn4PuNt.js.br +0 -0
  81. package/dist/_frontend/assets/tsMode-Ctn4PuNt.js.gz +0 -0
  82. package/dist/_frontend/assets/{typescript-BFzaCQP8.js → typescript-DGwzICBa.js} +1 -1
  83. package/dist/_frontend/assets/typescript-DGwzICBa.js.br +0 -0
  84. package/dist/_frontend/assets/typescript-DGwzICBa.js.gz +0 -0
  85. package/dist/_frontend/assets/{workers-DKwMBzKS.js → workers-CRRTWuKZ.js} +1 -1
  86. package/dist/_frontend/assets/workers-CRRTWuKZ.js.br +0 -0
  87. package/dist/_frontend/assets/workers-CRRTWuKZ.js.gz +0 -0
  88. package/dist/_frontend/assets/{xml-Cx6gHwq3.js → xml-CFZg-QqB.js} +1 -1
  89. package/dist/_frontend/assets/xml-CFZg-QqB.js.br +0 -0
  90. package/dist/_frontend/assets/xml-CFZg-QqB.js.gz +0 -0
  91. package/dist/_frontend/assets/{yaml-DyrfMWlf.js → yaml-DUIIx9CV.js} +1 -1
  92. package/dist/_frontend/assets/yaml-DUIIx9CV.js.br +0 -0
  93. package/dist/_frontend/assets/yaml-DUIIx9CV.js.gz +0 -0
  94. package/dist/_frontend/index.html +1 -1
  95. package/dist/_frontend/index.html.br +0 -0
  96. package/dist/_frontend/index.html.gz +0 -0
  97. package/dist/main.js +2152 -468
  98. package/dist/main.js.map +1 -1
  99. package/package.json +10 -4
  100. package/dist/_frontend/assets/ConnectorSettings-EYOQ72OS-DdSqeM08.js.br +0 -0
  101. package/dist/_frontend/assets/ConnectorSettings-EYOQ72OS-DdSqeM08.js.gz +0 -0
  102. package/dist/_frontend/assets/DrawerLayout-CNKNNVI7-CQ7wZgB6.js.br +0 -0
  103. package/dist/_frontend/assets/DrawerLayout-CNKNNVI7-CQ7wZgB6.js.gz +0 -0
  104. package/dist/_frontend/assets/ModelSettings-WBDPLI6O-CYfqXYXo.js.br +0 -0
  105. package/dist/_frontend/assets/ModelSettings-WBDPLI6O-CYfqXYXo.js.gz +0 -0
  106. package/dist/_frontend/assets/SandboxSettings-4GHBXULN-BPT3vPku.js.br +0 -0
  107. package/dist/_frontend/assets/SandboxSettings-4GHBXULN-BPT3vPku.js.gz +0 -0
  108. package/dist/_frontend/assets/SettingsBuilder-TD42LCIU-COZb69Wb.js +0 -2
  109. package/dist/_frontend/assets/SettingsBuilder-TD42LCIU-COZb69Wb.js.br +0 -0
  110. package/dist/_frontend/assets/SettingsBuilder-TD42LCIU-COZb69Wb.js.gz +0 -0
  111. package/dist/_frontend/assets/ShellRouteSync-T4WLY57Q-BKbLAZEv.js.br +0 -0
  112. package/dist/_frontend/assets/ShellRouteSync-T4WLY57Q-BKbLAZEv.js.gz +0 -0
  113. package/dist/_frontend/assets/SidebarLayout-JGLTENB3-BiZz45he.js.br +0 -0
  114. package/dist/_frontend/assets/SidebarLayout-JGLTENB3-BiZz45he.js.gz +0 -0
  115. package/dist/_frontend/assets/SkillSettings-UG6USX4N-DhQTOueg.js.br +0 -0
  116. package/dist/_frontend/assets/SkillSettings-UG6USX4N-DhQTOueg.js.gz +0 -0
  117. package/dist/_frontend/assets/WidgetLayout-6P6OUUTG-B1G5uZ4_.js.br +0 -0
  118. package/dist/_frontend/assets/WidgetLayout-6P6OUUTG-B1G5uZ4_.js.gz +0 -0
  119. package/dist/_frontend/assets/chunk-27MKI3HF-CVw53aeu.js.br +0 -0
  120. package/dist/_frontend/assets/chunk-62JRHF6Z-yJ2twjOf.js.br +0 -0
  121. package/dist/_frontend/assets/chunk-62JRHF6Z-yJ2twjOf.js.gz +0 -0
  122. package/dist/_frontend/assets/chunk-H75C5ETA-CX-fQN0e.js.br +0 -0
  123. package/dist/_frontend/assets/chunk-H75C5ETA-CX-fQN0e.js.gz +0 -0
  124. package/dist/_frontend/assets/cssMode-CEpMXY8Z.js.br +0 -0
  125. package/dist/_frontend/assets/cssMode-CEpMXY8Z.js.gz +0 -0
  126. package/dist/_frontend/assets/freemarker2-cQSgojlC.js.br +0 -0
  127. package/dist/_frontend/assets/freemarker2-cQSgojlC.js.gz +0 -0
  128. package/dist/_frontend/assets/handlebars-BQ-clQ7_.js.br +0 -0
  129. package/dist/_frontend/assets/handlebars-BQ-clQ7_.js.gz +0 -0
  130. package/dist/_frontend/assets/html-ChfWgJt-.js.br +0 -0
  131. package/dist/_frontend/assets/html-ChfWgJt-.js.gz +0 -0
  132. package/dist/_frontend/assets/htmlMode-D1PmpoAx.js.br +0 -0
  133. package/dist/_frontend/assets/htmlMode-D1PmpoAx.js.gz +0 -0
  134. package/dist/_frontend/assets/index-BRS0kvLQ.js.br +0 -0
  135. package/dist/_frontend/assets/index-BRS0kvLQ.js.gz +0 -0
  136. package/dist/_frontend/assets/index-aEojVaVa.js.br +0 -0
  137. package/dist/_frontend/assets/index-aEojVaVa.js.gz +0 -0
  138. package/dist/_frontend/assets/jsonMode-DWo0prLv.js.br +0 -0
  139. package/dist/_frontend/assets/jsonMode-DWo0prLv.js.gz +0 -0
  140. package/dist/_frontend/assets/liquid-Bs2__P9v.js.br +0 -0
  141. package/dist/_frontend/assets/liquid-Bs2__P9v.js.gz +0 -0
  142. package/dist/_frontend/assets/lspLanguageFeatures-zZ0stYf4.js.br +0 -0
  143. package/dist/_frontend/assets/lspLanguageFeatures-zZ0stYf4.js.gz +0 -0
  144. package/dist/_frontend/assets/mdx-C7c8MDI_.js.br +0 -0
  145. package/dist/_frontend/assets/mdx-C7c8MDI_.js.gz +0 -0
  146. package/dist/_frontend/assets/python-CLO39XAV.js.br +0 -0
  147. package/dist/_frontend/assets/python-CLO39XAV.js.gz +0 -0
  148. package/dist/_frontend/assets/razor-efddsYhk.js.br +0 -0
  149. package/dist/_frontend/assets/razor-efddsYhk.js.gz +0 -0
  150. package/dist/_frontend/assets/tsMode-xfchPgTX.js.br +0 -0
  151. package/dist/_frontend/assets/tsMode-xfchPgTX.js.gz +0 -0
  152. package/dist/_frontend/assets/typescript-BFzaCQP8.js.br +0 -0
  153. package/dist/_frontend/assets/typescript-BFzaCQP8.js.gz +0 -0
  154. package/dist/_frontend/assets/workers-DKwMBzKS.js.br +0 -0
  155. package/dist/_frontend/assets/workers-DKwMBzKS.js.gz +0 -0
  156. package/dist/_frontend/assets/xml-Cx6gHwq3.js.br +0 -0
  157. package/dist/_frontend/assets/xml-Cx6gHwq3.js.gz +0 -0
  158. package/dist/_frontend/assets/yaml-DyrfMWlf.js.br +0 -0
  159. package/dist/_frontend/assets/yaml-DyrfMWlf.js.gz +0 -0
package/dist/main.js CHANGED
@@ -8,28 +8,6 @@ var __export = (target, all) => {
8
8
  __defProp(target, name, { get: all[name], enumerable: true });
9
9
  };
10
10
 
11
- // src/db/agentStore.ts
12
- import { AgentSpecSchema } from "@truefoundry/trueforge-core/agent-session";
13
- function parseStoredAgentSpec(manifest) {
14
- return AgentSpecSchema.parse(manifest);
15
- }
16
- var AgentNameConflictError;
17
- var init_agentStore = __esm({
18
- "src/db/agentStore.ts"() {
19
- "use strict";
20
- AgentNameConflictError = class extends Error {
21
- tenant_id;
22
- agent_name;
23
- constructor({ tenant_id, name }, options) {
24
- super(`Agent name already exists: ${name}`, options);
25
- this.name = "AgentNameConflictError";
26
- this.tenant_id = tenant_id;
27
- this.agent_name = name;
28
- }
29
- };
30
- }
31
- });
32
-
33
11
  // src/config.ts
34
12
  var config_exports = {};
35
13
  __export(config_exports, {
@@ -38,7 +16,8 @@ __export(config_exports, {
38
16
  isOidcConfigured: () => isOidcConfigured,
39
17
  parseOidcScopes: () => parseOidcScopes
40
18
  });
41
- import { existsSync } from "fs";
19
+ import { existsSync as existsSync2 } from "fs";
20
+ import os from "os";
42
21
  import path from "path";
43
22
  import { fileURLToPath } from "url";
44
23
  import envPaths from "env-paths";
@@ -102,7 +81,7 @@ function parseBoolean(options) {
102
81
  }
103
82
  function resolveDefaultFrontendDir() {
104
83
  const packaged = path.join(PACKAGE_ROOT, "dist", "_frontend");
105
- if (existsSync(path.join(packaged, "index.html"))) {
84
+ if (existsSync2(path.join(packaged, "index.html"))) {
106
85
  return packaged;
107
86
  }
108
87
  return path.join(PACKAGE_ROOT, "..", "frontend", "dist");
@@ -129,6 +108,12 @@ function resolveSqlitePath() {
129
108
  const paths = envPaths(ENV_PATHS_APP_NAME, { suffix: "" });
130
109
  return path.join(paths.data, "db", "db.sqlite");
131
110
  }
111
+ function resolveLocalSandboxRootParent() {
112
+ return path.join(envPaths(ENV_PATHS_APP_NAME, { suffix: "" }).data, "sandboxes");
113
+ }
114
+ function resolveCodeModeSocketParent() {
115
+ return path.join(os.tmpdir(), "tf_cms");
116
+ }
132
117
  function resolveRedisUrl() {
133
118
  const raw = getEnv("REDIS_URL", { defaultValue: DEFAULT_REDIS_URL }) ?? DEFAULT_REDIS_URL;
134
119
  if (raw.trim() === "") {
@@ -305,7 +290,9 @@ var init_config = __esm({
305
290
  configuration = standalone ? {
306
291
  ...shared,
307
292
  STANDALONE: true,
308
- SQLITE_PATH: resolveSqlitePath()
293
+ SQLITE_PATH: resolveSqlitePath(),
294
+ LOCAL_SANDBOX_ROOT_PARENT: resolveLocalSandboxRootParent(),
295
+ CODE_MODE_SOCKET_PARENT: resolveCodeModeSocketParent()
309
296
  } : {
310
297
  ...shared,
311
298
  STANDALONE: false,
@@ -329,7 +316,1609 @@ var init_config = __esm({
329
316
  REDIS_URL: resolveRedisUrl(),
330
317
  OIDC: resolveOIDCConfig()
331
318
  };
332
- config_default = configuration;
319
+ config_default = configuration;
320
+ }
321
+ });
322
+
323
+ // src/db/agentStore.ts
324
+ import { AgentSpecSchema } from "@truefoundry/trueforge-core/agent-session";
325
+ function parseStoredAgentSpec(manifest) {
326
+ return AgentSpecSchema.parse(manifest);
327
+ }
328
+ var AgentNameConflictError;
329
+ var init_agentStore = __esm({
330
+ "src/db/agentStore.ts"() {
331
+ "use strict";
332
+ AgentNameConflictError = class extends Error {
333
+ tenant_id;
334
+ agent_name;
335
+ constructor({ tenant_id, name }, options) {
336
+ super(`Agent name already exists: ${name}`, options);
337
+ this.name = "AgentNameConflictError";
338
+ this.tenant_id = tenant_id;
339
+ this.agent_name = name;
340
+ }
341
+ };
342
+ }
343
+ });
344
+
345
+ // src/sandbox/local/sandboxScripts.gen.ts
346
+ var sandboxScripts;
347
+ var init_sandboxScripts_gen = __esm({
348
+ "src/sandbox/local/sandboxScripts.gen.ts"() {
349
+ "use strict";
350
+ sandboxScripts = {
351
+ mcpClientLocal: `#!/usr/bin/env python3
352
+ """Code Mode UDS client \u2014 same public surface as product mcp_client.py, stdlib only.
353
+
354
+ Authoritative reference (do not diverge on API/CLI/policy semantics):
355
+ packages/trueforge-core/src/core/sandbox/scripts/mcp_client.py
356
+
357
+ Inlined into TypeScript via scripts/generate-local-sandbox-scripts.mjs
358
+ (src/sandbox/local/sandboxScripts.gen.ts), same pattern as core sandboxScripts.
359
+ """
360
+
361
+ from __future__ import annotations
362
+
363
+ import argparse
364
+ import asyncio
365
+ import base64
366
+ import json
367
+ import logging
368
+ import os
369
+ import socket
370
+ import sys
371
+ import time
372
+ from pathlib import Path
373
+ from typing import Any, TypedDict
374
+
375
+ logger = logging.getLogger(__name__)
376
+
377
+ MAX_MESSAGE_BYTES = 64 * 1024 * 1024
378
+ _TOOLS_CACHE_TTL_SECONDS = 600
379
+
380
+
381
+ class _ServerConfig(TypedDict):
382
+ allowed_tools: list[str]
383
+
384
+
385
+ _inflight_list_tools: dict[str, asyncio.Task[list[dict[str, Any]]]] = {}
386
+
387
+ _raw_servers = os.environ.get("TFY_MCP_SERVERS")
388
+ _servers_map: dict[str, _ServerConfig] = (
389
+ json.loads(base64.b64decode(_raw_servers).decode()) if _raw_servers else {}
390
+ )
391
+ _enable_agent_approvals = os.environ.get("TFY_ENABLE_AGENT_APPROVALS", "true").lower() == "true"
392
+
393
+
394
+ def _sock_path() -> str:
395
+ path = os.environ.get("TFY_MCP_SOCK")
396
+ if not path:
397
+ raise RuntimeError("TFY_MCP_SOCK is not set")
398
+ return path
399
+
400
+
401
+ def _request_timeout() -> float:
402
+ return float(os.environ.get("TFY_CM_REQUEST_TIMEOUT_SECONDS", "60"))
403
+
404
+
405
+ def _check_tool_allowed(server: str, tool_name: str) -> None:
406
+ server_config = _servers_map.get(server)
407
+ if server_config is None:
408
+ raise RuntimeError(f"Access denied: MCP server '{server}' is not available for this agent") from None
409
+ server_tools = server_config.get("allowed_tools") or []
410
+ if len(server_tools) > 0 and tool_name not in server_tools:
411
+ raise RuntimeError(f"Access denied: tool '{tool_name}' is not enabled on server '{server}'") from None
412
+
413
+
414
+ def _cache_path(server: str) -> Path:
415
+ return Path(__file__).parent / f"{server}.tools.json"
416
+
417
+
418
+ def _read_tools_cache(server: str) -> list[dict[str, Any]] | None:
419
+ p = _cache_path(server)
420
+ if not p.exists():
421
+ return None
422
+ try:
423
+ raw = json.loads(p.read_text(encoding="utf-8"))
424
+ if not isinstance(raw, dict):
425
+ raise ValueError("cache root must be object")
426
+ fetched_at = raw.get("fetched_at")
427
+ tools = raw.get("tools")
428
+ if not isinstance(fetched_at, (int, float)) or not isinstance(tools, list):
429
+ raise ValueError("cache shape invalid")
430
+ if time.time() - float(fetched_at) > _TOOLS_CACHE_TTL_SECONDS:
431
+ p.unlink(missing_ok=True)
432
+ return None
433
+ return [t for t in tools if isinstance(t, dict)]
434
+ except Exception:
435
+ logger.exception("_read_tools_cache")
436
+ p.unlink(missing_ok=True)
437
+ return None
438
+
439
+
440
+ def _write_tools_cache(server: str, tools: list[dict[str, Any]]) -> None:
441
+ try:
442
+ payload = {"fetched_at": time.time(), "tools": tools}
443
+ _cache_path(server).write_text(json.dumps(payload), encoding="utf-8")
444
+ except Exception:
445
+ logger.exception("_write_tools_cache")
446
+
447
+
448
+ def _read_message(sock: socket.socket) -> Any:
449
+ body = b""
450
+ while True:
451
+ chunk = sock.recv(65536)
452
+ if not chunk:
453
+ break
454
+ body += chunk
455
+ if len(body) > MAX_MESSAGE_BYTES:
456
+ raise RuntimeError(f"message exceeds max {MAX_MESSAGE_BYTES} bytes")
457
+ return json.loads(body.decode("utf-8"))
458
+
459
+
460
+ def _write_message(sock: socket.socket, value: Any) -> None:
461
+ sock.sendall(json.dumps(value).encode("utf-8"))
462
+
463
+
464
+ def _uds_request_sync(payload: dict[str, Any]) -> Any:
465
+ """Connect \u2192 JSON request \u2192 write-close \u2192 JSON reply (no request_id)."""
466
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
467
+ try:
468
+ sock.settimeout(_request_timeout())
469
+ sock.connect(_sock_path())
470
+ _write_message(sock, payload)
471
+ sock.shutdown(socket.SHUT_WR)
472
+ reply = _read_message(sock)
473
+ finally:
474
+ sock.close()
475
+ if not isinstance(reply, dict):
476
+ raise RuntimeError(f"Code Mode reply is not an object: {reply!r}")
477
+ ok = reply.get("ok")
478
+ if ok is not True:
479
+ source = reply.get("source", "internal")
480
+ error = reply.get("error", "unknown error")
481
+ if source == "caller":
482
+ raise RuntimeError(f"Invalid MCP request: {error}")
483
+ if source == "transport":
484
+ raise RuntimeError(f"Code Mode transport error: {error}")
485
+ raise RuntimeError(f"Internal MCP error: {error}")
486
+ return reply.get("result")
487
+
488
+
489
+ async def _uds_request(payload: dict[str, Any]) -> Any:
490
+ return await asyncio.to_thread(_uds_request_sync, payload)
491
+
492
+
493
+ async def _fetch_tools(server: str) -> list[dict[str, Any]]:
494
+ result = await _uds_request({"op": "list_tools", "server": server})
495
+ if not isinstance(result, dict):
496
+ raise RuntimeError(f"list_tools '{server}' returned unexpected shape: {result!r}") from None
497
+ tools = result.get("tools", [])
498
+ if not isinstance(tools, list):
499
+ raise RuntimeError(f"list_tools '{server}' tools is not a list: {tools!r}") from None
500
+ return [t for t in tools if isinstance(t, dict)]
501
+
502
+
503
+ async def _fetch_and_cache_tools(server: str) -> list[dict[str, Any]]:
504
+ tools = await _fetch_tools(server)
505
+ _write_tools_cache(server, tools)
506
+ return tools
507
+
508
+
509
+ async def _get_tools(server: str) -> list[dict[str, Any]]:
510
+ cached = _read_tools_cache(server)
511
+ if cached is not None:
512
+ return cached
513
+ task = _inflight_list_tools.get(server)
514
+ if task is None:
515
+ task = asyncio.create_task(_fetch_and_cache_tools(server))
516
+ _inflight_list_tools[server] = task
517
+ task.add_done_callback(lambda _t: _inflight_list_tools.pop(server, None))
518
+ return await task
519
+
520
+
521
+ async def _get_tool(server: str, tool_name: str) -> dict[str, Any] | None:
522
+ for t in await _get_tools(server):
523
+ if t.get("name") == tool_name:
524
+ return t
525
+ return None
526
+
527
+
528
+ def _is_destructive(tool: dict[str, Any]) -> bool:
529
+ annotations = tool.get("annotations")
530
+ if annotations is None:
531
+ return False
532
+ if not isinstance(annotations, dict):
533
+ return False
534
+ destructive = annotations.get("destructiveHint")
535
+ read_only = annotations.get("readOnlyHint")
536
+ return bool(destructive) or (not read_only and read_only is not None)
537
+
538
+
539
+ async def _ensure_non_destructive(server: str, tool_name: str) -> None:
540
+ tool = await _get_tool(server, tool_name)
541
+ if tool is None:
542
+ raise RuntimeError(f"Tool '{tool_name}' not found on MCP server '{server}'") from None
543
+ if _is_destructive(tool):
544
+ raise RuntimeError(
545
+ f"Tool '{tool_name}' on MCP server '{server}' is destructive and cannot be called in Code Mode; "
546
+ f"call it directly so it can go through the user approval flow"
547
+ ) from None
548
+
549
+
550
+ def _project_call_tool_result(server: str, tool: str, result: Any) -> Any:
551
+ """Project an MCP-wire CallToolResult-shaped object into the user-facing Python value."""
552
+ if not isinstance(result, dict):
553
+ raise RuntimeError(f"call_tool reply for '{server}/{tool}' is malformed: expected object") from None
554
+
555
+ if result.get("isError"):
556
+ content = result.get("content")
557
+ text_parts: list[str] = []
558
+ if isinstance(content, list):
559
+ for c in content:
560
+ if isinstance(c, dict) and c.get("type") == "text":
561
+ text = c.get("text")
562
+ if isinstance(text, str) and text:
563
+ text_parts.append(text)
564
+ msg = "; ".join(text_parts) if text_parts else "tool returned an error"
565
+ raise RuntimeError(f"MCP tool error (server={server}, tool={tool}): {msg}") from None
566
+
567
+ if result.get("structuredContent") is not None:
568
+ return result["structuredContent"]
569
+
570
+ content = result.get("content")
571
+ if isinstance(content, list) and len(content) == 1:
572
+ first = content[0]
573
+ if isinstance(first, dict) and first.get("type") == "text":
574
+ text = first.get("text")
575
+ if isinstance(text, str) and text:
576
+ try:
577
+ return json.loads(text)
578
+ except Exception:
579
+ pass
580
+ if isinstance(content, list) and content:
581
+ return content
582
+ return None
583
+
584
+
585
+ async def call_tool(server: str, tool: str, body: dict[str, Any]) -> Any:
586
+ _check_tool_allowed(server, tool)
587
+ if _enable_agent_approvals:
588
+ await _ensure_non_destructive(server, tool)
589
+
590
+ raw = await _uds_request(
591
+ {
592
+ "op": "call_tool",
593
+ "server": server,
594
+ "tool": tool,
595
+ "arguments": body,
596
+ },
597
+ )
598
+ return _project_call_tool_result(server, tool, raw)
599
+
600
+
601
+ _USAGE = "mcp_client_local.py call-tool <server> <tool> <args-json>"
602
+
603
+
604
+ def _build_arg_parser() -> argparse.ArgumentParser:
605
+ parser = argparse.ArgumentParser(prog="mcp_client_local.py", usage=_USAGE)
606
+ sub = parser.add_subparsers(dest="cmd", required=True)
607
+
608
+ call_tool_p = sub.add_parser("call-tool", help="Invoke an MCP tool")
609
+ call_tool_p.add_argument("server")
610
+ call_tool_p.add_argument("tool")
611
+ call_tool_p.add_argument("args_json", metavar="args-json", type=json.loads)
612
+
613
+ return parser
614
+
615
+
616
+ async def _main() -> None:
617
+ args = _build_arg_parser().parse_args()
618
+ try:
619
+ if args.cmd == "call-tool":
620
+ result = await call_tool(args.server, args.tool, args.args_json)
621
+ print(json.dumps(result, default=str))
622
+ except RuntimeError as e:
623
+ sys.exit(str(e))
624
+
625
+
626
+ if __name__ == "__main__":
627
+ asyncio.run(_main())
628
+ `
629
+ };
630
+ }
631
+ });
632
+
633
+ // src/sandbox/local/schemas/jsonMessage.ts
634
+ import { z as z5 } from "zod";
635
+ var JsonMessageValueSchema;
636
+ var init_jsonMessage = __esm({
637
+ "src/sandbox/local/schemas/jsonMessage.ts"() {
638
+ "use strict";
639
+ JsonMessageValueSchema = z5.json();
640
+ }
641
+ });
642
+
643
+ // src/sandbox/local/core/frame.ts
644
+ function encodeJsonMessage(value) {
645
+ return Buffer.from(JSON.stringify(value), "utf8");
646
+ }
647
+ var MAX_MESSAGE_BYTES, JsonMessageReader;
648
+ var init_frame = __esm({
649
+ "src/sandbox/local/core/frame.ts"() {
650
+ "use strict";
651
+ init_jsonMessage();
652
+ MAX_MESSAGE_BYTES = 64 * 1024 * 1024;
653
+ JsonMessageReader = class {
654
+ #buffer = Buffer.alloc(0);
655
+ #maxBytes;
656
+ constructor(options = {}) {
657
+ this.#maxBytes = options.maxBytes ?? MAX_MESSAGE_BYTES;
658
+ }
659
+ push(chunk) {
660
+ if (this.#buffer.length + chunk.length > this.#maxBytes) {
661
+ throw new Error(`message exceeds max ${String(this.#maxBytes)} bytes`);
662
+ }
663
+ this.#buffer = Buffer.concat([this.#buffer, chunk]);
664
+ }
665
+ finish() {
666
+ try {
667
+ return JsonMessageValueSchema.parse(JSON.parse(this.#buffer.toString("utf8")));
668
+ } catch (error) {
669
+ throw new Error("invalid JSON message", { cause: error });
670
+ }
671
+ }
672
+ };
673
+ }
674
+ });
675
+
676
+ // src/sandbox/local/core/CodeModeUdsTransport.ts
677
+ import { CodeModeRequestSchema, validateNoPathTraversal } from "@truefoundry/trueforge-core/core";
678
+ import { chmodSync, existsSync as existsSync3, realpathSync, statSync } from "fs";
679
+ import { chmod, mkdir as mkdir2, rm as rm2, symlink, unlink, writeFile } from "fs/promises";
680
+ import { createServer } from "net";
681
+ import { dirname, isAbsolute, join, resolve } from "path";
682
+ import { ulid } from "ulid";
683
+ function assertCodeModeSocketParentPath(path7) {
684
+ if (!isAbsolute(path7)) {
685
+ throw new Error("codeModeSocketParentPath must be an absolute path");
686
+ }
687
+ validateNoPathTraversal(path7);
688
+ const resolved = resolve(path7);
689
+ if (!existsSync3(resolved) || !statSync(resolved).isDirectory()) {
690
+ throw new Error("codeModeSocketParentPath must be an existing directory");
691
+ }
692
+ const real = realpathSync(resolved);
693
+ const bytes = Buffer.byteLength(real);
694
+ if (bytes > MAX_CODE_MODE_SOCKET_PARENT_BYTES) {
695
+ throw new Error(
696
+ `codeModeSocketParentPath (${real}) must be at most ${String(MAX_CODE_MODE_SOCKET_PARENT_BYTES)} bytes (got ${String(bytes)})`
697
+ );
698
+ }
699
+ chmodSync(real, CODE_MODE_SOCKET_PARENT_MODE);
700
+ const mode = statSync(real).mode & 511;
701
+ if (mode !== CODE_MODE_SOCKET_PARENT_MODE) {
702
+ throw new Error(`codeModeSocketParentPath must be mode 0700 after chmod (got 0o${mode.toString(8)})`);
703
+ }
704
+ return real;
705
+ }
706
+ async function probeCodeModeUnixSocket(parentPath) {
707
+ const realParent = assertCodeModeSocketParentPath(parentPath);
708
+ const sockPath = join(realParent, ulid().toLowerCase());
709
+ await unlink(sockPath).catch(() => void 0);
710
+ const server = createServer();
711
+ try {
712
+ await new Promise((resolveListen, reject) => {
713
+ server.once("error", reject);
714
+ server.listen(sockPath, () => {
715
+ server.off("error", reject);
716
+ resolveListen();
717
+ });
718
+ });
719
+ } catch (error) {
720
+ const message = error instanceof Error ? error.message : String(error);
721
+ throw new Error(`listen ${sockPath} (${String(Buffer.byteLength(sockPath))} bytes): ${message}`, { cause: error });
722
+ } finally {
723
+ await new Promise((resolveClose) => {
724
+ server.close(() => {
725
+ resolveClose();
726
+ });
727
+ });
728
+ await unlink(sockPath).catch(() => void 0);
729
+ }
730
+ return { sockPath };
731
+ }
732
+ var MAX_CODE_MODE_SOCKET_PARENT_BYTES, CODE_MODE_SOCKET_PARENT_MODE, CODE_MODE_SOCKET_MODE, CodeModeUdsTransport;
733
+ var init_CodeModeUdsTransport = __esm({
734
+ "src/sandbox/local/core/CodeModeUdsTransport.ts"() {
735
+ "use strict";
736
+ init_sandboxScripts_gen();
737
+ init_frame();
738
+ MAX_CODE_MODE_SOCKET_PARENT_BYTES = 65;
739
+ CODE_MODE_SOCKET_PARENT_MODE = 448;
740
+ CODE_MODE_SOCKET_MODE = 384;
741
+ CodeModeUdsTransport = class {
742
+ codeModeSocketParentPath;
743
+ maxMessageBytes;
744
+ onProtocolError;
745
+ clientRemotePath;
746
+ sessionPromise;
747
+ server;
748
+ sockPath;
749
+ dispatcher;
750
+ cachedEnv;
751
+ constructor(options) {
752
+ this.codeModeSocketParentPath = assertCodeModeSocketParentPath(options.codeModeSocketParentPath);
753
+ this.maxMessageBytes = options.maxMessageBytes ?? MAX_MESSAGE_BYTES;
754
+ this.onProtocolError = options.onProtocolError;
755
+ this.clientRemotePath = options.clientRemotePath;
756
+ }
757
+ getClientInstall(params) {
758
+ return {
759
+ content: sandboxScripts.mcpClientLocal,
760
+ remotePath: this.clientRemotePath(params.sandboxId)
761
+ };
762
+ }
763
+ start(params) {
764
+ this.dispatcher = params.codeModeDispatcher;
765
+ this.sessionPromise ??= this.listenSession(params).catch((e) => {
766
+ this.sessionPromise = void 0;
767
+ this.cachedEnv = void 0;
768
+ throw e;
769
+ });
770
+ return this.sessionPromise;
771
+ }
772
+ async stop() {
773
+ const pending = this.sessionPromise;
774
+ this.sessionPromise = void 0;
775
+ this.cachedEnv = void 0;
776
+ if (pending !== void 0) {
777
+ try {
778
+ await pending;
779
+ } catch {
780
+ }
781
+ }
782
+ const server = this.server;
783
+ const sockPath = this.sockPath;
784
+ this.server = void 0;
785
+ this.sockPath = void 0;
786
+ if (server !== void 0) {
787
+ await new Promise((resolveClose) => {
788
+ server.close(() => {
789
+ resolveClose();
790
+ });
791
+ });
792
+ }
793
+ if (sockPath !== void 0) {
794
+ await unlink(sockPath).catch(() => void 0);
795
+ }
796
+ }
797
+ async listenSession(params) {
798
+ if (this.server !== void 0 && this.cachedEnv !== void 0) {
799
+ return { env: this.cachedEnv };
800
+ }
801
+ assertCodeModeSocketParentPath(this.codeModeSocketParentPath);
802
+ const sockPath = join(this.codeModeSocketParentPath, ulid().toLowerCase());
803
+ await unlink(sockPath).catch(() => void 0);
804
+ const server = createServer({ allowHalfOpen: true });
805
+ try {
806
+ await new Promise((resolveListen, reject) => {
807
+ server.once("error", reject);
808
+ server.listen(sockPath, () => {
809
+ server.off("error", reject);
810
+ resolveListen();
811
+ });
812
+ });
813
+ await chmod(sockPath, CODE_MODE_SOCKET_MODE);
814
+ } catch (error) {
815
+ server.close();
816
+ await unlink(sockPath).catch(() => void 0);
817
+ throw error;
818
+ }
819
+ this.server = server;
820
+ this.sockPath = sockPath;
821
+ server.on("connection", (socket) => {
822
+ this.handleConnection(socket);
823
+ });
824
+ const env = {
825
+ TFY_MCP_SOCK: sockPath,
826
+ TFY_CM_REQUEST_TIMEOUT_SECONDS: String(params.requestTimeoutSeconds)
827
+ };
828
+ this.cachedEnv = env;
829
+ return { env };
830
+ }
831
+ handleConnection(socket) {
832
+ const reader = new JsonMessageReader({ maxBytes: this.maxMessageBytes });
833
+ let settled = false;
834
+ socket.on("error", () => void 0);
835
+ const fail = (message) => {
836
+ if (settled) {
837
+ return;
838
+ }
839
+ settled = true;
840
+ this.onProtocolError?.(message);
841
+ socket.destroy();
842
+ };
843
+ socket.on("data", (chunk) => {
844
+ try {
845
+ reader.push(chunk);
846
+ } catch (error) {
847
+ fail(error instanceof Error ? error.message : String(error));
848
+ }
849
+ });
850
+ socket.on("end", () => {
851
+ if (settled) {
852
+ return;
853
+ }
854
+ settled = true;
855
+ void this.dispatchConnection(socket, reader).catch((error) => {
856
+ this.onProtocolError?.(error instanceof Error ? error.message : String(error));
857
+ socket.destroy();
858
+ });
859
+ });
860
+ }
861
+ async dispatchConnection(socket, reader) {
862
+ let request;
863
+ try {
864
+ const parsed = CodeModeRequestSchema.safeParse(reader.finish());
865
+ if (!parsed.success) {
866
+ const reply2 = {
867
+ ok: false,
868
+ error: "Malformed Code Mode request",
869
+ source: "caller"
870
+ };
871
+ socket.write(encodeJsonMessage(reply2));
872
+ socket.end();
873
+ return;
874
+ }
875
+ request = parsed.data;
876
+ } catch (error) {
877
+ this.onProtocolError?.(error instanceof Error ? error.message : String(error));
878
+ socket.destroy();
879
+ return;
880
+ }
881
+ const dispatcher = this.dispatcher;
882
+ if (dispatcher === void 0) {
883
+ const reply2 = {
884
+ ok: false,
885
+ error: "Code Mode dispatcher is not configured",
886
+ source: "internal"
887
+ };
888
+ socket.write(encodeJsonMessage(reply2));
889
+ socket.end();
890
+ return;
891
+ }
892
+ const reply = await dispatcher.dispatch({ request, traceCarrier: {} });
893
+ try {
894
+ socket.write(encodeJsonMessage(reply));
895
+ } finally {
896
+ socket.end();
897
+ }
898
+ }
899
+ };
900
+ }
901
+ });
902
+
903
+ // src/sandbox/local/core/hostRun.ts
904
+ import { getDefaultWritePaths, SandboxManager } from "@anthropic-ai/sandbox-runtime";
905
+ import { execFile, spawn } from "child_process";
906
+ import { randomUUID } from "crypto";
907
+ import { realpathSync as realpathSync2 } from "fs";
908
+ import { mkdir as mkdir3, rm as rm3 } from "fs/promises";
909
+ import { createRequire } from "module";
910
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2 } from "path";
911
+ import { promisify } from "util";
912
+ function codeModeSocketParentAllow() {
913
+ return codeModeSocketParentPath === void 0 ? [] : [codeModeSocketParentPath];
914
+ }
915
+ function requireActivePlatform() {
916
+ if (activePlatform === void 0) {
917
+ throw new Error("SRT platform is not set; call initSrt({ platform }) first");
918
+ }
919
+ return activePlatform;
920
+ }
921
+ function commandPath(platform) {
922
+ return COMMAND_PATH_BY_PLATFORM[platform];
923
+ }
924
+ function sandboxVenvPath(sandboxRootPath) {
925
+ return join2(sandboxRootPath, SANDBOX_VENV_DIR);
926
+ }
927
+ function allowedNetworkDomains() {
928
+ return [...LOCAL_SANDBOX_ALLOWED_DOMAINS];
929
+ }
930
+ function darwinSandboxNetworkShellPreamble() {
931
+ return [
932
+ 'if [ -n "${HTTP_PROXY:-}" ]; then export HTTP_PROXY="${HTTP_PROXY//localhost/127.0.0.1}"; fi',
933
+ 'if [ -n "${HTTPS_PROXY:-}" ]; then export HTTPS_PROXY="${HTTPS_PROXY//localhost/127.0.0.1}"; fi',
934
+ 'if [ -n "${http_proxy:-}" ]; then export http_proxy="${http_proxy//localhost/127.0.0.1}"; fi',
935
+ 'if [ -n "${https_proxy:-}" ]; then export https_proxy="${https_proxy//localhost/127.0.0.1}"; fi',
936
+ 'if [ -n "${ALL_PROXY:-}" ]; then export ALL_PROXY="${ALL_PROXY//localhost/127.0.0.1}"; fi',
937
+ 'if [ -n "${all_proxy:-}" ]; then export all_proxy="${all_proxy//localhost/127.0.0.1}"; fi',
938
+ 'if [ -n "${GRPC_PROXY:-}" ]; then export GRPC_PROXY="${GRPC_PROXY//localhost/127.0.0.1}"; fi',
939
+ 'if [ -n "${grpc_proxy:-}" ]; then export grpc_proxy="${grpc_proxy//localhost/127.0.0.1}"; fi'
940
+ ].join("; ");
941
+ }
942
+ function wrapSandboxCommand(params) {
943
+ if (params.platform !== "darwin") {
944
+ return params.command;
945
+ }
946
+ return `${darwinSandboxNetworkShellPreamble()}; ${params.command}`;
947
+ }
948
+ function srtHostBinaryNames(platform) {
949
+ return SRT_HOST_BINARIES_BY_PLATFORM[platform];
950
+ }
951
+ async function checkSrtHostDependencies() {
952
+ const result = await SandboxManager.checkDependenciesAsync();
953
+ return { errors: [...result.errors], warnings: [...result.warnings] };
954
+ }
955
+ async function resolveCommandOnHost(params) {
956
+ if (!/^[A-Za-z0-9._+-]+$/.test(params.name)) {
957
+ throw new Error(`invalid command name for resolveCommandOnHost: ${params.name}`);
958
+ }
959
+ const pathEnv = params.pathEnv !== void 0 && params.pathEnv.length > 0 ? params.pathEnv : commandPath(params.platform);
960
+ try {
961
+ const { stdout } = await execFileAsync("/bin/sh", ["-c", `command -v -- ${params.name}`], {
962
+ env: { PATH: pathEnv },
963
+ encoding: "utf8"
964
+ });
965
+ const resolved = stdout.trim().split(/\r?\n/).filter(Boolean).at(-1);
966
+ if (resolved === void 0 || resolved.length === 0 || !isAbsolute2(resolved)) {
967
+ return void 0;
968
+ }
969
+ return resolved;
970
+ } catch {
971
+ return void 0;
972
+ }
973
+ }
974
+ async function resolvePythonExecutableOnHost(params) {
975
+ if (!isAbsolute2(params.commandPath)) {
976
+ return void 0;
977
+ }
978
+ try {
979
+ const { stdout } = await execFileAsync(params.commandPath, ["-c", "import sys; print(sys.executable)"], {
980
+ encoding: "utf8",
981
+ timeout: 5e3
982
+ });
983
+ const executable = stdout.trim().split(/\r?\n/).filter(Boolean).at(-1);
984
+ if (executable === void 0 || executable.length === 0 || !isAbsolute2(executable)) {
985
+ return void 0;
986
+ }
987
+ return realpathSync2(executable);
988
+ } catch {
989
+ return void 0;
990
+ }
991
+ }
992
+ function denySharedDefaultWritePaths() {
993
+ return getDefaultWritePaths().filter((path7) => !path7.startsWith("/dev/"));
994
+ }
995
+ function platformAllowRead(platform) {
996
+ return [...ALLOW_READ_BY_PLATFORM[platform]];
997
+ }
998
+ function filesystemPolicy(params) {
999
+ return {
1000
+ allowWrite: [params.sandboxRootPath],
1001
+ denyWrite: denySharedDefaultWritePaths(),
1002
+ denyRead: ["/"],
1003
+ allowRead: [params.sandboxRootPath, ...codeModeSocketParentAllow(), ...platformAllowRead(params.platform)]
1004
+ };
1005
+ }
1006
+ function commandEnv(params) {
1007
+ const tmp = join2(params.sandboxRootPath, ".tmp");
1008
+ const home = join2(params.sandboxRootPath, ".home");
1009
+ const venvDir = sandboxVenvPath(params.sandboxRootPath);
1010
+ const locked = {
1011
+ HOME: home,
1012
+ TMPDIR: tmp,
1013
+ TMP: tmp,
1014
+ TEMP: tmp,
1015
+ VIRTUAL_ENV: venvDir,
1016
+ // Cwd-relative layout dirs (not host-absolute). Absolute paths under
1017
+ // `Application Support` break PATH lookup (`mcp-client: command not found`).
1018
+ PATH: `${join2(SANDBOX_VENV_DIR, "bin")}:mcp-client/bin:${commandPath(params.platform)}`
1019
+ };
1020
+ return {
1021
+ ...params.extra,
1022
+ ...locked
1023
+ };
1024
+ }
1025
+ function sessionFilesystem(platform) {
1026
+ const allowWrite = [];
1027
+ return {
1028
+ allowWrite,
1029
+ denyWrite: denySharedDefaultWritePaths(),
1030
+ denyRead: ["/"],
1031
+ allowRead: [...platformAllowRead(platform), ...codeModeSocketParentAllow()]
1032
+ };
1033
+ }
1034
+ function sessionNetwork(params) {
1035
+ const allowedDomains = allowedNetworkDomains();
1036
+ const deniedDomains = [];
1037
+ if (params.platform === "linux") {
1038
+ return {
1039
+ allowedDomains,
1040
+ deniedDomains,
1041
+ allowAllUnixSockets: true
1042
+ };
1043
+ }
1044
+ return {
1045
+ allowedDomains,
1046
+ deniedDomains,
1047
+ allowAllUnixSockets: false,
1048
+ allowUnixSockets: params.unixSockets ?? []
1049
+ };
1050
+ }
1051
+ function darwinUnixSocketPaths() {
1052
+ return [...darwinUnixSocketSandboxRoots, ...codeModeSocketParentAllow()];
1053
+ }
1054
+ function syncDarwinUnixSockets() {
1055
+ if (SandboxManager.getConfig() === void 0) {
1056
+ return;
1057
+ }
1058
+ const platform = requireActivePlatform();
1059
+ if (platform !== "darwin") {
1060
+ return;
1061
+ }
1062
+ SandboxManager.updateConfig(buildSessionConfig(platform));
1063
+ }
1064
+ function buildSessionConfig(platform) {
1065
+ return {
1066
+ network: sessionNetwork({ platform, unixSockets: darwinUnixSocketPaths() }),
1067
+ filesystem: sessionFilesystem(platform)
1068
+ };
1069
+ }
1070
+ async function createSandbox(sandboxRootPath) {
1071
+ await mkdir3(sandboxRootPath, { recursive: true, mode: 448 });
1072
+ await mkdir3(join2(sandboxRootPath, ".tmp"), { recursive: true, mode: 448 });
1073
+ await mkdir3(join2(sandboxRootPath, ".home"), { recursive: true, mode: 448 });
1074
+ const realRoot = realpathSync2(sandboxRootPath);
1075
+ darwinUnixSocketSandboxRoots.add(realRoot);
1076
+ syncDarwinUnixSockets();
1077
+ return realRoot;
1078
+ }
1079
+ async function removeSandbox(sandboxRootPath) {
1080
+ darwinUnixSocketSandboxRoots.delete(sandboxRootPath);
1081
+ syncDarwinUnixSockets();
1082
+ await rm3(sandboxRootPath, { recursive: true, force: true });
1083
+ }
1084
+ async function initSrt(params) {
1085
+ activePlatform = params.platform;
1086
+ codeModeSocketParentPath = params.codeModeSocketParentPath === void 0 ? void 0 : realpathSync2(params.codeModeSocketParentPath);
1087
+ await SandboxManager.initialize(buildSessionConfig(params.platform));
1088
+ }
1089
+ async function resetSrt() {
1090
+ codeModeSocketParentPath = void 0;
1091
+ activePlatform = void 0;
1092
+ await SandboxManager.reset();
1093
+ }
1094
+ function isSrtInitialized() {
1095
+ return activePlatform !== void 0 && SandboxManager.getConfig() !== void 0;
1096
+ }
1097
+ function killExecTree(child) {
1098
+ if (!child) {
1099
+ return;
1100
+ }
1101
+ const pid = child.pid;
1102
+ if (pid !== void 0 && process.platform !== "win32") {
1103
+ try {
1104
+ process.kill(-pid, "SIGKILL");
1105
+ return;
1106
+ } catch {
1107
+ }
1108
+ }
1109
+ if (!child.killed) {
1110
+ child.kill("SIGKILL");
1111
+ }
1112
+ }
1113
+ async function runSupervisorSession(params) {
1114
+ const {
1115
+ sandboxRootPath,
1116
+ command: rawCommand,
1117
+ shell,
1118
+ platform,
1119
+ cwd = sandboxRootPath,
1120
+ env,
1121
+ stdin,
1122
+ onChildSpawn,
1123
+ timeoutMs
1124
+ } = params;
1125
+ const command = wrapSandboxCommand({ platform, command: rawCommand });
1126
+ const wrap = await SandboxManager.wrapWithSandboxArgv(
1127
+ command,
1128
+ shell,
1129
+ {
1130
+ filesystem: filesystemPolicy({ sandboxRootPath, platform }),
1131
+ network: {
1132
+ allowedDomains: allowedNetworkDomains(),
1133
+ deniedDomains: []
1134
+ }
1135
+ },
1136
+ void 0,
1137
+ sandboxRootPath,
1138
+ { commandId: randomUUID(), commandText: command }
1139
+ );
1140
+ const [argv0, ...argvRest] = wrap.argv;
1141
+ if (argv0 === void 0) {
1142
+ throw new Error("wrapWithSandboxArgv returned empty argv");
1143
+ }
1144
+ const childEnv = {
1145
+ ...commandEnv({ sandboxRootPath, platform, ...env === void 0 ? {} : { extra: env } })
1146
+ };
1147
+ const child = spawn(argv0, argvRest, {
1148
+ cwd,
1149
+ env: childEnv,
1150
+ shell: false,
1151
+ // Detached process groups break stdin forwarding for upload (`cat` via pipe) under Jest.
1152
+ detached: stdin === void 0 && process.platform !== "win32",
1153
+ stdio: [stdin === void 0 ? "ignore" : "pipe", "pipe", "pipe"]
1154
+ });
1155
+ if (child.pid !== void 0) {
1156
+ onChildSpawn?.(child.pid);
1157
+ }
1158
+ if (stdin !== void 0) {
1159
+ const stdinStream = child.stdin;
1160
+ if (stdinStream === null) {
1161
+ killExecTree(child);
1162
+ SandboxManager.cleanupAfterCommand();
1163
+ throw new Error("stdin unavailable for sandboxed command");
1164
+ }
1165
+ stdinStream.on("error", () => void 0);
1166
+ await new Promise((resolve3, reject) => {
1167
+ stdinStream.end(stdin, (error) => {
1168
+ if (error) {
1169
+ reject(error);
1170
+ } else {
1171
+ resolve3();
1172
+ }
1173
+ });
1174
+ });
1175
+ }
1176
+ let stdoutText = "";
1177
+ let stderrText = "";
1178
+ let bufferedOutput = 0;
1179
+ let protocolError;
1180
+ let timedOut = false;
1181
+ let closed = false;
1182
+ const ignoreStreamError = (stream) => {
1183
+ stream?.on("error", () => void 0);
1184
+ };
1185
+ const appendOutput = (stream, chunk) => {
1186
+ bufferedOutput += chunk.length;
1187
+ if (bufferedOutput > MAX_OUTPUT_BYTES) {
1188
+ protocolError = `buffered output exceeded ${String(MAX_OUTPUT_BYTES)} bytes`;
1189
+ killExecTree(child);
1190
+ return;
1191
+ }
1192
+ const text = chunk.toString("utf8");
1193
+ if (stream === "stdout") {
1194
+ stdoutText += text;
1195
+ } else {
1196
+ stderrText += text;
1197
+ }
1198
+ };
1199
+ ignoreStreamError(child.stdout);
1200
+ ignoreStreamError(child.stderr);
1201
+ child.stdout?.on("data", (chunk) => {
1202
+ appendOutput("stdout", chunk);
1203
+ });
1204
+ child.stderr?.on("data", (chunk) => {
1205
+ appendOutput("stderr", chunk);
1206
+ });
1207
+ return await new Promise((resolve3, reject) => {
1208
+ const timer = setTimeout(() => {
1209
+ timedOut = true;
1210
+ killExecTree(child);
1211
+ }, timeoutMs);
1212
+ child.on("error", (error) => {
1213
+ if (closed) {
1214
+ return;
1215
+ }
1216
+ closed = true;
1217
+ clearTimeout(timer);
1218
+ SandboxManager.cleanupAfterCommand();
1219
+ reject(error);
1220
+ });
1221
+ child.on("close", (code) => {
1222
+ if (closed) {
1223
+ return;
1224
+ }
1225
+ closed = true;
1226
+ clearTimeout(timer);
1227
+ SandboxManager.cleanupAfterCommand();
1228
+ resolve3({
1229
+ stdoutText,
1230
+ stderrText,
1231
+ exitCode: typeof code === "number" ? code : timedOut ? 1 : 0,
1232
+ protocolError,
1233
+ timedOut,
1234
+ childPid: child.pid
1235
+ });
1236
+ });
1237
+ });
1238
+ }
1239
+ var execFileAsync, SRT_VENDOR, MAX_OUTPUT_BYTES, activePlatform, codeModeSocketParentPath, COMMAND_PATH_BY_PLATFORM, SANDBOX_VENV_DIR, LOCAL_SANDBOX_ALLOWED_DOMAINS, SRT_HOST_BINARIES_BY_PLATFORM, ALLOW_READ_BY_PLATFORM, darwinUnixSocketSandboxRoots;
1240
+ var init_hostRun = __esm({
1241
+ "src/sandbox/local/core/hostRun.ts"() {
1242
+ "use strict";
1243
+ execFileAsync = promisify(execFile);
1244
+ SRT_VENDOR = join2(
1245
+ dirname2(createRequire(import.meta.url).resolve("@anthropic-ai/sandbox-runtime/package.json")),
1246
+ "vendor"
1247
+ );
1248
+ MAX_OUTPUT_BYTES = 14 * 1024 * 1024;
1249
+ COMMAND_PATH_BY_PLATFORM = {
1250
+ // On macOS, prefer Homebrew ahead of `/usr/bin` shims (those need Xcode select
1251
+ // paths that we intentionally do not allow-read).
1252
+ darwin: "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin",
1253
+ linux: "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
1254
+ };
1255
+ SANDBOX_VENV_DIR = ".venv";
1256
+ LOCAL_SANDBOX_ALLOWED_DOMAINS = [
1257
+ "pypi.org",
1258
+ "*.pypi.org",
1259
+ "pythonhosted.org",
1260
+ "files.pythonhosted.org",
1261
+ "*.pythonhosted.org",
1262
+ "github.com",
1263
+ "api.github.com",
1264
+ "codeload.github.com",
1265
+ "*.github.com",
1266
+ "githubusercontent.com",
1267
+ "raw.githubusercontent.com",
1268
+ "objects.githubusercontent.com",
1269
+ "*.githubusercontent.com"
1270
+ ];
1271
+ SRT_HOST_BINARIES_BY_PLATFORM = {
1272
+ linux: ["bwrap", "socat", "rg"],
1273
+ darwin: []
1274
+ };
1275
+ ALLOW_READ_BY_PLATFORM = {
1276
+ darwin: [
1277
+ "/opt/homebrew/bin",
1278
+ "/usr/local",
1279
+ "/usr/bin",
1280
+ "/bin",
1281
+ "/usr/sbin",
1282
+ "/sbin",
1283
+ "/usr/lib",
1284
+ "/System/Library",
1285
+ "/Library",
1286
+ "/private/var/db/dyld",
1287
+ "/private/var/select",
1288
+ "/opt/homebrew",
1289
+ "/dev",
1290
+ // Darwin `/etc` is a symlink to `/private/etc`. SRT refuses that realpath as
1291
+ // "outside boundary" and keeps `(subpath "/etc")`. Seatbelt still checks the
1292
+ // resolved path, so Apple LibreSSL's fopen(`/private/etc/ssl/openssl.cnf`)
1293
+ // needs `/private/etc` listed too. Do not use a `[/]etc/**` glob: SRT treats
1294
+ // that as cwd-relative (`path.isAbsolute('[/]…')` is false).
1295
+ "/etc",
1296
+ "/private/etc"
1297
+ ],
1298
+ linux: [
1299
+ "/usr/bin",
1300
+ "/bin",
1301
+ "/usr/sbin",
1302
+ "/sbin",
1303
+ "/lib",
1304
+ "/lib64",
1305
+ "/usr/lib",
1306
+ "/usr/lib64",
1307
+ "/usr/local",
1308
+ "/etc",
1309
+ "/dev",
1310
+ "/proc",
1311
+ "/sys",
1312
+ SRT_VENDOR
1313
+ ]
1314
+ };
1315
+ darwinUnixSocketSandboxRoots = /* @__PURE__ */ new Set();
1316
+ }
1317
+ });
1318
+
1319
+ // src/sandbox/local/schemas/xferFileInfo.ts
1320
+ import { z as z6 } from "zod";
1321
+ var XferFileInfoSchema;
1322
+ var init_xferFileInfo = __esm({
1323
+ "src/sandbox/local/schemas/xferFileInfo.ts"() {
1324
+ "use strict";
1325
+ XferFileInfoSchema = z6.object({
1326
+ size: z6.number(),
1327
+ isDir: z6.boolean()
1328
+ });
1329
+ }
1330
+ });
1331
+
1332
+ // src/sandbox/local/provider/LocalSandboxProvider.ts
1333
+ var LocalSandboxProvider_exports = {};
1334
+ __export(LocalSandboxProvider_exports, {
1335
+ LocalSandboxProvider: () => LocalSandboxProvider,
1336
+ formatLocalSandboxSupportReason: () => formatLocalSandboxSupportReason,
1337
+ localSandboxUploadCommand: () => localSandboxUploadCommand
1338
+ });
1339
+ import {
1340
+ absolutizeRelativeExecEnv,
1341
+ SandboxFileNotFoundError,
1342
+ SandboxFileTooLargeError,
1343
+ SandboxNotAvailableError,
1344
+ SandboxPathIsDirectoryError,
1345
+ shellEscape,
1346
+ validateNoPathTraversal as validateNoPathTraversal2
1347
+ } from "@truefoundry/trueforge-core/core";
1348
+ import { execFile as execFile2 } from "child_process";
1349
+ import { existsSync as existsSync4, realpathSync as realpathSync3, statSync as statSync2 } from "fs";
1350
+ import { mkdir as mkdir4, mkdtemp, rm as rm4 } from "fs/promises";
1351
+ import { tmpdir } from "os";
1352
+ import { dirname as dirname3, isAbsolute as isAbsolute3, join as join3, relative, resolve as resolve2, sep } from "path";
1353
+ import { promisify as promisify2 } from "util";
1354
+ import { ulid as ulid2 } from "ulid";
1355
+ function formatLocalSandboxSupportReason(params) {
1356
+ const details = params.attempts.map(formatLocalSandboxSupportAttempt).join("; ");
1357
+ return details.length === 0 ? params.summary : `${params.summary}: ${details}`;
1358
+ }
1359
+ function formatLocalSandboxSupportAttempt(attempt) {
1360
+ if (attempt.resolved === void 0) {
1361
+ return attempt.kind === "host" ? `${attempt.name}: not on PATH` : `${attempt.name}: not on sandbox PATH`;
1362
+ }
1363
+ const parts = [`${attempt.name}: resolved=${attempt.resolved}`];
1364
+ if (attempt.executable !== void 0 && attempt.executable !== attempt.resolved) {
1365
+ parts.push(`executable=${attempt.executable}`);
1366
+ }
1367
+ if (attempt.protocolError !== void 0) {
1368
+ parts.push(`protocolError=${attempt.protocolError}`);
1369
+ }
1370
+ if (attempt.exitCode !== void 0) {
1371
+ parts.push(`exit=${String(attempt.exitCode)}`);
1372
+ }
1373
+ if (attempt.timedOut === true) {
1374
+ parts.push("timedOut");
1375
+ }
1376
+ if (attempt.stderr !== void 0 && attempt.stderr.length > 0) {
1377
+ parts.push(`stderr=${JSON.stringify(attempt.stderr)}`);
1378
+ }
1379
+ if (attempt.stdout !== void 0 && attempt.stdout.length > 0) {
1380
+ parts.push(`stdout=${JSON.stringify(attempt.stdout)}`);
1381
+ }
1382
+ return parts.join(" ");
1383
+ }
1384
+ function probeAttemptFromSession(params) {
1385
+ return {
1386
+ kind: params.kind,
1387
+ name: params.name,
1388
+ resolved: params.resolved,
1389
+ ...params.executable === void 0 ? {} : { executable: params.executable },
1390
+ exitCode: params.session.exitCode,
1391
+ stdout: params.session.stdoutText,
1392
+ stderr: params.session.stderrText,
1393
+ ...params.session.protocolError === void 0 ? {} : { protocolError: params.session.protocolError },
1394
+ timedOut: params.session.timedOut
1395
+ };
1396
+ }
1397
+ function unsupported(params) {
1398
+ return {
1399
+ supported: false,
1400
+ reason: params.reason,
1401
+ ...params.platform === void 0 ? {} : { platform: params.platform },
1402
+ ...params.attempts === void 0 ? {} : { attempts: params.attempts }
1403
+ };
1404
+ }
1405
+ function toSandboxRelativePath(params) {
1406
+ const rel = relative(params.sandboxRootPath, params.absolutePath);
1407
+ return rel === "" ? "." : rel;
1408
+ }
1409
+ function isStrictDescendant(params) {
1410
+ const rel = relative(params.ancestor, params.descendant);
1411
+ return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute3(rel);
1412
+ }
1413
+ function execFileErrorDetail(error) {
1414
+ if (!(error instanceof Error)) {
1415
+ return String(error);
1416
+ }
1417
+ const parts = [error.message];
1418
+ if ("stderr" in error && typeof error.stderr === "string" && error.stderr.length > 0) {
1419
+ parts.push(error.stderr);
1420
+ }
1421
+ if ("stdout" in error && typeof error.stdout === "string" && error.stdout.length > 0) {
1422
+ parts.push(error.stdout);
1423
+ }
1424
+ return parts.join(" ");
1425
+ }
1426
+ function sessionFailDetail(session) {
1427
+ return [session.protocolError, session.stderrText, session.stdoutText].filter((part) => part !== void 0 && part.length > 0).join(" ");
1428
+ }
1429
+ function localSandboxUploadCommand(remotePath) {
1430
+ const parent = dirname3(remotePath);
1431
+ return [
1432
+ `mkdir -p ${shellEscape(parent)}`,
1433
+ `{ chmod u+w ${shellEscape(remotePath)} 2>/dev/null || true; }`,
1434
+ `/bin/cat > ${shellEscape(remotePath)}`
1435
+ ].join(" && ");
1436
+ }
1437
+ var execFileAsync2, DEFAULT_EXEC_TIMEOUT_SECONDS, DEFAULT_FILE_MAX_BYTES, SUPPORT_PROBE_TIMEOUT_MS, VENV_CREATE_TIMEOUT_MS, VENV_PIP_TIMEOUT_MS, VENV_PYDANTIC_PIN, SHELL_CANDIDATES, PYTHON_CANDIDATES, LocalSandboxProvider;
1438
+ var init_LocalSandboxProvider = __esm({
1439
+ "src/sandbox/local/provider/LocalSandboxProvider.ts"() {
1440
+ "use strict";
1441
+ init_CodeModeUdsTransport();
1442
+ init_hostRun();
1443
+ init_xferFileInfo();
1444
+ execFileAsync2 = promisify2(execFile2);
1445
+ DEFAULT_EXEC_TIMEOUT_SECONDS = 60;
1446
+ DEFAULT_FILE_MAX_BYTES = 10 * 1024 * 1024;
1447
+ SUPPORT_PROBE_TIMEOUT_MS = 5e3;
1448
+ VENV_CREATE_TIMEOUT_MS = 6e4;
1449
+ VENV_PIP_TIMEOUT_MS = 12e4;
1450
+ VENV_PYDANTIC_PIN = "pydantic>=2.0.0,<3.0.0";
1451
+ SHELL_CANDIDATES = ["bash", "sh"];
1452
+ PYTHON_CANDIDATES = ["python3", "python"];
1453
+ LocalSandboxProvider = class _LocalSandboxProvider {
1454
+ type = "local";
1455
+ sandboxRootPathParent;
1456
+ codeModeSocketParentPath;
1457
+ support;
1458
+ fileMaxBytesForDownload;
1459
+ defaultExecTimeoutSeconds;
1460
+ srtInitialized = false;
1461
+ logger;
1462
+ /** Local SRT has no image build step — always ready. */
1463
+ static readyBuild = {
1464
+ status: "ready",
1465
+ reason: null,
1466
+ metadata: null
1467
+ };
1468
+ /**
1469
+ * Probe whether this host can run LocalSandboxProvider
1470
+ * (OS + Code Mode UDS listen + SRT host binaries + in-sandbox shell + Python 3).
1471
+ * On success, returns platform/shell/python to pass into the constructor as `support`.
1472
+ */
1473
+ static async isSupported(params) {
1474
+ if (process.platform !== "darwin" && process.platform !== "linux") {
1475
+ return unsupported({
1476
+ reason: `LocalSandboxProvider supports macOS and Linux only (got ${process.platform})`
1477
+ });
1478
+ }
1479
+ const platform = process.platform;
1480
+ let createdSocketParent;
1481
+ const socketParentPath = params?.codeModeSocketParentPath;
1482
+ try {
1483
+ const parentPath = socketParentPath ?? await mkdtemp(join3("/tmp", "tfc-"));
1484
+ if (socketParentPath === void 0) {
1485
+ createdSocketParent = parentPath;
1486
+ }
1487
+ try {
1488
+ await probeCodeModeUnixSocket(parentPath);
1489
+ } catch (error) {
1490
+ const message = error instanceof Error ? error.message : String(error);
1491
+ return unsupported({
1492
+ platform,
1493
+ attempts: [{ kind: "socket", name: "uds", resolved: parentPath, protocolError: message }],
1494
+ reason: `Code Mode UDS listen failed: ${message}`
1495
+ });
1496
+ }
1497
+ } finally {
1498
+ if (createdSocketParent !== void 0) {
1499
+ await rm4(createdSocketParent, { recursive: true, force: true });
1500
+ }
1501
+ }
1502
+ const hostPath = process.env["PATH"];
1503
+ const hostAttempts = [];
1504
+ for (const name of srtHostBinaryNames(platform)) {
1505
+ const resolved = await resolveCommandOnHost({
1506
+ platform,
1507
+ name,
1508
+ ...hostPath === void 0 || hostPath.length === 0 ? {} : { pathEnv: hostPath }
1509
+ });
1510
+ hostAttempts.push({ kind: "host", name, resolved });
1511
+ }
1512
+ if (hostAttempts.some((attempt) => attempt.resolved === void 0)) {
1513
+ const required = srtHostBinaryNames(platform).join(", ");
1514
+ return unsupported({
1515
+ platform,
1516
+ attempts: hostAttempts,
1517
+ reason: formatLocalSandboxSupportReason({
1518
+ summary: `SRT host dependencies missing (${platform}: ${required})`,
1519
+ attempts: hostAttempts
1520
+ })
1521
+ });
1522
+ }
1523
+ const srtDeps = await checkSrtHostDependencies();
1524
+ if (srtDeps.errors.length > 0) {
1525
+ return unsupported({
1526
+ platform,
1527
+ attempts: hostAttempts.length === 0 ? void 0 : hostAttempts,
1528
+ reason: `SRT host dependencies failed: ${srtDeps.errors.join("; ")}`
1529
+ });
1530
+ }
1531
+ const alreadyInitialized = isSrtInitialized();
1532
+ let probeRoot;
1533
+ const attempts = [...hostAttempts];
1534
+ const pythonAttempts = [];
1535
+ try {
1536
+ if (!alreadyInitialized) {
1537
+ await initSrt({
1538
+ platform,
1539
+ ...socketParentPath === void 0 ? {} : { codeModeSocketParentPath: socketParentPath }
1540
+ });
1541
+ }
1542
+ probeRoot = await createSandbox(await mkdtemp(join3(tmpdir(), "tfy-local-sandbox-support-")));
1543
+ let shell;
1544
+ for (const name of SHELL_CANDIDATES) {
1545
+ const resolved = await resolveCommandOnHost({ platform, name });
1546
+ if (resolved === void 0) {
1547
+ attempts.push({ kind: "shell", name, resolved: void 0 });
1548
+ continue;
1549
+ }
1550
+ const probe = await runSupervisorSession({
1551
+ sandboxRootPath: probeRoot,
1552
+ platform,
1553
+ shell: resolved,
1554
+ command: "echo shell-ok",
1555
+ timeoutMs: SUPPORT_PROBE_TIMEOUT_MS
1556
+ });
1557
+ const attempt = probeAttemptFromSession({ kind: "shell", name, resolved, session: probe });
1558
+ if (probe.protocolError === void 0 && probe.exitCode === 0 && probe.stdoutText.includes("shell-ok")) {
1559
+ shell = resolved;
1560
+ break;
1561
+ }
1562
+ attempts.push(attempt);
1563
+ }
1564
+ if (shell === void 0) {
1565
+ return unsupported({
1566
+ platform,
1567
+ attempts,
1568
+ reason: formatLocalSandboxSupportReason({
1569
+ summary: "No usable shell in sandbox (bash or sh via command -v)",
1570
+ attempts
1571
+ })
1572
+ });
1573
+ }
1574
+ let python;
1575
+ for (const name of PYTHON_CANDIDATES) {
1576
+ const resolved = await resolveCommandOnHost({ platform, name });
1577
+ if (resolved === void 0) {
1578
+ pythonAttempts.push({ kind: "python", name, resolved: void 0 });
1579
+ continue;
1580
+ }
1581
+ const executable = await resolvePythonExecutableOnHost({ commandPath: resolved }) ?? resolved;
1582
+ const probe = await runSupervisorSession({
1583
+ sandboxRootPath: probeRoot,
1584
+ platform,
1585
+ shell,
1586
+ command: `${shellEscape(executable)} -c ${shellEscape(
1587
+ "import sys; raise SystemExit(0 if sys.version_info[0] == 3 else 1)"
1588
+ )}`,
1589
+ timeoutMs: SUPPORT_PROBE_TIMEOUT_MS
1590
+ });
1591
+ const attempt = probeAttemptFromSession({
1592
+ kind: "python",
1593
+ name,
1594
+ resolved,
1595
+ executable,
1596
+ session: probe
1597
+ });
1598
+ if (probe.protocolError === void 0 && probe.exitCode === 0) {
1599
+ python = executable;
1600
+ break;
1601
+ }
1602
+ pythonAttempts.push(attempt);
1603
+ }
1604
+ if (python === void 0) {
1605
+ return unsupported({
1606
+ platform,
1607
+ attempts: pythonAttempts,
1608
+ reason: formatLocalSandboxSupportReason({
1609
+ summary: "No usable Python 3 interpreter in sandbox (python3 or python via command -v)",
1610
+ attempts: pythonAttempts
1611
+ })
1612
+ });
1613
+ }
1614
+ return { supported: true, platform, shell, python };
1615
+ } catch (error) {
1616
+ const message = error instanceof Error ? error.message : String(error);
1617
+ const seen = [...attempts, ...pythonAttempts];
1618
+ return unsupported({
1619
+ platform,
1620
+ attempts: seen.length === 0 ? void 0 : seen,
1621
+ reason: seen.length === 0 ? message : `${message}: ${formatLocalSandboxSupportReason({ summary: "probe aborted", attempts: seen })}`
1622
+ });
1623
+ } finally {
1624
+ if (probeRoot !== void 0) {
1625
+ await removeSandbox(probeRoot);
1626
+ }
1627
+ if (!alreadyInitialized) {
1628
+ await resetSrt();
1629
+ }
1630
+ }
1631
+ }
1632
+ constructor(options) {
1633
+ if (!options.support.supported) {
1634
+ throw new Error(`LocalSandboxProvider is not supported: ${options.support.reason}`);
1635
+ }
1636
+ if (!isAbsolute3(options.sandboxRootPathParent)) {
1637
+ throw new Error("sandboxRootPathParent must be an absolute path");
1638
+ }
1639
+ validateNoPathTraversal2(options.sandboxRootPathParent);
1640
+ const resolvedParent = resolve2(options.sandboxRootPathParent);
1641
+ this.sandboxRootPathParent = existsSync4(resolvedParent) && statSync2(resolvedParent).isDirectory() ? realpathSync3(resolvedParent) : resolvedParent;
1642
+ this.codeModeSocketParentPath = assertCodeModeSocketParentPath(options.codeModeSocketParentPath);
1643
+ this.support = options.support;
1644
+ this.fileMaxBytesForDownload = options.fileMaxBytesForDownload ?? DEFAULT_FILE_MAX_BYTES;
1645
+ this.defaultExecTimeoutSeconds = options.defaultExecTimeoutSeconds ?? DEFAULT_EXEC_TIMEOUT_SECONDS;
1646
+ this.logger = options.logger.child({ module: "LocalSandboxProvider" });
1647
+ }
1648
+ pythonC(code, relPath) {
1649
+ return `${this.support.python} -c ${shellEscape(code)} ${shellEscape(relPath)}`;
1650
+ }
1651
+ statCommand(relPath) {
1652
+ const code = [
1653
+ "import json, os, sys",
1654
+ "p = sys.argv[1]",
1655
+ "st = os.stat(p)",
1656
+ 'print(json.dumps({"size": st.st_size, "isDir": os.path.isdir(p)}))'
1657
+ ].join("\n");
1658
+ return this.pythonC(code, relPath);
1659
+ }
1660
+ base64EncodeCommand(relPath) {
1661
+ const code = [
1662
+ "import base64, sys",
1663
+ "p = sys.argv[1]",
1664
+ 'sys.stdout.write(base64.b64encode(open(p, "rb").read()).decode("ascii"))'
1665
+ ].join("\n");
1666
+ return this.pythonC(code, relPath);
1667
+ }
1668
+ buildImage() {
1669
+ return Promise.resolve(_LocalSandboxProvider.readyBuild);
1670
+ }
1671
+ getImageBuildStatus() {
1672
+ return Promise.resolve(_LocalSandboxProvider.readyBuild);
1673
+ }
1674
+ async ensureSrt() {
1675
+ if (this.srtInitialized) {
1676
+ return;
1677
+ }
1678
+ await initSrt({
1679
+ platform: this.support.platform,
1680
+ codeModeSocketParentPath: this.codeModeSocketParentPath
1681
+ });
1682
+ this.srtInitialized = true;
1683
+ }
1684
+ /** Missing, non-directory, or not a child of this provider's parent → recreate path in Sandbox. */
1685
+ ensureSandboxRoot(sandboxRootPath) {
1686
+ if (!isAbsolute3(sandboxRootPath)) {
1687
+ throw new SandboxNotAvailableError(sandboxRootPath);
1688
+ }
1689
+ const resolved = resolve2(sandboxRootPath);
1690
+ if (!existsSync4(resolved) || !statSync2(resolved).isDirectory()) {
1691
+ throw new SandboxNotAvailableError(sandboxRootPath);
1692
+ }
1693
+ const parentReal = existsSync4(this.sandboxRootPathParent) ? realpathSync3(this.sandboxRootPathParent) : this.sandboxRootPathParent;
1694
+ const realRoot = realpathSync3(resolved);
1695
+ if (!isStrictDescendant({ ancestor: parentReal, descendant: realRoot })) {
1696
+ throw new SandboxNotAvailableError(sandboxRootPath);
1697
+ }
1698
+ }
1699
+ /**
1700
+ * Host `python -m venv` (stdlib only), then sandboxed pip for pydantic.
1701
+ * Idempotent: skip create when `.venv/bin/python` exists; skip pip when import works.
1702
+ */
1703
+ async ensureVenv(sandboxRootPath) {
1704
+ const venvDir = join3(sandboxRootPath, SANDBOX_VENV_DIR);
1705
+ const venvPython = join3(venvDir, "bin", "python");
1706
+ if (!existsSync4(venvPython)) {
1707
+ try {
1708
+ await execFileAsync2(this.support.python, ["-m", "venv", venvDir], { timeout: VENV_CREATE_TIMEOUT_MS });
1709
+ } catch (error) {
1710
+ throw new Error(`Failed to create sandbox ${SANDBOX_VENV_DIR}: ${execFileErrorDetail(error)}`, {
1711
+ cause: error
1712
+ });
1713
+ }
1714
+ }
1715
+ const relPython = join3(SANDBOX_VENV_DIR, "bin", "python");
1716
+ const relPip = join3(SANDBOX_VENV_DIR, "bin", "pip");
1717
+ const check = await runSupervisorSession({
1718
+ sandboxRootPath,
1719
+ platform: this.support.platform,
1720
+ shell: this.support.shell,
1721
+ command: `${shellEscape(relPython)} -c ${shellEscape("import pydantic")}`,
1722
+ timeoutMs: SUPPORT_PROBE_TIMEOUT_MS
1723
+ });
1724
+ if (check.protocolError !== void 0) {
1725
+ throw new Error(`Sandbox venv pydantic check failed: ${check.protocolError}`);
1726
+ }
1727
+ if (check.exitCode === 0) {
1728
+ return;
1729
+ }
1730
+ const install = await runSupervisorSession({
1731
+ sandboxRootPath,
1732
+ platform: this.support.platform,
1733
+ shell: this.support.shell,
1734
+ command: `${shellEscape(relPip)} install --trusted-host pypi.org --trusted-host files.pythonhosted.org ${shellEscape(VENV_PYDANTIC_PIN)}`,
1735
+ timeoutMs: VENV_PIP_TIMEOUT_MS
1736
+ });
1737
+ if (install.protocolError !== void 0 || install.exitCode !== 0 || install.timedOut) {
1738
+ throw new Error(
1739
+ `Failed to pip install ${VENV_PYDANTIC_PIN} into sandbox ${SANDBOX_VENV_DIR}: ${sessionFailDetail(install)}`
1740
+ );
1741
+ }
1742
+ }
1743
+ resolveInSandboxRoot(sandboxRootPath, userPath) {
1744
+ validateNoPathTraversal2(userPath);
1745
+ const resolved = userPath.startsWith("/") ? resolve2(userPath) : resolve2(sandboxRootPath, userPath);
1746
+ const root = resolve2(sandboxRootPath);
1747
+ if (resolved !== root && !resolved.startsWith(root + sep)) {
1748
+ throw new SandboxFileNotFoundError(userPath);
1749
+ }
1750
+ return resolved;
1751
+ }
1752
+ async runSandboxCommand(params) {
1753
+ const session = await runSupervisorSession({
1754
+ sandboxRootPath: params.sandboxRootPath,
1755
+ platform: this.support.platform,
1756
+ shell: this.support.shell,
1757
+ command: params.command,
1758
+ ...params.stdin === void 0 ? {} : { stdin: params.stdin },
1759
+ timeoutMs: this.defaultExecTimeoutSeconds * 1e3
1760
+ });
1761
+ if (session.protocolError !== void 0) {
1762
+ throw new Error(session.protocolError);
1763
+ }
1764
+ return {
1765
+ exitCode: session.exitCode,
1766
+ stdoutText: session.stdoutText,
1767
+ stderrText: session.stderrText
1768
+ };
1769
+ }
1770
+ async getFileInfo(params) {
1771
+ const result = await this.runSandboxCommand({
1772
+ sandboxRootPath: params.sandboxRootPath,
1773
+ command: this.statCommand(params.relPath)
1774
+ });
1775
+ if (result.exitCode !== 0) {
1776
+ throw new SandboxFileNotFoundError(params.userPath);
1777
+ }
1778
+ return XferFileInfoSchema.parse(JSON.parse(result.stdoutText.trim()));
1779
+ }
1780
+ async createSandbox() {
1781
+ await this.ensureSrt();
1782
+ const sandboxId = await createSandbox(join3(this.sandboxRootPathParent, ulid2().toLowerCase()));
1783
+ this.logger.info("LocalSandboxProvider created sandbox", {
1784
+ sandboxId,
1785
+ shell: this.support.shell,
1786
+ python: this.support.python
1787
+ });
1788
+ await mkdir4(join3(sandboxId, this.getToolResultDumpDir()), { recursive: true, mode: 448 });
1789
+ await mkdir4(join3(sandboxId, this.getFileUploadsDir()), { recursive: true, mode: 448 });
1790
+ await mkdir4(join3(sandboxId, this.getSkillsDir()), { recursive: true, mode: 448 });
1791
+ await this.ensureVenv(sandboxId);
1792
+ return { sandboxId };
1793
+ }
1794
+ async exec(params) {
1795
+ this.ensureSandboxRoot(params.sandboxId);
1796
+ try {
1797
+ await this.ensureSrt();
1798
+ await this.ensureVenv(params.sandboxId);
1799
+ const sandboxRootPath = params.sandboxId;
1800
+ const cwd = params.cwd === void 0 || params.cwd === "" ? sandboxRootPath : this.resolveInSandboxRoot(sandboxRootPath, params.cwd);
1801
+ const timeoutSeconds = params.timeoutSeconds ?? this.defaultExecTimeoutSeconds;
1802
+ const env = params.env === void 0 ? void 0 : absolutizeRelativeExecEnv({ root: sandboxRootPath, env: params.env });
1803
+ const session = await runSupervisorSession({
1804
+ sandboxRootPath,
1805
+ platform: this.support.platform,
1806
+ shell: this.support.shell,
1807
+ command: params.command,
1808
+ cwd,
1809
+ ...env === void 0 ? {} : { env },
1810
+ timeoutMs: timeoutSeconds * 1e3
1811
+ });
1812
+ if (session.protocolError !== void 0) {
1813
+ return { success: false, error: session.protocolError };
1814
+ }
1815
+ const result = session.stdoutText + (session.stderrText ? session.stderrText : "");
1816
+ return {
1817
+ success: true,
1818
+ response: { exitCode: session.exitCode, result }
1819
+ };
1820
+ } catch (error) {
1821
+ if (error instanceof SandboxNotAvailableError) {
1822
+ throw error;
1823
+ }
1824
+ const message = error instanceof Error ? error.message : String(error);
1825
+ return { success: false, error: message };
1826
+ }
1827
+ }
1828
+ getAdditionalInstructions() {
1829
+ return [
1830
+ "SANDBOX RULES:",
1831
+ `- Platform: ${this.support.platform}.`,
1832
+ `- Commands run under the sandbox shell: ${this.support.shell}.`,
1833
+ "- A Python virtualenv lives at .venv. `python` and `pip` on PATH are that environment; packages you `pip install` persist in this sandbox.",
1834
+ "- uploads, skills, and tool-results live in the sandbox working directory.",
1835
+ "- ALL file creation and writes MUST stay within the sandbox working directory.",
1836
+ "- The Agent must NOT write outside the working directory (including host home and /tmp)."
1837
+ ].join("\n");
1838
+ }
1839
+ // Cwd-relative: SRT cwd is the sandbox root. Init mkdir/ln and the agent prompt must not use
1840
+ // host-absolute paths — those contain spaces under macOS Application Support and lose quoting.
1841
+ // uploads, skills, tool-results, git_downloader.py, .git-credentials, .venv
1842
+ // mcp-client/mcp_client.py
1843
+ getToolResultDumpDir() {
1844
+ return "tool-results";
1845
+ }
1846
+ getGitCredentialsPath() {
1847
+ return ".git-credentials";
1848
+ }
1849
+ getFileUploadsDir() {
1850
+ return "uploads";
1851
+ }
1852
+ getSkillsDir() {
1853
+ return "skills";
1854
+ }
1855
+ getGitDownloaderPath() {
1856
+ return "git_downloader.py";
1857
+ }
1858
+ async downloadFile(params) {
1859
+ this.ensureSandboxRoot(params.sandboxId);
1860
+ await this.ensureSrt();
1861
+ await this.ensureVenv(params.sandboxId);
1862
+ const sandboxRootPath = params.sandboxId;
1863
+ const absolutePath = this.resolveInSandboxRoot(sandboxRootPath, params.path);
1864
+ const relPath = toSandboxRelativePath({ sandboxRootPath, absolutePath });
1865
+ const info = await this.getFileInfo({ sandboxRootPath, relPath, userPath: params.path });
1866
+ if (info.isDir) {
1867
+ throw new SandboxPathIsDirectoryError(params.path);
1868
+ }
1869
+ if (info.size > this.fileMaxBytesForDownload) {
1870
+ throw new SandboxFileTooLargeError(params.path, info.size, this.fileMaxBytesForDownload);
1871
+ }
1872
+ const result = await this.runSandboxCommand({
1873
+ sandboxRootPath,
1874
+ command: this.base64EncodeCommand(relPath)
1875
+ });
1876
+ if (result.exitCode !== 0) {
1877
+ throw new SandboxFileNotFoundError(params.path);
1878
+ }
1879
+ const buf = Buffer.from(result.stdoutText.trim(), "base64");
1880
+ if (buf.length > this.fileMaxBytesForDownload) {
1881
+ throw new SandboxFileTooLargeError(params.path, buf.length, this.fileMaxBytesForDownload);
1882
+ }
1883
+ return buf;
1884
+ }
1885
+ /** Payload on stdin so large uploads stay off argv. */
1886
+ async uploadFile(params) {
1887
+ this.ensureSandboxRoot(params.sandboxId);
1888
+ await this.ensureSrt();
1889
+ await this.ensureVenv(params.sandboxId);
1890
+ if (params.content.length > this.fileMaxBytesForDownload) {
1891
+ throw new SandboxFileTooLargeError(params.remotePath, params.content.length, this.fileMaxBytesForDownload);
1892
+ }
1893
+ const sandboxRootPath = params.sandboxId;
1894
+ const absolutePath = this.resolveInSandboxRoot(sandboxRootPath, params.remotePath);
1895
+ const remotePath = toSandboxRelativePath({ sandboxRootPath, absolutePath });
1896
+ const result = await this.runSandboxCommand({
1897
+ sandboxRootPath,
1898
+ command: localSandboxUploadCommand(remotePath),
1899
+ stdin: params.content
1900
+ });
1901
+ if (result.exitCode !== 0) {
1902
+ const detail = [result.stderrText, result.stdoutText].filter((part) => part.length > 0).join(" ");
1903
+ throw new Error(
1904
+ detail.length > 0 ? `upload ${params.remotePath} failed (exit ${String(result.exitCode)}): ${detail}` : `upload ${params.remotePath} failed (exit ${String(result.exitCode)})`
1905
+ );
1906
+ }
1907
+ }
1908
+ createCodeModeTransport() {
1909
+ return new CodeModeUdsTransport({
1910
+ codeModeSocketParentPath: this.codeModeSocketParentPath,
1911
+ clientRemotePath: () => join3("mcp-client", "mcp_client.py")
1912
+ });
1913
+ }
1914
+ /** Reset process-scoped SRT for this provider. */
1915
+ async dispose() {
1916
+ if (this.srtInitialized) {
1917
+ await resetSrt();
1918
+ this.srtInitialized = false;
1919
+ }
1920
+ }
1921
+ };
333
1922
  }
334
1923
  });
335
1924
 
@@ -740,10 +2329,10 @@ async function assembleTurnRecord(db, args) {
740
2329
  }
741
2330
  const contextRows = await db.selectFrom("turn_thread as tt").leftJoin(
742
2331
  "turn_thread_context as ttc",
743
- (join) => join.on("ttc.session_id", "=", args.session_id).on("ttc.turn_id", "=", args.turn_id).onRef("ttc.thread_id", "=", "tt.thread_id")
2332
+ (join5) => join5.on("ttc.session_id", "=", args.session_id).on("ttc.turn_id", "=", args.turn_id).onRef("ttc.thread_id", "=", "tt.thread_id")
744
2333
  ).leftJoin(
745
2334
  "thread_context_log as l",
746
- (join) => join.on("l.session_id", "=", args.session_id).onRef("l.thread_id", "=", "tt.thread_id").onRef("l.append_id", "=", "ttc.append_id")
2335
+ (join5) => join5.on("l.session_id", "=", args.session_id).onRef("l.thread_id", "=", "tt.thread_id").onRef("l.append_id", "=", "ttc.append_id")
747
2336
  ).select([
748
2337
  "tt.thread_id",
749
2338
  jsonText(sql2.ref("tt.checkpoint")).as("checkpoint"),
@@ -840,10 +2429,10 @@ async function createTurn(db, input) {
840
2429
  if (prevTurnId != null) {
841
2430
  const prevRows = await trx.selectFrom("turn as t").leftJoin(
842
2431
  "turn_thread as tt",
843
- (join) => join.onRef("tt.session_id", "=", "t.session_id").onRef("tt.turn_id", "=", "t.turn_id")
2432
+ (join5) => join5.onRef("tt.session_id", "=", "t.session_id").onRef("tt.turn_id", "=", "t.turn_id")
844
2433
  ).leftJoin(
845
2434
  db.selectFrom("turn_thread_context").select(["thread_id", "turn_id", sql2`MAX(pos)`.as("max_pos")]).where("session_id", "=", input.session_id).where("turn_id", "=", prevTurnId).groupBy(["thread_id", "turn_id"]).as("tc_agg"),
846
- (join) => join.onRef("tc_agg.thread_id", "=", "tt.thread_id").onRef("tc_agg.turn_id", "=", "tt.turn_id")
2435
+ (join5) => join5.onRef("tc_agg.thread_id", "=", "tt.thread_id").onRef("tc_agg.turn_id", "=", "tt.turn_id")
847
2436
  ).select([
848
2437
  jsonText(sql2.ref("t.checkpoint")).as("turn_checkpoint"),
849
2438
  jsonText(sql2.ref("t.state")).as("turn_state"),
@@ -1212,7 +2801,7 @@ async function listTurnEvents(db, input) {
1212
2801
  const eventOrder = input.order === "desc" ? "desc" : "asc";
1213
2802
  const rows = await db.selectFrom("turn as t").leftJoin(
1214
2803
  (eb) => eb.selectFrom("session_event").select(["session_id", "turn_id", "event_id", jsonText(sql4.ref("event")).as("event")]).where("session_id", "=", input.session_id).where("turn_id", "=", input.turn_id).orderBy("event_id", eventOrder).limit(limit + 1).offset(offset).as("e"),
1215
- (join) => join.onRef("e.session_id", "=", "t.session_id").onRef("e.turn_id", "=", "t.turn_id")
2804
+ (join5) => join5.onRef("e.session_id", "=", "t.session_id").onRef("e.turn_id", "=", "t.turn_id")
1216
2805
  ).select(["t.turn_id", "e.event"]).where("t.session_id", "=", input.session_id).where("t.turn_id", "=", input.turn_id).orderBy("e.event_id", eventOrder).execute();
1217
2806
  if (rows.length === 0) {
1218
2807
  throw new TurnNotFoundError3(input.turn_id);
@@ -1272,7 +2861,7 @@ async function listSessionEvents(db, input) {
1272
2861
  const chainJson = JSON.stringify(chainIds);
1273
2862
  const rows = await db.selectFrom(sql4`json_each(${chainJson})`.as("c")).innerJoin(
1274
2863
  "session_event as e",
1275
- (join) => join.on("e.session_id", "=", input.session_id).on(sql4`e.turn_id = c.value`)
2864
+ (join5) => join5.on("e.session_id", "=", input.session_id).on(sql4`e.turn_id = c.value`)
1276
2865
  ).select([sql4`c.value`.as("turn_id"), jsonText(sql4.ref("e.event")).as("event")]).orderBy(sql4`c.key`, "desc").orderBy("e.event_id", "desc").limit(limit + 1).offset(cursor.offset).execute();
1277
2866
  return paginateSessionEventRows(rows, limit, cursor);
1278
2867
  }
@@ -1958,7 +3547,7 @@ var SqliteMcpServerStore_exports = {};
1958
3547
  __export(SqliteMcpServerStore_exports, {
1959
3548
  SqliteMcpServerStore: () => SqliteMcpServerStore
1960
3549
  });
1961
- import { ulid as ulid3 } from "ulid";
3550
+ import { ulid as ulid5 } from "ulid";
1962
3551
  function recordColumns2(eb) {
1963
3552
  return [
1964
3553
  "id",
@@ -2008,7 +3597,7 @@ var init_SqliteMcpServerStore = __esm({
2008
3597
  const timestamp = nowIso();
2009
3598
  try {
2010
3599
  return await db.insertInto("mcp_server").values({
2011
- id: ulid3(),
3600
+ id: ulid5(),
2012
3601
  tenant_id: input.tenant_id,
2013
3602
  name: input.name,
2014
3603
  manifest: jsonbBind(input.manifest),
@@ -2028,7 +3617,7 @@ var init_SqliteMcpServerStore = __esm({
2028
3617
  const db = transaction ?? this.#db;
2029
3618
  const timestamp = nowIso();
2030
3619
  return await db.insertInto("mcp_server").values({
2031
- id: ulid3(),
3620
+ id: ulid5(),
2032
3621
  tenant_id: input.tenant_id,
2033
3622
  name: input.name,
2034
3623
  manifest: jsonbBind(input.manifest),
@@ -2358,7 +3947,7 @@ var SqliteAgentStore_exports = {};
2358
3947
  __export(SqliteAgentStore_exports, {
2359
3948
  SqliteAgentStore: () => SqliteAgentStore
2360
3949
  });
2361
- import { ulid as ulid4 } from "ulid";
3950
+ import { ulid as ulid6 } from "ulid";
2362
3951
  function recordColumns5(eb) {
2363
3952
  return [
2364
3953
  "id",
@@ -2405,7 +3994,7 @@ var init_SqliteAgentStore = __esm({
2405
3994
  const timestamp = nowIso();
2406
3995
  try {
2407
3996
  const row = await db.insertInto("agent").values({
2408
- id: ulid4().toLowerCase(),
3997
+ id: ulid6().toLowerCase(),
2409
3998
  tenant_id: input.tenant_id,
2410
3999
  name: input.name,
2411
4000
  manifest: jsonbBind(input.manifest),
@@ -2669,9 +4258,9 @@ async function assembleTurnRecord2(db, args) {
2669
4258
  if (!turn) {
2670
4259
  return void 0;
2671
4260
  }
2672
- const contextRows = await db.selectFrom("turn_thread as tt").leftJoin(lateralUnnestBigintArrayWithOrdinality(sql11`tt.context_ids`, "c"), (join) => join.onTrue()).leftJoin(
4261
+ const contextRows = await db.selectFrom("turn_thread as tt").leftJoin(lateralUnnestBigintArrayWithOrdinality(sql11`tt.context_ids`, "c"), (join5) => join5.onTrue()).leftJoin(
2673
4262
  "thread_context_log as l",
2674
- (join) => join.on("l.session_id", "=", args.session_id).onRef("l.thread_id", "=", "tt.thread_id").onRef("l.append_id", "=", "c.append_id")
4263
+ (join5) => join5.on("l.session_id", "=", args.session_id).onRef("l.thread_id", "=", "tt.thread_id").onRef("l.append_id", "=", "c.append_id")
2675
4264
  ).select(["tt.thread_id", "tt.checkpoint", "tt.agent_info", "tt.current_context_usage", "l.body", "c.pos"]).where("tt.session_id", "=", args.session_id).where("tt.turn_id", "=", args.turn_id).orderBy("tt.thread_id").orderBy("c.pos").execute();
2676
4265
  const capabilityRows = await db.selectFrom("thread_capability_state").select(["thread_id", sql11`jsonb_object_agg(key, state)`.as("capability_state")]).where("session_id", "=", args.session_id).where("turn_id", "=", args.turn_id).groupBy("thread_id").execute();
2677
4266
  const capabilityByThread = /* @__PURE__ */ new Map();
@@ -2754,7 +4343,7 @@ async function createTurn2(db, input) {
2754
4343
  if (prevTurnId != null) {
2755
4344
  const prevRows = await trx.selectFrom("turn as t").leftJoin(
2756
4345
  "turn_thread as tt",
2757
- (join) => join.onRef("tt.session_id", "=", "t.session_id").onRef("tt.turn_id", "=", "t.turn_id")
4346
+ (join5) => join5.onRef("tt.session_id", "=", "t.session_id").onRef("tt.turn_id", "=", "t.turn_id")
2758
4347
  ).select([
2759
4348
  "t.checkpoint as turn_checkpoint",
2760
4349
  "t.state as turn_state",
@@ -3076,7 +4665,7 @@ async function listTurnEvents2(db, input) {
3076
4665
  const eventOrder = input.order === "desc" ? "desc" : "asc";
3077
4666
  const rows = await db.selectFrom("turn as t").leftJoin(
3078
4667
  (eb) => eb.selectFrom("session_event").select(["session_id", "turn_id", "event_id", "event"]).where("session_id", "=", input.session_id).where("turn_id", "=", input.turn_id).orderBy("event_id", eventOrder).limit(limit + 1).offset(offset).as("e"),
3079
- (join) => join.onRef("e.session_id", "=", "t.session_id").onRef("e.turn_id", "=", "t.turn_id")
4668
+ (join5) => join5.onRef("e.session_id", "=", "t.session_id").onRef("e.turn_id", "=", "t.turn_id")
3080
4669
  ).select(["t.turn_id", "e.event"]).where("t.session_id", "=", input.session_id).where("t.turn_id", "=", input.turn_id).orderBy("e.event_id", eventOrder).execute();
3081
4670
  if (rows.length === 0) {
3082
4671
  throw new TurnNotFoundError5(input.turn_id);
@@ -3111,7 +4700,7 @@ async function listSessionEvents2(db, input) {
3111
4700
  const chainIds = await resolveAncestorChain2(db, input.session_id, anchor);
3112
4701
  const rows = await db.selectFrom(unnestWithOrdinality(chainIds, "c")).innerJoin(
3113
4702
  "session_event as e",
3114
- (join) => join.on("e.session_id", "=", input.session_id).onRef("e.turn_id", "=", "c.turn_id")
4703
+ (join5) => join5.on("e.session_id", "=", input.session_id).onRef("e.turn_id", "=", "c.turn_id")
3115
4704
  ).select(["e.turn_id as turn_id", "e.event as event"]).orderBy("c.pos", "desc").orderBy("e.event_id", "desc").limit(limit + 1).offset(cursor.offset).execute();
3116
4705
  return paginateSessionEventRows2(rows, limit, cursor);
3117
4706
  }
@@ -3751,7 +5340,7 @@ var PostgresMcpServerStore_exports = {};
3751
5340
  __export(PostgresMcpServerStore_exports, {
3752
5341
  PostgresMcpServerStore: () => PostgresMcpServerStore
3753
5342
  });
3754
- import { ulid as ulid5 } from "ulid";
5343
+ import { ulid as ulid7 } from "ulid";
3755
5344
  function toRecord3(row) {
3756
5345
  return {
3757
5346
  id: row.id,
@@ -3799,7 +5388,7 @@ var init_PostgresMcpServerStore = __esm({
3799
5388
  const db = transaction ?? this.#db;
3800
5389
  try {
3801
5390
  const row = await db.insertInto("mcp_server").values({
3802
- id: ulid5(),
5391
+ id: ulid7(),
3803
5392
  tenant_id: input.tenant_id,
3804
5393
  name: input.name,
3805
5394
  manifest: json(input.manifest),
@@ -3819,7 +5408,7 @@ var init_PostgresMcpServerStore = __esm({
3819
5408
  async upsertServer(input, transaction) {
3820
5409
  const db = transaction ?? this.#db;
3821
5410
  const row = await db.insertInto("mcp_server").values({
3822
- id: ulid5(),
5411
+ id: ulid7(),
3823
5412
  tenant_id: input.tenant_id,
3824
5413
  name: input.name,
3825
5414
  manifest: json(input.manifest),
@@ -4142,7 +5731,7 @@ var PostgresAgentStore_exports = {};
4142
5731
  __export(PostgresAgentStore_exports, {
4143
5732
  PostgresAgentStore: () => PostgresAgentStore
4144
5733
  });
4145
- import { ulid as ulid6 } from "ulid";
5734
+ import { ulid as ulid8 } from "ulid";
4146
5735
  function toRecord6(row) {
4147
5736
  return {
4148
5737
  id: row.id,
@@ -4185,7 +5774,7 @@ var init_PostgresAgentStore = __esm({
4185
5774
  const db = transaction ?? this.#db;
4186
5775
  try {
4187
5776
  const row = await db.insertInto("agent").values({
4188
- id: ulid6().toLowerCase(),
5777
+ id: ulid8().toLowerCase(),
4189
5778
  tenant_id: input.tenant_id,
4190
5779
  name: input.name,
4191
5780
  manifest: json(input.manifest),
@@ -4218,8 +5807,41 @@ var init_PostgresAgentStore = __esm({
4218
5807
 
4219
5808
  // src/main.ts
4220
5809
  import { extractErrorLogFields as extractErrorLogFields9 } from "@truefoundry/trueforge-core/core";
4221
- import { mkdir } from "fs/promises";
5810
+ import { mkdir as mkdir5 } from "fs/promises";
4222
5811
  import path6 from "path";
5812
+
5813
+ // src/sandbox/localLifecycle.ts
5814
+ import { existsSync } from "fs";
5815
+ import { mkdir, rm } from "fs/promises";
5816
+ var SOCKET_PARENT_MODE = 448;
5817
+ async function prepareCodeModeSocketParent(params) {
5818
+ if (existsSync(params.path)) {
5819
+ params.logger.warn("Removing leftover Code Mode socket parent", { path: params.path });
5820
+ await rm(params.path, { recursive: true, force: true });
5821
+ }
5822
+ await mkdir(params.path, { recursive: true, mode: SOCKET_PARENT_MODE });
5823
+ }
5824
+ async function removeCodeModeSocketParent(socketParentPath) {
5825
+ await rm(socketParentPath, { recursive: true, force: true });
5826
+ }
5827
+ async function ensureLocalSandboxRootParent(sandboxRootPathParent) {
5828
+ await mkdir(sandboxRootPathParent, { recursive: true, mode: SOCKET_PARENT_MODE });
5829
+ }
5830
+
5831
+ // src/sandbox/localRuntime.ts
5832
+ init_config();
5833
+ var cachedSupport;
5834
+ function setCachedLocalSandboxSupport(support) {
5835
+ cachedSupport = support;
5836
+ }
5837
+ function getCachedLocalSandboxSupport() {
5838
+ return cachedSupport;
5839
+ }
5840
+ function isLocalSandboxFallbackEnabled() {
5841
+ return config_default.STANDALONE && cachedSupport?.supported === true;
5842
+ }
5843
+
5844
+ // src/main.ts
4223
5845
  import { serve } from "@hono/node-server";
4224
5846
  import {
4225
5847
  CancellationReason as CancellationReason4,
@@ -4229,7 +5851,7 @@ import { RequestReplyExecutor, RequestReplyRouter } from "@truefoundry/trueforge
4229
5851
 
4230
5852
  // src/app.ts
4231
5853
  import { swaggerUI } from "@hono/swagger-ui";
4232
- import { OpenAPIHono as OpenAPIHono14, z as z24 } from "@hono/zod-openapi";
5854
+ import { OpenAPIHono as OpenAPIHono14, z as z26 } from "@hono/zod-openapi";
4233
5855
  import { extractErrorLogFields as extractErrorLogFields7 } from "@truefoundry/trueforge-core/core";
4234
5856
  import { bodyLimit } from "hono/body-limit";
4235
5857
  import { HTTPException as HTTPException5 } from "hono/http-exception";
@@ -4251,6 +5873,8 @@ var NameSchema = z.string().min(2).max(64).regex(
4251
5873
  /^[a-z](?:[a-z0-9._-]{0,62}[a-z0-9])$/,
4252
5874
  'must be 2\u201364 lowercase chars: start with a letter, end with alphanumeric, optionally separated by ".", "_" or "-"'
4253
5875
  ).openapi("ResourceName");
5876
+ var PAGE_LIMIT = 25;
5877
+ var EVENTS_PAGE_LIMIT = 100;
4254
5878
  function uniqueNames(entries, ctx) {
4255
5879
  const seen = /* @__PURE__ */ new Set();
4256
5880
  for (const entry of entries) {
@@ -4281,9 +5905,9 @@ var CreateAgentRequestSchema = z2.object({
4281
5905
  name: NameSchema,
4282
5906
  manifest: AgentSpecSchema2
4283
5907
  }).strict().openapi("CreateAgentRequest");
4284
- var PutAgentRequestSchema = z2.object({
5908
+ var UpdateAgentRequestSchema = z2.object({
4285
5909
  manifest: AgentSpecSchema2
4286
- }).strict().openapi("PutAgentRequest");
5910
+ }).strict().openapi("UpdateAgentRequest");
4287
5911
  var AgentSchema = z2.object({
4288
5912
  id: z2.string().min(1).describe("Immutable server-generated agent identifier."),
4289
5913
  name: NameSchema,
@@ -4304,15 +5928,26 @@ var RequestErrorResponseSchema = z3.object({
4304
5928
  })
4305
5929
  }).openapi("RequestErrorResponse");
4306
5930
 
5931
+ // src/routes/openapiTags.ts
5932
+ var OPENAPI_DOCUMENT_TAGS = [
5933
+ { name: "Auth" /* AUTH */ },
5934
+ { name: "Capabilities" /* CAPABILITIES */ },
5935
+ { name: "Models" /* MODELS */ },
5936
+ { name: "MCP Servers" /* MCP_SERVERS */ },
5937
+ { name: "Skills" /* SKILLS */ },
5938
+ { name: "Sandboxes" /* SANDBOXES */ },
5939
+ { name: "Agents" /* AGENTS */ },
5940
+ { name: "Agent Sessions" /* AGENT_SESSIONS */ }
5941
+ ];
5942
+
4307
5943
  // src/routes/agentRoutes.ts
4308
- var AGENTS_TAG = "Agents";
4309
5944
  var AgentIdParamsSchema = z4.object({
4310
5945
  agent_id: z4.string().min(1).max(64).describe("Immutable agent identifier.")
4311
5946
  });
4312
5947
  var listAgentsRoute = createRoute({
4313
5948
  method: "get",
4314
5949
  path: "/",
4315
- tags: [AGENTS_TAG],
5950
+ tags: ["Agents" /* AGENTS */],
4316
5951
  summary: "List agents",
4317
5952
  description: "All configured agents for the tenant.",
4318
5953
  "x-fern-sdk-group-name": ["agents"],
@@ -4331,7 +5966,7 @@ var listAgentsRoute = createRoute({
4331
5966
  var createAgentRoute = createRoute({
4332
5967
  method: "post",
4333
5968
  path: "/",
4334
- tags: [AGENTS_TAG],
5969
+ tags: ["Agents" /* AGENTS */],
4335
5970
  summary: "Create an agent",
4336
5971
  description: "Creates an agent and allocates an immutable id. Fails if `name` is already taken. Name cannot be changed later.",
4337
5972
  "x-fern-sdk-group-name": ["agents"],
@@ -4364,7 +5999,7 @@ var createAgentRoute = createRoute({
4364
5999
  var getAgentRoute = createRoute({
4365
6000
  method: "get",
4366
6001
  path: "/{agent_id}",
4367
- tags: [AGENTS_TAG],
6002
+ tags: ["Agents" /* AGENTS */],
4368
6003
  summary: "Get an agent",
4369
6004
  description: "Fetch a configured agent by immutable id.",
4370
6005
  "x-fern-sdk-group-name": ["agents"],
@@ -4386,7 +6021,7 @@ var getAgentRoute = createRoute({
4386
6021
  var deleteAgentRoute = createRoute({
4387
6022
  method: "delete",
4388
6023
  path: "/{agent_id}",
4389
- tags: [AGENTS_TAG],
6024
+ tags: ["Agents" /* AGENTS */],
4390
6025
  summary: "Delete an agent",
4391
6026
  description: "Delete a configured agent by immutable id. Idempotent if already gone.",
4392
6027
  "x-fern-sdk-group-name": ["agents"],
@@ -4408,7 +6043,7 @@ var deleteAgentRoute = createRoute({
4408
6043
  var putAgentRoute = createRoute({
4409
6044
  method: "put",
4410
6045
  path: "/{agent_id}",
4411
- tags: [AGENTS_TAG],
6046
+ tags: ["Agents" /* AGENTS */],
4412
6047
  summary: "Update an agent",
4413
6048
  description: "Replaces the manifest for an existing agent keyed by immutable `agent_id`.",
4414
6049
  "x-fern-sdk-group-name": ["agents"],
@@ -4416,7 +6051,7 @@ var putAgentRoute = createRoute({
4416
6051
  request: {
4417
6052
  params: AgentIdParamsSchema,
4418
6053
  body: {
4419
- content: { "application/json": { schema: PutAgentRequestSchema } },
6054
+ content: { "application/json": { schema: UpdateAgentRequestSchema } },
4420
6055
  required: true
4421
6056
  }
4422
6057
  },
@@ -4447,6 +6082,7 @@ import {
4447
6082
  SkillMounter
4448
6083
  } from "@truefoundry/trueforge-core/core";
4449
6084
  import { HTTPException } from "hono/http-exception";
6085
+ import { join as join4 } from "path";
4450
6086
 
4451
6087
  // src/mcp/auth/mcpDcr.ts
4452
6088
  import {
@@ -4748,46 +6384,49 @@ async function completeMcpAuthorization(params) {
4748
6384
  });
4749
6385
  }
4750
6386
 
6387
+ // src/runtime/sessionResources.ts
6388
+ init_LocalSandboxProvider();
6389
+
4751
6390
  // src/sandbox/providerUtils.ts
4752
6391
  init_config();
4753
6392
  import { Daytona, DaytonaError } from "@daytona/sdk";
4754
6393
  import { DaytonaSandboxProvider, SANDBOX_IMAGE_URI } from "@truefoundry/trueforge-core/core";
4755
6394
 
4756
6395
  // src/schemas/sandboxProvider.ts
4757
- import { z as z5 } from "@hono/zod-openapi";
4758
- var DaytonaSandboxProviderAuthSchema = z5.object({
4759
- api_key: z5.string().min(1).describe(
6396
+ import { z as z7 } from "@hono/zod-openapi";
6397
+ var DaytonaSandboxProviderAuthSchema = z7.object({
6398
+ api_key: z7.string().min(1).describe(
4760
6399
  "Daytona API key. Responses are redacted; on PUT, a real value sets/rotates and a redacted value keeps the stored key."
4761
6400
  )
4762
6401
  }).strict().describe("Daytona authentication credentials.").openapi("DaytonaSandboxProviderAuth");
4763
- var DaytonaSandboxProviderSchema = z5.object({
4764
- type: z5.literal("daytona").describe("Daytona sandbox provider."),
6402
+ var DaytonaSandboxProviderSchema = z7.object({
6403
+ type: z7.literal("daytona").describe("Daytona sandbox provider."),
4765
6404
  auth: DaytonaSandboxProviderAuthSchema,
4766
- exec_timeout_ms: z5.number().int().positive().describe("Default sandbox command exec timeout in milliseconds."),
4767
- auto_stop_interval_in_minutes: z5.number().int().nonnegative().describe("Minutes of idle time before Daytona auto-stops the sandbox (0 disables)."),
4768
- auto_archive_interval_in_minutes: z5.number().int().nonnegative().describe("Minutes before Daytona auto-archives the sandbox (0 disables)."),
4769
- auto_delete_interval_in_minutes: z5.number().int().nonnegative().describe("Minutes before Daytona auto-deletes the sandbox (0 disables).")
6405
+ exec_timeout_ms: z7.number().int().positive().describe("Default sandbox command exec timeout in milliseconds."),
6406
+ auto_stop_interval_in_minutes: z7.number().int().nonnegative().describe("Minutes of idle time before Daytona auto-stops the sandbox (0 disables)."),
6407
+ auto_archive_interval_in_minutes: z7.number().int().nonnegative().describe("Minutes before Daytona auto-archives the sandbox (0 disables)."),
6408
+ auto_delete_interval_in_minutes: z7.number().int().nonnegative().describe("Minutes before Daytona auto-deletes the sandbox (0 disables).")
4770
6409
  }).strict();
4771
6410
  var SandboxProviderManifestSchema = DaytonaSandboxProviderSchema.openapi("SandboxProviderManifest");
4772
- var SandboxBuildStatusSchema = z5.enum(["pending", "ready", "failed"]).describe("Current build status.").openapi("SandboxBuildStatus");
4773
- var SandboxBuildMetadataSchema = z5.record(z5.string(), z5.string()).describe("Provider-specific build metadata (opaque string map).");
4774
- var SandboxStatusSchema = z5.object({
6411
+ var SandboxBuildStatusSchema = z7.enum(["pending", "ready", "failed"]).describe("Current build status.").openapi("SandboxBuildStatus");
6412
+ var SandboxBuildMetadataSchema = z7.record(z7.string(), z7.string()).describe("Provider-specific build metadata (opaque string map).");
6413
+ var SandboxStatusSchema = z7.object({
4775
6414
  status: SandboxBuildStatusSchema,
4776
- status_reason: z5.string().nullable().describe("Human-readable detail for the current status; null when ready."),
6415
+ status_reason: z7.string().nullable().describe("Human-readable detail for the current status; null when ready."),
4777
6416
  build_metadata: SandboxBuildMetadataSchema.nullable().describe(
4778
6417
  "Provider-specific build metadata; null when the provider has none."
4779
6418
  )
4780
6419
  }).strict();
4781
- var SandboxProviderSchema = z5.object({
6420
+ var ConfiguredSandboxProviderSchema = z7.object({
4782
6421
  manifest: SandboxProviderManifestSchema,
4783
6422
  status: SandboxBuildStatusSchema,
4784
- status_reason: z5.string().nullable().describe("Human-readable detail for the current status; null when ready.")
4785
- }).strict().openapi("SandboxProvider");
4786
- var PutSandboxProviderRequestSchema = z5.object({
6423
+ status_reason: z7.string().nullable().describe("Human-readable detail for the current status; null when ready.")
6424
+ }).strict().openapi("ConfiguredSandboxProvider");
6425
+ var UpdateSandboxProviderRequestSchema = z7.object({
4787
6426
  manifest: SandboxProviderManifestSchema
4788
- }).strict().openapi("PutSandboxProviderRequest");
4789
- var GetSandboxProviderResponseSchema = z5.object({
4790
- data: SandboxProviderSchema
6427
+ }).strict().openapi("UpdateSandboxProviderRequest");
6428
+ var GetSandboxProviderResponseSchema = z7.object({
6429
+ data: ConfiguredSandboxProviderSchema
4791
6430
  }).openapi("GetSandboxProviderResponse");
4792
6431
  function toDaytonaSandboxProviderInput(manifest) {
4793
6432
  return {
@@ -4868,57 +6507,57 @@ async function checkSnapshotStatus({
4868
6507
  }
4869
6508
 
4870
6509
  // src/schemas/mcpServer.ts
4871
- import { z as z6 } from "@hono/zod-openapi";
4872
- var McpServerTypeSchema = z6.enum(["remote"]).openapi("McpServerType");
4873
- var McpServerHeaderAuthSchema = z6.object({
4874
- type: z6.literal("header").describe("Authenticate with static HTTP headers."),
4875
- headers: z6.record(z6.string().min(1), z6.string().min(1)).refine((headers) => Object.keys(headers).length > 0, {
6510
+ import { z as z8 } from "@hono/zod-openapi";
6511
+ var McpServerTypeSchema = z8.enum(["remote"]).openapi("MCPServerType");
6512
+ var McpServerHeaderAuthSchema = z8.object({
6513
+ type: z8.literal("header").describe("Authenticate with static HTTP headers."),
6514
+ headers: z8.record(z8.string().min(1), z8.string().min(1)).refine((headers) => Object.keys(headers).length > 0, {
4876
6515
  message: "must include at least one header"
4877
6516
  }).describe(
4878
6517
  "Request headers for this MCP server. Responses are redacted; on PUT, a real value sets/rotates and a redacted value keeps the stored secret for that header name."
4879
6518
  )
4880
- }).strict().openapi("McpServerHeaderAuth");
4881
- var McpServerDcrAuthSchema = z6.object({
4882
- type: z6.literal("dcr").describe("Authenticate via OAuth Dynamic Client Registration.")
4883
- }).strict().openapi("McpServerDcrAuth");
4884
- var McpServerManifestAuthSchema = z6.discriminatedUnion("type", [McpServerHeaderAuthSchema, McpServerDcrAuthSchema]).describe("Optional auth settings. Omit when the server needs no credentials.").openapi("McpServerManifestAuth");
4885
- var McpServerDescriptionSchema = z6.string().trim().min(1).describe("Concise summary of what this MCP server provides.");
4886
- var McpServerManifestObjectSchema = z6.object({
6519
+ }).strict().openapi("MCPServerHeaderAuth");
6520
+ var McpServerDcrAuthSchema = z8.object({
6521
+ type: z8.literal("dcr").describe("Authenticate via OAuth Dynamic Client Registration.")
6522
+ }).strict().openapi("MCPServerDcrAuth");
6523
+ var McpServerManifestAuthSchema = z8.discriminatedUnion("type", [McpServerHeaderAuthSchema, McpServerDcrAuthSchema]).describe("Optional auth settings. Omit when the server needs no credentials.").openapi("MCPServerManifestAuth");
6524
+ var McpServerDescriptionSchema = z8.string().trim().min(1).describe("Concise summary of what this MCP server provides.");
6525
+ var McpServerManifestObjectSchema = z8.object({
4887
6526
  type: McpServerTypeSchema,
4888
6527
  name: NameSchema,
4889
- url: z6.url().describe("URL of the remote MCP server."),
6528
+ url: z8.url().describe("URL of the remote MCP server."),
4890
6529
  description: McpServerDescriptionSchema,
4891
6530
  auth: McpServerManifestAuthSchema.optional()
4892
6531
  }).strict();
4893
- var McpServerManifestSchema = McpServerManifestObjectSchema.openapi("McpServerManifest");
4894
- var McpAuthStatusSchema = z6.object({
4895
- status: z6.enum(["authenticated", "auth_required", "not_required"]).describe("Current auth state for this MCP server."),
4896
- authorization_url: z6.url().optional().describe("When auth is required, this contains the URL to redirect the user to for authorization.")
4897
- }).strict().describe("Current auth state.").openapi("McpAuthStatus");
4898
- var ConfiguredMcpServerSchema = z6.object({
6532
+ var McpServerManifestSchema = McpServerManifestObjectSchema.openapi("MCPServerManifest");
6533
+ var McpAuthStatusSchema = z8.object({
6534
+ status: z8.enum(["authenticated", "auth_required", "not_required"]).describe("Current auth state for this MCP server."),
6535
+ authorization_url: z8.url().optional().describe("When auth is required, this contains the URL to redirect the user to for authorization.")
6536
+ }).strict().describe("Current auth state.").openapi("MCPAuthStatus");
6537
+ var ConfiguredMcpServerSchema = z8.object({
4899
6538
  name: NameSchema,
4900
6539
  manifest: McpServerManifestSchema,
4901
6540
  auth_status: McpAuthStatusSchema
4902
- }).strict().openapi("ConfiguredMcpServer");
4903
- var CreateMcpServerRequestSchema = z6.object({
6541
+ }).strict().openapi("ConfiguredMCPServer");
6542
+ var CreateMcpServerRequestSchema = z8.object({
4904
6543
  manifest: McpServerManifestSchema
4905
- }).strict().openapi("CreateMcpServerRequest");
4906
- var PutMcpServerRequestSchema = z6.object({
6544
+ }).strict().openapi("CreateMCPServerRequest");
6545
+ var UpdateMcpServerRequestSchema = z8.object({
4907
6546
  manifest: McpServerManifestSchema
4908
- }).strict().openapi("PutMcpServerRequest");
4909
- var GetMcpServerResponseSchema = z6.object({ data: ConfiguredMcpServerSchema }).openapi("GetMcpServerResponse");
4910
- var ListMcpServersResponseSchema = z6.object({ data: z6.array(ConfiguredMcpServerSchema) }).openapi("ListMcpServersResponse");
4911
- var McpServerAuthPublicSchema = z6.discriminatedUnion("type", [
4912
- z6.object({ type: z6.literal("dcr") }).strict(),
4913
- z6.object({ type: z6.literal("header") }).strict()
4914
- ]).describe("Auth mechanism when configured (no secrets). Omit when the server needs no credentials.").openapi("McpServerAuthPublic");
4915
- var McpServerReadEntrySchema = z6.object({
6547
+ }).strict().openapi("UpdateMCPServerRequest");
6548
+ var GetMcpServerResponseSchema = z8.object({ data: ConfiguredMcpServerSchema }).openapi("GetMCPServerResponse");
6549
+ var ListMcpServersResponseSchema = z8.object({ data: z8.array(ConfiguredMcpServerSchema) }).openapi("ListMCPServersResponse");
6550
+ var McpServerAuthPublicSchema = z8.discriminatedUnion("type", [
6551
+ z8.object({ type: z8.literal("dcr") }).strict(),
6552
+ z8.object({ type: z8.literal("header") }).strict()
6553
+ ]).describe("Auth mechanism when configured (no secrets). Omit when the server needs no credentials.").openapi("MCPServerAuthPublic");
6554
+ var AvailableMcpServerSchema = z8.object({
4916
6555
  name: NameSchema,
4917
- url: z6.url().describe("URL of the remote MCP server."),
6556
+ url: z8.url().describe("URL of the remote MCP server."),
4918
6557
  auth: McpServerAuthPublicSchema.optional(),
4919
6558
  auth_status: McpAuthStatusSchema
4920
- }).strict().openapi("McpServerReadEntry");
4921
- var ListAvailableMcpServersResponseSchema = z6.object({ data: z6.array(McpServerReadEntrySchema) }).openapi("ListAvailableMcpServersResponse");
6559
+ }).strict().openapi("AvailableMCPServer");
6560
+ var ListAvailableMcpServersResponseSchema = z8.object({ data: z8.array(AvailableMcpServerSchema) }).openapi("ListAvailableMCPServersResponse");
4922
6561
  function resolveConfiguredMcpRequestHeaders(manifest) {
4923
6562
  if (manifest.auth?.type === "header") {
4924
6563
  return { ...manifest.auth.headers };
@@ -5066,20 +6705,40 @@ async function resolveGitSkills({
5066
6705
  }
5067
6706
  return resolved;
5068
6707
  }
6708
+ function localSandboxSessionSegment(sessionId) {
6709
+ if (sessionId === void 0 || sessionId.length === 0 || sessionId.includes("/") || sessionId.includes("..")) {
6710
+ return "_";
6711
+ }
6712
+ return sessionId;
6713
+ }
5069
6714
  async function resolveSandboxProvider({
5070
6715
  tenant_id,
5071
6716
  store,
5072
- logger
6717
+ logger,
6718
+ sessionId
5073
6719
  }) {
5074
6720
  const record = await store.getSandboxProvider(tenant_id);
5075
- if (record === void 0) {
6721
+ if (record !== void 0) {
6722
+ return toDaytonaSandboxProvider({
6723
+ manifest: record.manifest,
6724
+ tenant_id,
6725
+ logger,
6726
+ build_metadata: record.build_metadata
6727
+ });
6728
+ }
6729
+ if (!config_default.STANDALONE) {
5076
6730
  return void 0;
5077
6731
  }
5078
- return toDaytonaSandboxProvider({
5079
- manifest: record.manifest,
5080
- tenant_id,
5081
- logger,
5082
- build_metadata: record.build_metadata
6732
+ const support = getCachedLocalSandboxSupport();
6733
+ if (support?.supported !== true) {
6734
+ return void 0;
6735
+ }
6736
+ return new LocalSandboxProvider({
6737
+ sandboxRootPathParent: join4(config_default.LOCAL_SANDBOX_ROOT_PARENT, localSandboxSessionSegment(sessionId)),
6738
+ codeModeSocketParentPath: config_default.CODE_MODE_SOCKET_PARENT,
6739
+ support,
6740
+ fileMaxBytesForDownload: config_default.SANDBOX_FILE_MAX_BYTES_FOR_DOWNLOAD,
6741
+ logger
5083
6742
  });
5084
6743
  }
5085
6744
  function buildTurnSandbox(input) {
@@ -5091,7 +6750,6 @@ function buildTurnSandbox(input) {
5091
6750
  blockDestructiveToolsInCodeMode: true,
5092
6751
  mcpRequestTimeoutMs: config_default.MCP_REQUEST_TIMEOUT_MS,
5093
6752
  mcpConnectTimeoutMs: config_default.MCP_CONNECT_TIMEOUT_MS,
5094
- tenantName: input.tenantName,
5095
6753
  ...skillMounter ? { skillMounter } : {},
5096
6754
  tracing: input.tracing,
5097
6755
  logger: input.logger
@@ -5160,7 +6818,7 @@ async function validateAgentSpec({
5160
6818
  const hasSkills = requestedSkills.length > 0;
5161
6819
  if (wantsSandbox || hasSkills) {
5162
6820
  const record = await sandboxProviderStore.getSandboxProvider(tenant_id);
5163
- if (record === void 0) {
6821
+ if (record === void 0 && !isLocalSandboxFallbackEnabled()) {
5164
6822
  throw new HTTPException(422, {
5165
6823
  message: hasSkills ? "skills require a sandbox provider \u2014 configure via PUT /settings/sandbox-providers" : "sandbox is enabled but no sandbox provider is configured \u2014 PUT /settings/sandbox-providers"
5166
6824
  });
@@ -5183,14 +6841,14 @@ import {
5183
6841
  redisRequest,
5184
6842
  RequestTimeoutError
5185
6843
  } from "@truefoundry/trueforge-core/request-reply";
5186
- import { ulid as ulid2 } from "ulid";
5187
- import { z as z11 } from "zod";
6844
+ import { ulid as ulid4 } from "ulid";
6845
+ import { z as z13 } from "zod";
5188
6846
 
5189
6847
  // src/routes/sessionRoutes.ts
5190
- import { createRoute as createRoute2, z as z10 } from "@hono/zod-openapi";
6848
+ import { createRoute as createRoute2, z as z12 } from "@hono/zod-openapi";
5191
6849
 
5192
6850
  // src/schemas/events.ts
5193
- import { z as z7 } from "@hono/zod-openapi";
6851
+ import { z as z9 } from "@hono/zod-openapi";
5194
6852
  import {
5195
6853
  EventType,
5196
6854
  SessionEventItemSchema,
@@ -5210,7 +6868,7 @@ import {
5210
6868
  ToolResponseEventSchema,
5211
6869
  ToolResponseRequiredEventSchema
5212
6870
  } from "@truefoundry/trueforge-core/core";
5213
- var TurnStreamingEventSchema = z7.discriminatedUnion("type", [
6871
+ var TurnStreamingEventSchema = z9.discriminatedUnion("type", [
5214
6872
  ModelMessageEventSchema,
5215
6873
  ModelMessageDeltaEventSchema,
5216
6874
  ToolResponseEventSchema,
@@ -5224,105 +6882,94 @@ var TurnStreamingEventSchema = z7.discriminatedUnion("type", [
5224
6882
  TurnCreatedEventSchema,
5225
6883
  TurnDoneEventSchema
5226
6884
  ]).openapi("TurnStreamingEvent");
5227
- var SESSION_EVENTS_LIMIT = 100;
5228
- var ListSessionEventsRequestQuerySchema = z7.object({
5229
- page_token: z7.string().optional().describe(
6885
+ var ListSessionEventsRequestQuerySchema = z9.object({
6886
+ page_token: z9.string().optional().describe(
5230
6887
  "Pagination cursor from `pagination.next_page_token`. It retains the branch anchor turn and returns older events toward the session start."
5231
6888
  ),
5232
- last_turn_id: z7.string().optional().describe(
6889
+ last_turn_id: z9.string().optional().describe(
5233
6890
  "Newest turn in the listing window (initial load only; ignored when `page_token` is set). Lists that turn and its ancestors, newest events first. Omit to use the session last turn."
5234
6891
  ),
5235
- limit: z7.coerce.number().int().min(1).max(SESSION_EVENTS_LIMIT).optional().default(SESSION_EVENTS_LIMIT).describe(
5236
- `Max events per response. Default ${String(SESSION_EVENTS_LIMIT)}, max ${String(SESSION_EVENTS_LIMIT)}.`
5237
- )
6892
+ limit: z9.coerce.number().int().min(1).max(EVENTS_PAGE_LIMIT).optional().default(EVENTS_PAGE_LIMIT).describe(`Page size. Defaults to ${String(EVENTS_PAGE_LIMIT)}, max ${String(EVENTS_PAGE_LIMIT)}.`)
5238
6893
  }).openapi("ListSessionEventsRequestQuery");
5239
- var ListSessionEventsResponseSchema = z7.object({
5240
- data: z7.array(SessionEventItemSchema),
6894
+ var ListSessionEventsResponseSchema = z9.object({
6895
+ data: z9.array(SessionEventItemSchema),
5241
6896
  pagination: TokenPaginationSchema
5242
6897
  }).openapi("ListSessionEventsResponse");
5243
6898
 
5244
6899
  // src/schemas/session.ts
5245
- import { z as z8 } from "@hono/zod-openapi";
6900
+ import { z as z10 } from "@hono/zod-openapi";
5246
6901
  import { AgentSpecSchema as AgentSpecSchema3, SessionSchema, TokenPaginationSchema as TokenPaginationSchema2 } from "@truefoundry/trueforge-core/agent-session";
5247
- var SessionAgentNameRefSchema = z8.object({ name: NameSchema }).strict().openapi("SessionAgentNameRef");
5248
- var SessionAgentSpecBodySchema = z8.object({ spec: AgentSpecSchema3 }).strict().openapi("SessionAgentSpecBody");
5249
- var CreateSessionAgentSchema = z8.union([SessionAgentNameRefSchema, SessionAgentSpecBodySchema]).openapi("CreateSessionAgent");
6902
+ var SessionAgentNameRefSchema = z10.object({ name: NameSchema }).strict().openapi("SessionAgentNameRef");
6903
+ var SessionAgentSpecBodySchema = z10.object({ spec: AgentSpecSchema3 }).strict().openapi("SessionAgentSpecBody");
6904
+ var CreateSessionAgentSchema = z10.union([SessionAgentNameRefSchema, SessionAgentSpecBodySchema]).openapi("CreateSessionAgent");
5250
6905
  function isSessionAgentNameRef(agent) {
5251
6906
  return SessionAgentNameRefSchema.safeParse(agent).success;
5252
6907
  }
5253
- var CreateSessionRequestSchema = z8.object({
6908
+ var CreateSessionRequestSchema = z10.object({
5254
6909
  agent: CreateSessionAgentSchema
5255
6910
  }).strict().openapi("CreateSessionRequest");
5256
- var UpdateSessionRequestSchema = z8.object({
6911
+ var UpdateSessionRequestSchema = z10.object({
5257
6912
  agent: SessionAgentSpecBodySchema.optional()
5258
6913
  }).strict().openapi("UpdateSessionRequest");
5259
- var DEFAULT_SESSIONS_LIMIT = 10;
5260
- var SESSIONS_MAX_LIMIT = 100;
5261
- var IsoTimestampQueryParam = z8.iso.datetime({ offset: true }).openapi({ type: "string", format: "date-time" }).transform((s) => new Date(s));
5262
- var ListSessionsRequestQuerySchema = z8.object({
5263
- limit: z8.coerce.number().int().min(1).max(SESSIONS_MAX_LIMIT).optional().default(DEFAULT_SESSIONS_LIMIT).describe(`Page size. Defaults to ${String(DEFAULT_SESSIONS_LIMIT)}, max ${String(SESSIONS_MAX_LIMIT)}.`),
5264
- order: z8.enum(["asc", "desc"]).optional().default("desc").describe('Sort sessions by `updated_at`. Defaults to "desc".').openapi("ListSessionsOrder"),
5265
- page_token: z8.string().min(1).optional().describe("Opaque keyset cursor from a previous response `next_page_token`."),
6914
+ var IsoTimestampQueryParam = z10.iso.datetime({ offset: true }).openapi({ type: "string", format: "date-time" }).transform((s) => new Date(s));
6915
+ var ListSessionsRequestQuerySchema = z10.object({
6916
+ limit: z10.coerce.number().int().min(1).max(PAGE_LIMIT).optional().default(PAGE_LIMIT).describe(`Page size. Defaults to ${String(PAGE_LIMIT)}, max ${String(PAGE_LIMIT)}.`),
6917
+ order: z10.enum(["asc", "desc"]).optional().default("desc").describe('Sort sessions by `updated_at`. Defaults to "desc".').openapi("ListSessionsOrder"),
6918
+ page_token: z10.string().min(1).optional().describe("Opaque keyset cursor from a previous response `next_page_token`."),
5266
6919
  start_timestamp: IsoTimestampQueryParam.optional().describe(
5267
6920
  "Inclusive lower bound on `created_at` (ISO-8601 / RFC 3339)."
5268
6921
  ),
5269
6922
  end_timestamp: IsoTimestampQueryParam.optional().describe(
5270
6923
  "Inclusive upper bound on `created_at` (ISO-8601 / RFC 3339)."
5271
6924
  ),
5272
- agent_id: z8.string().min(1).optional().describe("When set, only sessions bound to this agent id are returned.")
6925
+ agent_id: z10.string().min(1).optional().describe("When set, only sessions bound to this agent id are returned.")
5273
6926
  }).openapi("ListSessionsRequestQuery");
5274
- var GetSessionResponseSchema = z8.object({
6927
+ var GetSessionResponseSchema = z10.object({
5275
6928
  data: SessionSchema
5276
6929
  }).openapi("GetSessionResponse");
5277
- var ListSessionsResponseSchema = z8.object({
5278
- data: z8.array(SessionSchema),
6930
+ var ListSessionsResponseSchema = z10.object({
6931
+ data: z10.array(SessionSchema),
5279
6932
  pagination: TokenPaginationSchema2
5280
6933
  }).openapi("ListSessionsResponse");
5281
6934
 
5282
6935
  // src/schemas/turn.ts
5283
- import { z as z9 } from "@hono/zod-openapi";
6936
+ import { z as z11 } from "@hono/zod-openapi";
5284
6937
  import { SessionEventSchema, TokenPaginationSchema as TokenPaginationSchema3, TurnSchema } from "@truefoundry/trueforge-core/agent-session";
5285
6938
  import { CreateTurnRequestSchema, TurnSchema as TurnSchema2 } from "@truefoundry/trueforge-core/agent-session";
5286
- var SubscribeTurnQuerySchema = z9.object({
6939
+ var SubscribeTurnQuerySchema = z11.object({
5287
6940
  // Query strings need coerce; map null/'' to undefined first so Number(null)→0
5288
6941
  // cannot silently become a resume cursor (and so OpenAPI does not advertise null).
5289
- after_sequence_number: z9.preprocess(
6942
+ after_sequence_number: z11.preprocess(
5290
6943
  (val) => val === null || val === "" ? void 0 : val,
5291
- z9.coerce.number().int().nonnegative().optional().describe(
6944
+ z11.coerce.number().int().nonnegative().optional().describe(
5292
6945
  "Exclusive resume cursor: replay only events with a sequence number greater than this value. Omit to start from the beginning of the live buffer."
5293
6946
  )
5294
6947
  )
5295
6948
  }).openapi("SubscribeTurnQuery");
5296
- var CancelSessionRequestSchema = z9.object({}).describe("Empty request body. Cancel identifies the session via the path only.").openapi("CancelSessionRequest");
5297
- var CancelSessionResponseSchema = z9.object({}).describe("Empty success body. HTTP 200 means the cancel request was accepted (or nothing was running).").openapi("CancelSessionResponse");
5298
- var DEFAULT_TURNS_LIMIT = 10;
5299
- var TURNS_MAX_LIMIT = 100;
5300
- var ListTurnsRequestQuerySchema = z9.object({
5301
- limit: z9.coerce.number().int().min(1).max(TURNS_MAX_LIMIT).optional().default(DEFAULT_TURNS_LIMIT).describe(`Page size. Defaults to ${String(DEFAULT_TURNS_LIMIT)}, max ${String(TURNS_MAX_LIMIT)}.`),
5302
- page_token: z9.string().optional().describe("Opaque token from a previous response `next_page_token`.")
6949
+ var CancelSessionRequestSchema = z11.object({}).describe("Empty request body. Cancel identifies the session via the path only.").openapi("CancelSessionRequest");
6950
+ var CancelSessionResponseSchema = z11.object({}).describe("Empty success body. HTTP 200 means the cancel request was accepted (or nothing was running).").openapi("CancelSessionResponse");
6951
+ var ListTurnsRequestQuerySchema = z11.object({
6952
+ limit: z11.coerce.number().int().min(1).max(PAGE_LIMIT).optional().default(PAGE_LIMIT).describe(`Page size. Defaults to ${String(PAGE_LIMIT)}, max ${String(PAGE_LIMIT)}.`),
6953
+ page_token: z11.string().optional().describe("Opaque token from a previous response `next_page_token`.")
5303
6954
  }).openapi("ListTurnsRequestQuery");
5304
- var GetTurnResponseSchema = z9.object({
6955
+ var GetTurnResponseSchema = z11.object({
5305
6956
  data: TurnSchema
5306
6957
  }).openapi("GetTurnResponse");
5307
- var ListTurnsResponseSchema = z9.object({
5308
- data: z9.array(TurnSchema),
6958
+ var ListTurnsResponseSchema = z11.object({
6959
+ data: z11.array(TurnSchema),
5309
6960
  pagination: TokenPaginationSchema3
5310
6961
  }).openapi("ListTurnsResponse");
5311
- var DEFAULT_TURN_EVENTS_LIMIT = 25;
5312
- var TURN_EVENTS_MAX_LIMIT = 100;
5313
- var ListTurnEventsRequestQuerySchema = z9.object({
5314
- limit: z9.coerce.number().int().min(1).max(TURN_EVENTS_MAX_LIMIT).optional().default(DEFAULT_TURN_EVENTS_LIMIT).describe(
5315
- `Max events per response. Default ${String(DEFAULT_TURN_EVENTS_LIMIT)}, max ${String(TURN_EVENTS_MAX_LIMIT)}.`
5316
- ),
5317
- page_token: z9.string().optional().describe("Opaque token from a previous response `next_page_token`."),
5318
- order: z9.enum(["asc", "desc"]).optional().default("asc").describe('Sort events by insertion order. Defaults to "asc".').openapi("ListTurnEventsOrder")
6962
+ var ListTurnEventsRequestQuerySchema = z11.object({
6963
+ limit: z11.coerce.number().int().min(1).max(EVENTS_PAGE_LIMIT).optional().default(EVENTS_PAGE_LIMIT).describe(`Page size. Defaults to ${String(EVENTS_PAGE_LIMIT)}, max ${String(EVENTS_PAGE_LIMIT)}.`),
6964
+ page_token: z11.string().optional().describe("Opaque token from a previous response `next_page_token`."),
6965
+ order: z11.enum(["asc", "desc"]).optional().default("asc").describe('Sort events by insertion order. Defaults to "asc".').openapi("ListTurnEventsOrder")
5319
6966
  }).openapi("ListTurnEventsRequestQuery");
5320
- var ListTurnEventsResponseSchema = z9.object({
5321
- data: z9.array(SessionEventSchema),
6967
+ var ListTurnEventsResponseSchema = z11.object({
6968
+ data: z11.array(SessionEventSchema),
5322
6969
  pagination: TokenPaginationSchema3
5323
6970
  }).openapi("ListTurnEventsResponse");
5324
- var DownloadSandboxFileRequestQuerySchema = z9.object({
5325
- path: z9.string().min(1).describe(
6971
+ var DownloadSandboxFileRequestQuerySchema = z11.object({
6972
+ path: z11.string().min(1).describe(
5326
6973
  "Absolute path of the file inside the sandbox, as listed in the assistant's `sandbox_artifacts` block."
5327
6974
  )
5328
6975
  }).openapi("DownloadSandboxFileRequestQuery");
@@ -5335,14 +6982,13 @@ var TOKEN_PAGINATION = {
5335
6982
  };
5336
6983
 
5337
6984
  // src/routes/sessionRoutes.ts
5338
- var SESSIONS_TAG = "Sessions";
5339
- var SessionIdParamsSchema = z10.object({
5340
- session_id: z10.string().min(1).max(64).describe("Session identifier.")
6985
+ var SessionIdParamsSchema = z12.object({
6986
+ session_id: z12.string().min(1).max(64).describe("Session identifier.")
5341
6987
  });
5342
6988
  var createSessionRoute = createRoute2({
5343
6989
  method: "post",
5344
6990
  path: "/",
5345
- tags: [SESSIONS_TAG],
6991
+ tags: ["Agent Sessions" /* AGENT_SESSIONS */],
5346
6992
  summary: "Create a session",
5347
6993
  description: 'Create a session with `agent` as either `{ name }` (named registry binding) or `{ spec: AgentSpec }` (inline). Named sessions snapshot the agent name at create and resolve the live agent on each turn. Responses use `{ type: "reference", name, id }` or `{ type: "inline", spec }`.',
5348
6994
  "x-fern-sdk-group-name": ["sessions"],
@@ -5375,7 +7021,7 @@ var createSessionRoute = createRoute2({
5375
7021
  var getSessionRoute = createRoute2({
5376
7022
  method: "get",
5377
7023
  path: "/{session_id}",
5378
- tags: [SESSIONS_TAG],
7024
+ tags: ["Agent Sessions" /* AGENT_SESSIONS */],
5379
7025
  summary: "Get a session",
5380
7026
  description: "Fetch a session by ID. Only the session creator (`created_by`) may fetch it.",
5381
7027
  "x-fern-sdk-group-name": ["sessions"],
@@ -5401,7 +7047,7 @@ var getSessionRoute = createRoute2({
5401
7047
  var deleteSessionRoute = createRoute2({
5402
7048
  method: "delete",
5403
7049
  path: "/{session_id}",
5404
- tags: [SESSIONS_TAG],
7050
+ tags: ["Agent Sessions" /* AGENT_SESSIONS */],
5405
7051
  summary: "Delete a session",
5406
7052
  description: "Delete a session and all related turns, events, and internal state. Only the session creator (`created_by`) may delete it. Idempotent if already gone.",
5407
7053
  "x-fern-sdk-group-name": ["sessions"],
@@ -5422,7 +7068,7 @@ var deleteSessionRoute = createRoute2({
5422
7068
  var updateSessionRoute = createRoute2({
5423
7069
  method: "patch",
5424
7070
  path: "/{session_id}",
5425
- tags: [SESSIONS_TAG],
7071
+ tags: ["Agent Sessions" /* AGENT_SESSIONS */],
5426
7072
  summary: "Update a session",
5427
7073
  description: "Update a session by replacing `agent` with `{ spec: AgentSpec }`. Named (reference) sessions reject agent updates. An empty body is a valid no-op that refreshes `updated_at`. Only the session creator (`created_by`) may update it.",
5428
7074
  "x-fern-sdk-group-name": ["sessions"],
@@ -5460,7 +7106,7 @@ var updateSessionRoute = createRoute2({
5460
7106
  var listSessionsRoute = createRoute2({
5461
7107
  method: "get",
5462
7108
  path: "/",
5463
- tags: [SESSIONS_TAG],
7109
+ tags: ["Agent Sessions" /* AGENT_SESSIONS */],
5464
7110
  summary: "List sessions",
5465
7111
  description: "List the caller's sessions (newest first by default), token-paginated. Results are scoped to the authenticated identity via the session store's `created_by` filter (not a client query param). Optional `agent_id` filters to sessions bound to that named agent. Pass `page_token` to fetch the next page, keeping the other query params constant.",
5466
7112
  "x-fern-sdk-group-name": ["sessions"],
@@ -5483,7 +7129,7 @@ var listSessionsRoute = createRoute2({
5483
7129
  var cancelSessionRoute = createRoute2({
5484
7130
  method: "post",
5485
7131
  path: "/{session_id}/cancel",
5486
- tags: [SESSIONS_TAG],
7132
+ tags: ["Agent Sessions" /* AGENT_SESSIONS */],
5487
7133
  summary: "Cancel a running turn in a session",
5488
7134
  description: "Cancel the running last turn for a session. Only the session creator (`created_by`) may cancel.",
5489
7135
  "x-fern-sdk-group-name": ["sessions"],
@@ -5517,7 +7163,7 @@ var cancelSessionRoute = createRoute2({
5517
7163
  var listSessionEventsRoute = createRoute2({
5518
7164
  method: "get",
5519
7165
  path: "/{session_id}/events",
5520
- tags: [SESSIONS_TAG],
7166
+ tags: ["Agent Sessions" /* AGENT_SESSIONS */],
5521
7167
  summary: "List session events",
5522
7168
  description: "List session events as `{ turn_id, event }` across the active turn branch (newest first), including persisted events from a running tip. Each turn contributes turn.created, content events (model.message, tool.call, \u2026), and turn.done when terminal; streaming deltas are not included. Use `page_token` to paginate backward toward older events while retaining the original branch anchor. Only the session creator (`created_by`) may list events.",
5523
7169
  "x-fern-sdk-group-name": ["sessions"],
@@ -5549,9 +7195,9 @@ var listSessionEventsRoute = createRoute2({
5549
7195
 
5550
7196
  // src/runtime/peeringIds.ts
5551
7197
  import { HTTPException as HTTPException2 } from "hono/http-exception";
5552
- import { ulid } from "ulid";
7198
+ import { ulid as ulid3 } from "ulid";
5553
7199
  function mintPeeredTurnId(executorId) {
5554
- return `${ulid().toLowerCase()}.${executorId}`;
7200
+ return `${ulid3().toLowerCase()}.${executorId}`;
5555
7201
  }
5556
7202
  function executorFromTurnId(turnId) {
5557
7203
  const parts = turnId.split(".");
@@ -5564,10 +7210,10 @@ function executorFromTurnId(turnId) {
5564
7210
  // src/apis/sessions.ts
5565
7211
  var TENANT_ID = "default";
5566
7212
  var SESSIONS_CANCEL_PATH = "sessions/cancel";
5567
- var CancelPeerBodySchema = z11.object({
5568
- session_id: z11.string(),
5569
- turn_id: z11.string(),
5570
- reason: z11.enum(CancellationReason)
7213
+ var CancelPeerBodySchema = z13.object({
7214
+ session_id: z13.string(),
7215
+ turn_id: z13.string(),
7216
+ reason: z13.enum(CancellationReason)
5571
7217
  });
5572
7218
  function toWireSession(record) {
5573
7219
  return {
@@ -5668,7 +7314,7 @@ function checkSessionAccess({ userRef, createdBy }) {
5668
7314
  function createSessionsRouter(deps) {
5669
7315
  const createSessionHandler = async (c) => {
5670
7316
  const body = c.req.valid("json");
5671
- const sessionId = ulid2().toLowerCase();
7317
+ const sessionId = ulid4().toLowerCase();
5672
7318
  if (isSessionAgentNameRef(body.agent)) {
5673
7319
  const agent = await deps.agentStore.getAgent({ tenant_id: TENANT_ID, name: body.agent.name });
5674
7320
  if (agent === void 0) {
@@ -5929,13 +7575,13 @@ import { extractErrorLogFields as extractErrorLogFields2 } from "@truefoundry/tr
5929
7575
  // src/auth/cookies.ts
5930
7576
  init_config();
5931
7577
  import { deleteCookie, getCookie, setCookie } from "hono/cookie";
5932
- import { z as z12 } from "zod";
7578
+ import { z as z14 } from "zod";
5933
7579
  var OAUTH_STATE_COOKIE = "oauth_state";
5934
7580
  var ID_TOKEN_COOKIE = "id_token";
5935
- var OAuthStateSchema = z12.object({
5936
- state: z12.string(),
5937
- code_verifier: z12.string(),
5938
- return_to: z12.string()
7581
+ var OAuthStateSchema = z14.object({
7582
+ state: z14.string(),
7583
+ code_verifier: z14.string(),
7584
+ return_to: z14.string()
5939
7585
  });
5940
7586
  function getAuthCookieAttributes() {
5941
7587
  return {
@@ -6232,36 +7878,36 @@ var adminAuthMiddleware = async (c, next) => {
6232
7878
  import { createRoute as createRoute3 } from "@hono/zod-openapi";
6233
7879
 
6234
7880
  // src/schemas/auth.ts
6235
- import { z as z13 } from "@hono/zod-openapi";
6236
- var AuthLoginQuerySchema = z13.object({
6237
- return_to: z13.string().optional().describe('Path to return to after login. Must be a same-origin relative path; anything else falls back to "/".')
7881
+ import { z as z15 } from "@hono/zod-openapi";
7882
+ var AuthLoginQuerySchema = z15.object({
7883
+ return_to: z15.string().optional().describe('Path to return to after login. Must be a same-origin relative path; anything else falls back to "/".')
6238
7884
  });
6239
- var OAuthCallbackQuerySchema = z13.object({
6240
- code: z13.string().min(1).optional().describe("Authorization code, present when the user granted consent."),
6241
- state: z13.string().min(1).describe("Opaque token; correlates this callback to its pending login. Always present, success or error."),
6242
- error: z13.string().optional().describe("Error code from the identity provider, present instead of `code` if the user denied consent."),
6243
- error_description: z13.string().optional().describe("Human-readable error detail from the identity provider when `error` is set.")
7885
+ var OAuthCallbackQuerySchema = z15.object({
7886
+ code: z15.string().min(1).optional().describe("Authorization code, present when the user granted consent."),
7887
+ state: z15.string().min(1).describe("Opaque token; correlates this callback to its pending login. Always present, success or error."),
7888
+ error: z15.string().optional().describe("Error code from the identity provider, present instead of `code` if the user denied consent."),
7889
+ error_description: z15.string().optional().describe("Human-readable error detail from the identity provider when `error` is set.")
6244
7890
  });
6245
- var OAuthCallbackSuccessSchema = z13.object({
6246
- success: z13.literal(true).describe("Present when the OAuth callback completed without a return_to.")
7891
+ var OAuthCallbackSuccessSchema = z15.object({
7892
+ success: z15.literal(true).describe("Present when the OAuth callback completed without a return_to.")
6247
7893
  });
6248
- var MeResponseSchema = z13.object({
6249
- type: z13.enum(["default", "oidc-connected"]).describe(
7894
+ var GetMeResponseSchema = z15.object({
7895
+ type: z15.enum(["default", "oidc-connected"]).describe(
6250
7896
  "Session kind: `default` when no valid OIDC session; `oidc-connected` after a successful browser login."
6251
7897
  ),
6252
- email: z13.string().describe('User email from the ID token when connected; `"default"` when anonymous.'),
6253
- role: z13.string().describe("Caller role.")
6254
- }).openapi("MeResponse");
7898
+ email: z15.string().describe('User email from the ID token when connected; `"default"` when anonymous.'),
7899
+ role: z15.string().describe("Caller role.")
7900
+ }).openapi("GetMeResponse");
6255
7901
 
6256
7902
  // src/routes/authRoutes.ts
6257
- var AUTH_TAG = "Auth";
6258
7903
  var authLoginRoute = createRoute3({
6259
7904
  method: "get",
6260
7905
  path: "/login",
6261
- tags: [AUTH_TAG],
7906
+ tags: ["Auth" /* AUTH */],
6262
7907
  summary: "Start the login flow",
6263
7908
  description: "Redirects the browser to the configured identity provider. In local/single-binary mode, redirects straight back into the app \u2014 there is nothing to log into.",
6264
7909
  "x-fern-ignore": true,
7910
+ "x-excluded": true,
6265
7911
  request: { query: AuthLoginQuerySchema },
6266
7912
  responses: {
6267
7913
  302: { description: "Redirect to the IdP authorization endpoint." }
@@ -6270,10 +7916,11 @@ var authLoginRoute = createRoute3({
6270
7916
  var oAuthCallbackRoute = createRoute3({
6271
7917
  method: "get",
6272
7918
  path: "/callback",
6273
- tags: [AUTH_TAG],
7919
+ tags: ["Auth" /* AUTH */],
6274
7920
  summary: "Login callback",
6275
7921
  description: "Browser-redirect target hit by the identity provider after login, never called directly by SDK consumers. In local/single-binary mode, redirects straight back into the app.",
6276
7922
  "x-fern-ignore": true,
7923
+ "x-excluded": true,
6277
7924
  request: { query: OAuthCallbackQuerySchema },
6278
7925
  responses: {
6279
7926
  302: { description: "Redirect back into the app on success, or to /?error=<reason> on failure." }
@@ -6282,11 +7929,11 @@ var oAuthCallbackRoute = createRoute3({
6282
7929
  var authLogoutRoute = createRoute3({
6283
7930
  method: "post",
6284
7931
  path: "/logout",
6285
- tags: [AUTH_TAG],
7932
+ tags: ["Auth" /* AUTH */],
6286
7933
  summary: "Clear the local session",
6287
7934
  description: "Ends the local harness session only \u2014 does not hit the IdP end-session endpoint. A no-op in local/single-binary mode, since there is no real session to clear.",
6288
- "x-fern-sdk-group-name": ["auth"],
6289
- "x-fern-sdk-method-name": "logout",
7935
+ "x-fern-ignore": true,
7936
+ "x-excluded": true,
6290
7937
  responses: {
6291
7938
  204: { description: "Session cookie cleared." }
6292
7939
  }
@@ -6294,14 +7941,14 @@ var authLogoutRoute = createRoute3({
6294
7941
  var meRoute = createRoute3({
6295
7942
  method: "get",
6296
7943
  path: "/me",
6297
- tags: [AUTH_TAG],
7944
+ tags: ["Auth" /* AUTH */],
6298
7945
  summary: "Current session",
6299
7946
  description: "Returns the authenticated caller identity. When auth is enabled this requires a valid `id_token` cookie or `Authorization: Bearer` ID token (401 otherwise). When auth is disabled, returns the default identity.",
6300
7947
  "x-fern-sdk-group-name": ["auth"],
6301
7948
  "x-fern-sdk-method-name": "me",
6302
7949
  responses: {
6303
7950
  200: {
6304
- content: { "application/json": { schema: MeResponseSchema } },
7951
+ content: { "application/json": { schema: GetMeResponseSchema } },
6305
7952
  description: "Session type and identity for the current request."
6306
7953
  },
6307
7954
  401: {
@@ -6341,6 +7988,9 @@ function createAuthRouter(params) {
6341
7988
  const pending = readOAuthStateCookie({ context: c, logger: params.logger });
6342
7989
  clearAuthCookie({ context: c, name: OAUTH_STATE_COOKIE });
6343
7990
  if (pending?.state !== query.state || query.error || !query.code) {
7991
+ if (await resolveAuthUser(c)) {
7992
+ return c.redirect("/", 302);
7993
+ }
6344
7994
  const description = query.error_description?.trim();
6345
7995
  const reason = query.error && description ? description : "login_failed";
6346
7996
  return c.redirect(oauthErrorRedirect(reason), 302);
@@ -6356,6 +8006,9 @@ function createAuthRouter(params) {
6356
8006
  return c.redirect(safeReturnTo(pending.return_to), 302);
6357
8007
  } catch (error) {
6358
8008
  params.logger.error("Failed to exchange authorization code", extractErrorLogFields2(error));
8009
+ if (await resolveAuthUser(c)) {
8010
+ return c.redirect(safeReturnTo(pending.return_to), 302);
8011
+ }
6359
8012
  const reason = error instanceof Error ? error.message : "login_failed";
6360
8013
  return c.redirect(oauthErrorRedirect(reason), 302);
6361
8014
  }
@@ -6380,25 +8033,29 @@ import { OpenAPIHono as OpenAPIHono4 } from "@hono/zod-openapi";
6380
8033
  import { extractErrorLogFields as extractErrorLogFields3 } from "@truefoundry/trueforge-core/core";
6381
8034
 
6382
8035
  // src/routes/capabilityRoutes.ts
6383
- import { createRoute as createRoute4, z as z14 } from "@hono/zod-openapi";
6384
- var GetCapabilitiesResponseSchema = z14.object({
6385
- data: z14.object({
6386
- sandbox: z14.object({
6387
- enabled: z14.boolean().describe("Whether a sandbox provider is configured for this tenant.")
6388
- }),
6389
- skill: z14.object({
6390
- enabled: z14.boolean().describe("Whether skills are available. False when sandbox is not enabled (skills require a sandbox)."),
6391
- reason: z14.string().optional().describe("Present when skills are disabled. Explains why.")
6392
- }),
6393
- settings: z14.object({
6394
- enabled: z14.boolean().describe("Whether the settings UI/API is enabled.")
6395
- })
6396
- })
8036
+ import { createRoute as createRoute4, z as z16 } from "@hono/zod-openapi";
8037
+ var SandboxCapabilitySchema = z16.object({
8038
+ enabled: z16.boolean().describe("Whether a sandbox provider is configured for this tenant.")
8039
+ }).openapi("SandboxCapability");
8040
+ var SkillCapabilitySchema = z16.object({
8041
+ enabled: z16.boolean().describe("Whether skills are available. False when sandbox is not enabled (skills require a sandbox)."),
8042
+ reason: z16.string().optional().describe("Present when skills are disabled. Explains why.")
8043
+ }).openapi("SkillCapability");
8044
+ var SettingsCapabilitySchema = z16.object({
8045
+ enabled: z16.boolean().describe("Whether the settings UI/API is enabled.")
8046
+ }).openapi("SettingsCapability");
8047
+ var CapabilitiesDataSchema = z16.object({
8048
+ sandbox: SandboxCapabilitySchema,
8049
+ skill: SkillCapabilitySchema,
8050
+ settings: SettingsCapabilitySchema
8051
+ }).openapi("CapabilitiesData");
8052
+ var GetCapabilitiesResponseSchema = z16.object({
8053
+ data: CapabilitiesDataSchema
6397
8054
  }).openapi("GetCapabilitiesResponse");
6398
8055
  var getCapabilitiesRoute = createRoute4({
6399
8056
  method: "get",
6400
8057
  path: "/",
6401
- tags: ["Capabilities"],
8058
+ tags: ["Capabilities" /* CAPABILITIES */],
6402
8059
  summary: "Get server capabilities",
6403
8060
  "x-fern-sdk-group-name": ["server"],
6404
8061
  "x-fern-sdk-method-name": "get_capabilities",
@@ -6436,7 +8093,7 @@ function createCapabilitiesRouter(deps) {
6436
8093
  } catch (error) {
6437
8094
  deps.logger.warn("Sandbox image status check failed; reporting sandbox disabled", extractErrorLogFields3(error));
6438
8095
  }
6439
- const sandboxEnabled = status === "ready";
8096
+ const sandboxEnabled = status === "ready" || status === void 0 && isLocalSandboxFallbackEnabled();
6440
8097
  const settingsEnabled = isAdmin(resolveUserContext(c));
6441
8098
  return c.json(
6442
8099
  {
@@ -6460,42 +8117,42 @@ import { SUPPORTED_REASONING_EFFORTS as SUPPORTED_REASONING_EFFORTS2 } from "@tr
6460
8117
  import { createRoute as createRoute5 } from "@hono/zod-openapi";
6461
8118
 
6462
8119
  // src/schemas/mcpCatalog.ts
6463
- import { z as z15 } from "@hono/zod-openapi";
6464
- var CatalogMcpServerSchema = z15.object({
8120
+ import { z as z17 } from "@hono/zod-openapi";
8121
+ var CatalogMcpServerSchema = z17.object({
6465
8122
  type: McpServerTypeSchema,
6466
8123
  name: NameSchema,
6467
- logo: z15.url().optional().describe("URL of the MCP server logo asset."),
6468
- url: z15.url().describe("URL of the remote MCP server."),
8124
+ logo: z17.url().optional().describe("URL of the MCP server logo asset."),
8125
+ url: z17.url().describe("URL of the remote MCP server."),
6469
8126
  description: McpServerDescriptionSchema,
6470
8127
  auth: McpServerManifestAuthSchema.optional()
6471
- }).strict().openapi("CatalogMcpServer");
6472
- var McpCatalogFileSchema = z15.object({
6473
- mcp_servers: z15.array(CatalogMcpServerSchema)
8128
+ }).strict().openapi("CatalogMCPServer");
8129
+ var McpCatalogFileSchema = z17.object({
8130
+ mcp_servers: z17.array(CatalogMcpServerSchema)
6474
8131
  }).strict().superRefine((file, ctx) => {
6475
8132
  uniqueNames(file.mcp_servers, ctx);
6476
8133
  });
6477
- var GetMcpServerCatalogResponseSchema = z15.object({
6478
- data: z15.array(CatalogMcpServerSchema)
6479
- }).openapi("GetMcpServerCatalogResponse");
8134
+ var GetMcpServerCatalogResponseSchema = z17.object({
8135
+ data: z17.array(CatalogMcpServerSchema)
8136
+ }).openapi("GetMCPServerCatalogResponse");
6480
8137
 
6481
8138
  // src/schemas/modelCatalog.ts
6482
- import { z as z17 } from "@hono/zod-openapi";
8139
+ import { z as z19 } from "@hono/zod-openapi";
6483
8140
 
6484
8141
  // src/schemas/modelProvider.ts
6485
- import { z as z16 } from "@hono/zod-openapi";
8142
+ import { z as z18 } from "@hono/zod-openapi";
6486
8143
  import { SUPPORTED_REASONING_EFFORTS, VERCEL_AI_PROVIDER_NAMES } from "@truefoundry/trueforge-core/core";
6487
- var ModelProviderTypeSchema = z16.enum(VERCEL_AI_PROVIDER_NAMES).openapi("ModelProviderType");
6488
- var ReasoningEffortSchema = z16.enum(SUPPORTED_REASONING_EFFORTS).openapi("ReasoningEffort");
6489
- var ModelPropertiesSchema = z16.object({
6490
- context_length: z16.number().int().positive().optional().describe("Maximum context window size in tokens."),
6491
- max_output_tokens: z16.number().int().positive().optional().describe("Maximum output tokens the model can generate."),
6492
- reasoning_efforts: z16.array(ReasoningEffortSchema).min(1).optional().describe("Supported reasoning-effort values for this model.")
8144
+ var ModelProviderTypeSchema = z18.enum(VERCEL_AI_PROVIDER_NAMES).openapi("ModelProviderType");
8145
+ var ReasoningEffortSchema = z18.enum(SUPPORTED_REASONING_EFFORTS).openapi("ReasoningEffort");
8146
+ var ModelPropertiesSchema = z18.object({
8147
+ context_length: z18.number().int().positive().optional().describe("Maximum context window size in tokens."),
8148
+ max_output_tokens: z18.number().int().positive().optional().describe("Maximum output tokens the model can generate."),
8149
+ reasoning_efforts: z18.array(ReasoningEffortSchema).min(1).optional().describe("Supported reasoning-effort values for this model.")
6493
8150
  }).strict().describe("Optional model capability metadata.").openapi("ModelProperties");
6494
- var ModelEntrySchema = z16.object({
6495
- model_id: z16.string().min(1).describe("Upstream, provider-specific identifier sent to the provider API."),
8151
+ var ConfiguredModelSchema = z18.object({
8152
+ model_id: z18.string().min(1).describe("Upstream, provider-specific identifier sent to the provider API."),
6496
8153
  name: NameSchema,
6497
8154
  properties: ModelPropertiesSchema
6498
- }).strict().openapi("ModelEntry");
8155
+ }).strict().openapi("ConfiguredModel");
6499
8156
  function refineUniqueModels(models, ctx) {
6500
8157
  uniqueNames(models, ctx);
6501
8158
  const seenModelIds = /* @__PURE__ */ new Set();
@@ -6510,22 +8167,22 @@ function refineUniqueModels(models, ctx) {
6510
8167
  seenModelIds.add(model.model_id);
6511
8168
  }
6512
8169
  }
6513
- var ModelProviderAuthSchema = z16.object({
6514
- api_key: z16.string().min(1).describe(
8170
+ var ModelProviderAuthSchema = z18.object({
8171
+ api_key: z18.string().min(1).describe(
6515
8172
  "Provider API key. Responses are redacted; on PUT, a real value sets/rotates and a redacted value keeps the stored key."
6516
8173
  )
6517
8174
  }).strict().describe("Provider authentication credentials.").openapi("ModelProviderAuth");
6518
- var ModelProviderManifestBaseSchema = z16.object({
8175
+ var ModelProviderManifestBaseSchema = z18.object({
6519
8176
  auth: ModelProviderAuthSchema,
6520
- models: z16.array(ModelEntrySchema).min(1).describe("Models exposed by this provider (at least one).")
8177
+ models: z18.array(ConfiguredModelSchema).min(1).describe("Models exposed by this provider (at least one).")
6521
8178
  }).strict();
6522
8179
  function wellKnownProviderSchema({
6523
8180
  type,
6524
8181
  base_url
6525
8182
  }) {
6526
8183
  return ModelProviderManifestBaseSchema.extend({
6527
- type: z16.literal(type),
6528
- base_url: z16.url().default(base_url).describe("Override of the provider's default API base URL.")
8184
+ type: z18.literal(type),
8185
+ base_url: z18.url().default(base_url).describe("Override of the provider's default API base URL.")
6529
8186
  }).strict();
6530
8187
  }
6531
8188
  var OpenAiModelProviderSchema = wellKnownProviderSchema({
@@ -6561,15 +8218,15 @@ var AlibabaModelProviderSchema = wellKnownProviderSchema({
6561
8218
  base_url: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
6562
8219
  }).openapi("AlibabaModelProvider");
6563
8220
  var CustomModelProviderSchema = ModelProviderManifestBaseSchema.extend({
6564
- type: z16.literal("custom"),
8221
+ type: z18.literal("custom"),
6565
8222
  name: NameSchema,
6566
- base_url: z16.url().describe("Base URL of the provider's API."),
8223
+ base_url: z18.url().describe("Base URL of the provider's API."),
6567
8224
  auth: ModelProviderAuthSchema.optional()
6568
8225
  }).strict().openapi("CustomModelProvider");
6569
8226
  function refineModelProviderManifest(manifest, ctx) {
6570
8227
  refineUniqueModels(manifest.models, ctx);
6571
8228
  }
6572
- var ModelProviderBodySchema = z16.discriminatedUnion("type", [
8229
+ var ModelProviderBodySchema = z18.discriminatedUnion("type", [
6573
8230
  OpenAiModelProviderSchema,
6574
8231
  AnthropicModelProviderSchema,
6575
8232
  GoogleGeminiModelProviderSchema,
@@ -6584,87 +8241,87 @@ var ModelProviderManifestSchema = ModelProviderBodySchema.openapi("ModelProvider
6584
8241
  function modelProviderName(provider) {
6585
8242
  return provider.type === "custom" ? provider.name : provider.type;
6586
8243
  }
6587
- var ModelProviderSchema = z16.object({
8244
+ var ConfiguredModelProviderSchema = z18.object({
6588
8245
  name: NameSchema,
6589
8246
  manifest: ModelProviderManifestSchema
6590
- }).strict().openapi("ModelProvider");
6591
- var CreateModelProviderRequestSchema = z16.object({
8247
+ }).strict().openapi("ConfiguredModelProvider");
8248
+ var CreateModelProviderRequestSchema = z18.object({
6592
8249
  manifest: ModelProviderManifestSchema
6593
8250
  }).strict().openapi("CreateModelProviderRequest");
6594
- var PutModelProviderRequestSchema = z16.object({
8251
+ var UpdateModelProviderRequestSchema = z18.object({
6595
8252
  manifest: ModelProviderManifestSchema
6596
- }).strict().openapi("PutModelProviderRequest");
6597
- var GetModelProviderResponseSchema = z16.object({
6598
- data: ModelProviderSchema
8253
+ }).strict().openapi("UpdateModelProviderRequest");
8254
+ var GetModelProviderResponseSchema = z18.object({
8255
+ data: ConfiguredModelProviderSchema
6599
8256
  }).openapi("GetModelProviderResponse");
6600
- var ListModelProvidersResponseSchema = z16.object({
6601
- data: z16.array(ModelProviderSchema)
8257
+ var ListModelProvidersResponseSchema = z18.object({
8258
+ data: z18.array(ConfiguredModelProviderSchema)
6602
8259
  }).openapi("ListModelProvidersResponse");
6603
- var ModelListProviderSchema = z16.object({
6604
- name: z16.string().min(1).describe("Configured provider resource name; matches the FQN prefix of `name`.")
6605
- }).strict().describe("Owning configured provider.").openapi("ModelListProvider");
6606
- var ModelSchema = z16.object({
6607
- name: z16.string().describe('Fully qualified name `provider_name/model_name`, e.g. "openai/gpt-5-6-sol". Unique within a tenant.'),
6608
- model_id: z16.string().describe("Upstream, provider-specific identifier sent to the provider API."),
6609
- provider: ModelListProviderSchema,
8260
+ var AvailableModelProviderSchema = z18.object({
8261
+ name: z18.string().min(1).describe("Configured provider resource name; matches the FQN prefix of `name`.")
8262
+ }).strict().describe("Owning configured provider.").openapi("AvailableModelProvider");
8263
+ var AvailableModelSchema = z18.object({
8264
+ name: z18.string().describe('Fully qualified name `provider_name/model_name`, e.g. "openai/gpt-5-6-sol". Unique within a tenant.'),
8265
+ model_id: z18.string().describe("Upstream, provider-specific identifier sent to the provider API."),
8266
+ provider: AvailableModelProviderSchema,
6610
8267
  properties: ModelPropertiesSchema
6611
- }).strict().openapi("Model");
6612
- var ListModelsResponseSchema = z16.object({
6613
- data: z16.array(ModelSchema)
6614
- }).openapi("ListModelsResponse");
8268
+ }).strict().openapi("AvailableModel");
8269
+ var ListAvailableModelsResponseSchema = z18.object({
8270
+ data: z18.array(AvailableModelSchema)
8271
+ }).openapi("ListAvailableModelsResponse");
6615
8272
 
6616
8273
  // src/schemas/modelCatalog.ts
6617
8274
  var CatalogWellKnownModelProviderTypeSchema = ModelProviderTypeSchema.exclude(["custom"]).openapi(
6618
8275
  "CatalogWellKnownModelProviderType"
6619
8276
  );
6620
- var CatalogWellKnownModelProviderSchema = z17.object({
8277
+ var CatalogModelSchema = ConfiguredModelSchema.openapi("CatalogModel");
8278
+ var CatalogWellKnownModelProviderSchema = z19.object({
6621
8279
  type: CatalogWellKnownModelProviderTypeSchema,
6622
- logo: z17.url().optional().describe("URL of the provider logo asset"),
6623
- models: z17.array(ModelEntrySchema).describe("Preset models")
8280
+ logo: z19.url().optional().describe("URL of the provider logo asset"),
8281
+ models: z19.array(CatalogModelSchema).describe("Preset models")
6624
8282
  }).strict().openapi("CatalogWellKnownModelProvider");
6625
- var CatalogCustomModelProviderSchema = z17.object({
6626
- type: z17.literal("custom"),
6627
- supported_reasoning_efforts: z17.array(ReasoningEffortSchema).describe("Supported reasoning-effort values for this provider")
8283
+ var CatalogCustomModelProviderSchema = z19.object({
8284
+ type: z19.literal("custom"),
8285
+ supported_reasoning_efforts: z19.array(ReasoningEffortSchema).describe("Supported reasoning-effort values for this provider")
6628
8286
  }).strict().openapi("CatalogCustomModelProvider");
6629
- var CatalogModelProviderSchema = z17.union([CatalogWellKnownModelProviderSchema, CatalogCustomModelProviderSchema]).openapi("CatalogModelProvider");
6630
- var ModelCatalogFileSchema = z17.object({
6631
- providers: z17.array(CatalogWellKnownModelProviderSchema)
8287
+ var CatalogModelProviderSchema = z19.union([CatalogWellKnownModelProviderSchema, CatalogCustomModelProviderSchema]).openapi("CatalogModelProvider");
8288
+ var ModelCatalogFileSchema = z19.object({
8289
+ providers: z19.array(CatalogWellKnownModelProviderSchema)
6632
8290
  }).strict().superRefine((file, ctx) => {
6633
8291
  uniqueTypes(file.providers, ctx);
6634
8292
  for (const provider of file.providers) {
6635
8293
  refineUniqueModels(provider.models, ctx);
6636
8294
  }
6637
8295
  });
6638
- var GetModelProviderCatalogResponseSchema = z17.object({
6639
- data: z17.array(CatalogModelProviderSchema)
8296
+ var GetModelProviderCatalogResponseSchema = z19.object({
8297
+ data: z19.array(CatalogModelProviderSchema)
6640
8298
  }).openapi("GetModelProviderCatalogResponse");
6641
8299
 
6642
8300
  // src/schemas/sandboxCatalog.ts
6643
- import { z as z18 } from "@hono/zod-openapi";
6644
- var CatalogDaytonaSandboxProviderSchema = DaytonaSandboxProviderSchema.omit({ auth: true }).strict().openapi("CatalogDaytonaSandboxProvider");
6645
- var CatalogSandboxProviderSchema = CatalogDaytonaSandboxProviderSchema;
6646
- var SandboxCatalogFileSchema = z18.object({
6647
- providers: z18.array(CatalogSandboxProviderSchema)
8301
+ import { z as z20 } from "@hono/zod-openapi";
8302
+ var CatalogSandboxProviderSchema = DaytonaSandboxProviderSchema.omit({ auth: true }).strict().openapi("CatalogSandboxProvider");
8303
+ var SandboxCatalogFileSchema = z20.object({
8304
+ providers: z20.array(CatalogSandboxProviderSchema)
6648
8305
  }).strict();
6649
- var GetSandboxProviderCatalogResponseSchema = z18.object({
6650
- data: z18.array(CatalogSandboxProviderSchema)
8306
+ var GetSandboxProviderCatalogResponseSchema = z20.object({
8307
+ data: z20.array(CatalogSandboxProviderSchema)
6651
8308
  }).openapi("GetSandboxProviderCatalogResponse");
6652
8309
 
6653
8310
  // src/schemas/skillCatalog.ts
6654
- import { z as z20 } from "@hono/zod-openapi";
8311
+ import { z as z22 } from "@hono/zod-openapi";
6655
8312
 
6656
8313
  // src/schemas/skill.ts
6657
- import { z as z19 } from "@hono/zod-openapi";
6658
- var SkillTypeSchema = z19.enum(["git"]).openapi("SkillType");
8314
+ import { z as z21 } from "@hono/zod-openapi";
8315
+ var SkillTypeSchema = z21.enum(["git"]).openapi("SkillType");
6659
8316
  var GIT_URL_REGEX = /^https:\/\/(github\.com\/[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+|gitlab\.com\/[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)+)(\/|\.git)?$/;
6660
8317
  var GIT_REF_REGEX = /^[A-Za-z0-9._\-/]+$/;
6661
8318
  var SKILL_PATH_REGEX = /^[A-Za-z0-9._\-/]+$/;
6662
8319
  var hasParentTraversal = (value) => value.split("/").includes("..");
6663
- var SkillGitUrlSchema = z19.string().trim().min(1).regex(GIT_URL_REGEX, "Must be a GitHub or GitLab HTTPS URL").refine((v) => !hasParentTraversal(v), 'URL must not contain ".." segments').describe("Full HTTPS URL of a GitHub or GitLab repository.");
6664
- var SkillGitPathSchema = z19.string().trim().min(1).regex(SKILL_PATH_REGEX, 'Path may only contain letters, numbers, ".", "_", "-", and "/"').refine((v) => !hasParentTraversal(v), 'Path must not contain ".." segments').refine((v) => !v.split("/").includes("."), 'Path must not contain "." segments').refine((v) => v.replace(/^\/+|\/+$/g, "").length > 0, "Path must reference a subdirectory, not only slashes").describe("Path to the skill directory within the repository. Omit to use the repository root.");
6665
- var SkillGitRefSchema = z19.string().trim().min(1).regex(GIT_REF_REGEX, 'Ref may only contain letters, numbers, ".", "_", "-", and "/"').refine((v) => !hasParentTraversal(v), 'Ref must not contain ".." segments').refine((v) => v.replace(/^\/+|\/+$/g, "").length > 0, "Ref must not consist only of slashes").describe("Git ref \u2014 branch name, tag, or commit SHA.");
6666
- var SkillDescriptionSchema = z19.string().trim().min(1).describe("Concise guidance for when the agent should use the skill.");
6667
- var SkillManifestObjectSchema = z19.object({
8320
+ var SkillGitUrlSchema = z21.string().trim().min(1).regex(GIT_URL_REGEX, "Must be a GitHub or GitLab HTTPS URL").refine((v) => !hasParentTraversal(v), 'URL must not contain ".." segments').describe("Full HTTPS URL of a GitHub or GitLab repository.");
8321
+ var SkillGitPathSchema = z21.string().trim().min(1).regex(SKILL_PATH_REGEX, 'Path may only contain letters, numbers, ".", "_", "-", and "/"').refine((v) => !hasParentTraversal(v), 'Path must not contain ".." segments').refine((v) => !v.split("/").includes("."), 'Path must not contain "." segments').refine((v) => v.replace(/^\/+|\/+$/g, "").length > 0, "Path must reference a subdirectory, not only slashes").describe("Path to the skill directory within the repository. Omit to use the repository root.");
8322
+ var SkillGitRefSchema = z21.string().trim().min(1).regex(GIT_REF_REGEX, 'Ref may only contain letters, numbers, ".", "_", "-", and "/"').refine((v) => !hasParentTraversal(v), 'Ref must not contain ".." segments').refine((v) => v.replace(/^\/+|\/+$/g, "").length > 0, "Ref must not consist only of slashes").describe("Git ref \u2014 branch name, tag, or commit SHA.");
8323
+ var SkillDescriptionSchema = z21.string().trim().min(1).describe("Concise guidance for when the agent should use the skill.");
8324
+ var SkillManifestObjectSchema = z21.object({
6668
8325
  type: SkillTypeSchema,
6669
8326
  name: NameSchema,
6670
8327
  url: SkillGitUrlSchema,
@@ -6673,26 +8330,26 @@ var SkillManifestObjectSchema = z19.object({
6673
8330
  description: SkillDescriptionSchema
6674
8331
  }).strict();
6675
8332
  var SkillManifestSchema = SkillManifestObjectSchema.openapi("SkillManifest");
6676
- var ConfiguredSkillSchema = z19.object({
8333
+ var ConfiguredSkillSchema = z21.object({
6677
8334
  name: NameSchema,
6678
8335
  manifest: SkillManifestSchema
6679
8336
  }).strict().openapi("ConfiguredSkill");
6680
- var CreateSkillRequestSchema = z19.object({
8337
+ var CreateSkillRequestSchema = z21.object({
6681
8338
  manifest: SkillManifestSchema
6682
8339
  }).strict().openapi("CreateSkillRequest");
6683
- var PutSkillRequestSchema = z19.object({
8340
+ var UpdateSkillRequestSchema = z21.object({
6684
8341
  manifest: SkillManifestSchema
6685
- }).strict().openapi("PutSkillRequest");
6686
- var GetSkillResponseSchema = z19.object({ data: ConfiguredSkillSchema }).openapi("GetSkillResponse");
6687
- var ListSkillsResponseSchema = z19.object({ data: z19.array(ConfiguredSkillSchema) }).openapi("ListSkillsResponse");
6688
- var SkillReadEntrySchema = z19.object({
8342
+ }).strict().openapi("UpdateSkillRequest");
8343
+ var GetSkillResponseSchema = z21.object({ data: ConfiguredSkillSchema }).openapi("GetSkillResponse");
8344
+ var ListSkillsResponseSchema = z21.object({ data: z21.array(ConfiguredSkillSchema) }).openapi("ListSkillsResponse");
8345
+ var AvailableSkillSchema = z21.object({
6689
8346
  name: NameSchema,
6690
8347
  description: SkillDescriptionSchema
6691
- }).strict().openapi("SkillReadEntry");
6692
- var ListAvailableSkillsResponseSchema = z19.object({ data: z19.array(SkillReadEntrySchema) }).openapi("ListAvailableSkillsResponse");
8348
+ }).strict().openapi("AvailableSkill");
8349
+ var ListAvailableSkillsResponseSchema = z21.object({ data: z21.array(AvailableSkillSchema) }).openapi("ListAvailableSkillsResponse");
6693
8350
 
6694
8351
  // src/schemas/skillCatalog.ts
6695
- var CatalogSkillSchema = z20.object({
8352
+ var CatalogSkillSchema = z22.object({
6696
8353
  type: SkillTypeSchema,
6697
8354
  name: NameSchema,
6698
8355
  url: SkillGitUrlSchema,
@@ -6700,24 +8357,23 @@ var CatalogSkillSchema = z20.object({
6700
8357
  ref: SkillGitRefSchema,
6701
8358
  description: SkillDescriptionSchema
6702
8359
  }).strict().openapi("CatalogSkill");
6703
- var SkillCatalogFileSchema = z20.object({
6704
- skills: z20.array(CatalogSkillSchema)
8360
+ var SkillCatalogFileSchema = z22.object({
8361
+ skills: z22.array(CatalogSkillSchema)
6705
8362
  }).strict().superRefine((file, ctx) => {
6706
8363
  uniqueNames(file.skills, ctx);
6707
8364
  });
6708
- var GetSkillCatalogResponseSchema = z20.object({
6709
- data: z20.array(CatalogSkillSchema)
8365
+ var GetSkillCatalogResponseSchema = z22.object({
8366
+ data: z22.array(CatalogSkillSchema)
6710
8367
  }).openapi("GetSkillCatalogResponse");
6711
8368
 
6712
8369
  // src/routes/catalogRoutes.ts
6713
- var CATALOG_TAG = "Catalog";
6714
8370
  var listModelProviderCatalogRoute = createRoute5({
6715
8371
  method: "get",
6716
8372
  path: "/model-providers",
6717
- tags: [CATALOG_TAG],
8373
+ tags: ["Models" /* MODELS */],
6718
8374
  summary: "Get the model catalog",
6719
8375
  description: "Shipped model-provider presets (discovery-only). Copy into PUT /settings/model-providers to configure. Includes a `custom` sentinel with `supported_reasoning_efforts`.",
6720
- "x-fern-sdk-group-name": ["catalog", "modelProviders"],
8376
+ "x-fern-sdk-group-name": ["catalogs", "modelProviders"],
6721
8377
  "x-fern-sdk-method-name": "list",
6722
8378
  responses: {
6723
8379
  200: {
@@ -6733,10 +8389,10 @@ var listModelProviderCatalogRoute = createRoute5({
6733
8389
  var listMcpServerCatalogRoute = createRoute5({
6734
8390
  method: "get",
6735
8391
  path: "/mcp-servers",
6736
- tags: [CATALOG_TAG],
8392
+ tags: ["MCP Servers" /* MCP_SERVERS */],
6737
8393
  summary: "Get the MCP catalog",
6738
8394
  description: "Shipped MCP server presets (discovery-only). Copy into PUT /settings/mcp-servers to configure.",
6739
- "x-fern-sdk-group-name": ["catalog", "mcpServers"],
8395
+ "x-fern-sdk-group-name": ["catalogs", "mcpServers"],
6740
8396
  "x-fern-sdk-method-name": "list",
6741
8397
  responses: {
6742
8398
  200: {
@@ -6752,10 +8408,10 @@ var listMcpServerCatalogRoute = createRoute5({
6752
8408
  var listSkillCatalogRoute = createRoute5({
6753
8409
  method: "get",
6754
8410
  path: "/skills",
6755
- tags: [CATALOG_TAG],
8411
+ tags: ["Skills" /* SKILLS */],
6756
8412
  summary: "Get the skill catalog",
6757
8413
  description: "Shipped skill presets (discovery-only). Copy into PUT /settings/skills to configure.",
6758
- "x-fern-sdk-group-name": ["catalog", "skills"],
8414
+ "x-fern-sdk-group-name": ["catalogs", "skills"],
6759
8415
  "x-fern-sdk-method-name": "list",
6760
8416
  responses: {
6761
8417
  200: {
@@ -6771,10 +8427,10 @@ var listSkillCatalogRoute = createRoute5({
6771
8427
  var listSandboxProviderCatalogRoute = createRoute5({
6772
8428
  method: "get",
6773
8429
  path: "/sandbox-providers",
6774
- tags: [CATALOG_TAG],
8430
+ tags: ["Sandboxes" /* SANDBOXES */],
6775
8431
  summary: "Get the sandbox provider catalog",
6776
8432
  description: "Shipped sandbox-provider presets (discovery-only). Copy into PUT /settings/sandbox-providers to configure.",
6777
- "x-fern-sdk-group-name": ["catalog", "sandboxProviders"],
8433
+ "x-fern-sdk-group-name": ["catalogs", "sandboxProviders"],
6778
8434
  "x-fern-sdk-method-name": "list",
6779
8435
  responses: {
6780
8436
  200: {
@@ -6822,15 +8478,15 @@ import { extractErrorLogFields as extractErrorLogFields4, McpConnectionError as
6822
8478
 
6823
8479
  // src/routes/mcpOAuthRoutes.ts
6824
8480
  import { createRoute as createRoute6 } from "@hono/zod-openapi";
6825
- var MCP_OAUTH_TAG = "MCP OAuth";
6826
8481
  var mcpOAuthCallbackRoute = createRoute6({
6827
8482
  method: "get",
6828
8483
  path: "/callback",
6829
- tags: [MCP_OAUTH_TAG],
8484
+ tags: ["MCP Servers" /* MCP_SERVERS */],
6830
8485
  summary: "OAuth callback for MCP authorization",
6831
8486
  description: "Browser redirect target for MCP server OAuth. The authorization server redirects here with `code`/`state` (or `error`). Exchanges the code for tokens, then either returns JSON or redirects to the `return_to` path supplied at authorize time. Not called by the SDK \u2014 browsers hit this URL directly.",
6832
8487
  // Browser-redirect target hit directly by the authorization server, never called by SDK.
6833
8488
  "x-fern-ignore": true,
8489
+ "x-excluded": true,
6834
8490
  request: {
6835
8491
  query: OAuthCallbackQuerySchema
6836
8492
  },
@@ -6923,12 +8579,11 @@ init_config();
6923
8579
  init_mcpServerStore();
6924
8580
 
6925
8581
  // src/routes/mcpServerRoutes.ts
6926
- import { createRoute as createRoute7, z as z21 } from "@hono/zod-openapi";
6927
- var MCP_SERVERS_TAG = "MCP Servers";
8582
+ import { createRoute as createRoute7, z as z23 } from "@hono/zod-openapi";
6928
8583
  var listAvailableMcpServersRoute = createRoute7({
6929
8584
  method: "get",
6930
8585
  path: "/",
6931
- tags: [MCP_SERVERS_TAG],
8586
+ tags: ["MCP Servers" /* MCP_SERVERS */],
6932
8587
  summary: "List MCP servers for chat",
6933
8588
  description: "MCP servers as a slim name/url list for the composer. No auth or auth_status.",
6934
8589
  "x-fern-sdk-group-name": ["mcpServers"],
@@ -6947,7 +8602,7 @@ var listAvailableMcpServersRoute = createRoute7({
6947
8602
  var listMcpServersRoute = createRoute7({
6948
8603
  method: "get",
6949
8604
  path: "/",
6950
- tags: [MCP_SERVERS_TAG],
8605
+ tags: ["MCP Servers" /* MCP_SERVERS */],
6951
8606
  summary: "List MCP servers",
6952
8607
  description: "All MCP servers with nested auth_status (settings / admin projection). Header auth values are redacted.",
6953
8608
  "x-fern-sdk-group-name": ["settings", "mcpServers"],
@@ -6967,13 +8622,13 @@ var listMcpServersRoute = createRoute7({
6967
8622
  }
6968
8623
  }
6969
8624
  });
6970
- var McpServerNameParamsSchema = z21.object({
6971
- name: z21.string().min(1).describe("MCP server name.")
8625
+ var McpServerNameParamsSchema = z23.object({
8626
+ name: z23.string().min(1).describe("MCP server name.")
6972
8627
  });
6973
8628
  var getMcpServerRoute = createRoute7({
6974
8629
  method: "get",
6975
8630
  path: "/{name}",
6976
- tags: [MCP_SERVERS_TAG],
8631
+ tags: ["MCP Servers" /* MCP_SERVERS */],
6977
8632
  summary: "Get a single MCP server by name",
6978
8633
  description: "A single MCP server by name, with nested auth_status (settings / admin projection). Header auth values are redacted.",
6979
8634
  "x-fern-sdk-group-name": ["settings", "mcpServers"],
@@ -6995,7 +8650,7 @@ var getMcpServerRoute = createRoute7({
6995
8650
  var createMcpServerRoute = createRoute7({
6996
8651
  method: "post",
6997
8652
  path: "/",
6998
- tags: [MCP_SERVERS_TAG],
8653
+ tags: ["MCP Servers" /* MCP_SERVERS */],
6999
8654
  summary: "Create an MCP server",
7000
8655
  description: "Creates an MCP server by `name`. Fails if `name` is already taken. Runs DCR registration when `auth.type` is `dcr`. Header secrets: real value required; redacted with no stored value returns 400.",
7001
8656
  "x-fern-sdk-group-name": ["settings", "mcpServers"],
@@ -7028,14 +8683,14 @@ var createMcpServerRoute = createRoute7({
7028
8683
  var putMcpServerRoute = createRoute7({
7029
8684
  method: "put",
7030
8685
  path: "/",
7031
- tags: [MCP_SERVERS_TAG],
8686
+ tags: ["MCP Servers" /* MCP_SERVERS */],
7032
8687
  summary: "Create or replace an MCP server",
7033
8688
  description: "Create or replace by `name`. Does not start DCR or change oauth client columns. Header secrets: real value sets/rotates; redacted keeps existing (400 if none).",
7034
8689
  "x-fern-sdk-group-name": ["settings", "mcpServers"],
7035
- "x-fern-sdk-method-name": "upsert",
8690
+ "x-fern-sdk-method-name": "create_or_update",
7036
8691
  request: {
7037
8692
  body: {
7038
- content: { "application/json": { schema: PutMcpServerRequestSchema } },
8693
+ content: { "application/json": { schema: UpdateMcpServerRequestSchema } },
7039
8694
  required: true
7040
8695
  }
7041
8696
  },
@@ -7054,16 +8709,16 @@ var putMcpServerRoute = createRoute7({
7054
8709
  }
7055
8710
  }
7056
8711
  });
7057
- var ListMcpServerToolsResponseSchema = z21.object({
8712
+ var ListMcpServerToolsResponseSchema = z23.object({
7058
8713
  // TODO: Type tools/list entries to the MCP tool shape (name, description, inputSchema, …) for OpenAPI quality.
7059
- data: z21.array(z21.record(z21.string(), z21.unknown())).describe("MCP `tools/list` entries, passed through verbatim from the MCP server.")
7060
- }).openapi("ListMcpServerToolsResponse");
8714
+ data: z23.array(z23.record(z23.string(), z23.unknown())).describe("MCP `tools/list` entries, passed through verbatim from the MCP server.")
8715
+ }).openapi("ListMCPServerToolsResponse");
7061
8716
  var listMcpServerToolsRoute = createRoute7({
7062
8717
  method: "get",
7063
8718
  path: "/{name}/tools",
7064
- tags: [MCP_SERVERS_TAG],
8719
+ tags: ["MCP Servers" /* MCP_SERVERS */],
7065
8720
  summary: "List tools of an MCP server",
7066
- "x-fern-sdk-group-name": ["settings", "mcpServers"],
8721
+ "x-fern-sdk-group-name": ["mcpServers"],
7067
8722
  "x-fern-sdk-method-name": "list_tools",
7068
8723
  description: "All tools exposed by the given MCP server (non-paginated), as returned by the MCP `tools/list` call.",
7069
8724
  request: {
@@ -7076,7 +8731,7 @@ var listMcpServerToolsRoute = createRoute7({
7076
8731
  },
7077
8732
  401: {
7078
8733
  content: { "application/json": { schema: RequestErrorResponseSchema } },
7079
- description: "Harness session login required."
8734
+ description: "OIDC is configured and the request has no valid session cookie."
7080
8735
  },
7081
8736
  404: {
7082
8737
  content: { "application/json": { schema: RequestErrorResponseSchema } },
@@ -7092,15 +8747,15 @@ var listMcpServerToolsRoute = createRoute7({
7092
8747
  }
7093
8748
  }
7094
8749
  });
7095
- var McpAuthorizeQuerySchema = z21.object({
7096
- return_to: z21.string().optional().describe(
8750
+ var McpAuthorizeQuerySchema = z23.object({
8751
+ return_to: z23.string().optional().describe(
7097
8752
  "Optional path to return to after OAuth. Must be a same-origin relative path; the OAuth callback redirects here with `isSuccess`/`reason` appended."
7098
8753
  )
7099
8754
  });
7100
8755
  var authorizeMcpServerRoute = createRoute7({
7101
8756
  method: "get",
7102
8757
  path: "/{name}/authorize",
7103
- tags: [MCP_SERVERS_TAG],
8758
+ tags: ["MCP Servers" /* MCP_SERVERS */],
7104
8759
  summary: "Start (or short-circuit) the auth flow for an MCP server",
7105
8760
  "x-fern-sdk-group-name": ["mcpServers"],
7106
8761
  "x-fern-sdk-method-name": "authorize",
@@ -7139,7 +8794,7 @@ var authorizeMcpServerRoute = createRoute7({
7139
8794
  var deleteAuthorizationMcpServerRoute = createRoute7({
7140
8795
  method: "delete",
7141
8796
  path: "/{name}/authorize",
7142
- tags: [MCP_SERVERS_TAG],
8797
+ tags: ["MCP Servers" /* MCP_SERVERS */],
7143
8798
  summary: "Disconnect OAuth for an MCP server",
7144
8799
  "x-fern-sdk-group-name": ["mcpServers"],
7145
8800
  "x-fern-sdk-method-name": "delete_authorization",
@@ -7246,7 +8901,7 @@ function toConfiguredMcpServer({
7246
8901
  })
7247
8902
  };
7248
8903
  }
7249
- function toMcpServerReadEntry({
8904
+ function toAvailableMcpServer({
7250
8905
  record,
7251
8906
  token
7252
8907
  }) {
@@ -7402,53 +9057,10 @@ function createSettingsMcpServersRouter(deps) {
7402
9057
  throw error;
7403
9058
  }
7404
9059
  };
7405
- const listToolsHandler = async (c) => {
7406
- const { name } = c.req.valid("param");
7407
- const userRef = deps.resolveUserContext(c).userRef;
7408
- const connection = await getMcpConnection({
7409
- tenant_id: TENANT_ID,
7410
- name,
7411
- store: deps.mcpServerStore,
7412
- tokenStore: deps.tokenStore,
7413
- clientName: config_default.MCP_DCR_OAUTH_CLIENT_NAME,
7414
- userRef
7415
- });
7416
- if (connection === void 0) {
7417
- return c.json({ error: { message: `MCP server not found: ${name}` } }, 404);
7418
- }
7419
- const remote = new RemoteMCP({
7420
- id: name,
7421
- name,
7422
- url: connection.url,
7423
- headers: connection.headers,
7424
- requestTimeoutMs: config_default.MCP_REQUEST_TIMEOUT_MS,
7425
- connectTimeoutMs: config_default.MCP_CONNECT_TIMEOUT_MS,
7426
- logger: deps.logger,
7427
- signal: c.req.raw.signal
7428
- });
7429
- try {
7430
- const response = await remote.listTools();
7431
- if (isAuthRequired(response)) {
7432
- return c.json({ error: { message: `MCP server "${name}" requires authentication` } }, 422);
7433
- }
7434
- const data = response.result.tools.map((tool) => omitUndefinedEntries({ ...tool }));
7435
- return c.json({ data }, 200);
7436
- } catch (error) {
7437
- if (error instanceof McpConnectionError4) {
7438
- deps.logger.warn(`MCP tools/list failed for "${name}"`, extractErrorLogFields5(error));
7439
- if (error.statusCode === 401) {
7440
- return c.json({ error: { message: error.message } }, 422);
7441
- }
7442
- return c.json({ error: { message: error.message } }, 502);
7443
- }
7444
- throw error;
7445
- }
7446
- };
7447
9060
  const router = new OpenAPIHono7();
7448
9061
  router.openapi(listMcpServersRoute, listHandler);
7449
9062
  router.openapi(createMcpServerRoute, createHandler);
7450
9063
  router.openapi(putMcpServerRoute, putHandler);
7451
- router.openapi(listMcpServerToolsRoute, listToolsHandler);
7452
9064
  router.openapi(getMcpServerRoute, getHandler);
7453
9065
  return router;
7454
9066
  }
@@ -7498,6 +9110,48 @@ function createMcpServersRouter(deps) {
7498
9110
  return c.json({ error: { message: "Internal server error" } }, 500);
7499
9111
  }
7500
9112
  };
9113
+ const listToolsHandler = async (c) => {
9114
+ const { name } = c.req.valid("param");
9115
+ const userRef = deps.resolveUserContext(c).userRef;
9116
+ const connection = await getMcpConnection({
9117
+ tenant_id: TENANT_ID,
9118
+ name,
9119
+ store: deps.mcpServerStore,
9120
+ tokenStore: deps.tokenStore,
9121
+ clientName: config_default.MCP_DCR_OAUTH_CLIENT_NAME,
9122
+ userRef
9123
+ });
9124
+ if (connection === void 0) {
9125
+ return c.json({ error: { message: `MCP server not found: ${name}` } }, 404);
9126
+ }
9127
+ const remote = new RemoteMCP({
9128
+ id: name,
9129
+ name,
9130
+ url: connection.url,
9131
+ headers: connection.headers,
9132
+ requestTimeoutMs: config_default.MCP_REQUEST_TIMEOUT_MS,
9133
+ connectTimeoutMs: config_default.MCP_CONNECT_TIMEOUT_MS,
9134
+ logger: deps.logger,
9135
+ signal: c.req.raw.signal
9136
+ });
9137
+ try {
9138
+ const response = await remote.listTools();
9139
+ if (isAuthRequired(response)) {
9140
+ return c.json({ error: { message: `MCP server "${name}" requires authentication` } }, 422);
9141
+ }
9142
+ const data = response.result.tools.map((tool) => omitUndefinedEntries({ ...tool }));
9143
+ return c.json({ data }, 200);
9144
+ } catch (error) {
9145
+ if (error instanceof McpConnectionError4) {
9146
+ deps.logger.warn(`MCP tools/list failed for "${name}"`, extractErrorLogFields5(error));
9147
+ if (error.statusCode === 401) {
9148
+ return c.json({ error: { message: error.message } }, 422);
9149
+ }
9150
+ return c.json({ error: { message: error.message } }, 502);
9151
+ }
9152
+ throw error;
9153
+ }
9154
+ };
7501
9155
  const deleteAuthorizationHandler = async (c) => {
7502
9156
  const { name } = c.req.valid("param");
7503
9157
  const userRef = deps.resolveUserContext(c).userRef;
@@ -7518,11 +9172,12 @@ function createMcpServersRouter(deps) {
7518
9172
  const tokens = await deps.tokenStore.getTokens({ ids: dcrIds, userRef });
7519
9173
  return c.json(
7520
9174
  {
7521
- data: records.map((record) => toMcpServerReadEntry({ record, token: tokens.get(record.id) }))
9175
+ data: records.map((record) => toAvailableMcpServer({ record, token: tokens.get(record.id) }))
7522
9176
  },
7523
9177
  200
7524
9178
  );
7525
9179
  });
9180
+ router.openapi(listMcpServerToolsRoute, listToolsHandler);
7526
9181
  router.openapi(authorizeMcpServerRoute, authorizeHandler);
7527
9182
  router.openapi(deleteAuthorizationMcpServerRoute, deleteAuthorizationHandler);
7528
9183
  return router;
@@ -7533,18 +9188,18 @@ import { OpenAPIHono as OpenAPIHono8 } from "@hono/zod-openapi";
7533
9188
 
7534
9189
  // src/routes/modelRoutes.ts
7535
9190
  import { createRoute as createRoute8 } from "@hono/zod-openapi";
7536
- var listModelsRoute = createRoute8({
9191
+ var listAvailableModelsRoute = createRoute8({
7537
9192
  method: "get",
7538
9193
  path: "/",
7539
- tags: ["Models"],
7540
- summary: "List models",
9194
+ tags: ["Models" /* MODELS */],
9195
+ summary: "List models for chat",
7541
9196
  "x-fern-sdk-group-name": ["models"],
7542
9197
  "x-fern-sdk-method-name": "list",
7543
- description: "Models across all configured model providers, addressed by fully qualified name `name/model_name`.",
9198
+ description: "Configured models as a slim FQN list for the composer.",
7544
9199
  responses: {
7545
9200
  200: {
7546
- content: { "application/json": { schema: ListModelsResponseSchema } },
7547
- description: "All models of all configured providers."
9201
+ content: { "application/json": { schema: ListAvailableModelsResponseSchema } },
9202
+ description: "All configured models (chat projection)."
7548
9203
  },
7549
9204
  401: {
7550
9205
  content: { "application/json": { schema: RequestErrorResponseSchema } },
@@ -7557,7 +9212,7 @@ var listModelsRoute = createRoute8({
7557
9212
  function createModelsRouter(deps) {
7558
9213
  const router = new OpenAPIHono8();
7559
9214
  router.openapi(
7560
- listModelsRoute,
9215
+ listAvailableModelsRoute,
7561
9216
  async (c) => c.json({ data: await deps.modelProviderStore.listModels(TENANT_ID) }, 200)
7562
9217
  );
7563
9218
  return router;
@@ -7572,11 +9227,10 @@ import { OpenAPIHono as OpenAPIHono9 } from "@hono/zod-openapi";
7572
9227
 
7573
9228
  // src/routes/modelProviderRoutes.ts
7574
9229
  import { createRoute as createRoute9 } from "@hono/zod-openapi";
7575
- var MODEL_PROVIDERS_TAG = "Model Providers";
7576
9230
  var listModelProvidersRoute = createRoute9({
7577
9231
  method: "get",
7578
9232
  path: "/",
7579
- tags: [MODEL_PROVIDERS_TAG],
9233
+ tags: ["Models" /* MODELS */],
7580
9234
  summary: "List configured model providers",
7581
9235
  description: "All configured providers with nested manifests.",
7582
9236
  "x-fern-sdk-group-name": ["settings", "modelProviders"],
@@ -7599,7 +9253,7 @@ var listModelProvidersRoute = createRoute9({
7599
9253
  var createModelProviderRoute = createRoute9({
7600
9254
  method: "post",
7601
9255
  path: "/",
7602
- tags: [MODEL_PROVIDERS_TAG],
9256
+ tags: ["Models" /* MODELS */],
7603
9257
  summary: "Create a model provider",
7604
9258
  description: "Creates a provider (models included). Fails if `name` is already taken. Well-known types use `type` as `name` (one each); `custom` is named by the caller. `auth.api_key`: real value required; redacted with no stored secret returns 400.",
7605
9259
  "x-fern-sdk-group-name": ["settings", "modelProviders"],
@@ -7628,14 +9282,14 @@ var createModelProviderRoute = createRoute9({
7628
9282
  var putModelProviderRoute = createRoute9({
7629
9283
  method: "put",
7630
9284
  path: "/",
7631
- tags: [MODEL_PROVIDERS_TAG],
9285
+ tags: ["Models" /* MODELS */],
7632
9286
  summary: "Create or replace a model provider",
7633
9287
  description: "Create or replace a provider (models included). Well-known types use `type` as `name` (one each); `custom` is named by the caller. `auth.api_key`: real value sets/rotates; redacted keeps existing (400 if none).",
7634
9288
  "x-fern-sdk-group-name": ["settings", "modelProviders"],
7635
- "x-fern-sdk-method-name": "upsert",
9289
+ "x-fern-sdk-method-name": "create_or_update",
7636
9290
  request: {
7637
9291
  body: {
7638
- content: { "application/json": { schema: PutModelProviderRequestSchema } },
9292
+ content: { "application/json": { schema: UpdateModelProviderRequestSchema } },
7639
9293
  required: true
7640
9294
  }
7641
9295
  },
@@ -7744,11 +9398,10 @@ import { withTimeout } from "@truefoundry/trueforge-core/core";
7744
9398
 
7745
9399
  // src/routes/sandboxProviderRoutes.ts
7746
9400
  import { createRoute as createRoute10 } from "@hono/zod-openapi";
7747
- var SANDBOX_PROVIDERS_TAG = "Sandbox Providers";
7748
9401
  var getSandboxProviderRoute = createRoute10({
7749
9402
  method: "get",
7750
9403
  path: "/",
7751
- tags: [SANDBOX_PROVIDERS_TAG],
9404
+ tags: ["Sandboxes" /* SANDBOXES */],
7752
9405
  summary: "Get the configured sandbox provider",
7753
9406
  description: "The single configured sandbox provider for this tenant. `auth.api_key` is redacted.",
7754
9407
  "x-fern-sdk-group-name": ["settings", "sandboxProviders"],
@@ -7767,14 +9420,14 @@ var getSandboxProviderRoute = createRoute10({
7767
9420
  var putSandboxProviderRoute = createRoute10({
7768
9421
  method: "put",
7769
9422
  path: "/",
7770
- tags: [SANDBOX_PROVIDERS_TAG],
9423
+ tags: ["Sandboxes" /* SANDBOXES */],
7771
9424
  summary: "Create or replace the sandbox provider",
7772
9425
  description: "Upserts the single sandbox provider for this tenant: creates it or replaces its entire configuration. `auth.api_key`: real value sets/rotates; redacted keeps existing (400 if none).",
7773
9426
  "x-fern-sdk-group-name": ["settings", "sandboxProviders"],
7774
- "x-fern-sdk-method-name": "upsert",
9427
+ "x-fern-sdk-method-name": "create_or_update",
7775
9428
  request: {
7776
9429
  body: {
7777
- content: { "application/json": { schema: PutSandboxProviderRequestSchema } },
9430
+ content: { "application/json": { schema: UpdateSandboxProviderRequestSchema } },
7778
9431
  required: true
7779
9432
  }
7780
9433
  },
@@ -7887,11 +9540,10 @@ import { OpenAPIHono as OpenAPIHono11 } from "@hono/zod-openapi";
7887
9540
 
7888
9541
  // src/routes/skillRoutes.ts
7889
9542
  import { createRoute as createRoute11 } from "@hono/zod-openapi";
7890
- var SKILLS_TAG = "Skills";
7891
9543
  var listAvailableSkillsRoute = createRoute11({
7892
9544
  method: "get",
7893
9545
  path: "/",
7894
- tags: [SKILLS_TAG],
9546
+ tags: ["Skills" /* SKILLS */],
7895
9547
  summary: "List skills for chat",
7896
9548
  description: "Configured skills as a slim name/description list for the composer.",
7897
9549
  "x-fern-sdk-group-name": ["skills"],
@@ -7910,7 +9562,7 @@ var listAvailableSkillsRoute = createRoute11({
7910
9562
  var listConfiguredSkillsRoute = createRoute11({
7911
9563
  method: "get",
7912
9564
  path: "/",
7913
- tags: [SKILLS_TAG],
9565
+ tags: ["Skills" /* SKILLS */],
7914
9566
  summary: "List configured skills",
7915
9567
  description: "All configured skills with nested manifests (settings / admin projection).",
7916
9568
  "x-fern-sdk-group-name": ["settings", "skills"],
@@ -7933,7 +9585,7 @@ var listConfiguredSkillsRoute = createRoute11({
7933
9585
  var createSkillRoute = createRoute11({
7934
9586
  method: "post",
7935
9587
  path: "/",
7936
- tags: [SKILLS_TAG],
9588
+ tags: ["Skills" /* SKILLS */],
7937
9589
  summary: "Create a skill",
7938
9590
  description: "Creates a skill keyed by `name`. Fails if `name` is already taken.",
7939
9591
  "x-fern-sdk-group-name": ["settings", "skills"],
@@ -7962,14 +9614,14 @@ var createSkillRoute = createRoute11({
7962
9614
  var putSkillRoute = createRoute11({
7963
9615
  method: "put",
7964
9616
  path: "/",
7965
- tags: [SKILLS_TAG],
9617
+ tags: ["Skills" /* SKILLS */],
7966
9618
  summary: "Create or replace a skill",
7967
9619
  description: "Full upsert keyed by `name`: creates the skill or replaces its entire manifest.",
7968
9620
  "x-fern-sdk-group-name": ["settings", "skills"],
7969
- "x-fern-sdk-method-name": "upsert",
9621
+ "x-fern-sdk-method-name": "create_or_update",
7970
9622
  request: {
7971
9623
  body: {
7972
- content: { "application/json": { schema: PutSkillRequestSchema } },
9624
+ content: { "application/json": { schema: UpdateSkillRequestSchema } },
7973
9625
  required: true
7974
9626
  }
7975
9627
  },
@@ -8097,27 +9749,27 @@ import {
8097
9749
  } from "@truefoundry/trueforge-core/agent-session";
8098
9750
  import {
8099
9751
  AgentHarnessError,
9752
+ existingSandboxIdForProvider,
8100
9753
  extractErrorLogFields as extractErrorLogFields6,
8101
9754
  isAgentInputUserMessage,
8102
9755
  isFileContentPart,
8103
9756
  McpConnectionError as McpConnectionError5,
9757
+ rawSandboxId,
8104
9758
  SandboxError as SandboxError2,
8105
- validateSandboxOwnedByTenant,
8106
9759
  VercelAILLM
8107
9760
  } from "@truefoundry/trueforge-core/core";
8108
9761
  import { HTTPException as HTTPException4 } from "hono/http-exception";
8109
9762
  import { streamSSE } from "hono/streaming";
8110
9763
 
8111
9764
  // src/routes/turnRoutes.ts
8112
- import { createRoute as createRoute12, z as z22 } from "@hono/zod-openapi";
8113
- var SESSIONS_TAG2 = "Sessions";
9765
+ import { createRoute as createRoute12, z as z24 } from "@hono/zod-openapi";
8114
9766
  var TurnIdParamsSchema = SessionIdParamsSchema.extend({
8115
- turn_id: z22.string().min(1).describe("Turn identifier.")
9767
+ turn_id: z24.string().min(1).describe("Turn identifier.")
8116
9768
  });
8117
9769
  var listTurnsRoute = createRoute12({
8118
9770
  method: "get",
8119
9771
  path: "/{session_id}/turns",
8120
- tags: [SESSIONS_TAG2],
9772
+ tags: ["Agent Sessions" /* AGENT_SESSIONS */],
8121
9773
  summary: "List turns in a session",
8122
9774
  description: "List turns for a session (newest first by default), token-paginated. Only the session creator (`created_by`) may list turns.",
8123
9775
  "x-fern-sdk-group-name": ["sessions"],
@@ -8149,7 +9801,7 @@ var listTurnsRoute = createRoute12({
8149
9801
  var getTurnRoute = createRoute12({
8150
9802
  method: "get",
8151
9803
  path: "/{session_id}/turns/{turn_id}",
8152
- tags: [SESSIONS_TAG2],
9804
+ tags: ["Agent Sessions" /* AGENT_SESSIONS */],
8153
9805
  summary: "Get a turn",
8154
9806
  description: "Fetch a single turn by ID. Only the session creator (`created_by`) may fetch it.",
8155
9807
  "x-fern-sdk-group-name": ["sessions"],
@@ -8174,8 +9826,8 @@ var getTurnRoute = createRoute12({
8174
9826
  });
8175
9827
  var downloadSandboxFileRoute = createRoute12({
8176
9828
  method: "get",
8177
- path: "/{session_id}/turns/{turn_id}/download",
8178
- tags: [SESSIONS_TAG2],
9829
+ path: "/{session_id}/turns/{turn_id}/download-sandbox-file",
9830
+ tags: ["Agent Sessions" /* AGENT_SESSIONS */],
8179
9831
  summary: "Download a file from the turn sandbox",
8180
9832
  description: "Download a file from the sandbox this turn ran in. Paths come from the assistant's `sandbox_artifacts` block. Only the session creator (`created_by`) may download.",
8181
9833
  "x-fern-sdk-group-name": ["sessions"],
@@ -8186,7 +9838,7 @@ var downloadSandboxFileRoute = createRoute12({
8186
9838
  },
8187
9839
  responses: {
8188
9840
  200: {
8189
- content: { "application/octet-stream": { schema: z22.string().openapi({ format: "binary" }) } },
9841
+ content: { "application/octet-stream": { schema: z24.string().openapi({ format: "binary" }) } },
8190
9842
  description: "File contents."
8191
9843
  },
8192
9844
  400: {
@@ -8195,7 +9847,7 @@ var downloadSandboxFileRoute = createRoute12({
8195
9847
  },
8196
9848
  403: {
8197
9849
  content: { "application/json": { schema: RequestErrorResponseSchema } },
8198
- description: "Caller is not the session creator, or sandbox belongs to another tenant."
9850
+ description: "Caller is not the session creator."
8199
9851
  },
8200
9852
  404: {
8201
9853
  content: { "application/json": { schema: RequestErrorResponseSchema } },
@@ -8222,7 +9874,7 @@ var downloadSandboxFileRoute = createRoute12({
8222
9874
  var listTurnEventsRoute = createRoute12({
8223
9875
  method: "get",
8224
9876
  path: "/{session_id}/turns/{turn_id}/events",
8225
- tags: [SESSIONS_TAG2],
9877
+ tags: ["Agent Sessions" /* AGENT_SESSIONS */],
8226
9878
  summary: "List turn events",
8227
9879
  description: "Paginated persisted events for a turn (insertion order by default). Only the session creator (`created_by`) may list events.",
8228
9880
  "x-fern-sdk-group-name": ["sessions"],
@@ -8254,7 +9906,7 @@ var listTurnEventsRoute = createRoute12({
8254
9906
  var createAndExecuteTurnRoute = createRoute12({
8255
9907
  method: "post",
8256
9908
  path: "/{session_id}/turns",
8257
- tags: [SESSIONS_TAG2],
9909
+ tags: ["Agent Sessions" /* AGENT_SESSIONS */],
8258
9910
  summary: "Create and execute a turn in a session",
8259
9911
  description: `Create a turn within a session and execute it.
8260
9912
  Only the session creator (\`created_by\`) may create turns.
@@ -8318,7 +9970,7 @@ Use \`previous_turn_id\` to chain to the session's last turn (defaults to \`auto
8318
9970
  var subscribeTurnRoute = createRoute12({
8319
9971
  method: "get",
8320
9972
  path: "/{session_id}/turns/{turn_id}/subscribe",
8321
- tags: [SESSIONS_TAG2],
9973
+ tags: ["Agent Sessions" /* AGENT_SESSIONS */],
8322
9974
  summary: "Subscribe to a running turn",
8323
9975
  description: "Subscribe to the live SSE stream for a turn. Only the session creator (`created_by`) may subscribe. Pass `after_sequence_number` to resume after a disconnect (exclusive \u2014 events after this sequence number are replayed).",
8324
9976
  "x-fern-sdk-group-name": ["sessions"],
@@ -8599,7 +10251,7 @@ var StreamGoneError = class extends Error {
8599
10251
  };
8600
10252
 
8601
10253
  // src/runtime/sandboxFilePath.ts
8602
- import { SandboxError, validateNoPathTraversal } from "@truefoundry/trueforge-core/core";
10254
+ import { SandboxError, validateNoPathTraversal as validateNoPathTraversal3 } from "@truefoundry/trueforge-core/core";
8603
10255
  var SandboxInvalidPathError = class extends SandboxError {
8604
10256
  statusCode = 400;
8605
10257
  constructor({ path: path7, reason }) {
@@ -8625,7 +10277,7 @@ function validateSandboxFilePath(path7) {
8625
10277
  reason: `must not have a segment longer than ${String(MAX_SEGMENT_BYTES)} bytes`
8626
10278
  });
8627
10279
  }
8628
- validateNoPathTraversal(path7);
10280
+ validateNoPathTraversal3(path7);
8629
10281
  }
8630
10282
 
8631
10283
  // src/apis/turns.ts
@@ -8662,7 +10314,8 @@ function createTurnResolver(deps) {
8662
10314
  modelProviderStore,
8663
10315
  logger,
8664
10316
  signal,
8665
- userRef
10317
+ userRef,
10318
+ sessionId
8666
10319
  } = deps;
8667
10320
  return new TurnResourceResolver({
8668
10321
  llm: async (name) => {
@@ -8702,14 +10355,19 @@ function createTurnResolver(deps) {
8702
10355
  const provider = await resolveSandboxProvider({
8703
10356
  tenant_id: TENANT_ID,
8704
10357
  store: sandboxProviderStore,
8705
- logger
10358
+ logger,
10359
+ sessionId
8706
10360
  });
8707
10361
  if (provider === void 0) {
8708
10362
  throw new HTTPException4(422, {
8709
10363
  message: "no sandbox provider configured \u2014 PUT /settings/sandbox-providers"
8710
10364
  });
8711
10365
  }
8712
- if (existingSandboxId === void 0) {
10366
+ const carriedSandboxId = existingSandboxIdForProvider({
10367
+ existingSandboxId,
10368
+ currentProviderType: provider.type
10369
+ });
10370
+ if (carriedSandboxId === void 0 && provider.type !== "local") {
8713
10371
  const status = await checkSnapshotStatus({ store: sandboxProviderStore, tenant_id: TENANT_ID, logger });
8714
10372
  if (status?.status !== "ready") {
8715
10373
  throw new HTTPException4(422, {
@@ -8727,9 +10385,8 @@ function createTurnResolver(deps) {
8727
10385
  logger,
8728
10386
  gitSkills,
8729
10387
  fileDownloadEnabled: spec.config.sandbox.file_downloads,
8730
- existingSandboxId,
8731
- tracing,
8732
- tenantName: TENANT_ID
10388
+ existingSandboxId: carriedSandboxId,
10389
+ tracing
8733
10390
  });
8734
10391
  },
8735
10392
  agent: async (agentId) => {
@@ -8879,13 +10536,13 @@ function createTurnsRouter(deps) {
8879
10536
  const provider = await resolveSandboxProvider({
8880
10537
  tenant_id: TENANT_ID,
8881
10538
  store: deps.sandboxProviderStore,
8882
- logger: deps.logger
10539
+ logger: deps.logger,
10540
+ sessionId
8883
10541
  });
8884
10542
  if (provider === void 0) {
8885
10543
  return c.json({ error: { message: "No sandbox provider configured" } }, 412);
8886
10544
  }
8887
- validateSandboxOwnedByTenant(sandboxId, TENANT_ID);
8888
- const content = await provider.downloadFile({ sandboxId, path: path7 });
10545
+ const content = await provider.downloadFile({ sandboxId: rawSandboxId(sandboxId), path: path7 });
8889
10546
  return c.body(toArrayBuffer(content), 200, {
8890
10547
  "Content-Type": "application/octet-stream",
8891
10548
  "Content-Length": String(content.byteLength),
@@ -8954,7 +10611,8 @@ function createTurnsRouter(deps) {
8954
10611
  modelProviderStore: deps.modelProviderStore,
8955
10612
  logger: deps.logger,
8956
10613
  signal: abortController.signal,
8957
- userRef: deps.resolveUserContext(c).userRef
10614
+ userRef: deps.resolveUserContext(c).userRef,
10615
+ sessionId
8958
10616
  });
8959
10617
  const title = session.record.last_turn_id ? void 0 : deriveSessionTitle(body.input);
8960
10618
  let turn;
@@ -9134,9 +10792,9 @@ function readPackageVersion() {
9134
10792
  var PACKAGE_VERSION = readPackageVersion();
9135
10793
 
9136
10794
  // src/zodErrorResponse.ts
9137
- import { z as z23 } from "@hono/zod-openapi";
10795
+ import { z as z25 } from "@hono/zod-openapi";
9138
10796
  function zodErrorResponse(c, error) {
9139
- return c.json({ error: { message: z23.prettifyError(error) } }, 400);
10797
+ return c.json({ error: { message: z25.prettifyError(error) } }, 400);
9140
10798
  }
9141
10799
  var zodValidationHook = (result, c) => {
9142
10800
  if (!result.success) {
@@ -9155,7 +10813,7 @@ function createRequestBodyLimitMiddleware(maxSize) {
9155
10813
  }
9156
10814
  function createAppErrorHandler(params) {
9157
10815
  return (error, c) => {
9158
- if (error instanceof z24.ZodError) {
10816
+ if (error instanceof z26.ZodError) {
9159
10817
  return zodErrorResponse(c, error);
9160
10818
  }
9161
10819
  if (error instanceof HTTPException5) {
@@ -9177,7 +10835,8 @@ var openApiDocConfig = {
9177
10835
  title: "TrueForge API",
9178
10836
  description: "HTTP API for the TrueForge agent server (`/api/v1`). Interactive docs are served at `/api/v1/docs` (OpenAPI JSON at `/api/v1/openapi.json`).\n\n**Authentication:** Standalone deployments (no OIDC) accept requests without credentials \u2014 middleware stamps a local default user. When OIDC is configured, protected routes require a valid `id_token` cookie or `Authorization: Bearer` ID token. There is no built-in API-key scheme; pass custom headers only if your reverse proxy or IdP layer requires them.\n\nCovers DB-backed sessions, the agent registry, settings catalogs, and model/MCP/skill/sandbox providers.",
9179
10837
  version: PACKAGE_VERSION
9180
- }
10838
+ },
10839
+ tags: OPENAPI_DOCUMENT_TAGS
9181
10840
  };
9182
10841
  function registerOpenApiBearerAuth(app) {
9183
10842
  app.openAPIRegistry.registerComponent("securitySchemes", BEARER_AUTH_SCHEME, {
@@ -9238,7 +10897,7 @@ function createServerApp(deps) {
9238
10897
  )
9239
10898
  );
9240
10899
  app.route(
9241
- "/api/v1/catalog",
10900
+ "/api/v1/catalogs",
9242
10901
  withAuth(
9243
10902
  createCatalogRouter({
9244
10903
  modelCatalog: deps.modelCatalog,
@@ -9608,7 +11267,7 @@ init_config();
9608
11267
  import { serveStatic } from "@hono/node-server/serve-static";
9609
11268
  import { every } from "hono/combine";
9610
11269
  import { compress } from "hono/compress";
9611
- import { existsSync as existsSync2 } from "fs";
11270
+ import { existsSync as existsSync5 } from "fs";
9612
11271
  import path3 from "path";
9613
11272
  var SERVER_PATH_PREFIXES = ["/api", "/healthz"];
9614
11273
  var HASHED_ASSET_PREFIX = "/assets/";
@@ -9618,7 +11277,7 @@ function isServerPath(pathname) {
9618
11277
  return SERVER_PATH_PREFIXES.some((prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`));
9619
11278
  }
9620
11279
  function mountFrontend(app, dir) {
9621
- if (!existsSync2(path3.join(dir, "index.html"))) {
11280
+ if (!existsSync5(path3.join(dir, "index.html"))) {
9622
11281
  return false;
9623
11282
  }
9624
11283
  const serveFile = serveStatic({ root: dir, precompressed: true });
@@ -9751,9 +11410,9 @@ var ActiveTurnRegistry = class {
9751
11410
  */
9752
11411
  track(input) {
9753
11412
  const key = activeTurnKey(input.sessionId, input.turnId);
9754
- const { promise: waitUntilCompleted, resolve } = Promise.withResolvers();
11413
+ const { promise: waitUntilCompleted, resolve: resolve3 } = Promise.withResolvers();
9755
11414
  const markCompleted = () => {
9756
- resolve(void 0);
11415
+ resolve3(void 0);
9757
11416
  };
9758
11417
  const run = {
9759
11418
  abortController: input.abortController,
@@ -9943,7 +11602,7 @@ try {
9943
11602
  }
9944
11603
  async function createStandalonePersistence(options) {
9945
11604
  const { sqlitePath, logger } = options;
9946
- await mkdir(path6.dirname(sqlitePath), { recursive: true });
11605
+ await mkdir5(path6.dirname(sqlitePath), { recursive: true });
9947
11606
  const [{ createSqliteDb: createSqliteDb2 }, { migrateSqliteToLatest: migrateSqliteToLatest2 }, sqliteStores] = await Promise.all([
9948
11607
  Promise.resolve().then(() => (init_client(), client_exports)),
9949
11608
  Promise.resolve().then(() => (init_migrateSqlite(), migrateSqlite_exports)),
@@ -10092,6 +11751,26 @@ try {
10092
11751
  });
10093
11752
  if (configuration2.STANDALONE) {
10094
11753
  printStandaloneStartupBanner({ version: PACKAGE_VERSION, color: shouldColorize() });
11754
+ await prepareCodeModeSocketParent({ path: configuration2.CODE_MODE_SOCKET_PARENT, logger });
11755
+ await ensureLocalSandboxRootParent(configuration2.LOCAL_SANDBOX_ROOT_PARENT);
11756
+ const { LocalSandboxProvider: LocalSandboxProvider2 } = await Promise.resolve().then(() => (init_LocalSandboxProvider(), LocalSandboxProvider_exports));
11757
+ const support = await LocalSandboxProvider2.isSupported({
11758
+ codeModeSocketParentPath: configuration2.CODE_MODE_SOCKET_PARENT
11759
+ });
11760
+ setCachedLocalSandboxSupport(support);
11761
+ if (support.supported) {
11762
+ logger.info("Local sandbox fallback is available", {
11763
+ platform: support.platform,
11764
+ shell: support.shell,
11765
+ python: support.python
11766
+ });
11767
+ } else {
11768
+ logger.warn("Local sandbox fallback is unavailable", {
11769
+ reason: support.reason,
11770
+ ...support.platform === void 0 ? {} : { platform: support.platform },
11771
+ ...support.attempts === void 0 ? {} : { attempts: support.attempts }
11772
+ });
11773
+ }
10095
11774
  } else {
10096
11775
  logger.info("TrueForge starting", { mode: "distributed" });
10097
11776
  }
@@ -10146,9 +11825,9 @@ try {
10146
11825
  logger.warn(`Drain timed out after ${String(configuration2.GRACEFUL_TIMEOUT_SECONDS)}s, exiting`);
10147
11826
  process.exit(1);
10148
11827
  }, configuration2.GRACEFUL_TIMEOUT_SECONDS * 1e3).unref();
10149
- const closed = new Promise((resolve) => {
11828
+ const closed = new Promise((resolve3) => {
10150
11829
  server.close(() => {
10151
- resolve();
11830
+ resolve3();
10152
11831
  });
10153
11832
  });
10154
11833
  await activeTurns.shutdownAndWait(CancellationReason4.Abandoned);
@@ -10160,6 +11839,11 @@ try {
10160
11839
  await redis?.close().catch((error) => {
10161
11840
  logger.warn("[Redis] Error closing client during shutdown", extractErrorLogFields9(error));
10162
11841
  });
11842
+ if (configuration2.STANDALONE) {
11843
+ await removeCodeModeSocketParent(configuration2.CODE_MODE_SOCKET_PARENT).catch((error) => {
11844
+ logger.warn("Error removing Code Mode socket parent during shutdown", extractErrorLogFields9(error));
11845
+ });
11846
+ }
10163
11847
  await destroyDb();
10164
11848
  process.exit(0);
10165
11849
  };