@truefoundry/trueforge 0.1.0 → 0.1.1

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 (160) hide show
  1. package/README.md +30 -0
  2. package/dist/_frontend/assets/{ConnectorSettings-H37ZFC6Z-BoDzRBt-.js → ConnectorSettings-4CYGRWPH-EWp5lwOt.js} +1 -1
  3. package/dist/_frontend/assets/ConnectorSettings-4CYGRWPH-EWp5lwOt.js.br +0 -0
  4. package/dist/_frontend/assets/ConnectorSettings-4CYGRWPH-EWp5lwOt.js.gz +0 -0
  5. package/dist/_frontend/assets/{DockLayout-KEMFRWIR-2H2_fGVk.js → DockLayout-FEDQX2UT-DbN-pvPO.js} +1 -1
  6. package/dist/_frontend/assets/{DrawerLayout-ZVWCKEIS-DFGUqK6q.js → DrawerLayout-NMA7P64K-0IGRaUAW.js} +2 -2
  7. package/dist/_frontend/assets/DrawerLayout-NMA7P64K-0IGRaUAW.js.br +0 -0
  8. package/dist/_frontend/assets/DrawerLayout-NMA7P64K-0IGRaUAW.js.gz +0 -0
  9. package/dist/_frontend/assets/{ModelSettings-QJMYHCMD-DaR_pzAb.js → ModelSettings-H5FKRZLK-CEQWPDWn.js} +1 -1
  10. package/dist/_frontend/assets/ModelSettings-H5FKRZLK-CEQWPDWn.js.br +0 -0
  11. package/dist/_frontend/assets/ModelSettings-H5FKRZLK-CEQWPDWn.js.gz +0 -0
  12. package/dist/_frontend/assets/{SandboxSettings-BUBMLW7Q-ChiNe2Y8.js → SandboxSettings-CBSC3C3G-PWbj0tIQ.js} +1 -1
  13. package/dist/_frontend/assets/SandboxSettings-CBSC3C3G-PWbj0tIQ.js.br +0 -0
  14. package/dist/_frontend/assets/SandboxSettings-CBSC3C3G-PWbj0tIQ.js.gz +0 -0
  15. package/dist/_frontend/assets/SettingsBuilder-EGSH7BUA-Cvx-hLzU.js +2 -0
  16. package/dist/_frontend/assets/SettingsBuilder-EGSH7BUA-Cvx-hLzU.js.br +0 -0
  17. package/dist/_frontend/assets/SettingsBuilder-EGSH7BUA-Cvx-hLzU.js.gz +0 -0
  18. package/dist/_frontend/assets/ShellRouteSync-53FTXNZZ-BIksLzSL.js +1 -0
  19. package/dist/_frontend/assets/ShellRouteSync-53FTXNZZ-BIksLzSL.js.br +0 -0
  20. package/dist/_frontend/assets/ShellRouteSync-53FTXNZZ-BIksLzSL.js.gz +0 -0
  21. package/dist/_frontend/assets/{SidebarLayout-F3XFXW7B-BQV8bkzL.js → SidebarLayout-5WM2BHZG-Ceb_9WvC.js} +2 -2
  22. package/dist/_frontend/assets/SidebarLayout-5WM2BHZG-Ceb_9WvC.js.br +0 -0
  23. package/dist/_frontend/assets/SidebarLayout-5WM2BHZG-Ceb_9WvC.js.gz +0 -0
  24. package/dist/_frontend/assets/{SkillSettings-IRMGDFRG-ChKre3S8.js → SkillSettings-A2OQSQUT-C-AShr7L.js} +1 -1
  25. package/dist/_frontend/assets/SkillSettings-A2OQSQUT-C-AShr7L.js.br +0 -0
  26. package/dist/_frontend/assets/SkillSettings-A2OQSQUT-C-AShr7L.js.gz +0 -0
  27. package/dist/_frontend/assets/TrueForgeUIWithRouter-2AQ3HW6L-DexVXzlD.js +1 -0
  28. package/dist/_frontend/assets/{WidgetLayout-SZMABKIR-BRKArpEm.js → WidgetLayout-IKGVAZFV-Q2dDfnNQ.js} +1 -1
  29. package/dist/_frontend/assets/WidgetLayout-IKGVAZFV-Q2dDfnNQ.js.br +0 -0
  30. package/dist/_frontend/assets/WidgetLayout-IKGVAZFV-Q2dDfnNQ.js.gz +0 -0
  31. package/dist/_frontend/assets/{chunk-7EEOWDLE-BIwInB6G.js → chunk-4STVL6W6-DO6mK5-E.js} +2 -2
  32. package/dist/_frontend/assets/chunk-4STVL6W6-DO6mK5-E.js.br +0 -0
  33. package/dist/_frontend/assets/chunk-4STVL6W6-DO6mK5-E.js.gz +0 -0
  34. package/dist/_frontend/assets/chunk-62JRHF6Z-BpHQergm.js +12 -0
  35. package/dist/_frontend/assets/chunk-62JRHF6Z-BpHQergm.js.br +0 -0
  36. package/dist/_frontend/assets/chunk-62JRHF6Z-BpHQergm.js.gz +0 -0
  37. package/dist/_frontend/assets/chunk-BMLCISMH-BtIzChti.js +4 -0
  38. package/dist/_frontend/assets/chunk-BMLCISMH-BtIzChti.js.br +0 -0
  39. package/dist/_frontend/assets/chunk-BMLCISMH-BtIzChti.js.gz +0 -0
  40. package/dist/_frontend/assets/{cssMode-Bb1lR8Ti.js → cssMode-tBxiXBbi.js} +1 -1
  41. package/dist/_frontend/assets/cssMode-tBxiXBbi.js.br +0 -0
  42. package/dist/_frontend/assets/cssMode-tBxiXBbi.js.gz +0 -0
  43. package/dist/_frontend/assets/{freemarker2-BrrF7mKG.js → freemarker2-C8-_g6KH.js} +1 -1
  44. package/dist/_frontend/assets/freemarker2-C8-_g6KH.js.br +0 -0
  45. package/dist/_frontend/assets/freemarker2-C8-_g6KH.js.gz +0 -0
  46. package/dist/_frontend/assets/{handlebars-WQ7HMT1b.js → handlebars-CVTAyJx2.js} +1 -1
  47. package/dist/_frontend/assets/handlebars-CVTAyJx2.js.br +0 -0
  48. package/dist/_frontend/assets/handlebars-CVTAyJx2.js.gz +0 -0
  49. package/dist/_frontend/assets/{html-CoOPnhCb.js → html-CFO-PVK6.js} +1 -1
  50. package/dist/_frontend/assets/html-CFO-PVK6.js.br +0 -0
  51. package/dist/_frontend/assets/html-CFO-PVK6.js.gz +0 -0
  52. package/dist/_frontend/assets/{htmlMode-BW7taYty.js → htmlMode-CFCanlO-.js} +1 -1
  53. package/dist/_frontend/assets/htmlMode-CFCanlO-.js.br +0 -0
  54. package/dist/_frontend/assets/htmlMode-CFCanlO-.js.gz +0 -0
  55. package/dist/_frontend/assets/{index-DFQqb6R1.js → index-BIWWiCC7.js} +3 -3
  56. package/dist/_frontend/assets/index-BIWWiCC7.js.br +0 -0
  57. package/dist/_frontend/assets/index-BIWWiCC7.js.gz +0 -0
  58. package/dist/_frontend/assets/{index-ChCGltYc.js → index-CSSyjy0I.js} +262 -265
  59. package/dist/_frontend/assets/index-CSSyjy0I.js.br +0 -0
  60. package/dist/_frontend/assets/index-CSSyjy0I.js.gz +0 -0
  61. package/dist/_frontend/assets/{javascript-CPINtbIt.js → javascript-MIQGaM22.js} +1 -1
  62. package/dist/_frontend/assets/{jsonMode-C8qDzOfb.js → jsonMode-D5gVPPeB.js} +1 -1
  63. package/dist/_frontend/assets/jsonMode-D5gVPPeB.js.br +0 -0
  64. package/dist/_frontend/assets/jsonMode-D5gVPPeB.js.gz +0 -0
  65. package/dist/_frontend/assets/{liquid-zGC9H1Dt.js → liquid-DHNaEubO.js} +1 -1
  66. package/dist/_frontend/assets/liquid-DHNaEubO.js.br +0 -0
  67. package/dist/_frontend/assets/liquid-DHNaEubO.js.gz +0 -0
  68. package/dist/_frontend/assets/{lspLanguageFeatures-B6VPH2Sv.js → lspLanguageFeatures-QCSfugnT.js} +1 -1
  69. package/dist/_frontend/assets/lspLanguageFeatures-QCSfugnT.js.br +0 -0
  70. package/dist/_frontend/assets/lspLanguageFeatures-QCSfugnT.js.gz +0 -0
  71. package/dist/_frontend/assets/{mdx-BrQrGCP9.js → mdx-BXTfxgKC.js} +1 -1
  72. package/dist/_frontend/assets/mdx-BXTfxgKC.js.br +0 -0
  73. package/dist/_frontend/assets/mdx-BXTfxgKC.js.gz +0 -0
  74. package/dist/_frontend/assets/{python-zKGpYLwR.js → python-2xJ5-a_I.js} +1 -1
  75. package/dist/_frontend/assets/python-2xJ5-a_I.js.br +0 -0
  76. package/dist/_frontend/assets/python-2xJ5-a_I.js.gz +0 -0
  77. package/dist/_frontend/assets/{razor-BJ38lYsd.js → razor-pc2nnHiH.js} +1 -1
  78. package/dist/_frontend/assets/razor-pc2nnHiH.js.br +0 -0
  79. package/dist/_frontend/assets/razor-pc2nnHiH.js.gz +0 -0
  80. package/dist/_frontend/assets/{tsMode-dPLqGMg8.js → tsMode-DDOTqjiA.js} +1 -1
  81. package/dist/_frontend/assets/tsMode-DDOTqjiA.js.br +0 -0
  82. package/dist/_frontend/assets/tsMode-DDOTqjiA.js.gz +0 -0
  83. package/dist/_frontend/assets/{typescript-BJlivWry.js → typescript-B8kVUDuw.js} +1 -1
  84. package/dist/_frontend/assets/typescript-B8kVUDuw.js.br +0 -0
  85. package/dist/_frontend/assets/typescript-B8kVUDuw.js.gz +0 -0
  86. package/dist/_frontend/assets/{workers-fMwav4jB.js → workers-BKOSx8Az.js} +1 -1
  87. package/dist/_frontend/assets/workers-BKOSx8Az.js.br +0 -0
  88. package/dist/_frontend/assets/workers-BKOSx8Az.js.gz +0 -0
  89. package/dist/_frontend/assets/{xml-DZW470wi.js → xml--HuG4XfW.js} +1 -1
  90. package/dist/_frontend/assets/xml--HuG4XfW.js.br +0 -0
  91. package/dist/_frontend/assets/xml--HuG4XfW.js.gz +0 -0
  92. package/dist/_frontend/assets/{yaml-Bs1DumYp.js → yaml-BM2qwYh9.js} +1 -1
  93. package/dist/_frontend/assets/yaml-BM2qwYh9.js.br +0 -0
  94. package/dist/_frontend/assets/yaml-BM2qwYh9.js.gz +0 -0
  95. package/dist/_frontend/index.html +1 -1
  96. package/dist/_frontend/index.html.br +0 -0
  97. package/dist/_frontend/index.html.gz +0 -0
  98. package/dist/cli.js +23 -1
  99. package/dist/cli.js.map +1 -1
  100. package/dist/main.js +388 -77
  101. package/dist/main.js.map +1 -1
  102. package/package.json +11 -7
  103. package/dist/_frontend/assets/ConnectorSettings-H37ZFC6Z-BoDzRBt-.js.br +0 -0
  104. package/dist/_frontend/assets/ConnectorSettings-H37ZFC6Z-BoDzRBt-.js.gz +0 -0
  105. package/dist/_frontend/assets/DrawerLayout-ZVWCKEIS-DFGUqK6q.js.br +0 -0
  106. package/dist/_frontend/assets/DrawerLayout-ZVWCKEIS-DFGUqK6q.js.gz +0 -0
  107. package/dist/_frontend/assets/ModelSettings-QJMYHCMD-DaR_pzAb.js.br +0 -0
  108. package/dist/_frontend/assets/ModelSettings-QJMYHCMD-DaR_pzAb.js.gz +0 -0
  109. package/dist/_frontend/assets/SandboxSettings-BUBMLW7Q-ChiNe2Y8.js.br +0 -0
  110. package/dist/_frontend/assets/SandboxSettings-BUBMLW7Q-ChiNe2Y8.js.gz +0 -0
  111. package/dist/_frontend/assets/SettingsBuilder-26XSGIHK-DmgjXL0m.js +0 -2
  112. package/dist/_frontend/assets/SettingsBuilder-26XSGIHK-DmgjXL0m.js.br +0 -0
  113. package/dist/_frontend/assets/SettingsBuilder-26XSGIHK-DmgjXL0m.js.gz +0 -0
  114. package/dist/_frontend/assets/SidebarLayout-F3XFXW7B-BQV8bkzL.js.br +0 -0
  115. package/dist/_frontend/assets/SidebarLayout-F3XFXW7B-BQV8bkzL.js.gz +0 -0
  116. package/dist/_frontend/assets/SkillSettings-IRMGDFRG-ChKre3S8.js.br +0 -0
  117. package/dist/_frontend/assets/SkillSettings-IRMGDFRG-ChKre3S8.js.gz +0 -0
  118. package/dist/_frontend/assets/WidgetLayout-SZMABKIR-BRKArpEm.js.br +0 -0
  119. package/dist/_frontend/assets/WidgetLayout-SZMABKIR-BRKArpEm.js.gz +0 -0
  120. package/dist/_frontend/assets/chunk-7EEOWDLE-BIwInB6G.js.br +0 -0
  121. package/dist/_frontend/assets/chunk-7EEOWDLE-BIwInB6G.js.gz +0 -0
  122. package/dist/_frontend/assets/chunk-LRQHHZDX-eb14Z2nA.js +0 -1
  123. package/dist/_frontend/assets/chunk-LRQHHZDX-eb14Z2nA.js.br +0 -0
  124. package/dist/_frontend/assets/chunk-LRQHHZDX-eb14Z2nA.js.gz +0 -0
  125. package/dist/_frontend/assets/cssMode-Bb1lR8Ti.js.br +0 -0
  126. package/dist/_frontend/assets/cssMode-Bb1lR8Ti.js.gz +0 -0
  127. package/dist/_frontend/assets/freemarker2-BrrF7mKG.js.br +0 -0
  128. package/dist/_frontend/assets/freemarker2-BrrF7mKG.js.gz +0 -0
  129. package/dist/_frontend/assets/handlebars-WQ7HMT1b.js.br +0 -0
  130. package/dist/_frontend/assets/handlebars-WQ7HMT1b.js.gz +0 -0
  131. package/dist/_frontend/assets/html-CoOPnhCb.js.br +0 -0
  132. package/dist/_frontend/assets/html-CoOPnhCb.js.gz +0 -0
  133. package/dist/_frontend/assets/htmlMode-BW7taYty.js.br +0 -0
  134. package/dist/_frontend/assets/htmlMode-BW7taYty.js.gz +0 -0
  135. package/dist/_frontend/assets/index-ChCGltYc.js.br +0 -0
  136. package/dist/_frontend/assets/index-ChCGltYc.js.gz +0 -0
  137. package/dist/_frontend/assets/index-DFQqb6R1.js.br +0 -0
  138. package/dist/_frontend/assets/index-DFQqb6R1.js.gz +0 -0
  139. package/dist/_frontend/assets/jsonMode-C8qDzOfb.js.br +0 -0
  140. package/dist/_frontend/assets/jsonMode-C8qDzOfb.js.gz +0 -0
  141. package/dist/_frontend/assets/liquid-zGC9H1Dt.js.br +0 -0
  142. package/dist/_frontend/assets/liquid-zGC9H1Dt.js.gz +0 -0
  143. package/dist/_frontend/assets/lspLanguageFeatures-B6VPH2Sv.js.br +0 -0
  144. package/dist/_frontend/assets/lspLanguageFeatures-B6VPH2Sv.js.gz +0 -0
  145. package/dist/_frontend/assets/mdx-BrQrGCP9.js.br +0 -0
  146. package/dist/_frontend/assets/mdx-BrQrGCP9.js.gz +0 -0
  147. package/dist/_frontend/assets/python-zKGpYLwR.js.br +0 -0
  148. package/dist/_frontend/assets/python-zKGpYLwR.js.gz +0 -0
  149. package/dist/_frontend/assets/razor-BJ38lYsd.js.br +0 -0
  150. package/dist/_frontend/assets/razor-BJ38lYsd.js.gz +0 -0
  151. package/dist/_frontend/assets/tsMode-dPLqGMg8.js.br +0 -0
  152. package/dist/_frontend/assets/tsMode-dPLqGMg8.js.gz +0 -0
  153. package/dist/_frontend/assets/typescript-BJlivWry.js.br +0 -0
  154. package/dist/_frontend/assets/typescript-BJlivWry.js.gz +0 -0
  155. package/dist/_frontend/assets/workers-fMwav4jB.js.br +0 -0
  156. package/dist/_frontend/assets/workers-fMwav4jB.js.gz +0 -0
  157. package/dist/_frontend/assets/xml-DZW470wi.js.br +0 -0
  158. package/dist/_frontend/assets/xml-DZW470wi.js.gz +0 -0
  159. package/dist/_frontend/assets/yaml-Bs1DumYp.js.br +0 -0
  160. package/dist/_frontend/assets/yaml-Bs1DumYp.js.gz +0 -0
package/dist/main.js CHANGED
@@ -92,8 +92,12 @@ function parseBoolean(options) {
92
92
  return defaultValue;
93
93
  }
94
94
  const value = raw.trim().toLowerCase();
95
- if (value === "true") return true;
96
- if (value === "false") return false;
95
+ if (value === "true") {
96
+ return true;
97
+ }
98
+ if (value === "false") {
99
+ return false;
100
+ }
97
101
  throw new Error(`Environment variable ${envKey} must be "true" or "false", got "${raw}"`);
98
102
  }
99
103
  function resolveDefaultFrontendDir() {
@@ -192,11 +196,13 @@ function getPublicBaseUrl(config = configuration) {
192
196
  }
193
197
  return config.PUBLIC_BASE_URL;
194
198
  }
195
- var DEFAULT_PORT, PACKAGE_ROOT, LOCAL_EXECUTOR_ID, ENV_PATHS_APP_NAME, DEFAULT_POSTGRES_USER, DEFAULT_POSTGRES_PASSWORD, DEFAULT_POSTGRES_DB, DEFAULT_POSTGRES_HOST, DEFAULT_POSTGRES_PORT, DEFAULT_REDIS_URL, DEFAULT_OIDC_USER_REFERENCE_CLAIM, DEFAULT_OIDC_USER_ROLE_CLAIM, DEFAULT_OIDC_ADMIN_ROLE_VALUE, DEFAULT_OIDC_SCOPES, serverExecutionTimeoutSeconds, standalone, port, shared, configuration, config_default;
199
+ var DEFAULT_PORT, DEFAULT_HOST, DEFAULT_MAX_REQUEST_BODY_BYTES, PACKAGE_ROOT, LOCAL_EXECUTOR_ID, ENV_PATHS_APP_NAME, DEFAULT_POSTGRES_USER, DEFAULT_POSTGRES_PASSWORD, DEFAULT_POSTGRES_DB, DEFAULT_POSTGRES_HOST, DEFAULT_POSTGRES_PORT, DEFAULT_REDIS_URL, DEFAULT_OIDC_USER_REFERENCE_CLAIM, DEFAULT_OIDC_USER_ROLE_CLAIM, DEFAULT_OIDC_ADMIN_ROLE_VALUE, DEFAULT_OIDC_SCOPES, serverExecutionTimeoutSeconds, standalone, port, host, shared, configuration, config_default;
196
200
  var init_config = __esm({
197
201
  "src/config.ts"() {
198
202
  "use strict";
199
203
  DEFAULT_PORT = 8790;
204
+ DEFAULT_HOST = "localhost";
205
+ DEFAULT_MAX_REQUEST_BODY_BYTES = 30 * 1024 * 1024;
200
206
  PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
201
207
  LOCAL_EXECUTOR_ID = "local";
202
208
  ENV_PATHS_APP_NAME = "trueforge";
@@ -221,10 +227,12 @@ var init_config = __esm({
221
227
  defaultValue: true
222
228
  });
223
229
  port = parsePort(getEnv("PORT"));
230
+ host = getEnv("HOST", { defaultValue: DEFAULT_HOST }) ?? DEFAULT_HOST;
224
231
  shared = {
225
232
  LOG_LEVEL: getEnv("LOG_LEVEL", { defaultValue: "info" }) ?? "info",
226
233
  NODE_ENV: getEnv("NODE_ENV"),
227
234
  PORT: port,
235
+ HOST: host,
228
236
  EXECUTOR_ID: standalone ? LOCAL_EXECUTOR_ID : randomAlphanumeric(6),
229
237
  MODEL_CATALOG_PATH: resolveOptionalPathEnv("MODEL_CATALOG_PATH"),
230
238
  MCP_CATALOG_PATH: resolveOptionalPathEnv("MCP_CATALOG_PATH"),
@@ -247,6 +255,11 @@ var init_config = __esm({
247
255
  raw: getEnv("SANDBOX_FILE_MAX_BYTES_FOR_DOWNLOAD"),
248
256
  defaultValue: 20971520
249
257
  }),
258
+ MAX_REQUEST_BODY_BYTES: parsePositiveInt({
259
+ envKey: "MAX_REQUEST_BODY_BYTES",
260
+ raw: getEnv("MAX_REQUEST_BODY_BYTES"),
261
+ defaultValue: DEFAULT_MAX_REQUEST_BODY_BYTES
262
+ }),
250
263
  GRACEFUL_TIMEOUT_SECONDS: parsePositiveInt({
251
264
  envKey: "GRACEFUL_TIMEOUT_SECONDS",
252
265
  raw: getEnv("GRACEFUL_TIMEOUT_SECONDS"),
@@ -576,15 +589,15 @@ __export(migrateSqlite_exports, {
576
589
  migrateSqliteToLatest: () => migrateSqliteToLatest
577
590
  });
578
591
  import { promises as fs2 } from "fs";
579
- import path3 from "path";
592
+ import path4 from "path";
580
593
  import { FileMigrationProvider, Migrator } from "kysely/migration";
581
594
  async function migrateSqliteToLatest(db) {
582
595
  const migrator = new Migrator({
583
596
  db,
584
597
  provider: new FileMigrationProvider({
585
598
  fs: fs2,
586
- path: path3,
587
- migrationFolder: path3.join(import.meta.dirname, "sqlite", "migrations")
599
+ path: path4,
600
+ migrationFolder: path4.join(import.meta.dirname, "sqlite", "migrations")
588
601
  })
589
602
  });
590
603
  const { error, results } = await migrator.migrateToLatest();
@@ -613,8 +626,8 @@ import { sql } from "kysely";
613
626
  function jsonbBind(value) {
614
627
  return sql`jsonb(${JSON.stringify(value)})`;
615
628
  }
616
- function jsonbSet(column, path6, value) {
617
- return sql`jsonb_set(${column}, ${path6}, ${jsonbBind(value)})`;
629
+ function jsonbSet(column, path7, value) {
630
+ return sql`jsonb_set(${column}, ${path7}, ${jsonbBind(value)})`;
618
631
  }
619
632
  function jsonText(column) {
620
633
  return sql`json(${column})`;
@@ -724,7 +737,9 @@ async function assembleTurnRecord(db, args) {
724
737
  "created_at",
725
738
  "updated_at"
726
739
  ]).where("session_id", "=", args.session_id).where("turn_id", "=", args.turn_id).executeTakeFirst();
727
- if (!turn) return void 0;
740
+ if (!turn) {
741
+ return void 0;
742
+ }
728
743
  const contextRows = await db.selectFrom("turn_thread as tt").leftJoin(
729
744
  "turn_thread_context as ttc",
730
745
  (join) => join.on("ttc.session_id", "=", args.session_id).on("ttc.turn_id", "=", args.turn_id).onRef("ttc.thread_id", "=", "tt.thread_id")
@@ -806,7 +821,9 @@ async function createTurn(db, input) {
806
821
  try {
807
822
  await db.transaction().execute(async (trx) => {
808
823
  const locked = await trx.selectFrom("session").select(["last_turn_id"]).where("session_id", "=", input.session_id).executeTakeFirst();
809
- if (!locked) throw new SessionNotFoundError(input.session_id);
824
+ if (!locked) {
825
+ throw new SessionNotFoundError(input.session_id);
826
+ }
810
827
  let sessionUpdate = trx.updateTable("session").set({
811
828
  last_turn_id: input.turn.turn_id,
812
829
  updated_at: nowIso(),
@@ -845,7 +862,9 @@ async function createTurn(db, input) {
845
862
  }
846
863
  prevCheckpoint = first.turn_checkpoint;
847
864
  for (const row of prevRows) {
848
- if (row.thread_id === null) continue;
865
+ if (row.thread_id === null) {
866
+ continue;
867
+ }
849
868
  if (row.thread_checkpoint === null || row.current_context_usage === null) {
850
869
  throw new SessionStoreInvariantError2(`previous turn_thread row for ${row.thread_id} is incomplete`);
851
870
  }
@@ -997,7 +1016,9 @@ async function createTurn(db, input) {
997
1016
  }
998
1017
  const capabilityStateRows = [];
999
1018
  for (const capability of input.capability_states) {
1000
- if (capability.capability_state === null) continue;
1019
+ if (capability.capability_state === null) {
1020
+ continue;
1021
+ }
1001
1022
  for (const [key, state] of Object.entries(capability.capability_state)) {
1002
1023
  capabilityStateRows.push({
1003
1024
  session_id: input.session_id,
@@ -1014,8 +1035,12 @@ async function createTurn(db, input) {
1014
1035
  }
1015
1036
  });
1016
1037
  } catch (err) {
1017
- if (err instanceof SessionStoreNotFoundError3 || err instanceof SessionStoreConflictError3) throw err;
1018
- if (isUniqueViolation(err)) throw new TurnAlreadyExistsError(input.turn.turn_id, { cause: err });
1038
+ if (err instanceof SessionStoreNotFoundError3 || err instanceof SessionStoreConflictError3) {
1039
+ throw err;
1040
+ }
1041
+ if (isUniqueViolation(err)) {
1042
+ throw new TurnAlreadyExistsError(input.turn.turn_id, { cause: err });
1043
+ }
1019
1044
  throw err;
1020
1045
  }
1021
1046
  }
@@ -1040,7 +1065,9 @@ async function freezeAndGetTurn(db, input) {
1040
1065
  }).execute();
1041
1066
  }
1042
1067
  const record = await assembleTurnRecord(trx, input);
1043
- if (!record) throw new TurnNotFoundError(input.turn_id);
1068
+ if (!record) {
1069
+ throw new TurnNotFoundError(input.turn_id);
1070
+ }
1044
1071
  return record;
1045
1072
  });
1046
1073
  }
@@ -1088,7 +1115,9 @@ async function updateTurnState(db, input) {
1088
1115
  const numUpdated = Number(result.numUpdatedRows);
1089
1116
  if (numUpdated === 0) {
1090
1117
  const existing = await trx.selectFrom("turn").select([jsonText(sql2.ref("state")).as("state")]).where("session_id", "=", input.session_id).where("turn_id", "=", input.turn_id).executeTakeFirst();
1091
- if (!existing) throw new TurnNotFoundError(input.turn_id);
1118
+ if (!existing) {
1119
+ throw new TurnNotFoundError(input.turn_id);
1120
+ }
1092
1121
  throw new TurnNotRunningError(input.turn_id, terminalTurnState(existing.state, input.turn_id));
1093
1122
  }
1094
1123
  await trx.insertInto("session_event").values({
@@ -1157,7 +1186,9 @@ import {
1157
1186
  } from "@truefoundry/trueforge-core/agent-session/store/SessionStoreErrors";
1158
1187
  import { sql as sql4 } from "kysely";
1159
1188
  async function appendToEvents(db, input) {
1160
- if (input.events.length === 0) return;
1189
+ if (input.events.length === 0) {
1190
+ return;
1191
+ }
1161
1192
  const keys = {
1162
1193
  session_id: input.session_id,
1163
1194
  turn_id: input.turn_id
@@ -1202,11 +1233,17 @@ async function resolveAncestorChain(db, sessionId, anchor) {
1202
1233
  let oldestId = chain[0];
1203
1234
  while (oldestId && oldestId !== anchor.turn_id) {
1204
1235
  const oldest = await db.selectFrom("turn").select([jsonText(sql4.ref("ancestor_ids")).as("ancestor_ids")]).where("session_id", "=", sessionId).where("turn_id", "=", oldestId).executeTakeFirst();
1205
- if (!oldest) break;
1236
+ if (!oldest) {
1237
+ break;
1238
+ }
1206
1239
  const older = oldest.ancestor_ids.filter((id) => !seen.has(id));
1207
- if (older.length === 0) break;
1240
+ if (older.length === 0) {
1241
+ break;
1242
+ }
1208
1243
  chain.unshift(...older);
1209
- for (const id of older) seen.add(id);
1244
+ for (const id of older) {
1245
+ seen.add(id);
1246
+ }
1210
1247
  oldestId = older[0];
1211
1248
  }
1212
1249
  return chain;
@@ -2448,15 +2485,15 @@ __export(migratePostgres_exports, {
2448
2485
  migrateToLatest: () => migrateToLatest
2449
2486
  });
2450
2487
  import { promises as fs3 } from "fs";
2451
- import path4 from "path";
2488
+ import path5 from "path";
2452
2489
  import { FileMigrationProvider as FileMigrationProvider2, Migrator as Migrator2 } from "kysely/migration";
2453
2490
  async function migrateToLatest(db) {
2454
2491
  const migrator = new Migrator2({
2455
2492
  db,
2456
2493
  provider: new FileMigrationProvider2({
2457
2494
  fs: fs3,
2458
- path: path4,
2459
- migrationFolder: path4.join(import.meta.dirname, "postgres", "migrations")
2495
+ path: path5,
2496
+ migrationFolder: path5.join(import.meta.dirname, "postgres", "migrations")
2460
2497
  })
2461
2498
  });
2462
2499
  const { error, results } = await migrator.migrateToLatest();
@@ -2506,8 +2543,8 @@ import { sql as sql9 } from "kysely";
2506
2543
  function json(value) {
2507
2544
  return sql9`${JSON.stringify(value)}::jsonb`;
2508
2545
  }
2509
- function jsonbSet2(target, path6, newValue) {
2510
- return sql9`jsonb_set(${target}, ${path6}, ${newValue})`;
2546
+ function jsonbSet2(target, path7, newValue) {
2547
+ return sql9`jsonb_set(${target}, ${path7}, ${newValue})`;
2511
2548
  }
2512
2549
  function now() {
2513
2550
  return sql9`now()`;
@@ -2621,7 +2658,9 @@ async function assertTurnRunning2(db, keys) {
2621
2658
  }
2622
2659
  async function assembleTurnRecord2(db, args) {
2623
2660
  const turn = await db.selectFrom("turn").selectAll().where("session_id", "=", args.session_id).where("turn_id", "=", args.turn_id).executeTakeFirst();
2624
- if (!turn) return void 0;
2661
+ if (!turn) {
2662
+ return void 0;
2663
+ }
2625
2664
  const contextRows = await db.selectFrom("turn_thread as tt").leftJoin(lateralUnnestBigintArrayWithOrdinality(sql11`tt.context_ids`, "c"), (join) => join.onTrue()).leftJoin(
2626
2665
  "thread_context_log as l",
2627
2666
  (join) => join.on("l.session_id", "=", args.session_id).onRef("l.thread_id", "=", "tt.thread_id").onRef("l.append_id", "=", "c.append_id")
@@ -2690,7 +2729,9 @@ async function createTurn2(db, input) {
2690
2729
  try {
2691
2730
  await db.transaction().execute(async (trx) => {
2692
2731
  const locked = await trx.selectFrom("session").select(["last_turn_id"]).where("session_id", "=", input.session_id).forUpdate().executeTakeFirst();
2693
- if (!locked) throw new SessionNotFoundError4(input.session_id);
2732
+ if (!locked) {
2733
+ throw new SessionNotFoundError4(input.session_id);
2734
+ }
2694
2735
  await trx.updateTable("session").set({
2695
2736
  last_turn_id: input.turn.turn_id,
2696
2737
  updated_at: sql11`now()`,
@@ -2722,7 +2763,9 @@ async function createTurn2(db, input) {
2722
2763
  }
2723
2764
  prevCheckpoint = first.turn_checkpoint;
2724
2765
  for (const row of prevRows) {
2725
- if (row.thread_id === null) continue;
2766
+ if (row.thread_id === null) {
2767
+ continue;
2768
+ }
2726
2769
  if (row.thread_checkpoint === null || row.current_context_usage === null || row.context_ids === null) {
2727
2770
  throw new SessionStoreInvariantError4(`previous turn_thread row for ${row.thread_id} is incomplete`);
2728
2771
  }
@@ -2830,7 +2873,9 @@ async function createTurn2(db, input) {
2830
2873
  }
2831
2874
  const capabilityStateRows = [];
2832
2875
  for (const capability of input.capability_states) {
2833
- if (capability.capability_state === null) continue;
2876
+ if (capability.capability_state === null) {
2877
+ continue;
2878
+ }
2834
2879
  for (const [key, state] of Object.entries(capability.capability_state)) {
2835
2880
  capabilityStateRows.push({
2836
2881
  session_id: input.session_id,
@@ -2847,8 +2892,12 @@ async function createTurn2(db, input) {
2847
2892
  }
2848
2893
  });
2849
2894
  } catch (err) {
2850
- if (err instanceof SessionStoreNotFoundError4 || err instanceof SessionStoreConflictError4) throw err;
2851
- if (isUniqueViolation2(err)) throw new TurnAlreadyExistsError2(input.turn.turn_id, { cause: err });
2895
+ if (err instanceof SessionStoreNotFoundError4 || err instanceof SessionStoreConflictError4) {
2896
+ throw err;
2897
+ }
2898
+ if (isUniqueViolation2(err)) {
2899
+ throw new TurnAlreadyExistsError2(input.turn.turn_id, { cause: err });
2900
+ }
2852
2901
  throw err;
2853
2902
  }
2854
2903
  }
@@ -2873,7 +2922,9 @@ async function freezeAndGetTurn2(db, input) {
2873
2922
  }).execute();
2874
2923
  }
2875
2924
  const record = await assembleTurnRecord2(trx, input);
2876
- if (!record) throw new TurnNotFoundError3(input.turn_id);
2925
+ if (!record) {
2926
+ throw new TurnNotFoundError3(input.turn_id);
2927
+ }
2877
2928
  return record;
2878
2929
  });
2879
2930
  }
@@ -2912,7 +2963,9 @@ async function updateTurnState2(db, input) {
2912
2963
  const numUpdated = Number(result.numUpdatedRows);
2913
2964
  if (numUpdated === 0) {
2914
2965
  const existing = await trx.selectFrom("turn").select("state").where("session_id", "=", input.session_id).where("turn_id", "=", input.turn_id).executeTakeFirst();
2915
- if (!existing) throw new TurnNotFoundError3(input.turn_id);
2966
+ if (!existing) {
2967
+ throw new TurnNotFoundError3(input.turn_id);
2968
+ }
2916
2969
  throw new TurnNotRunningError2(input.turn_id, terminalTurnState2(existing.state, input.turn_id));
2917
2970
  }
2918
2971
  await trx.insertInto("session_event").values({
@@ -2983,7 +3036,9 @@ import {
2983
3036
  } from "@truefoundry/trueforge-core/agent-session/store/SessionStoreErrors";
2984
3037
  import { sql as sql13 } from "kysely";
2985
3038
  async function appendToEvents2(db, input) {
2986
- if (input.events.length === 0) return;
3039
+ if (input.events.length === 0) {
3040
+ return;
3041
+ }
2987
3042
  const keys = {
2988
3043
  session_id: input.session_id,
2989
3044
  turn_id: input.turn_id
@@ -3058,11 +3113,17 @@ async function resolveAncestorChain2(db, sessionId, anchor) {
3058
3113
  let oldestId = chain[0];
3059
3114
  while (oldestId && oldestId !== anchor.turn_id) {
3060
3115
  const oldest = await db.selectFrom("turn").select(["ancestor_ids"]).where("session_id", "=", sessionId).where("turn_id", "=", oldestId).executeTakeFirst();
3061
- if (!oldest) break;
3116
+ if (!oldest) {
3117
+ break;
3118
+ }
3062
3119
  const older = oldest.ancestor_ids.filter((id) => !seen.has(id));
3063
- if (older.length === 0) break;
3120
+ if (older.length === 0) {
3121
+ break;
3122
+ }
3064
3123
  chain.unshift(...older);
3065
- for (const id of older) seen.add(id);
3124
+ for (const id of older) {
3125
+ seen.add(id);
3126
+ }
3066
3127
  oldestId = older[0];
3067
3128
  }
3068
3129
  return chain;
@@ -4138,19 +4199,19 @@ var init_PostgresAgentStore = __esm({
4138
4199
  // src/main.ts
4139
4200
  import { extractErrorLogFields as extractErrorLogFields8 } from "@truefoundry/trueforge-core/core";
4140
4201
  import { mkdir } from "fs/promises";
4141
- import path5 from "path";
4202
+ import path6 from "path";
4142
4203
  import { serve } from "@hono/node-server";
4143
4204
  import {
4144
4205
  CancellationReason as CancellationReason6,
4145
4206
  Sessions
4146
4207
  } from "@truefoundry/trueforge-core/agent-session";
4147
4208
  import { RequestReplyExecutor, RequestReplyRouter } from "@truefoundry/trueforge-core/request-reply";
4148
- import winston from "winston";
4149
4209
 
4150
4210
  // src/app.ts
4151
4211
  import { swaggerUI } from "@hono/swagger-ui";
4152
4212
  import { OpenAPIHono as OpenAPIHono14, z as z24 } from "@hono/zod-openapi";
4153
4213
  import { extractErrorLogFields as extractErrorLogFields6 } from "@truefoundry/trueforge-core/core";
4214
+ import { bodyLimit } from "hono/body-limit";
4154
4215
  import { HTTPException as HTTPException6 } from "hono/http-exception";
4155
4216
 
4156
4217
  // src/apis/agents.ts
@@ -4744,6 +4805,7 @@ function toDaytonaSandboxProvider({
4744
4805
  const { apiKey, ...settings } = toDaytonaSandboxProviderInput(manifest);
4745
4806
  return new DaytonaSandboxProvider({
4746
4807
  client: new Daytona({ apiKey }),
4808
+ apiKey,
4747
4809
  ...settings,
4748
4810
  tenantName: tenant_id,
4749
4811
  sandboxImage: build_metadata?.["image_uri"] ?? SANDBOX_IMAGE_URI,
@@ -4759,6 +4821,13 @@ function toSandboxStatus(build) {
4759
4821
  build_metadata: build.metadata
4760
4822
  };
4761
4823
  }
4824
+ function sandboxStatusFromRecord(record) {
4825
+ return {
4826
+ status: record.status,
4827
+ status_reason: record.status_reason,
4828
+ build_metadata: record.build_metadata
4829
+ };
4830
+ }
4762
4831
  var READY_REVALIDATE_INTERVAL_MS = 13 * 24 * 60 * 60 * 1e3;
4763
4832
  async function checkSnapshotStatus({
4764
4833
  store,
@@ -4769,11 +4838,7 @@ async function checkSnapshotStatus({
4769
4838
  if (!record) {
4770
4839
  return void 0;
4771
4840
  }
4772
- const persisted = {
4773
- status: record.status,
4774
- status_reason: record.status_reason,
4775
- build_metadata: record.build_metadata
4776
- };
4841
+ const persisted = sandboxStatusFromRecord(record);
4777
4842
  const readyIsFresh = record.status === "ready" && Date.now() - Date.parse(record.updated_at) < READY_REVALIDATE_INTERVAL_MS;
4778
4843
  if (record.status === "failed" || readyIsFresh) {
4779
4844
  return persisted;
@@ -4791,8 +4856,8 @@ async function checkSnapshotStatus({
4791
4856
  build = await provider.getImageBuildStatus();
4792
4857
  }
4793
4858
  const next = toSandboxStatus(build);
4794
- await store.updateSandboxStatus({ tenant_id, ...next });
4795
- return next;
4859
+ const updated = await store.updateSandboxStatus({ tenant_id, ...next });
4860
+ return updated ? sandboxStatusFromRecord(updated) : next;
4796
4861
  }
4797
4862
 
4798
4863
  // src/schemas/mcpServer.ts
@@ -8210,6 +8275,10 @@ Use \`previous_turn_id\` to chain to the session's last turn (defaults to \`auto
8210
8275
  content: { "application/json": { schema: RequestErrorResponseSchema } },
8211
8276
  description: "Requested action cannot be performed on the session because it is no longer usable."
8212
8277
  },
8278
+ 413: {
8279
+ content: { "application/json": { schema: RequestErrorResponseSchema } },
8280
+ description: "Request body exceeds the configured maximum size (MAX_REQUEST_BODY_BYTES)."
8281
+ },
8213
8282
  422: {
8214
8283
  content: { "application/json": { schema: RequestErrorResponseSchema } },
8215
8284
  description: "The request is well-formed but cannot be processed: a referenced resource is missing (named agent, model, MCP server, skill, or sandbox provider), sandbox is required but unavailable (e.g. file uploads), or the turn input conflicts with current state (e.g. unresolved tool calls or approvals, tool name collision)."
@@ -8503,30 +8572,30 @@ var StreamGoneError = class extends Error {
8503
8572
  import { SandboxError, validateNoPathTraversal } from "@truefoundry/trueforge-core/core";
8504
8573
  var SandboxInvalidPathError = class extends SandboxError {
8505
8574
  statusCode = 400;
8506
- constructor({ path: path6, reason }) {
8507
- super(`Path ${reason}: ${path6}`);
8575
+ constructor({ path: path7, reason }) {
8576
+ super(`Path ${reason}: ${path7}`);
8508
8577
  this.name = "SandboxInvalidPathError";
8509
8578
  }
8510
8579
  };
8511
8580
  var MAX_PATH_BYTES = 4095;
8512
8581
  var MAX_SEGMENT_BYTES = 255;
8513
- function validateSandboxFilePath(path6) {
8514
- if (!path6.startsWith("/")) {
8515
- throw new SandboxInvalidPathError({ path: path6, reason: "must be absolute" });
8582
+ function validateSandboxFilePath(path7) {
8583
+ if (!path7.startsWith("/")) {
8584
+ throw new SandboxInvalidPathError({ path: path7, reason: "must be absolute" });
8516
8585
  }
8517
- if (path6.includes("\0")) {
8518
- throw new SandboxInvalidPathError({ path: path6, reason: "must not contain a NUL byte" });
8586
+ if (path7.includes("\0")) {
8587
+ throw new SandboxInvalidPathError({ path: path7, reason: "must not contain a NUL byte" });
8519
8588
  }
8520
- if (Buffer.byteLength(path6) > MAX_PATH_BYTES) {
8521
- throw new SandboxInvalidPathError({ path: path6, reason: `must be at most ${String(MAX_PATH_BYTES)} bytes` });
8589
+ if (Buffer.byteLength(path7) > MAX_PATH_BYTES) {
8590
+ throw new SandboxInvalidPathError({ path: path7, reason: `must be at most ${String(MAX_PATH_BYTES)} bytes` });
8522
8591
  }
8523
- if (path6.split("/").some((segment) => Buffer.byteLength(segment) > MAX_SEGMENT_BYTES)) {
8592
+ if (path7.split("/").some((segment) => Buffer.byteLength(segment) > MAX_SEGMENT_BYTES)) {
8524
8593
  throw new SandboxInvalidPathError({
8525
- path: path6,
8594
+ path: path7,
8526
8595
  reason: `must not have a segment longer than ${String(MAX_SEGMENT_BYTES)} bytes`
8527
8596
  });
8528
8597
  }
8529
- validateNoPathTraversal(path6);
8598
+ validateNoPathTraversal(path7);
8530
8599
  }
8531
8600
 
8532
8601
  // src/apis/turns.ts
@@ -8545,8 +8614,8 @@ function toArrayBuffer(content) {
8545
8614
  new Uint8Array(buffer).set(content);
8546
8615
  return buffer;
8547
8616
  }
8548
- function toContentDisposition(path6) {
8549
- const fileName = path6.split("/").filter(Boolean).pop() ?? "download";
8617
+ function toContentDisposition(path7) {
8618
+ const fileName = path7.split("/").filter(Boolean).pop() ?? "download";
8550
8619
  const encoded = encodeURIComponent(fileName).replace(
8551
8620
  /['()*]/g,
8552
8621
  (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`
@@ -8758,10 +8827,10 @@ function createTurnsRouter(deps) {
8758
8827
  };
8759
8828
  const downloadSandboxFileHandler = async (c) => {
8760
8829
  const { session_id: sessionId, turn_id: turnId } = c.req.valid("param");
8761
- const { path: path6 } = c.req.valid("query");
8830
+ const { path: path7 } = c.req.valid("query");
8762
8831
  let sandboxId;
8763
8832
  try {
8764
- validateSandboxFilePath(path6);
8833
+ validateSandboxFilePath(path7);
8765
8834
  const session = await deps.sessions.get({ tenant_id: TENANT_ID, session_id: sessionId });
8766
8835
  if (!session) {
8767
8836
  return c.json({ error: { message: `Session not found: ${sessionId}` } }, 404);
@@ -8786,11 +8855,11 @@ function createTurnsRouter(deps) {
8786
8855
  return c.json({ error: { message: "No sandbox provider configured" } }, 412);
8787
8856
  }
8788
8857
  validateSandboxOwnedByTenant(sandboxId, TENANT_ID);
8789
- const content = await provider.downloadFile({ sandboxId, path: path6 });
8858
+ const content = await provider.downloadFile({ sandboxId, path: path7 });
8790
8859
  return c.body(toArrayBuffer(content), 200, {
8791
8860
  "Content-Type": "application/octet-stream",
8792
8861
  "Content-Length": String(content.byteLength),
8793
- "Content-Disposition": toContentDisposition(path6),
8862
+ "Content-Disposition": toContentDisposition(path7),
8794
8863
  "Cache-Control": "private, no-store"
8795
8864
  });
8796
8865
  } catch (error) {
@@ -8802,7 +8871,7 @@ function createTurnsRouter(deps) {
8802
8871
  sessionId,
8803
8872
  turnId,
8804
8873
  sandboxId,
8805
- path: path6
8874
+ path: path7
8806
8875
  });
8807
8876
  return c.json({ error: { message: "Failed to download file from sandbox" } }, 424);
8808
8877
  }
@@ -9012,6 +9081,28 @@ function createTurnsRouter(deps) {
9012
9081
  return router;
9013
9082
  }
9014
9083
 
9084
+ // src/app.ts
9085
+ init_config();
9086
+
9087
+ // src/packageVersion.ts
9088
+ import { readFileSync } from "fs";
9089
+ import path2 from "path";
9090
+ import { fileURLToPath as fileURLToPath2 } from "url";
9091
+ var PACKAGE_ROOT2 = path2.resolve(path2.dirname(fileURLToPath2(import.meta.url)), "..");
9092
+ function readPackageVersion() {
9093
+ const packageJsonPath = path2.join(PACKAGE_ROOT2, "package.json");
9094
+ const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
9095
+ if (typeof parsed !== "object" || parsed === null || !("version" in parsed)) {
9096
+ throw new Error(`${packageJsonPath} is missing a version field`);
9097
+ }
9098
+ const { version } = parsed;
9099
+ if (typeof version !== "string" || version.length === 0) {
9100
+ throw new Error(`${packageJsonPath} version must be a non-empty string`);
9101
+ }
9102
+ return version;
9103
+ }
9104
+ var PACKAGE_VERSION = readPackageVersion();
9105
+
9015
9106
  // src/zodErrorResponse.ts
9016
9107
  import { z as z23 } from "@hono/zod-openapi";
9017
9108
  function zodErrorResponse(c, error) {
@@ -9026,6 +9117,12 @@ var zodValidationHook = (result, c) => {
9026
9117
 
9027
9118
  // src/app.ts
9028
9119
  var BEARER_AUTH_SCHEME = "BearerAuth";
9120
+ function createRequestBodyLimitMiddleware(maxSize) {
9121
+ return bodyLimit({
9122
+ maxSize,
9123
+ onError: (c) => c.json({ error: { message: `Request body exceeds the maximum size of ${String(maxSize)} bytes` } }, 413)
9124
+ });
9125
+ }
9029
9126
  function createAppErrorHandler(params) {
9030
9127
  return (error, c) => {
9031
9128
  if (error instanceof z24.ZodError) {
@@ -9049,7 +9146,7 @@ var openApiDocConfig = {
9049
9146
  info: {
9050
9147
  title: "TrueForge API",
9051
9148
  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.",
9052
- version: "0.1.0"
9149
+ version: PACKAGE_VERSION
9053
9150
  }
9054
9151
  };
9055
9152
  function registerOpenApiBearerAuth(app) {
@@ -9088,6 +9185,7 @@ function withAdminAuth(router) {
9088
9185
  function createServerApp(deps) {
9089
9186
  const app = new OpenAPIHono14({ defaultHook: zodValidationHook });
9090
9187
  const authEnabled = deps.oidcClient != null;
9188
+ app.use("*", createRequestBodyLimitMiddleware(config_default.MAX_REQUEST_BODY_BYTES));
9091
9189
  app.get("/healthz", (c) => c.text("OK!"));
9092
9190
  app.route("/api/v1/auth", createAuthRouter({ oidcClient: deps.oidcClient, logger: deps.logger }));
9093
9191
  app.route(
@@ -9480,7 +9578,7 @@ import { serveStatic } from "@hono/node-server/serve-static";
9480
9578
  import { every } from "hono/combine";
9481
9579
  import { compress } from "hono/compress";
9482
9580
  import { existsSync as existsSync2 } from "fs";
9483
- import path2 from "path";
9581
+ import path3 from "path";
9484
9582
  var SERVER_PATH_PREFIXES = ["/api", "/healthz"];
9485
9583
  var HASHED_ASSET_PREFIX = "/assets/";
9486
9584
  var IMMUTABLE_CACHE_CONTROL = "public, max-age=31536000, immutable";
@@ -9489,7 +9587,7 @@ function isServerPath(pathname) {
9489
9587
  return SERVER_PATH_PREFIXES.some((prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`));
9490
9588
  }
9491
9589
  function mountFrontend(app, dir) {
9492
- if (!existsSync2(path2.join(dir, "index.html"))) {
9590
+ if (!existsSync2(path3.join(dir, "index.html"))) {
9493
9591
  return false;
9494
9592
  }
9495
9593
  const serveFile = serveStatic({ root: dir, precompressed: true });
@@ -9504,14 +9602,108 @@ function mountFrontend(app, dir) {
9504
9602
  };
9505
9603
  const serveCompressed = every(compress(), serveWithCacheHeaders);
9506
9604
  const serveBuild = async (c, next) => {
9507
- if (isServerPath(c.req.path)) return next();
9508
- if (c.req.method !== "GET" && c.req.method !== "HEAD") return next();
9605
+ if (isServerPath(c.req.path)) {
9606
+ return next();
9607
+ }
9608
+ if (c.req.method !== "GET" && c.req.method !== "HEAD") {
9609
+ return next();
9610
+ }
9509
9611
  return serveCompressed(c, next);
9510
9612
  };
9613
+ const serveAppShell = serveStatic({ root: dir, rewriteRequestPath: () => "/index.html", precompressed: true });
9614
+ const serveSpaFallback = async (c, next) => {
9615
+ if (isServerPath(c.req.path)) {
9616
+ return next();
9617
+ }
9618
+ if (c.req.method !== "GET" && c.req.method !== "HEAD") {
9619
+ return next();
9620
+ }
9621
+ if (c.req.header("accept")?.includes("text/html") !== true) {
9622
+ return next();
9623
+ }
9624
+ const response = await serveAppShell(c, next);
9625
+ if (response instanceof Response) {
9626
+ response.headers.set("Cache-Control", REVALIDATE_CACHE_CONTROL);
9627
+ response.headers.set("Vary", "Accept-Encoding");
9628
+ }
9629
+ return response;
9630
+ };
9511
9631
  app.use("/*", serveBuild);
9632
+ app.use("/*", serveSpaFallback);
9512
9633
  return true;
9513
9634
  }
9514
9635
 
9636
+ // src/logger.ts
9637
+ import winston from "winston";
9638
+ var STANDALONE_META_SKIP = /* @__PURE__ */ new Set(["level", "message", "timestamp", "version", "stack", "splat"]);
9639
+ function shouldColorize() {
9640
+ const noColor = process.env["NO_COLOR"];
9641
+ if (noColor !== void 0 && noColor !== "") {
9642
+ return false;
9643
+ }
9644
+ const forceColor = process.env["FORCE_COLOR"];
9645
+ if (forceColor === "0") {
9646
+ return false;
9647
+ }
9648
+ if (forceColor !== void 0 && forceColor !== "") {
9649
+ return true;
9650
+ }
9651
+ return process.stderr.isTTY;
9652
+ }
9653
+ function standaloneMeta(info) {
9654
+ const copied = { ...info };
9655
+ const meta = {};
9656
+ for (const key of Object.keys(copied)) {
9657
+ if (STANDALONE_META_SKIP.has(key)) {
9658
+ continue;
9659
+ }
9660
+ meta[key] = copied[key];
9661
+ }
9662
+ if (Object.keys(meta).length === 0) {
9663
+ return "";
9664
+ }
9665
+ return ` ${JSON.stringify(meta)}`;
9666
+ }
9667
+ function standaloneFormat(color) {
9668
+ const parts = [
9669
+ winston.format.timestamp({ format: "HH:mm:ss" }),
9670
+ winston.format.errors({ stack: true })
9671
+ ];
9672
+ if (color) {
9673
+ parts.push(winston.format.colorize({ level: true }));
9674
+ }
9675
+ parts.push(
9676
+ winston.format.printf((info) => {
9677
+ const extra = standaloneMeta(info);
9678
+ const stack = typeof info["stack"] === "string" ? `
9679
+ ${info["stack"]}` : "";
9680
+ return `${String(info["timestamp"])} ${info.level} ${String(info.message)}${extra}${stack}`;
9681
+ })
9682
+ );
9683
+ return winston.format.combine(...parts);
9684
+ }
9685
+ function jsonFormat() {
9686
+ return winston.format.combine(
9687
+ winston.format.timestamp(),
9688
+ winston.format.errors({ stack: true }),
9689
+ winston.format.json()
9690
+ );
9691
+ }
9692
+ function serverLogFormat(options) {
9693
+ if (options.standalone) {
9694
+ return standaloneFormat(shouldColorize());
9695
+ }
9696
+ return jsonFormat();
9697
+ }
9698
+ function createServerLogger(options) {
9699
+ return winston.createLogger({
9700
+ level: options.level,
9701
+ defaultMeta: { version: options.version },
9702
+ format: serverLogFormat({ standalone: options.standalone }),
9703
+ transports: [new winston.transports.Console()]
9704
+ });
9705
+ }
9706
+
9515
9707
  // src/runtime/activeTurns.ts
9516
9708
  import "@truefoundry/trueforge-core/agent-session";
9517
9709
  function activeTurnKey(sessionId, turnId) {
@@ -9594,6 +9786,118 @@ var ActiveTurnRegistry = class {
9594
9786
  }
9595
9787
  };
9596
9788
 
9789
+ // src/startupBanner.ts
9790
+ var STANDALONE_MODE_DISCLAIMER = "Standalone mode is intended for local use on your own machine. It is not hardened for production or shared internet access \u2014 please keep it on localhost. We cannot take responsibility for data loss or unauthorized access if this mode is used beyond that.";
9791
+ var ANSI_SHADOW = {
9792
+ T: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D", " \u2588\u2588\u2551 ", " \u2588\u2588\u2551 ", " \u2588\u2588\u2551 ", " \u255A\u2550\u255D "],
9793
+ R: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u255A\u2550\u255D \u255A\u2550\u255D"],
9794
+ U: ["\u2588\u2588\u2557 \u2588\u2588\u2557", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
9795
+ E: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D", "\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u255D ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"],
9796
+ F: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D", "\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u255D ", "\u2588\u2588\u2551 ", "\u255A\u2550\u255D "],
9797
+ O: [" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
9798
+ G: [" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ", "\u2588\u2588\u2551 \u2588\u2588\u2588\u2557", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u2550\u255D "]
9799
+ };
9800
+ var WORDMARK_TEXT = "TRUEFORGE";
9801
+ var WARNING_INNER_WIDTH = 72;
9802
+ var BRAND_COLOR = "\x1B[38;5;99m";
9803
+ var YELLOW = "\x1B[33m";
9804
+ var BOLD = "\x1B[1m";
9805
+ var DIM = "\x1B[2m";
9806
+ var RESET = "\x1B[0m";
9807
+ function glyphFor(ch) {
9808
+ const glyph = ANSI_SHADOW[ch];
9809
+ if (glyph === void 0) {
9810
+ throw new Error(`No ASCII glyph for ${JSON.stringify(ch)}`);
9811
+ }
9812
+ return glyph;
9813
+ }
9814
+ function rowOf(glyph, row) {
9815
+ const cell = glyph[row];
9816
+ if (cell === void 0) {
9817
+ throw new Error(`Glyph is missing row ${String(row)}`);
9818
+ }
9819
+ return cell;
9820
+ }
9821
+ function renderTrueForgeWordmark() {
9822
+ const glyphs = [];
9823
+ for (let i = 0; i < WORDMARK_TEXT.length; i += 1) {
9824
+ const ch = WORDMARK_TEXT[i];
9825
+ if (ch === void 0) {
9826
+ throw new Error(`Missing character at index ${String(i)}`);
9827
+ }
9828
+ glyphs.push(glyphFor(ch));
9829
+ }
9830
+ const rowCount = glyphs[0]?.length;
9831
+ if (rowCount === void 0) {
9832
+ throw new Error("Wordmark text must not be empty");
9833
+ }
9834
+ const rows = [];
9835
+ for (let row = 0; row < rowCount; row += 1) {
9836
+ rows.push(glyphs.map((glyph) => rowOf(glyph, row)).join(""));
9837
+ }
9838
+ return rows.join("\n");
9839
+ }
9840
+ function wrapText(text, width) {
9841
+ const words = text.split(/\s+/).filter((word) => word.length > 0);
9842
+ const lines = [];
9843
+ let current = "";
9844
+ for (const word of words) {
9845
+ if (current.length === 0) {
9846
+ if (word.length > width) {
9847
+ lines.push(word);
9848
+ } else {
9849
+ current = word;
9850
+ }
9851
+ continue;
9852
+ }
9853
+ const next = `${current} ${word}`;
9854
+ if (next.length <= width) {
9855
+ current = next;
9856
+ } else {
9857
+ lines.push(current);
9858
+ current = word.length > width ? "" : word;
9859
+ if (word.length > width) {
9860
+ lines.push(word);
9861
+ }
9862
+ }
9863
+ }
9864
+ if (current.length > 0) {
9865
+ lines.push(current);
9866
+ }
9867
+ return lines;
9868
+ }
9869
+ function paint(options) {
9870
+ if (!options.enabled) {
9871
+ return options.text;
9872
+ }
9873
+ return `${options.color}${options.text}${RESET}`;
9874
+ }
9875
+ function warningBox(message) {
9876
+ const label = " WARNING ";
9877
+ const inner = wrapText(message, WARNING_INNER_WIDTH);
9878
+ const width = Math.max(WARNING_INNER_WIDTH, ...inner.map((line) => line.length), label.length);
9879
+ const topPad = width + 1 - label.length;
9880
+ const top = `\u250C\u2500${label}${"\u2500".repeat(topPad)}\u2510`;
9881
+ const body = inner.map((line) => `\u2502 ${line.padEnd(width)} \u2502`);
9882
+ const bottom = `\u2514${"\u2500".repeat(width + 2)}\u2518`;
9883
+ return [top, ...body, bottom].join("\n");
9884
+ }
9885
+ function formatStandaloneStartupBanner(options) {
9886
+ const { version, color } = options;
9887
+ const wordmark = paint({ text: renderTrueForgeWordmark(), color: `${BOLD}${BRAND_COLOR}`, enabled: color });
9888
+ const subtitle = paint({ text: ` TrueForge v${version} \xB7 standalone`, color: DIM, enabled: color });
9889
+ const warning = paint({ text: warningBox(STANDALONE_MODE_DISCLAIMER), color: `${BOLD}${YELLOW}`, enabled: color });
9890
+ return { wordmark: `
9891
+ ${wordmark}
9892
+ ${subtitle}
9893
+ `, warning };
9894
+ }
9895
+ function printStandaloneStartupBanner(options) {
9896
+ const { wordmark, warning } = formatStandaloneStartupBanner(options);
9897
+ console.log(wordmark);
9898
+ console.warn(warning);
9899
+ }
9900
+
9597
9901
  // src/main.ts
9598
9902
  var configuration2;
9599
9903
  var isOidcConfigured2;
@@ -9608,7 +9912,7 @@ try {
9608
9912
  }
9609
9913
  async function createStandalonePersistence(options) {
9610
9914
  const { sqlitePath, logger } = options;
9611
- await mkdir(path5.dirname(sqlitePath), { recursive: true });
9915
+ await mkdir(path6.dirname(sqlitePath), { recursive: true });
9612
9916
  const [{ createSqliteDb: createSqliteDb2 }, { migrateSqliteToLatest: migrateSqliteToLatest2 }, sqliteStores] = await Promise.all([
9613
9917
  Promise.resolve().then(() => (init_client(), client_exports)),
9614
9918
  Promise.resolve().then(() => (init_migrateSqlite(), migrateSqlite_exports)),
@@ -9750,11 +10054,16 @@ async function createServerRuntime(persistence, logger) {
9750
10054
  return { activeTurns, app, destroyDb, redis, requestReplyRouter };
9751
10055
  }
9752
10056
  try {
9753
- const logger = winston.createLogger({
10057
+ const logger = createServerLogger({
9754
10058
  level: configuration2.LOG_LEVEL,
9755
- format: winston.format.combine(winston.format.timestamp(), winston.format.json()),
9756
- transports: [new winston.transports.Console()]
10059
+ standalone: configuration2.STANDALONE,
10060
+ version: PACKAGE_VERSION
9757
10061
  });
10062
+ if (configuration2.STANDALONE) {
10063
+ printStandaloneStartupBanner({ version: PACKAGE_VERSION, color: shouldColorize() });
10064
+ } else {
10065
+ logger.info("TrueForge starting", { mode: "distributed" });
10066
+ }
9758
10067
  const { activeTurns, app, destroyDb, redis, requestReplyRouter } = configuration2.STANDALONE ? await createServerRuntime(
9759
10068
  await createStandalonePersistence({ sqlitePath: configuration2.SQLITE_PATH, logger }),
9760
10069
  logger
@@ -9787,8 +10096,8 @@ try {
9787
10096
  });
9788
10097
  await requestReplyExecutor.init();
9789
10098
  }
9790
- const server = serve({ fetch: app.fetch, port: configuration2.PORT }, (info) => {
9791
- console.log(`Agent server listening on http://localhost:${String(info.port)} (docs at /api/v1/docs)`);
10099
+ const server = serve({ fetch: app.fetch, port: configuration2.PORT, hostname: configuration2.HOST }, (info) => {
10100
+ logger.info(`Agent server listening on http://${configuration2.HOST}:${String(info.port)} (docs at /api/v1/docs)`);
9792
10101
  });
9793
10102
  server.on("error", (error) => {
9794
10103
  console.error("Failed to start server:", error instanceof Error ? error.message : error);
@@ -9797,7 +10106,9 @@ try {
9797
10106
  if (configuration2.NODE_ENV !== "development") {
9798
10107
  let shuttingDown = false;
9799
10108
  const shutdown = async (signal) => {
9800
- if (shuttingDown) return;
10109
+ if (shuttingDown) {
10110
+ return;
10111
+ }
9801
10112
  shuttingDown = true;
9802
10113
  logger.info(`Received ${signal}, draining connections before shutdown`);
9803
10114
  setTimeout(() => {