bazilion 0.14.2 → 0.15.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 (119) hide show
  1. package/dist/cli.js +1126 -334
  2. package/dist/cli.js.map +1 -1
  3. package/dist/daemon.js +8180 -3651
  4. package/dist/daemon.js.map +1 -1
  5. package/dist/migrations/0001_init.sql +171 -3
  6. package/dist/web/client/assets/ChatPane-55j13bBj.js +74 -0
  7. package/dist/web/client/assets/{ConfirmDialog-_bmG-wVG.js → ConfirmDialog-CGZzom9E.js} +1 -1
  8. package/dist/web/client/assets/{FieldRow-Do1cBkRc.js → FieldRow-DYNU9gWb.js} +1 -1
  9. package/dist/web/client/assets/{ProductionTeamPolicyEditor-XP-QJul4.js → ProductionTeamPolicyEditor-DT5Sl8jd.js} +1 -1
  10. package/dist/web/client/assets/{ProfileCommunicationEditor-CwdFPdpk.js → ProfileCommunicationEditor-TyCU5vXX.js} +1 -1
  11. package/dist/web/client/assets/ResultCard-Cps_lXVN.js +1 -0
  12. package/dist/web/client/assets/ResultSourceConversation-CGhKTSTD.js +1 -0
  13. package/dist/web/client/assets/TeamTabs-BZHyRnbd.js +1 -0
  14. package/dist/web/client/assets/{TemplatesTabs-D4WHvs6b.js → TemplatesTabs-BHzkPMRC.js} +1 -1
  15. package/dist/web/client/assets/{UnsavedChangesGuard-B1aLqiK5.js → UnsavedChangesGuard-CFQcKsrf.js} +1 -1
  16. package/dist/web/client/assets/_id-9nCIhbVd.js +1 -0
  17. package/dist/web/client/assets/{_id-3w4PFfma.js → _id-BSu3AqKA.js} +1 -1
  18. package/dist/web/client/assets/_id-CGd21j3k.js +1 -0
  19. package/dist/web/client/assets/{_id-BH3v_vI_.js → _id-D_eKokkd.js} +1 -1
  20. package/dist/web/client/assets/{_id-Cx7o4Zmf.js → _id-SRMFlIgP.js} +1 -1
  21. package/dist/web/client/assets/{activity-A_HrG1Tv.js → activity-Cn2-9PpZ.js} +1 -1
  22. package/dist/web/client/assets/{agents-BeeFCt2c.js → agents-CCmiZek6.js} +1 -1
  23. package/dist/web/client/assets/{agents-C8OVKq3N.js → agents-NY3VrZgB.js} +1 -1
  24. package/dist/web/client/assets/{approvals-BIdZ9BSn.js → approvals-CPDAPgrM.js} +1 -1
  25. package/dist/web/client/assets/{attention-B9bUrxiU.js → attention-Qra_9_OI.js} +1 -1
  26. package/dist/web/client/assets/chevron-right-DXE4CWf2.js +1 -0
  27. package/dist/web/client/assets/{config-By86S7k-.js → config-D8EO0LPB.js} +1 -1
  28. package/dist/web/client/assets/{inbox-DlXQCJLe.js → inbox-C5W-sm1q.js} +1 -1
  29. package/dist/web/client/assets/{index-CLdmsmiN.js → index-BUhhk_u9.js} +2 -2
  30. package/dist/web/client/assets/{learning-BqZIw2nA.js → learning-Dr-7_7J4.js} +1 -1
  31. package/dist/web/client/assets/login-Bu5UxbJh.js +1 -0
  32. package/dist/web/client/assets/{mcp-DKSY2yVa.js → mcp-BueuwQiv.js} +1 -1
  33. package/dist/web/client/assets/{members-9GuOiq6J.js → members-ZikuYNcy.js} +1 -1
  34. package/dist/web/client/assets/{memory-DiGMI6FC.js → memory-1JUJlPrn.js} +1 -1
  35. package/dist/web/client/assets/{policy-TBGQffw5.js → policy-DbkhsFNA.js} +1 -1
  36. package/dist/web/client/assets/results-mMr8Ar2u.js +1 -0
  37. package/dist/web/client/assets/routes-DJYB-MS9.js +1 -0
  38. package/dist/web/client/assets/{services-1Z398CES.js → services-kfQA9cpJ.js} +1 -1
  39. package/dist/web/client/assets/{skills-BP2gciFt.js → skills-C6pcgxaC.js} +1 -1
  40. package/dist/web/client/assets/styles-CcNf8jT_.css +2 -0
  41. package/dist/web/client/assets/{teams-tNoMDJ_M.js → teams-C6_Noz5X.js} +1 -1
  42. package/dist/web/client/assets/{teams-DBIivGT7.js → teams-LB0KBXY9.js} +1 -1
  43. package/dist/web/client/assets/telegram-A2jaI1QM.js +1 -0
  44. package/dist/web/client/assets/{tokens-eDWCGQQ2.js → tokens-BGlUpJcL.js} +1 -1
  45. package/dist/web/client/assets/{triggers-DZME3IcK.js → triggers-C54_feGg.js} +1 -1
  46. package/dist/web/client/assets/{welcome-B49sh_uf.js → welcome-DyUIswRi.js} +1 -1
  47. package/dist/web/client/assets/wire-constants-CBD-lTaa.js +1 -0
  48. package/dist/web/client/assets/{x-CHc7_-TH.js → x-BY2MPLr9.js} +1 -1
  49. package/dist/web/server/assets/{ChatPane-CgSe7tsy.js → ChatPane-DJ30FdFG.js} +1122 -107
  50. package/dist/web/server/assets/ResultCard-B3zD9N0t.js +143 -0
  51. package/dist/web/server/assets/ResultSourceConversation-zYogeOWp.js +64 -0
  52. package/dist/web/server/assets/{TeamTabs-Co3n2exj.js → TeamTabs-TCKjCvMA.js} +4 -0
  53. package/dist/web/server/assets/{_id-sNu5aXZt.js → _id-1LwoHhJg.js} +2 -1
  54. package/dist/web/server/assets/{_id-BeKhPKw3.js → _id-1hTvnKLN2.js} +7 -3
  55. package/dist/web/server/assets/{_id-BAA9KJBU.js → _id-B2EEyStS.js} +1 -1
  56. package/dist/web/server/assets/_id-BDEpe6k0.js +6 -0
  57. package/dist/web/server/assets/{_id-BqWTB8Wl.js → _id-B_-CDHOi2.js} +1 -1
  58. package/dist/web/server/assets/{_id-DxCnEFpm.js → _id-BvSWsPI5.js} +3 -4
  59. package/dist/web/server/assets/{_id-ndxFHx0t.js → _id-CDBtO_jL.js} +2 -2
  60. package/dist/web/server/assets/{_id-DvlDkk04.js → _id-DDNVQx3y.js} +1 -1
  61. package/dist/web/server/assets/{_id-BYKpT2Tu.js → _id-DFHB9JFQ.js} +1 -1
  62. package/dist/web/server/assets/{_id-HMy7jM3P.js → _id-DxFLSIio.js} +1 -1
  63. package/dist/web/server/assets/{_id-DYU6uIBx.js → _id-T_8btpbB.js} +1 -1
  64. package/dist/web/server/assets/_id-wyjrf0JG.js +17 -0
  65. package/dist/web/server/assets/{_tanstack-start-manifest_v-Duhf0z7H.js → _tanstack-start-manifest_v-DP4ZUBw3.js} +102 -75
  66. package/dist/web/server/assets/{activity-BWv1goK6.js → activity-5fWKPDxj.js} +2 -2
  67. package/dist/web/server/assets/{activity-LjpblbDL.js → activity-C88qrNqv.js} +1 -1
  68. package/dist/web/server/assets/{activity-9VP6QDTH.js → activity-D73w4mkZ.js} +1 -1
  69. package/dist/web/server/assets/{agents-DjyQk94u.js → agents-DB5hCSAI.js} +1 -1
  70. package/dist/web/server/assets/{agents-BJAv919x.js → agents-DRSo7lRX.js} +1 -1
  71. package/dist/web/server/assets/{approvals-BYi84gwl.js → approvals-4y9bZF4r.js} +1 -1
  72. package/dist/web/server/assets/{approvals-7JOoBchn.js → approvals-DTdrltyk.js} +1 -1
  73. package/dist/web/server/assets/{approvals-Bz123liE.js → approvals-DXoN-T57.js} +15 -1
  74. package/dist/web/server/assets/{attention-UhqPK_Ro.js → attention-BFx8ZkmH.js} +1 -1
  75. package/dist/web/server/assets/{auth-7BHTH-KS.js → auth-_eaCwhyq.js} +1 -1
  76. package/dist/web/server/assets/{config-BMiajTZP.js → config-B7JIfv_7.js} +1 -1
  77. package/dist/web/server/assets/{daemon-client-CI5NHwNC.js → daemon-client-B06W1Qa2.js} +54 -0
  78. package/dist/web/server/assets/{inbox-Dv55rGYq.js → inbox-YRpDQcvp.js} +1 -1
  79. package/dist/web/server/assets/{learning-C2upa1CE.js → learning-UBZb6h6M.js} +1 -1
  80. package/dist/web/server/assets/{login-BNj3U6ok.js → login-B_-9L8TU.js} +7 -2
  81. package/dist/web/server/assets/{login-Be8eP4Jp.js → login-rDox7PRD.js} +5 -2
  82. package/dist/web/server/assets/{mcp-BvnvlVFd.js → mcp-Dv6RXMaS.js} +1 -1
  83. package/dist/web/server/assets/{members-BP3UTqu1.js → members-BFTKSlOe.js} +1 -1
  84. package/dist/web/server/assets/{members-DiYtr-zm.js → members-CD3GiTfF.js} +1 -1
  85. package/dist/web/server/assets/{members-DHYTVgwb.js → members-cc5SeEaW.js} +2 -2
  86. package/dist/web/server/assets/{memory-CkXUgf5s.js → memory-C5i_GxlY.js} +2 -2
  87. package/dist/web/server/assets/{memory-DTFEmfF3.js → memory-CVqnyUaM.js} +1 -1
  88. package/dist/web/server/assets/{memory-BqV-Afy1.js → memory-D0PQgZAn.js} +1 -1
  89. package/dist/web/server/assets/{policy-18fdTFO4.js → policy-C-p8HIhA.js} +1 -1
  90. package/dist/web/server/assets/{policy-Bvvav8fh.js → policy-Dgjb0_gK.js} +2 -2
  91. package/dist/web/server/assets/{policy-BddK4xom.js → policy-bLKwvnja.js} +1 -1
  92. package/dist/web/server/assets/results-Cfev4KNt.js +12 -0
  93. package/dist/web/server/assets/results-Cj-Dlw3C.js +173 -0
  94. package/dist/web/server/assets/results-CwEQ5EUL.js +12 -0
  95. package/dist/web/server/assets/{router-CY_pCgP1.js → router-d3sTMpMX.js} +60 -43
  96. package/dist/web/server/assets/{routes-DAU2g7Uz.js → routes-B765DVhj.js} +1 -1
  97. package/dist/web/server/assets/{routes-BgHisAxH.js → routes-MIZz6uMr.js} +2 -2
  98. package/dist/web/server/assets/{routes-COtkyZG1.js → routes-cW4MWU18.js} +3 -3
  99. package/dist/web/server/assets/{services-DozuJtzX.js → services-BCbdn3R9.js} +1 -1
  100. package/dist/web/server/assets/{skills-DfGSJTCB.js → skills-Bpoa8MIp.js} +1 -1
  101. package/dist/web/server/assets/{teams-be05ci17.js → teams-CXzwqzcE.js} +1 -1
  102. package/dist/web/server/assets/{teams-DPnAX4wD.js → teams-hWGjbpUA.js} +1 -1
  103. package/dist/web/server/assets/{telegram-Dj4Wwhd6.js → telegram-CoNeOgqZ.js} +465 -1
  104. package/dist/web/server/assets/{telegram-1AWL6lFw.js → telegram-DhpvtGUc.js} +1 -1
  105. package/dist/web/server/assets/{telegram-DbOFpTFm.js → telegram-Zd1rIZxg.js} +1 -1
  106. package/dist/web/server/assets/{tokens-DjM8GsgS.js → tokens-Bjp19Vkn.js} +1 -1
  107. package/dist/web/server/assets/{triggers-Nezfw-Bd.js → triggers-DJlcvXzn.js} +1 -1
  108. package/dist/web/server/server.js +31 -27
  109. package/dist/worker.js +294 -104
  110. package/dist/worker.js.map +1 -1
  111. package/package.json +3 -3
  112. package/dist/web/client/assets/TeamTabs-DIRiKcqx.js +0 -1
  113. package/dist/web/client/assets/_id-DIiVVxBl.js +0 -1
  114. package/dist/web/client/assets/chevron-right-VWrk_dTX.js +0 -1
  115. package/dist/web/client/assets/login-CCMPXiny.js +0 -1
  116. package/dist/web/client/assets/routes-g3yUOhCb.js +0 -1
  117. package/dist/web/client/assets/styles-B5vcgXln.css +0 -2
  118. package/dist/web/client/assets/telegram-CJG_DFdI.js +0 -1
  119. package/dist/web/client/assets/wire-constants-zZ7Wjb9C.js +0 -74
package/dist/worker.js CHANGED
@@ -1136,7 +1136,7 @@ function translatePiEvent(e) {
1136
1136
  type: "tool_call",
1137
1137
  id: block.id,
1138
1138
  name: block.name,
1139
- arguments: JSON.stringify(block.arguments ?? {})
1139
+ arguments: block.name === "ask_user" ? "{}" : JSON.stringify(block.arguments ?? {})
1140
1140
  });
1141
1141
  }
1142
1142
  }
@@ -1148,6 +1148,15 @@ function translatePiEvent(e) {
1148
1148
  return out;
1149
1149
  }
1150
1150
  case "tool_execution_end": {
1151
+ if (e.toolName === "ask_user")
1152
+ return [
1153
+ {
1154
+ type: "tool_result",
1155
+ id: e.toolCallId,
1156
+ name: e.toolName,
1157
+ result: "Question outcome is available through the question card or authorized conversation history."
1158
+ }
1159
+ ];
1151
1160
  const text = extractToolResultText(e.result);
1152
1161
  if (e.isError) {
1153
1162
  return [{ type: "tool_error", id: e.toolCallId, name: e.toolName, error: text }];
@@ -1159,6 +1168,7 @@ function translatePiEvent(e) {
1159
1168
  id: e.toolCallId,
1160
1169
  name: e.toolName,
1161
1170
  result: text,
1171
+ resultReference: extractResultReference(e.result),
1162
1172
  ...images.length > 0 ? { images } : {}
1163
1173
  }
1164
1174
  ];
@@ -1178,7 +1188,11 @@ function extractAssistantToolCalls(m) {
1178
1188
  const out = [];
1179
1189
  for (const block of m.content ?? []) {
1180
1190
  if (block.type === "toolCall") {
1181
- out.push({ id: block.id, name: block.name, arguments: JSON.stringify(block.arguments ?? {}) });
1191
+ out.push({
1192
+ id: block.id,
1193
+ name: block.name,
1194
+ arguments: block.name === "ask_user" ? "{}" : JSON.stringify(block.arguments ?? {})
1195
+ });
1182
1196
  }
1183
1197
  }
1184
1198
  return out;
@@ -1215,7 +1229,7 @@ function stringifyContent(content) {
1215
1229
  }
1216
1230
  return out;
1217
1231
  }
1218
- function piMessagesToProviderView(messages) {
1232
+ function piMessagesToProviderView(messages, questionResultVisible) {
1219
1233
  const out = [];
1220
1234
  for (const m of messages) {
1221
1235
  switch (m.role) {
@@ -1240,12 +1254,22 @@ function piMessagesToProviderView(messages) {
1240
1254
  }
1241
1255
  case "toolResult": {
1242
1256
  const tr = m;
1257
+ if (tr.toolName === "ask_user" && !questionResultVisible?.(m)) {
1258
+ out.push({
1259
+ role: "tool",
1260
+ toolCallId: tr.toolCallId,
1261
+ toolName: "ask_user",
1262
+ content: "Question content has not been released for this view."
1263
+ });
1264
+ break;
1265
+ }
1243
1266
  const images = extractToolResultImages({ content: tr.content });
1244
1267
  out.push({
1245
1268
  role: "tool",
1246
1269
  content: stringifyContent(tr.content),
1247
1270
  toolCallId: tr.toolCallId,
1248
1271
  toolName: tr.toolName,
1272
+ result: extractResultReference(m),
1249
1273
  ...images.length > 0 ? { images } : {}
1250
1274
  });
1251
1275
  break;
@@ -1256,14 +1280,58 @@ function piMessagesToProviderView(messages) {
1256
1280
  }
1257
1281
  return out;
1258
1282
  }
1283
+ function extractResultReference(value) {
1284
+ if (!value || typeof value !== "object") return void 0;
1285
+ const details = value.details;
1286
+ const id = details?.result?.resultId;
1287
+ return typeof id === "string" && /^[a-f0-9-]{36}$/.test(id) ? { resultId: id } : void 0;
1288
+ }
1289
+
1290
+ // ../daemon/src/lib/result-source.ts
1291
+ import { closeSync as closeSync2, constants as constants2, fstatSync as fstatSync2, openSync as openSync2, readSync, realpathSync as realpathSync2 } from "fs";
1292
+ import { basename as basename2, join as join13 } from "path";
1293
+ function readCanonicalSessionFile(directory, filename, sessionId, minimumEntryOffset = 0) {
1294
+ if (basename2(filename) !== filename) throw new Error("Invalid session filename");
1295
+ if (realpathSync2(directory) !== directory) throw new Error("Result session escaped its owner");
1296
+ const fd = openSync2(
1297
+ join13(directory, filename),
1298
+ constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK
1299
+ );
1300
+ try {
1301
+ const stat2 = fstatSync2(fd);
1302
+ if (!stat2.isFile() || stat2.size > 64 * 1024 * 1024)
1303
+ throw new Error("Result source session is unavailable or too large");
1304
+ const buffer = Buffer.alloc(stat2.size + 1);
1305
+ let count = 0;
1306
+ while (count < buffer.length) {
1307
+ const n = readSync(fd, buffer, count, buffer.length - count, null);
1308
+ if (n === 0) break;
1309
+ count += n;
1310
+ }
1311
+ if (count !== stat2.size) throw new Error("Result source session changed while reading");
1312
+ const entries = [];
1313
+ let position = 0;
1314
+ for (const line of buffer.subarray(0, count).toString("utf8").split("\n")) {
1315
+ if (line.trim()) entries.push({ entry: JSON.parse(line), offset: position });
1316
+ position += Buffer.byteLength(line) + 1;
1317
+ }
1318
+ const header = entries[0]?.entry;
1319
+ if (header?.type !== "session" || header.id !== sessionId)
1320
+ throw new Error("Result source session does not match the active Agent session");
1321
+ return entries.filter((row, index) => index === 0 || row.offset >= minimumEntryOffset).map((row) => row.entry);
1322
+ } finally {
1323
+ closeSync2(fd);
1324
+ }
1325
+ }
1259
1326
 
1260
1327
  // ../daemon/src/runtime/pi/session.ts
1261
- import { existsSync as existsSync14, mkdirSync as mkdirSync7, readdirSync as readdirSync8, statSync as statSync5, writeFileSync as writeFileSync9 } from "fs";
1262
- import { basename as basename3, join as join19 } from "path";
1328
+ import { existsSync as existsSync14, mkdirSync as mkdirSync7, statSync as statSync5, writeFileSync as writeFileSync9 } from "fs";
1329
+ import { basename as basename4, join as join20 } from "path";
1263
1330
  import {
1264
1331
  createAssistantMessageEventStream
1265
1332
  } from "@earendil-works/pi-ai";
1266
1333
  import {
1334
+ buildSessionContext,
1267
1335
  createAgentSession,
1268
1336
  DefaultResourceLoader,
1269
1337
  SessionManager,
@@ -1523,18 +1591,18 @@ import {
1523
1591
 
1524
1592
  // ../daemon/src/runtime/session/prompt.ts
1525
1593
  import { existsSync as existsSync10, readdirSync as readdirSync6 } from "fs";
1526
- import { join as join13 } from "path";
1594
+ import { join as join14 } from "path";
1527
1595
 
1528
1596
  // ../daemon/src/runtime/safe-files.ts
1529
1597
  import {
1530
- closeSync as closeSync2,
1531
- constants as constants2,
1532
- fstatSync as fstatSync2,
1598
+ closeSync as closeSync3,
1599
+ constants as constants3,
1600
+ fstatSync as fstatSync3,
1533
1601
  lstatSync as lstatSync2,
1534
1602
  mkdirSync as mkdirSync5,
1535
- openSync as openSync2,
1603
+ openSync as openSync3,
1536
1604
  readFileSync as readFileSync9,
1537
- realpathSync as realpathSync2
1605
+ realpathSync as realpathSync3
1538
1606
  } from "fs";
1539
1607
  import { dirname as dirname3, isAbsolute as isAbsolute2, relative as relative2, resolve as resolve4 } from "path";
1540
1608
  function resolveRealDirectory(path) {
@@ -1543,7 +1611,7 @@ function resolveRealDirectory(path) {
1543
1611
  if (entry.isSymbolicLink() || !entry.isDirectory()) {
1544
1612
  throw new Error("expected a real directory");
1545
1613
  }
1546
- return realpathSync2(requested);
1614
+ return realpathSync3(requested);
1547
1615
  }
1548
1616
  function readRegularFileNoFollow(path) {
1549
1617
  const entry = lstatSync2(path);
@@ -1551,15 +1619,15 @@ function readRegularFileNoFollow(path) {
1551
1619
  if (!entry.isFile()) throw new Error("target is not a regular file");
1552
1620
  let fd;
1553
1621
  try {
1554
- fd = openSync2(path, constants2.O_RDONLY | noFollowFlag());
1555
- if (!fstatSync2(fd).isFile()) throw new Error("target is not a regular file");
1622
+ fd = openSync3(path, constants3.O_RDONLY | noFollowFlag());
1623
+ if (!fstatSync3(fd).isFile()) throw new Error("target is not a regular file");
1556
1624
  return readFileSync9(fd, "utf8");
1557
1625
  } finally {
1558
- if (fd !== void 0) closeSync2(fd);
1626
+ if (fd !== void 0) closeSync3(fd);
1559
1627
  }
1560
1628
  }
1561
1629
  function noFollowFlag() {
1562
- return typeof constants2.O_NOFOLLOW === "number" ? constants2.O_NOFOLLOW : 0;
1630
+ return typeof constants3.O_NOFOLLOW === "number" ? constants3.O_NOFOLLOW : 0;
1563
1631
  }
1564
1632
 
1565
1633
  // ../daemon/src/runtime/session/prompt.ts
@@ -1570,9 +1638,9 @@ function loadPromptSkills(skillsDir, attachedNames) {
1570
1638
  const discovered = readdirSync6(skillsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && attached.has(entry.name)).sort((a, b) => a.name.localeCompare(b.name));
1571
1639
  const loaded = [];
1572
1640
  for (const entry of discovered) {
1573
- const hostDir = join13(skillsDir, entry.name);
1641
+ const hostDir = join14(skillsDir, entry.name);
1574
1642
  try {
1575
- const parsed = parseSkillContent(readRegularFileNoFollow(join13(hostDir, "SKILL.md")));
1643
+ const parsed = parseSkillContent(readRegularFileNoFollow(join14(hostDir, "SKILL.md")));
1576
1644
  const safeLabel = entry.name.replace(/[^A-Za-z0-9._-]/g, "_") || "skill";
1577
1645
  loaded.push({
1578
1646
  name: entry.name,
@@ -1591,7 +1659,7 @@ function loadProtectedHomeDocuments(agentDir) {
1591
1659
  const root = resolveRealDirectory(agentDir);
1592
1660
  const read = (file) => {
1593
1661
  try {
1594
- return readRegularFileNoFollow(join13(root, file));
1662
+ return readRegularFileNoFollow(join14(root, file));
1595
1663
  } catch (error) {
1596
1664
  if (error.code === "ENOENT") return null;
1597
1665
  throw new Error(`private Agent home is unsafe at ${file}`);
@@ -1733,8 +1801,8 @@ function boundReviewedLessons(lessons, maxCharacters = 8e3) {
1733
1801
  }
1734
1802
 
1735
1803
  // ../daemon/src/runtime/shell/tooling.ts
1736
- import { existsSync as existsSync11, lstatSync as lstatSync3, realpathSync as realpathSync3 } from "fs";
1737
- import { dirname as dirname4, isAbsolute as isAbsolute4, join as join15, relative as relative4, sep as sep4 } from "path";
1804
+ import { existsSync as existsSync11, lstatSync as lstatSync3, realpathSync as realpathSync4 } from "fs";
1805
+ import { dirname as dirname4, isAbsolute as isAbsolute4, join as join16, relative as relative4, sep as sep4 } from "path";
1738
1806
  import {
1739
1807
  createBashToolDefinition,
1740
1808
  createLocalBashOperations,
@@ -2122,10 +2190,10 @@ function createApprovalGatedBashTool(base, host) {
2122
2190
  // ../daemon/src/runtime/shell/docker.ts
2123
2191
  import { spawn } from "child_process";
2124
2192
  import { randomUUID as randomUUID18 } from "crypto";
2125
- import { closeSync as closeSync3, constants as constants3, mkdtempSync as mkdtempSync2, openSync as openSync3, rmSync as rmSync10, writeFileSync as writeFileSync7 } from "fs";
2193
+ import { closeSync as closeSync4, constants as constants4, mkdtempSync as mkdtempSync2, openSync as openSync4, rmSync as rmSync10, writeFileSync as writeFileSync7 } from "fs";
2126
2194
  import { access, lstat, realpath, stat } from "fs/promises";
2127
2195
  import { tmpdir as tmpdir2 } from "os";
2128
- import { delimiter, isAbsolute as isAbsolute3, join as join14, relative as relative3, sep as sep3 } from "path";
2196
+ import { delimiter, isAbsolute as isAbsolute3, join as join15, relative as relative3, sep as sep3 } from "path";
2129
2197
  var CONTAINER_WORKDIR = "/workspace";
2130
2198
  var MAX_TIMEOUT_MS = 2147483647;
2131
2199
  var MAX_STDERR_CHARS = 16 * 1024;
@@ -2314,7 +2382,7 @@ async function resolveRequiredRoot(root) {
2314
2382
  }
2315
2383
  async function inspectDockerExecutableIdentity(dockerPath) {
2316
2384
  try {
2317
- await access(dockerPath, constants3.X_OK);
2385
+ await access(dockerPath, constants4.X_OK);
2318
2386
  const info = await stat(dockerPath, { bigint: true });
2319
2387
  if (!info.isFile()) throw new Error("not a regular file");
2320
2388
  return {
@@ -2349,7 +2417,7 @@ async function assertLocalDockerSocket(endpoint) {
2349
2417
  try {
2350
2418
  const info = await stat(socketPath);
2351
2419
  if (!info.isSocket()) throw new Error("not a Unix socket");
2352
- await access(socketPath, constants3.R_OK | constants3.W_OK);
2420
+ await access(socketPath, constants4.R_OK | constants4.W_OK);
2353
2421
  } catch {
2354
2422
  throw new Error("Docker sandbox local Unix socket is unavailable");
2355
2423
  }
@@ -2454,7 +2522,7 @@ function executeDockerRun(spec, options) {
2454
2522
  windowsHide: true,
2455
2523
  env: spec.processEnv
2456
2524
  });
2457
- if (envFileFd !== void 0) closeSync3(envFileFd);
2525
+ if (envFileFd !== void 0) closeSync4(envFileFd);
2458
2526
  let settled = false;
2459
2527
  let termination;
2460
2528
  let timeoutHandle;
@@ -2510,16 +2578,16 @@ function executeDockerRun(spec, options) {
2510
2578
  });
2511
2579
  }
2512
2580
  function openAnonymousEnvFile(content) {
2513
- const dir = mkdtempSync2(join14(tmpdir2(), "bazilion-docker-env-"));
2514
- const path = join14(dir, "environment");
2581
+ const dir = mkdtempSync2(join15(tmpdir2(), "bazilion-docker-env-"));
2582
+ const path = join15(dir, "environment");
2515
2583
  let fd;
2516
2584
  try {
2517
2585
  writeFileSync7(path, content, { encoding: "utf8", flag: "wx", mode: 384 });
2518
- fd = openSync3(path, constants3.O_RDONLY | constants3.O_NOFOLLOW);
2586
+ fd = openSync4(path, constants4.O_RDONLY | constants4.O_NOFOLLOW);
2519
2587
  rmSync10(dir, { recursive: true, force: true });
2520
2588
  return fd;
2521
2589
  } catch (error) {
2522
- if (fd !== void 0) closeSync3(fd);
2590
+ if (fd !== void 0) closeSync4(fd);
2523
2591
  rmSync10(dir, { recursive: true, force: true });
2524
2592
  throw error;
2525
2593
  }
@@ -2537,7 +2605,7 @@ function resolveTimeoutMs(timeout) {
2537
2605
  }
2538
2606
  async function resolveMountSource(cwd) {
2539
2607
  try {
2540
- await access(cwd, constants3.F_OK);
2608
+ await access(cwd, constants4.F_OK);
2541
2609
  const info = await stat(cwd);
2542
2610
  if (!info.isDirectory()) {
2543
2611
  throw new Error(`Docker sandbox mount is not a directory: ${cwd}`);
@@ -2894,7 +2962,7 @@ function createSessionShellTools(cwd, env, options = {}) {
2894
2962
  image: config.sandboxImage,
2895
2963
  env: buildSandboxContainerEnv(env, config.envAllowlist),
2896
2964
  readOnlyMounts: [
2897
- ...existsSync11(join15(cwd, "memory")) ? [safeReadOnlyMount(join15(cwd, "memory"), SANDBOX_MEMORY_DIR, cwd)] : [],
2965
+ ...existsSync11(join16(cwd, "memory")) ? [safeReadOnlyMount(join16(cwd, "memory"), SANDBOX_MEMORY_DIR, cwd)] : [],
2898
2966
  ...options.inputsDir ? [safeReadOnlyMount(options.inputsDir, SANDBOX_INPUTS_DIR, dirname4(options.inputsDir))] : [],
2899
2967
  ...(options.skillMounts ?? []).map(
2900
2968
  (mount) => safeReadOnlyMount(mount.source, mount.target, dirname4(mount.source))
@@ -2915,8 +2983,8 @@ function safeReadOnlyMount(source, target, expectedRoot) {
2915
2983
  if (info.isSymbolicLink() || !info.isDirectory()) {
2916
2984
  throw new Error(`Docker sandbox read-only mount must be a real directory: ${source}`);
2917
2985
  }
2918
- const sourceRoot = realpathSync3(expectedRoot);
2919
- const canonicalSource = realpathSync3(source);
2986
+ const sourceRoot = realpathSync4(expectedRoot);
2987
+ const canonicalSource = realpathSync4(source);
2920
2988
  const rel = relative4(sourceRoot, canonicalSource);
2921
2989
  const contained = rel === "" || rel !== ".." && !rel.startsWith(`..${sep4}`) && !isAbsolute4(rel);
2922
2990
  if (!contained) {
@@ -2927,18 +2995,18 @@ function safeReadOnlyMount(source, target, expectedRoot) {
2927
2995
 
2928
2996
  // ../daemon/src/runtime/worker/runtime.ts
2929
2997
  import {
2930
- constants as constants4,
2998
+ constants as constants5,
2931
2999
  existsSync as existsSync12,
2932
3000
  lstatSync as lstatSync4,
2933
3001
  mkdirSync as mkdirSync6,
2934
3002
  mkdtempSync as mkdtempSync3,
2935
- realpathSync as realpathSync4,
3003
+ realpathSync as realpathSync5,
2936
3004
  rmSync as rmSync11,
2937
3005
  statSync as statSync3
2938
3006
  } from "fs";
2939
3007
  import { access as access2 } from "fs/promises";
2940
3008
  import { tmpdir as tmpdir3 } from "os";
2941
- import { isAbsolute as isAbsolute5, join as join16, relative as relative5, sep as sep5 } from "path";
3009
+ import { isAbsolute as isAbsolute5, join as join17, relative as relative5, sep as sep5 } from "path";
2942
3010
  var REDACTED_VALUE = "[REDACTED]";
2943
3011
  var REASONING_LEVELS = /* @__PURE__ */ new Set([
2944
3012
  "off",
@@ -2949,6 +3017,8 @@ var REASONING_LEVELS = /* @__PURE__ */ new Set([
2949
3017
  "xhigh"
2950
3018
  ]);
2951
3019
  var CONFIGURED_KEYS = /* @__PURE__ */ new Set([
3020
+ "questionEnabled",
3021
+ "conversation",
2952
3022
  "kind",
2953
3023
  "agent",
2954
3024
  "message",
@@ -2962,6 +3032,7 @@ var CONFIGURED_KEYS = /* @__PURE__ */ new Set([
2962
3032
  "bashApprovalMode"
2963
3033
  ]);
2964
3034
  var CONFIGURED_REQUIRED_KEYS = /* @__PURE__ */ new Set([
3035
+ "conversation",
2965
3036
  "kind",
2966
3037
  "agent",
2967
3038
  "message",
@@ -2971,6 +3042,8 @@ var CONFIGURED_REQUIRED_KEYS = /* @__PURE__ */ new Set([
2971
3042
  "bashApprovalMode"
2972
3043
  ]);
2973
3044
  var PROTECTED_KEYS = /* @__PURE__ */ new Set([
3045
+ "questionEnabled",
3046
+ "conversation",
2974
3047
  "kind",
2975
3048
  "agent",
2976
3049
  "message",
@@ -2984,7 +3057,9 @@ var PROTECTED_KEYS = /* @__PURE__ */ new Set([
2984
3057
  "apiKeyRefreshEnabled",
2985
3058
  "scratch"
2986
3059
  ]);
2987
- var PROTECTED_REQUIRED_KEYS = new Set([...PROTECTED_KEYS].filter((key) => key !== "images"));
3060
+ var PROTECTED_REQUIRED_KEYS = new Set(
3061
+ [...PROTECTED_KEYS].filter((key) => key !== "images" && key !== "questionEnabled")
3062
+ );
2988
3063
  var REVIEW_KEYS = /* @__PURE__ */ new Set([
2989
3064
  "kind",
2990
3065
  "agentId",
@@ -3044,6 +3119,8 @@ function validateMinimalWorkerProcessEnv(env, scratch, platform = process.platfo
3044
3119
  function parseWorkerInput(value) {
3045
3120
  const input = objectRecord(value, "worker input");
3046
3121
  const kind = input.kind;
3122
+ if (input.questionEnabled !== void 0 && typeof input.questionEnabled !== "boolean")
3123
+ throw new Error("Invalid worker question capability");
3047
3124
  if (kind === "configured_operator_http") {
3048
3125
  assertExactKeys(input, CONFIGURED_KEYS, "configured worker input", CONFIGURED_REQUIRED_KEYS);
3049
3126
  if (!isResolvedAgent(input.agent)) throw new Error("worker: configured input requires an agent");
@@ -3052,6 +3129,7 @@ function parseWorkerInput(value) {
3052
3129
  throw new Error("worker: configured input requires enabledProviders");
3053
3130
  }
3054
3131
  requireString(input.turnId, "turnId");
3132
+ assertConversationTarget(input.conversation);
3055
3133
  if (input.bashApprovalMode !== "interactive" && input.bashApprovalMode !== "auto_deny") {
3056
3134
  throw new Error("worker: configured input requires a valid bashApprovalMode");
3057
3135
  }
@@ -3073,6 +3151,7 @@ function parseWorkerInput(value) {
3073
3151
  if (!isResolvedAgent(input.agent)) throw new Error("worker: protected input requires an agent");
3074
3152
  requireString(input.message, "message");
3075
3153
  requireString(input.turnId, "turnId");
3154
+ assertConversationTarget(input.conversation);
3076
3155
  if (input.bashApprovalMode !== "auto_deny") {
3077
3156
  throw new Error("worker: protected input requires auto-deny shell approval");
3078
3157
  }
@@ -3469,7 +3548,7 @@ function requireCanonicalRealDirectory(value, name) {
3469
3548
  if (entry.isSymbolicLink() || !entry.isDirectory()) {
3470
3549
  throw new Error("unsafe entry");
3471
3550
  }
3472
- const canonical = realpathSync4(value);
3551
+ const canonical = realpathSync5(value);
3473
3552
  if (canonical !== value) throw new Error("non-canonical entry");
3474
3553
  return canonical;
3475
3554
  } catch {
@@ -3480,7 +3559,7 @@ function resolveCanonicalDirectory(value, name) {
3480
3559
  try {
3481
3560
  const entry = statSync3(value);
3482
3561
  if (!entry.isDirectory()) throw new Error("not a directory");
3483
- return realpathSync4(value);
3562
+ return realpathSync5(value);
3484
3563
  } catch {
3485
3564
  throw new Error(`worker: ${name} is unavailable or unsafe`);
3486
3565
  }
@@ -3516,13 +3595,13 @@ function requireCanonicalWindowsDirectory(value, name) {
3516
3595
  if (!value || !isAbsolute5(value) || !existsSync12(value) || !statSync3(value).isDirectory()) {
3517
3596
  throw new Error(`minimal worker bootstrap requires a valid ${name} directory`);
3518
3597
  }
3519
- return realpathSync4(value);
3598
+ return realpathSync5(value);
3520
3599
  }
3521
3600
  function requireCanonicalWindowsFile(value, name) {
3522
3601
  if (!value || !isAbsolute5(value) || !existsSync12(value) || !statSync3(value).isFile()) {
3523
3602
  throw new Error(`minimal worker bootstrap requires a valid ${name} executable`);
3524
3603
  }
3525
- return realpathSync4(value);
3604
+ return realpathSync5(value);
3526
3605
  }
3527
3606
  function objectRecord(value, label) {
3528
3607
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -3565,13 +3644,61 @@ function lexicallyWithin(root, candidate) {
3565
3644
  const rel = relative5(root, candidate);
3566
3645
  return rel === "" || rel !== ".." && !rel.startsWith(`..${sep5}`) && !isAbsolute5(rel);
3567
3646
  }
3647
+ function assertConversationTarget(value) {
3648
+ const target = objectRecord(value, "conversation target");
3649
+ assertExactKeys(target, /* @__PURE__ */ new Set(["id", "filename"]), "conversation target");
3650
+ if (typeof target.id !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(target.id) || target.filename !== `${target.id}.jsonl`)
3651
+ throw new Error("Invalid worker conversation target");
3652
+ }
3653
+
3654
+ // ../daemon/src/runtime/tools/ask-user.ts
3655
+ function askUserTool(ask) {
3656
+ return {
3657
+ def: {
3658
+ name: "ask_user",
3659
+ description: "Ask the operator one clarification question and wait for an explicit choice, free-text answer, Skip, or a typed no-answer outcome. An answer is information only: it never grants shell or communication permission. Do not ask for credentials. Do not invent a response when no answer is returned.",
3660
+ parameters: {
3661
+ type: "object",
3662
+ additionalProperties: false,
3663
+ required: ["prompt", "choices"],
3664
+ properties: {
3665
+ prompt: { type: "string", minLength: 1, maxLength: 4096 },
3666
+ choices: {
3667
+ type: "array",
3668
+ minItems: 2,
3669
+ maxItems: 4,
3670
+ items: {
3671
+ type: "object",
3672
+ additionalProperties: false,
3673
+ required: ["label"],
3674
+ properties: {
3675
+ label: { type: "string", minLength: 1, maxLength: 120 },
3676
+ description: { type: "string", minLength: 1, maxLength: 500 }
3677
+ }
3678
+ }
3679
+ },
3680
+ recommendedIndex: { type: "integer", minimum: 0, maximum: 3 }
3681
+ }
3682
+ }
3683
+ },
3684
+ async invoke(args, context) {
3685
+ if (!context?.toolCallId) throw new Error("Question requires its actual tool call identity");
3686
+ const { receipt, ...result } = await ask(
3687
+ context.toolCallId,
3688
+ args
3689
+ );
3690
+ const text = JSON.stringify(result);
3691
+ return receipt ? { content: [{ type: "text", text }], questionReceipt: receipt } : text;
3692
+ }
3693
+ };
3694
+ }
3568
3695
 
3569
3696
  // ../daemon/src/runtime/pi/tools.ts
3570
3697
  import { Type as Type2 } from "typebox";
3571
3698
 
3572
3699
  // ../daemon/src/runtime/tools/bootstrap.ts
3573
3700
  import { existsSync as existsSync13, rmSync as rmSync12 } from "fs";
3574
- import { join as join17 } from "path";
3701
+ import { join as join18 } from "path";
3575
3702
  function bootstrapTool(agentDir) {
3576
3703
  return {
3577
3704
  def: {
@@ -3580,7 +3707,7 @@ function bootstrapTool(agentDir) {
3580
3707
  parameters: { type: "object", properties: {} }
3581
3708
  },
3582
3709
  async invoke() {
3583
- const path = join17(agentDir, "BOOTSTRAP.md");
3710
+ const path = join18(agentDir, "BOOTSTRAP.md");
3584
3711
  if (existsSync13(path)) {
3585
3712
  rmSync12(path);
3586
3713
  return "BOOTSTRAP.md removed. Bootstrap is complete.";
@@ -3721,15 +3848,15 @@ function browserTools(host, agentId) {
3721
3848
 
3722
3849
  // ../daemon/src/runtime/tools/deliver-file.ts
3723
3850
  import {
3724
- closeSync as closeSync4,
3725
- constants as constants5,
3726
- fstatSync as fstatSync3,
3727
- openSync as openSync4,
3728
- readFileSync as readFileSync10,
3729
- realpathSync as realpathSync5,
3851
+ closeSync as closeSync5,
3852
+ constants as constants6,
3853
+ fstatSync as fstatSync4,
3854
+ openSync as openSync5,
3855
+ readSync as readSync2,
3856
+ realpathSync as realpathSync6,
3730
3857
  statSync as statSync4
3731
3858
  } from "fs";
3732
- import { basename as basename2, extname, isAbsolute as isAbsolute6, relative as relative6, resolve as resolve5, sep as sep6 } from "path";
3859
+ import { basename as basename3, extname, isAbsolute as isAbsolute6, relative as relative6, resolve as resolve5, sep as sep6 } from "path";
3733
3860
  var MAX_DELIVER_BYTES = 25 * 1024 * 1024;
3734
3861
  var MIME = {
3735
3862
  ".pdf": "application/pdf",
@@ -3749,17 +3876,17 @@ function isWithin2(root, candidate) {
3749
3876
  const rel = relative6(root, candidate);
3750
3877
  return rel === "" || rel !== ".." && !rel.startsWith(`..${sep6}`) && !isAbsolute6(rel);
3751
3878
  }
3752
- function deliverFileTool(cwd, sink) {
3879
+ function deliverFileTool(cwd, sink, sessionId = "") {
3753
3880
  const workspacePath = resolve5(cwd);
3754
3881
  let workspaceRealPath = null;
3755
3882
  try {
3756
- workspaceRealPath = realpathSync5(workspacePath);
3883
+ workspaceRealPath = realpathSync6(workspacePath);
3757
3884
  } catch {
3758
3885
  }
3759
3886
  return {
3760
3887
  def: {
3761
3888
  name: "deliver_file",
3762
- description: "Send a file from your workspace to the user so they can download it (web), receive it as a document (Telegram), or save it (CLI). Use this to deliver reports, exports, or any artifact you produced. Max 25 MB.",
3889
+ description: "Send a file from your workspace to the user so they can download it (web), receive it as a document (Telegram), or save it (CLI). Use this to deliver reports, exports, or any artifact you produced. Max 25 MiB. Saved delivery remains subject to Team Policy.",
3763
3890
  parameters: {
3764
3891
  type: "object",
3765
3892
  properties: {
@@ -3772,7 +3899,7 @@ function deliverFileTool(cwd, sink) {
3772
3899
  additionalProperties: false
3773
3900
  }
3774
3901
  },
3775
- async invoke(args) {
3902
+ async invoke(args, context) {
3776
3903
  const p = String(args.path ?? "");
3777
3904
  if (!p) throw new Error("deliver_file: path is required");
3778
3905
  if (isAbsolute6(p)) {
@@ -3788,7 +3915,7 @@ function deliverFileTool(cwd, sink) {
3788
3915
  let real;
3789
3916
  let expected;
3790
3917
  try {
3791
- real = realpathSync5(abs);
3918
+ real = realpathSync6(abs);
3792
3919
  expected = statSync4(real);
3793
3920
  } catch {
3794
3921
  throw new Error(`deliver_file: no such file: ${p}`);
@@ -3798,13 +3925,13 @@ function deliverFileTool(cwd, sink) {
3798
3925
  }
3799
3926
  let fd;
3800
3927
  try {
3801
- fd = openSync4(real, constants5.O_RDONLY | constants5.O_NOFOLLOW | constants5.O_NONBLOCK);
3928
+ fd = openSync5(real, constants6.O_RDONLY | constants6.O_NOFOLLOW | constants6.O_NONBLOCK);
3802
3929
  } catch {
3803
3930
  throw new Error(`deliver_file: no such file: ${p}`);
3804
3931
  }
3805
3932
  let data;
3806
3933
  try {
3807
- const stat2 = fstatSync3(fd);
3934
+ const stat2 = fstatSync4(fd);
3808
3935
  if (!stat2.isFile()) {
3809
3936
  throw new Error(`deliver_file: not a regular file: ${p}`);
3810
3937
  }
@@ -3813,7 +3940,7 @@ function deliverFileTool(cwd, sink) {
3813
3940
  }
3814
3941
  let confirmedReal;
3815
3942
  try {
3816
- confirmedReal = realpathSync5(abs);
3943
+ confirmedReal = realpathSync6(abs);
3817
3944
  } catch {
3818
3945
  throw new Error(`deliver_file: path changed during validation: ${p}`);
3819
3946
  }
@@ -3822,32 +3949,53 @@ function deliverFileTool(cwd, sink) {
3822
3949
  }
3823
3950
  if (stat2.size > MAX_DELIVER_BYTES) {
3824
3951
  throw new Error(
3825
- `deliver_file: "${basename2(abs)}" is too large (${(stat2.size / 1024 / 1024).toFixed(1)} MB > 25 MB)`
3952
+ `deliver_file: "${basename3(abs)}" is too large (${(stat2.size / 1024 / 1024).toFixed(1)} MB > 25 MB)`
3826
3953
  );
3827
3954
  }
3828
- data = readFileSync10(fd).toString("base64");
3955
+ const buffer = Buffer.alloc(MAX_DELIVER_BYTES + 1);
3956
+ let length = 0;
3957
+ while (length < buffer.length) {
3958
+ const count = readSync2(fd, buffer, length, buffer.length - length, null);
3959
+ if (count === 0) break;
3960
+ length += count;
3961
+ }
3962
+ if (length > MAX_DELIVER_BYTES) throw new Error("deliver_file: file exceeds 25 MiB");
3963
+ data = buffer.subarray(0, length).toString("base64");
3829
3964
  } finally {
3830
- closeSync4(fd);
3965
+ closeSync5(fd);
3831
3966
  }
3832
- const name = basename2(abs);
3967
+ const name = basename3(abs);
3833
3968
  const mimeType = MIME[extname(abs).toLowerCase()] ?? "application/octet-stream";
3834
- sink({ name, mimeType, data });
3835
- return `Delivered "${name}" (${mimeType}) to the user.`;
3969
+ if (!context?.toolCallId || !sessionId)
3970
+ throw new Error("deliver_file: missing source operation");
3971
+ const result = await sink(
3972
+ { name, mimeType, data },
3973
+ { sessionId, toolCallId: context.toolCallId }
3974
+ );
3975
+ return {
3976
+ content: [
3977
+ {
3978
+ type: "text",
3979
+ text: `Saved "${name}" (${mimeType}) as result ${result.resultId}. Delivery is subject to Team Policy.`
3980
+ }
3981
+ ],
3982
+ result
3983
+ };
3836
3984
  }
3837
3985
  };
3838
3986
  }
3839
3987
 
3840
3988
  // ../daemon/src/runtime/tools/home.ts
3841
3989
  import {
3842
- closeSync as closeSync5,
3843
- constants as constants6,
3844
- fstatSync as fstatSync4,
3990
+ closeSync as closeSync6,
3991
+ constants as constants7,
3992
+ fstatSync as fstatSync5,
3845
3993
  lstatSync as lstatSync5,
3846
- openSync as openSync5,
3994
+ openSync as openSync6,
3847
3995
  readdirSync as readdirSync7,
3848
3996
  writeFileSync as writeFileSync8
3849
3997
  } from "fs";
3850
- import { join as join18 } from "path";
3998
+ import { join as join19 } from "path";
3851
3999
  var HOME_FILES_READABLE = [
3852
4000
  "IDENTITY.md",
3853
4001
  "SOUL.md",
@@ -3912,18 +4060,18 @@ function homeTools(agentDir) {
3912
4060
  let fd;
3913
4061
  try {
3914
4062
  rejectExistingSymlinkOrNonFile(path);
3915
- fd = openSync5(
4063
+ fd = openSync6(
3916
4064
  path,
3917
- constants6.O_WRONLY | constants6.O_CREAT | constants6.O_TRUNC | noFollowFlag(),
4065
+ constants7.O_WRONLY | constants7.O_CREAT | constants7.O_TRUNC | noFollowFlag(),
3918
4066
  384
3919
4067
  );
3920
- if (!fstatSync4(fd).isFile()) throw new Error("target is not a regular file");
4068
+ if (!fstatSync5(fd).isFile()) throw new Error("target is not a regular file");
3921
4069
  writeFileSync8(fd, content, "utf8");
3922
4070
  } catch (err) {
3923
4071
  const msg = err instanceof Error ? err.message : String(err);
3924
4072
  throw new Error(`home_write: could not write ${file}: ${msg}`);
3925
4073
  } finally {
3926
- if (fd !== void 0) closeSync5(fd);
4074
+ if (fd !== void 0) closeSync6(fd);
3927
4075
  }
3928
4076
  return `wrote ${file} (${Buffer.byteLength(content, "utf8")} bytes)`;
3929
4077
  }
@@ -3968,7 +4116,7 @@ function resolvePrivateHomeRoot(agentDir) {
3968
4116
  }
3969
4117
  }
3970
4118
  function privateHomeFile(homeRoot, file) {
3971
- return join18(homeRoot, file);
4119
+ return join19(homeRoot, file);
3972
4120
  }
3973
4121
  function rejectSymlinkOrNonFile(path) {
3974
4122
  const entry = lstatSync5(path);
@@ -4912,18 +5060,18 @@ function ourToolToPiTool(h) {
4912
5060
  label: h.def.name,
4913
5061
  description: h.def.description,
4914
5062
  parameters: Type2.Unsafe(h.def.parameters),
4915
- async execute(_toolCallId, params) {
4916
- const out = await h.invoke(params);
5063
+ async execute(toolCallId, params) {
5064
+ const out = await h.invoke(params, { toolCallId });
4917
5065
  return {
4918
5066
  content: toPiContent(out),
4919
- details: {}
5067
+ details: typeof out === "object" && !Array.isArray(out) ? "result" in out ? { result: out.result } : { questionReceipt: out.questionReceipt } : {}
4920
5068
  };
4921
5069
  }
4922
5070
  };
4923
5071
  }
4924
5072
  function toPiContent(out) {
4925
5073
  if (typeof out === "string") return [{ type: "text", text: out }];
4926
- return out.map(
5074
+ return (Array.isArray(out) ? out : out.content).map(
4927
5075
  (p) => p.type === "text" ? { type: "text", text: p.text } : { type: "image", data: p.data, mimeType: p.mimeType }
4928
5076
  );
4929
5077
  }
@@ -4947,8 +5095,9 @@ function createBazilionCustomTools(opts) {
4947
5095
  handlers.push(...mcpProxyTools(opts.mcpHost, opts.mcpTools));
4948
5096
  }
4949
5097
  if (opts.fileSink) {
4950
- handlers.push(deliverFileTool(opts.agent.team.path, opts.fileSink));
5098
+ handlers.push(deliverFileTool(opts.agent.team.path, opts.fileSink, opts.sessionId));
4951
5099
  }
5100
+ if (opts.askUser) handlers.push(askUserTool(opts.askUser));
4952
5101
  return handlers.map(ourToolToPiTool);
4953
5102
  }
4954
5103
  function createProtectedBazilionCustomTools(opts) {
@@ -4959,8 +5108,9 @@ function createProtectedBazilionCustomTools(opts) {
4959
5108
  protectedWebFetchTool(),
4960
5109
  ...messagingTools(opts.messagingHost, opts.agent.agent.id),
4961
5110
  ...userMdTools(opts.userMdHost, opts.agent.team.id),
4962
- deliverFileTool(opts.agent.team.path, opts.fileSink)
5111
+ deliverFileTool(opts.agent.team.path, opts.fileSink, opts.sessionId)
4963
5112
  ];
5113
+ if (opts.askUser) handlers.push(askUserTool(opts.askUser));
4964
5114
  return handlers.map(ourToolToPiTool);
4965
5115
  }
4966
5116
 
@@ -4984,6 +5134,7 @@ async function createBazilionSession(opts) {
4984
5134
  mcpHost,
4985
5135
  mcpTools,
4986
5136
  fileSink,
5137
+ askUser,
4987
5138
  bashApprovalHost,
4988
5139
  refreshApiKey,
4989
5140
  restricted
@@ -5005,7 +5156,7 @@ async function createBazilionSession(opts) {
5005
5156
  const cwd = agent.team.path;
5006
5157
  if (!existsSync14(cwd)) mkdirSync7(cwd, { recursive: true });
5007
5158
  const promptSkills = restricted ? [] : loadPromptSkills(paths.skillsDir, agent.skills);
5008
- const uploadsDir = join19(agent.agent.dir, "uploads");
5159
+ const uploadsDir = join20(agent.agent.dir, "uploads");
5009
5160
  const shellTools = restricted ? null : createSessionShellTools(cwd, env, {
5010
5161
  ...existsSync14(uploadsDir) ? { inputsDir: uploadsDir } : {},
5011
5162
  skillMounts: promptSkills.map((skill) => ({
@@ -5014,9 +5165,9 @@ async function createBazilionSession(opts) {
5014
5165
  })),
5015
5166
  approvalHost: bashApprovalHost
5016
5167
  });
5017
- const sessionDir = restricted?.sessionDir ?? join19(paths.agentDir(agent.agent.id), "sessions");
5168
+ const sessionDir = restricted?.sessionDir ?? join20(paths.agentDir(agent.agent.id), "sessions");
5018
5169
  mkdirSync7(sessionDir, { recursive: true });
5019
- const existing = restricted ? null : findMostRecent(sessionDir);
5170
+ const existing = restricted ? null : explicitConversationFile(sessionDir, opts.conversation);
5020
5171
  const sessionManager = existing ? SessionManager.open(existing, sessionDir, cwd) : SessionManager.create(cwd, sessionDir);
5021
5172
  const settingsManager = SettingsManager.inMemory({
5022
5173
  compaction: { enabled: false },
@@ -5033,7 +5184,7 @@ async function createBazilionSession(opts) {
5033
5184
  });
5034
5185
  const resourceLoader = new DefaultResourceLoader({
5035
5186
  cwd,
5036
- agentDir: join19(paths.home, "pi"),
5187
+ agentDir: join20(paths.home, "pi"),
5037
5188
  settingsManager,
5038
5189
  noExtensions: true,
5039
5190
  noSkills: true,
@@ -5052,13 +5203,15 @@ async function createBazilionSession(opts) {
5052
5203
  mcpHost,
5053
5204
  mcpTools,
5054
5205
  fileSink,
5206
+ askUser,
5207
+ sessionId: sessionManager.getSessionId(),
5055
5208
  env
5056
5209
  });
5057
5210
  const customTools = shellTools?.customBash ? [...bazilionTools, shellTools.customBash] : bazilionTools;
5058
5211
  const allowedTools = [...shellTools?.hostToolNames ?? [], ...customTools.map((t) => t.name)];
5059
5212
  const { session } = await createAgentSession({
5060
5213
  cwd,
5061
- agentDir: join19(paths.home, "pi"),
5214
+ agentDir: join20(paths.home, "pi"),
5062
5215
  model,
5063
5216
  thinkingLevel: toPiThinkingLevel(restricted?.reasoningLevel ?? agent.reasoningLevel),
5064
5217
  tools: allowedTools,
@@ -5115,7 +5268,7 @@ async function createProtectedBazilionSession(opts) {
5115
5268
  opts.bashApprovalHost
5116
5269
  );
5117
5270
  mkdirSync7(opts.paths.sessionDir, { recursive: true });
5118
- const existing = findMostRecent(opts.paths.sessionDir);
5271
+ const existing = explicitConversationFile(opts.paths.sessionDir, opts.conversation);
5119
5272
  const sessionManager = existing ? SessionManager.open(existing, opts.paths.sessionDir, opts.paths.teamDir) : SessionManager.create(opts.paths.teamDir, opts.paths.sessionDir);
5120
5273
  const settingsManager = createInMemorySettingsManager();
5121
5274
  const bazilionPrompt = redactJsonValue(
@@ -5144,7 +5297,9 @@ async function createProtectedBazilionSession(opts) {
5144
5297
  memory: opts.memory,
5145
5298
  messagingHost: opts.messagingHost,
5146
5299
  userMdHost: opts.userMdHost,
5147
- fileSink: opts.fileSink
5300
+ fileSink: opts.fileSink,
5301
+ askUser: opts.askUser,
5302
+ sessionId: sessionManager.getSessionId()
5148
5303
  });
5149
5304
  if (!shellTools.customBash) throw new Error("protected Docker bash tool is unavailable");
5150
5305
  const customTools = [...bazilionTools, shellTools.customBash];
@@ -5222,7 +5377,7 @@ async function createRestrictedReviewSession(opts) {
5222
5377
  function materializeProtectedCredentialFile(runtime, scratch) {
5223
5378
  const fields = [...runtime.credentialEnv ?? []];
5224
5379
  if (runtime.credentialFile) {
5225
- const path = join19(scratch.tempDir, "provider-credential.json");
5380
+ const path = join20(scratch.tempDir, "provider-credential.json");
5226
5381
  writeFileSync9(path, runtime.credentialFile.content, {
5227
5382
  encoding: "utf8",
5228
5383
  mode: 384,
@@ -5357,19 +5512,12 @@ function toPiThinkingLevel(level) {
5357
5512
  return "medium";
5358
5513
  }
5359
5514
  }
5360
- function findMostRecent(sessionDir) {
5361
- if (!existsSync14(sessionDir)) return null;
5362
- let newest = null;
5363
- for (const entry of readdirSync8(sessionDir)) {
5364
- if (!entry.endsWith(".jsonl")) continue;
5365
- const path = join19(sessionDir, entry);
5366
- try {
5367
- const s = statSync5(path);
5368
- if (!newest || s.mtimeMs > newest.mtimeMs) newest = { path, mtimeMs: s.mtimeMs };
5369
- } catch {
5370
- }
5371
- }
5372
- return newest?.path ?? null;
5515
+ function explicitConversationFile(directory, target) {
5516
+ if (!target || !/^[0-9a-f-]{36}$/i.test(target.id) || target.filename !== `${target.id}.jsonl`)
5517
+ throw new Error("Explicit conversation target is required");
5518
+ const file = join20(directory, target.filename);
5519
+ readCanonicalSessionFile(directory, target.filename, target.id);
5520
+ return file;
5373
5521
  }
5374
5522
 
5375
5523
  // ../daemon/src/runtime/worker/api-key-refresh.ts
@@ -5602,6 +5750,7 @@ async function createSessionForInput(input, ipcCall) {
5602
5750
  }) : void 0;
5603
5751
  const handle2 = await createBazilionSession({
5604
5752
  agent: input.agent,
5753
+ conversation: input.conversation,
5605
5754
  paths,
5606
5755
  env: process.env,
5607
5756
  memory: memory2,
@@ -5614,7 +5763,15 @@ async function createSessionForInput(input, ipcCall) {
5614
5763
  mcpHost,
5615
5764
  mcpTools: input.mcpTools,
5616
5765
  bashApprovalHost: bashApprovalHost2,
5617
- fileSink: (file) => emit({ kind: "event", event: { type: "file", ...file } })
5766
+ askUser: input.questionEnabled ? (toolCallId, question) => ipcCall("askUser", { toolCallId, question }) : void 0,
5767
+ fileSink: async (file, source) => {
5768
+ const result = await ipcCall(
5769
+ "publishResult",
5770
+ { ...file, ...source }
5771
+ );
5772
+ emit({ kind: "event", event: { type: "file", ...file, result } });
5773
+ return result;
5774
+ }
5618
5775
  });
5619
5776
  return { handle: handle2 };
5620
5777
  }
@@ -5646,6 +5803,7 @@ async function createSessionForInput(input, ipcCall) {
5646
5803
  });
5647
5804
  const handle = await createProtectedBazilionSession({
5648
5805
  agent: input.agent,
5806
+ conversation: input.conversation,
5649
5807
  runtime: input.runtime,
5650
5808
  paths: input.paths,
5651
5809
  scratch: input.scratch,
@@ -5654,8 +5812,16 @@ async function createSessionForInput(input, ipcCall) {
5654
5812
  messagingHost,
5655
5813
  userMdHost,
5656
5814
  bashApprovalHost,
5815
+ askUser: input.questionEnabled ? (toolCallId, question) => ipcCall("askUser", { toolCallId, question }) : void 0,
5657
5816
  refreshApiKey,
5658
- fileSink: (file) => emit({ kind: "event", event: { type: "file", ...file } })
5817
+ fileSink: async (file, source) => {
5818
+ const result = await ipcCall("publishResult", {
5819
+ ...file,
5820
+ ...source
5821
+ });
5822
+ emit({ kind: "event", event: { type: "file", ...file, result } });
5823
+ return result;
5824
+ }
5659
5825
  });
5660
5826
  return { handle };
5661
5827
  }
@@ -5684,7 +5850,30 @@ async function main() {
5684
5850
  const ipcCall = createWorkerIpcCall();
5685
5851
  const { handle, reviewState } = await createSessionForInput(input, ipcCall);
5686
5852
  session = handle.session;
5853
+ const consumptionAcks = /* @__PURE__ */ new Set();
5687
5854
  const unsubscribe = session.subscribe((piEvent) => {
5855
+ if (input.kind !== "restricted_review" && input.questionEnabled && piEvent.type === "message_end" && piEvent.message.role === "toolResult" && piEvent.message.toolName === "ask_user" && !piEvent.message.isError) {
5856
+ const message = piEvent.message;
5857
+ const content = message.content[0];
5858
+ if (content?.type === "text") {
5859
+ try {
5860
+ const result = JSON.parse(content.text);
5861
+ if (typeof result.questionId === "string") {
5862
+ const ack = new Promise((resolve6) => setImmediate(resolve6)).then(
5863
+ () => ipcCall("questionConsumed", {
5864
+ questionId: result.questionId,
5865
+ toolCallId: message.toolCallId
5866
+ })
5867
+ ).catch(() => {
5868
+ }).finally(() => {
5869
+ consumptionAcks.delete(ack);
5870
+ });
5871
+ consumptionAcks.add(ack);
5872
+ }
5873
+ } catch {
5874
+ }
5875
+ }
5876
+ }
5688
5877
  if (reviewState && piEvent.type === "tool_execution_end" && piEvent.toolName === "propose_lesson" && piEvent.isError) {
5689
5878
  reviewState.recordValidationFailure();
5690
5879
  if (reviewState.validationFailures() >= 2) void session?.abort();
@@ -5704,6 +5893,7 @@ async function main() {
5704
5893
  promptImages.length > 0 ? { images: promptImages } : void 0
5705
5894
  );
5706
5895
  await session.agent.waitForIdle();
5896
+ await Promise.all(consumptionAcks);
5707
5897
  if (reviewState) {
5708
5898
  if (reviewState.validationFailures() >= 2) {
5709
5899
  throw new Error("review output remained invalid after one schema-repair attempt");