bulkhead-runtime 0.1.0 → 2026.4.5-beta.2

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 (199) hide show
  1. package/README.md +344 -262
  2. package/dist/cli.js +5 -1
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config/index.d.ts +28 -0
  5. package/dist/config/index.d.ts.map +1 -1
  6. package/dist/config/index.js +9 -6
  7. package/dist/config/index.js.map +1 -1
  8. package/dist/credentials/store.d.ts.map +1 -1
  9. package/dist/credentials/store.js +39 -15
  10. package/dist/credentials/store.js.map +1 -1
  11. package/dist/index.d.ts +18 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +38 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/infra/warning-filter.js +1 -1
  16. package/dist/infra/warning-filter.js.map +1 -1
  17. package/dist/logging/subsystem.d.ts +15 -1
  18. package/dist/logging/subsystem.d.ts.map +1 -1
  19. package/dist/logging/subsystem.js +310 -45
  20. package/dist/logging/subsystem.js.map +1 -1
  21. package/dist/memory/embedding-batch.d.ts +38 -0
  22. package/dist/memory/embedding-batch.d.ts.map +1 -0
  23. package/dist/memory/embedding-batch.js +253 -0
  24. package/dist/memory/embedding-batch.js.map +1 -0
  25. package/dist/memory/embedding-cache.d.ts +16 -0
  26. package/dist/memory/embedding-cache.d.ts.map +1 -0
  27. package/dist/memory/embedding-cache.js +113 -0
  28. package/dist/memory/embedding-cache.js.map +1 -0
  29. package/dist/memory/embeddings-debug.js +1 -1
  30. package/dist/memory/embeddings.d.ts +1 -0
  31. package/dist/memory/embeddings.d.ts.map +1 -1
  32. package/dist/memory/embeddings.js +115 -92
  33. package/dist/memory/embeddings.js.map +1 -1
  34. package/dist/memory/file-indexer.d.ts +26 -0
  35. package/dist/memory/file-indexer.d.ts.map +1 -0
  36. package/dist/memory/file-indexer.js +258 -0
  37. package/dist/memory/file-indexer.js.map +1 -0
  38. package/dist/memory/hybrid.d.ts.map +1 -1
  39. package/dist/memory/hybrid.js +6 -2
  40. package/dist/memory/hybrid.js.map +1 -1
  41. package/dist/memory/index.d.ts +5 -0
  42. package/dist/memory/index.d.ts.map +1 -1
  43. package/dist/memory/index.js +5 -2
  44. package/dist/memory/index.js.map +1 -1
  45. package/dist/memory/session-indexer.d.ts +41 -0
  46. package/dist/memory/session-indexer.d.ts.map +1 -0
  47. package/dist/memory/session-indexer.js +367 -0
  48. package/dist/memory/session-indexer.js.map +1 -0
  49. package/dist/memory/simple-manager.d.ts +6 -0
  50. package/dist/memory/simple-manager.d.ts.map +1 -1
  51. package/dist/memory/simple-manager.js +35 -12
  52. package/dist/memory/simple-manager.js.map +1 -1
  53. package/dist/memory/ssrf.d.ts +18 -0
  54. package/dist/memory/ssrf.d.ts.map +1 -0
  55. package/dist/memory/ssrf.js +305 -0
  56. package/dist/memory/ssrf.js.map +1 -0
  57. package/dist/package.json +8 -5
  58. package/dist/platform/platform.d.ts.map +1 -1
  59. package/dist/platform/platform.js +30 -7
  60. package/dist/platform/platform.js.map +1 -1
  61. package/dist/platform/types.d.ts +2 -0
  62. package/dist/platform/types.d.ts.map +1 -1
  63. package/dist/runtime/agent.d.ts +8 -0
  64. package/dist/runtime/agent.d.ts.map +1 -1
  65. package/dist/runtime/agent.js +194 -46
  66. package/dist/runtime/agent.js.map +1 -1
  67. package/dist/runtime/api-key-rotation.d.ts +26 -0
  68. package/dist/runtime/api-key-rotation.d.ts.map +1 -0
  69. package/dist/runtime/api-key-rotation.js +174 -0
  70. package/dist/runtime/api-key-rotation.js.map +1 -0
  71. package/dist/runtime/context-guard.d.ts +32 -0
  72. package/dist/runtime/context-guard.d.ts.map +1 -0
  73. package/dist/runtime/context-guard.js +61 -0
  74. package/dist/runtime/context-guard.js.map +1 -0
  75. package/dist/runtime/failover-error.d.ts +62 -0
  76. package/dist/runtime/failover-error.d.ts.map +1 -0
  77. package/dist/runtime/failover-error.js +733 -0
  78. package/dist/runtime/failover-error.js.map +1 -0
  79. package/dist/runtime/failover-policy.d.ts +5 -0
  80. package/dist/runtime/failover-policy.d.ts.map +1 -0
  81. package/dist/runtime/failover-policy.js +18 -0
  82. package/dist/runtime/failover-policy.js.map +1 -0
  83. package/dist/runtime/index.d.ts +11 -0
  84. package/dist/runtime/index.d.ts.map +1 -1
  85. package/dist/runtime/index.js +11 -0
  86. package/dist/runtime/index.js.map +1 -1
  87. package/dist/runtime/memory-flush.d.ts +24 -0
  88. package/dist/runtime/memory-flush.d.ts.map +1 -0
  89. package/dist/runtime/memory-flush.js +64 -0
  90. package/dist/runtime/memory-flush.js.map +1 -0
  91. package/dist/runtime/memory-tools.d.ts +14 -0
  92. package/dist/runtime/memory-tools.d.ts.map +1 -0
  93. package/dist/runtime/memory-tools.js +58 -0
  94. package/dist/runtime/memory-tools.js.map +1 -0
  95. package/dist/runtime/model-fallback.d.ts +56 -0
  96. package/dist/runtime/model-fallback.d.ts.map +1 -0
  97. package/dist/runtime/model-fallback.js +301 -0
  98. package/dist/runtime/model-fallback.js.map +1 -0
  99. package/dist/runtime/model-fallback.types.d.ts +14 -0
  100. package/dist/runtime/model-fallback.types.d.ts.map +1 -0
  101. package/dist/runtime/model-fallback.types.js +3 -0
  102. package/dist/runtime/model-fallback.types.js.map +1 -0
  103. package/dist/runtime/retry.d.ts +24 -0
  104. package/dist/runtime/retry.d.ts.map +1 -0
  105. package/dist/runtime/retry.js +102 -0
  106. package/dist/runtime/retry.js.map +1 -0
  107. package/dist/runtime/session-pruning.d.ts +22 -0
  108. package/dist/runtime/session-pruning.d.ts.map +1 -0
  109. package/dist/runtime/session-pruning.js +118 -0
  110. package/dist/runtime/session-pruning.js.map +1 -0
  111. package/dist/runtime/stream-adapters.d.ts +11 -0
  112. package/dist/runtime/stream-adapters.d.ts.map +1 -0
  113. package/dist/runtime/stream-adapters.js +46 -0
  114. package/dist/runtime/stream-adapters.js.map +1 -0
  115. package/dist/runtime/subagent.d.ts +83 -0
  116. package/dist/runtime/subagent.d.ts.map +1 -0
  117. package/dist/runtime/subagent.js +190 -0
  118. package/dist/runtime/subagent.js.map +1 -0
  119. package/dist/runtime/tool-result-truncation.d.ts +25 -0
  120. package/dist/runtime/tool-result-truncation.d.ts.map +1 -0
  121. package/dist/runtime/tool-result-truncation.js +115 -0
  122. package/dist/runtime/tool-result-truncation.js.map +1 -0
  123. package/dist/sandbox/cgroup.d.ts +4 -1
  124. package/dist/sandbox/cgroup.d.ts.map +1 -1
  125. package/dist/sandbox/cgroup.js +28 -15
  126. package/dist/sandbox/cgroup.js.map +1 -1
  127. package/dist/sandbox/index.d.ts +2 -1
  128. package/dist/sandbox/index.d.ts.map +1 -1
  129. package/dist/sandbox/index.js +2 -1
  130. package/dist/sandbox/index.js.map +1 -1
  131. package/dist/sandbox/ipc.d.ts +4 -1
  132. package/dist/sandbox/ipc.d.ts.map +1 -1
  133. package/dist/sandbox/ipc.js +33 -17
  134. package/dist/sandbox/ipc.js.map +1 -1
  135. package/dist/sandbox/manager.d.ts +1 -2
  136. package/dist/sandbox/manager.d.ts.map +1 -1
  137. package/dist/sandbox/manager.js +136 -130
  138. package/dist/sandbox/manager.js.map +1 -1
  139. package/dist/sandbox/namespace.d.ts +1 -1
  140. package/dist/sandbox/namespace.d.ts.map +1 -1
  141. package/dist/sandbox/namespace.js +36 -37
  142. package/dist/sandbox/namespace.js.map +1 -1
  143. package/dist/sandbox/rootfs.d.ts +6 -1
  144. package/dist/sandbox/rootfs.d.ts.map +1 -1
  145. package/dist/sandbox/rootfs.js +114 -30
  146. package/dist/sandbox/rootfs.js.map +1 -1
  147. package/dist/sandbox/seccomp-apply.d.ts +9 -0
  148. package/dist/sandbox/seccomp-apply.d.ts.map +1 -0
  149. package/dist/sandbox/seccomp-apply.js +227 -0
  150. package/dist/sandbox/seccomp-apply.js.map +1 -0
  151. package/dist/sandbox/seccomp.js +3 -3
  152. package/dist/sandbox/seccomp.js.map +1 -1
  153. package/dist/sandbox/types.d.ts +1 -3
  154. package/dist/sandbox/types.d.ts.map +1 -1
  155. package/dist/sandbox/types.js.map +1 -1
  156. package/dist/sandbox/worker.d.ts +3 -0
  157. package/dist/sandbox/worker.d.ts.map +1 -1
  158. package/dist/sandbox/worker.js +84 -17
  159. package/dist/sandbox/worker.js.map +1 -1
  160. package/dist/sessions/index.d.ts +1 -0
  161. package/dist/sessions/index.d.ts.map +1 -1
  162. package/dist/sessions/index.js +1 -0
  163. package/dist/sessions/index.js.map +1 -1
  164. package/dist/sessions/store.d.ts +2 -2
  165. package/dist/sessions/store.d.ts.map +1 -1
  166. package/dist/sessions/store.js +49 -27
  167. package/dist/sessions/store.js.map +1 -1
  168. package/dist/sessions/transcript-events.d.ts +11 -0
  169. package/dist/sessions/transcript-events.d.ts.map +1 -0
  170. package/dist/sessions/transcript-events.js +40 -0
  171. package/dist/sessions/transcript-events.js.map +1 -0
  172. package/dist/shared/agent-session.d.ts +10 -0
  173. package/dist/shared/agent-session.d.ts.map +1 -0
  174. package/dist/shared/agent-session.js +33 -0
  175. package/dist/shared/agent-session.js.map +1 -0
  176. package/dist/shared/constants.d.ts +6 -0
  177. package/dist/shared/constants.d.ts.map +1 -0
  178. package/dist/shared/constants.js +11 -0
  179. package/dist/shared/constants.js.map +1 -0
  180. package/dist/shared/fs.d.ts +7 -0
  181. package/dist/shared/fs.d.ts.map +1 -0
  182. package/dist/shared/fs.js +14 -0
  183. package/dist/shared/fs.js.map +1 -0
  184. package/dist/shared/index.d.ts +4 -0
  185. package/dist/shared/index.d.ts.map +1 -0
  186. package/dist/shared/index.js +4 -0
  187. package/dist/shared/index.js.map +1 -0
  188. package/dist/skills/enablement.d.ts.map +1 -1
  189. package/dist/skills/enablement.js +2 -2
  190. package/dist/skills/enablement.js.map +1 -1
  191. package/dist/workspace/runner.d.ts.map +1 -1
  192. package/dist/workspace/runner.js +436 -105
  193. package/dist/workspace/runner.js.map +1 -1
  194. package/dist/workspace/types.d.ts +1 -0
  195. package/dist/workspace/types.d.ts.map +1 -1
  196. package/dist/workspace/workspace.d.ts.map +1 -1
  197. package/dist/workspace/workspace.js +12 -3
  198. package/dist/workspace/workspace.js.map +1 -1
  199. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"enablement.d.ts","sourceRoot":"","sources":["../../src/skills/enablement.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEvE,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IACpC,WAAW,IAAI,kBAAkB,EAAE,CAAC;IACpC,cAAc,IAAI,MAAM,EAAE,CAAC;CAC5B;AAMD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,aAAa,GACtB,eAAe,CAmDjB"}
1
+ {"version":3,"file":"enablement.d.ts","sourceRoot":"","sources":["../../src/skills/enablement.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGvE,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IACpC,WAAW,IAAI,kBAAkB,EAAE,CAAC;IACpC,cAAc,IAAI,MAAM,EAAE,CAAC;CAC5B;AAMD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,aAAa,GACtB,eAAe,CAkDjB"}
@@ -1,5 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
+ import { atomicWriteFileSync } from "../shared/index.js";
3
4
  export function createSkillEnablement(workspaceDir, registry) {
4
5
  const filePath = path.join(workspaceDir, "enabled-skills.json");
5
6
  function loadState() {
@@ -12,8 +13,7 @@ export function createSkillEnablement(workspaceDir, registry) {
12
13
  }
13
14
  }
14
15
  function saveState(state) {
15
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
16
- fs.writeFileSync(filePath, JSON.stringify(state, null, 2));
16
+ atomicWriteFileSync(filePath, JSON.stringify(state, null, 2));
17
17
  }
18
18
  return {
19
19
  enable(skillId) {
@@ -1 +1 @@
1
- {"version":3,"file":"enablement.js","sourceRoot":"","sources":["../../src/skills/enablement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAelC,MAAM,UAAU,qBAAqB,CACnC,YAAoB,EACpB,QAAuB;IAEvB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,qBAAqB,CAAC,CAAC;IAEhE,SAAS,SAAS;QAChB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAoB,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,SAAS,SAAS,CAAC,KAAsB;QACvC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED,OAAO;QACL,MAAM,CAAC,OAAO;YACZ,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC;gBAAE,OAAO,KAAK,CAAC;YACzC,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;YAC1B,IAAI,KAAK,CAAC,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,OAAO,IAAI,CAAC;YACvD,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAClC,SAAS,CAAC,KAAK,CAAC,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,CAAC,OAAO;YACb,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;YAC1B,MAAM,GAAG,GAAG,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACjD,IAAI,GAAG,KAAK,CAAC,CAAC;gBAAE,OAAO,KAAK,CAAC;YAC7B,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACnC,SAAS,CAAC,KAAK,CAAC,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,SAAS,CAAC,OAAO;YACf,OAAO,SAAS,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACrD,CAAC;QAED,WAAW;YACT,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC,aAAa;iBACvB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;iBAC7B,MAAM,CAAC,CAAC,CAAC,EAA2B,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;QAC7D,CAAC;QAED,cAAc;YACZ,OAAO,SAAS,EAAE,CAAC,aAAa,CAAC;QACnC,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"enablement.js","sourceRoot":"","sources":["../../src/skills/enablement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAczD,MAAM,UAAU,qBAAqB,CACnC,YAAoB,EACpB,QAAuB;IAEvB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,qBAAqB,CAAC,CAAC;IAEhE,SAAS,SAAS;QAChB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAoB,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,SAAS,SAAS,CAAC,KAAsB;QACvC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,OAAO;QACL,MAAM,CAAC,OAAO;YACZ,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC;gBAAE,OAAO,KAAK,CAAC;YACzC,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;YAC1B,IAAI,KAAK,CAAC,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,OAAO,IAAI,CAAC;YACvD,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAClC,SAAS,CAAC,KAAK,CAAC,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,CAAC,OAAO;YACb,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;YAC1B,MAAM,GAAG,GAAG,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACjD,IAAI,GAAG,KAAK,CAAC,CAAC;gBAAE,OAAO,KAAK,CAAC;YAC7B,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACnC,SAAS,CAAC,KAAK,CAAC,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,SAAS,CAAC,OAAO;YACf,OAAO,SAAS,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACrD,CAAC;QAED,WAAW;YACT,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC,aAAa;iBACvB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;iBAC7B,MAAM,CAAC,CAAC,CAAC,EAA2B,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;QAC7D,CAAC;QAED,cAAc;YACZ,OAAO,SAAS,EAAE,CAAC,aAAa,CAAC;QACnC,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/workspace/runner.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAU1D,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,eAAe,CAAC;IACxB,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,MAAM,EAAE,eAAe,CAAC;IACxB,aAAa,EAAE,aAAa,CAAC;IAC7B,WAAW,EAAE,eAAe,CAAC;CAC9B;AAED,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,sBAAsB,GAC1B,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,cAAc,CAAC,CA+L3D"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/workspace/runner.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAsB1D,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,eAAe,CAAC;IACxB,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,MAAM,EAAE,eAAe,CAAC;IACxB,aAAa,EAAE,aAAa,CAAC;IAC7B,WAAW,EAAE,eAAe,CAAC;CAC9B;AAED,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,sBAAsB,GAC1B,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,cAAc,CAAC,CAgX3D"}
@@ -1,25 +1,31 @@
1
1
  import * as path from "node:path";
2
2
  import * as fs from "node:fs";
3
3
  import * as url from "node:url";
4
+ import { spawn } from "node:child_process";
4
5
  import { getOrCreateSession, updateSession, } from "../sessions/index.js";
5
6
  import { createSandboxManager } from "../sandbox/manager.js";
6
7
  import { createIpcPeer } from "../sandbox/ipc.js";
8
+ import { DEFAULT_MODEL, DEFAULT_PROVIDER, PROTECTED_SYSTEM_ENV_KEYS, buildProviderEnvKey, } from "../shared/index.js";
9
+ import { runWithModelFallback, createCooldownStore } from "../runtime/model-fallback.js";
10
+ import { resolveContextWindowInfo, evaluateContextWindowGuard, CONTEXT_WINDOW_HARD_MIN_TOKENS } from "../runtime/context-guard.js";
11
+ import { collectProviderApiKeys, executeWithApiKeyRotation } from "../runtime/api-key-rotation.js";
12
+ import { createSubsystemLogger } from "../logging/subsystem.js";
13
+ const log = createSubsystemLogger("workspace-runner");
7
14
  export function createWorkspaceRunner(ctx) {
8
15
  const sandboxManager = createSandboxManager();
16
+ const cooldownStore = createCooldownStore();
9
17
  return async function run(options) {
10
18
  const sessionId = options.sessionId ?? `session_${Date.now()}`;
11
- const modelId = options.model ?? ctx.config.model ?? "claude-sonnet-4-20250514";
12
- const provider = options.provider ?? ctx.config.provider ?? "anthropic";
13
- const providerEnvKey = `${provider.toUpperCase().replace(/-/g, "_")}_API_KEY`;
14
- const apiKey = options.apiKey ?? ctx.config.apiKey ?? process.env[providerEnvKey] ?? "";
15
- const sessionsDir = path.join(ctx.workspaceDir, "sessions");
19
+ const requestedModelId = options.model ?? ctx.config.model ?? DEFAULT_MODEL;
20
+ const requestedProvider = options.provider ?? ctx.config.provider ?? DEFAULT_PROVIDER;
21
+ const sessionsDir = path.join(ctx.workspaceDir, "sessions", sessionId);
16
22
  fs.mkdirSync(sessionsDir, { recursive: true });
17
- getOrCreateSession(ctx.workspaceDir, sessionId, { model: modelId });
23
+ await getOrCreateSession(ctx.workspaceDir, sessionId, { model: requestedModelId });
18
24
  await ctx.hooks.run("session_start", { sessionId });
19
25
  await ctx.hooks.run("before_agent_start", {
20
26
  sessionId,
21
27
  message: options.message,
22
- model: modelId,
28
+ model: requestedModelId,
23
29
  });
24
30
  let skillsPrompt = "";
25
31
  if (ctx.config.skills?.enabled !== false) {
@@ -40,119 +46,444 @@ export function createWorkspaceRunner(ctx) {
40
46
  ]
41
47
  .filter(Boolean)
42
48
  .join("\n\n");
43
- const caps = await sandboxManager.capabilities();
44
- const hasOsIsolation = caps.platform === "linux" && caps.hasUnshare && caps.hasMountNamespace;
45
- const workerConfig = {
46
- workspaceDir: ctx.workspaceDir,
47
- sessionsDir,
48
- sessionId,
49
- message: options.message,
50
- model: modelId,
51
- provider,
52
- systemPrompt: systemPrompt || undefined,
53
- enableCodingTools: hasOsIsolation,
54
- };
55
- const workerPath = resolveWorkerPath();
56
- const workerEnv = {
57
- SANDBOX_WORKER_CONFIG: JSON.stringify(workerConfig),
58
- };
59
- if (apiKey) {
60
- workerEnv[providerEnvKey] = apiKey;
61
- }
62
- const sandboxConfig = {
63
- memoryLimitMb: 512,
64
- pidsLimit: 100,
65
- timeoutMs: 5 * 60 * 1000,
66
- networkIsolation: false,
67
- };
68
- const sandboxProcess = await sandboxManager.spawn({
69
- command: process.execPath,
70
- args: [
71
- "--experimental-vm-modules",
72
- "--no-warnings",
73
- workerPath,
74
- ],
75
- env: workerEnv,
76
- cwd: ctx.workspaceDir,
77
- config: sandboxConfig,
78
- protectedKeys: apiKey ? [providerEnvKey] : [],
79
- onStderr: (data) => {
80
- process.stderr.write(`[sandbox:${ctx.userId}] ${data}`);
81
- },
82
- });
83
- const hostPeer = createIpcPeer(sandboxProcess.stdout, sandboxProcess.stdin);
84
- hostPeer.handle("memory.search", async (params) => {
85
- const { query, maxResults } = params;
86
- return ctx.memory.search(query, { maxResults });
87
- });
88
- hostPeer.handle("memory.store", async (params) => {
89
- const { content, metadata } = params;
90
- const id = await ctx.memory.store(content, metadata);
91
- return { id };
92
- });
93
- hostPeer.handle("credentials.resolve", async (params) => {
94
- const { skillId } = params;
95
- return ctx.credentials.resolve(skillId);
96
- });
97
- hostPeer.handle("skill.execute", async (params) => {
98
- const { skillId, params: skillParams } = params;
99
- if (!ctx.skills.isEnabled(skillId)) {
100
- throw new Error(`Skill "${skillId}" is not enabled for this workspace`);
49
+ async function executeWithModel(provider, modelId) {
50
+ let modelContextWindow;
51
+ try {
52
+ const { getModel } = await import("@mariozechner/pi-ai");
53
+ const resolvedModel = getModel(provider, modelId);
54
+ modelContextWindow = resolvedModel.contextWindow;
101
55
  }
102
- const creds = await ctx.credentials.resolve(skillId);
103
- return {
104
- skillId,
105
- credentials: creds ? "available" : "none",
106
- params: skillParams,
107
- injectedEnv: creds ? Object.keys(creds) : [],
108
- };
109
- });
110
- hostPeer.start();
111
- // If the child process dies, stop the IPC peer so pending calls reject
112
- // instead of hanging forever. This prevents a deadlock where
113
- // hostPeer.call() awaits a response that will never come.
114
- sandboxProcess.waitForExit().then(() => hostPeer.stop(), () => hostPeer.stop());
115
- let result;
116
- try {
117
- const response = await hostPeer.call("agent.run", {
118
- message: options.message,
119
- sessionId,
120
- model: modelId,
121
- provider,
122
- systemPrompt: systemPrompt || undefined,
56
+ catch {
57
+ // model resolution may fail — proceed with config/default tokens
58
+ }
59
+ const ctxInfo = resolveContextWindowInfo({
60
+ modelContextWindow,
61
+ configContextTokens: options.contextTokens,
123
62
  });
124
- if (response.error) {
125
- throw new Error(`Agent execution failed: ${response.error}`);
63
+ const ctxGuard = evaluateContextWindowGuard({ info: ctxInfo });
64
+ if (ctxGuard.shouldBlock) {
65
+ throw new Error(`Context window too small (${ctxGuard.tokens} tokens). Minimum: ${CONTEXT_WINDOW_HARD_MIN_TOKENS}.`);
126
66
  }
127
- result = {
128
- response: response.response ?? "",
129
- sessionId: response.sessionId ?? sessionId,
130
- };
131
- }
132
- catch (err) {
133
- hostPeer.stop();
134
- sandboxProcess.kill();
135
- await sandboxProcess.waitForExit();
136
- throw err;
67
+ const providerEnvKey = buildProviderEnvKey(provider);
68
+ const apiKeys = [];
69
+ if (options.apiKey)
70
+ apiKeys.push(options.apiKey);
71
+ if (options.apiKeys) {
72
+ apiKeys.push(...options.apiKeys);
73
+ }
74
+ if (ctx.config.apiKey)
75
+ apiKeys.push(ctx.config.apiKey);
76
+ apiKeys.push(...collectProviderApiKeys(provider));
77
+ const uniqueKeys = [...new Set(apiKeys.filter((k) => k.length > 0))];
78
+ if (uniqueKeys.length === 0) {
79
+ const envVal = process.env[providerEnvKey];
80
+ if (envVal)
81
+ uniqueKeys.push(envVal);
82
+ }
83
+ async function runWithKey(apiKey) {
84
+ const workerConfig = {
85
+ workspaceDir: ctx.workspaceDir,
86
+ sessionsDir,
87
+ sessionId,
88
+ message: options.message,
89
+ model: modelId,
90
+ provider,
91
+ systemPrompt: systemPrompt || undefined,
92
+ enableCodingTools: true,
93
+ contextTokens: options.contextTokens,
94
+ maxRetries: options.maxRetries,
95
+ enableSubagents: options.enableSubagents,
96
+ };
97
+ const workerPath = resolveWorkerPath();
98
+ const projectDir = path.dirname(path.dirname(workerPath));
99
+ const workerConfigJson = JSON.stringify(workerConfig);
100
+ const MAX_ENV_SIZE = 128 * 1024;
101
+ if (workerConfigJson.length > MAX_ENV_SIZE) {
102
+ throw new Error(`SANDBOX_WORKER_CONFIG exceeds ${MAX_ENV_SIZE} bytes (${workerConfigJson.length} bytes). ` +
103
+ `Reduce the message or systemPrompt size.`);
104
+ }
105
+ const workerEnv = {
106
+ SANDBOX_WORKER_CONFIG: workerConfigJson,
107
+ };
108
+ if (apiKey) {
109
+ workerEnv[providerEnvKey] = apiKey;
110
+ }
111
+ const sandboxConfig = {
112
+ memoryLimitMb: 512,
113
+ pidsLimit: 100,
114
+ timeoutMs: 5 * 60 * 1000,
115
+ networkIsolation: true,
116
+ mountBinds: [
117
+ { source: projectDir, target: projectDir, readonly: true },
118
+ ],
119
+ };
120
+ const workerArgs = [
121
+ "--experimental-vm-modules",
122
+ "--no-warnings",
123
+ ];
124
+ if (workerPath.endsWith(".ts")) {
125
+ workerArgs.push("--import", "tsx");
126
+ }
127
+ workerArgs.push(workerPath);
128
+ const sandboxProcess = await sandboxManager.spawn({
129
+ command: process.execPath,
130
+ args: workerArgs,
131
+ env: workerEnv,
132
+ cwd: ctx.workspaceDir,
133
+ config: sandboxConfig,
134
+ protectedKeys: apiKey ? [providerEnvKey] : [],
135
+ onStderr: (data) => {
136
+ process.stderr.write(`[sandbox:${ctx.userId}] ${data}`);
137
+ },
138
+ });
139
+ const agentTimeoutMs = (sandboxConfig.timeoutMs ?? 5 * 60 * 1000) + 60_000;
140
+ const hostPeer = createIpcPeer(sandboxProcess.stdout, sandboxProcess.stdin, { callTimeoutMs: agentTimeoutMs });
141
+ const ipcRateLimiter = createIpcRateLimiter();
142
+ hostPeer.handle("memory.search", async (params) => {
143
+ ipcRateLimiter.check("memory.search");
144
+ const p = params;
145
+ const query = p?.query;
146
+ if (typeof query !== "string" || query.length === 0) {
147
+ throw new Error("memory.search: 'query' must be a non-empty string");
148
+ }
149
+ if (query.length > 10_000) {
150
+ throw new Error("memory.search: 'query' exceeds maximum length (10000)");
151
+ }
152
+ const maxResults = typeof p?.maxResults === "number"
153
+ ? Math.min(Math.max(1, Math.floor(p.maxResults)), 100)
154
+ : undefined;
155
+ return ctx.memory.search(query, { maxResults });
156
+ });
157
+ hostPeer.handle("memory.store", async (params) => {
158
+ ipcRateLimiter.check("memory.store");
159
+ const p = params;
160
+ const content = p?.content;
161
+ if (typeof content !== "string" || content.length === 0) {
162
+ throw new Error("memory.store: 'content' must be a non-empty string");
163
+ }
164
+ if (content.length > 1_000_000) {
165
+ throw new Error("memory.store: 'content' exceeds maximum length (1000000)");
166
+ }
167
+ const metadata = p?.metadata;
168
+ if (metadata !== undefined) {
169
+ const metaStr = JSON.stringify(metadata);
170
+ if (metaStr.length > 10_000) {
171
+ throw new Error("memory.store: 'metadata' exceeds maximum serialized size (10000 bytes)");
172
+ }
173
+ }
174
+ const id = await ctx.memory.store(content, metadata);
175
+ return { id };
176
+ });
177
+ hostPeer.handle("skill.execute", async (params) => {
178
+ ipcRateLimiter.check("skill.execute");
179
+ const p = params;
180
+ const skillId = p?.skillId;
181
+ if (typeof skillId !== "string" || skillId.length === 0) {
182
+ throw new Error("skill.execute: 'skillId' must be a non-empty string");
183
+ }
184
+ if (!ctx.skills.isEnabled(skillId)) {
185
+ throw new Error(`Skill "${skillId}" is not enabled for this workspace`);
186
+ }
187
+ const skillEntry = ctx.skillRegistry.get(skillId);
188
+ if (!skillEntry) {
189
+ throw new Error(`Skill "${skillId}" not found in registry`);
190
+ }
191
+ const execScript = findSkillExecutable(skillEntry.path);
192
+ if (!execScript) {
193
+ throw new Error(`Skill "${skillId}" has no executable script. ` +
194
+ `Add execute.js, execute.mjs, or execute.sh to the skill directory.`);
195
+ }
196
+ let resolvedScript;
197
+ try {
198
+ resolvedScript = fs.realpathSync(execScript);
199
+ }
200
+ catch {
201
+ throw new Error(`Skill script "${execScript}" does not exist or is a broken symlink`);
202
+ }
203
+ const resolvedSkillDir = fs.realpathSync(skillEntry.path);
204
+ if (!resolvedScript.startsWith(resolvedSkillDir + path.sep)) {
205
+ throw new Error(`Skill script "${execScript}" resolves outside skill directory`);
206
+ }
207
+ const credentials = await ctx.credentials.resolve(skillId) ?? {};
208
+ const execParams = (p?.params ?? {});
209
+ const result = await executeSkillScript(execScript, execParams, credentials, resolvedSkillDir);
210
+ return { result };
211
+ });
212
+ hostPeer.handle("hooks.before_tool_call", async (params) => {
213
+ ipcRateLimiter.check("hooks.before_tool_call");
214
+ const p = params;
215
+ const toolName = typeof p?.toolName === "string" ? p.toolName : "unknown";
216
+ const input = (p?.input && typeof p.input === "object") ? p.input : {};
217
+ await ctx.hooks.run("before_tool_call", { toolName, input });
218
+ return {};
219
+ });
220
+ hostPeer.handle("hooks.after_tool_call", async (params) => {
221
+ ipcRateLimiter.check("hooks.after_tool_call");
222
+ const p = params;
223
+ const toolName = typeof p?.toolName === "string" ? p.toolName : "unknown";
224
+ const input = (p?.input && typeof p.input === "object") ? p.input : {};
225
+ await ctx.hooks.run("after_tool_call", { toolName, input, result: p?.result });
226
+ return {};
227
+ });
228
+ hostPeer.handle("agent.run.subagent", async (params) => {
229
+ ipcRateLimiter.check("agent.run.subagent");
230
+ const p = (params ?? {});
231
+ const subMessage = typeof p.message === "string" ? p.message : "";
232
+ if (!subMessage) {
233
+ return { error: "agent.run.subagent: 'message' is required" };
234
+ }
235
+ try {
236
+ const subResult = await run({
237
+ message: subMessage,
238
+ sessionId: typeof p.sessionId === "string" ? p.sessionId : undefined,
239
+ model: typeof p.model === "string" ? p.model : undefined,
240
+ provider: typeof p.provider === "string" ? p.provider : undefined,
241
+ systemPrompt: typeof p.systemPrompt === "string" ? p.systemPrompt : undefined,
242
+ });
243
+ return { response: subResult.response, sessionId: subResult.sessionId };
244
+ }
245
+ catch (err) {
246
+ return { error: err instanceof Error ? err.message : String(err) };
247
+ }
248
+ });
249
+ const onEvent = options.onEvent;
250
+ if (onEvent) {
251
+ hostPeer.handle("agent.event", async (params) => {
252
+ const event = (params ?? {});
253
+ try {
254
+ onEvent(event);
255
+ }
256
+ catch { /* caller error */ }
257
+ return {};
258
+ });
259
+ }
260
+ hostPeer.start();
261
+ // If the child process dies, stop the IPC peer so pending calls reject
262
+ // instead of hanging forever. This prevents a deadlock where
263
+ // hostPeer.call() awaits a response that will never come.
264
+ sandboxProcess.waitForExit().then(() => hostPeer.stop(), () => hostPeer.stop());
265
+ let result;
266
+ try {
267
+ const response = await hostPeer.call("agent.run", {
268
+ message: options.message,
269
+ sessionId,
270
+ model: modelId,
271
+ provider,
272
+ systemPrompt: systemPrompt || undefined,
273
+ });
274
+ if (response.error) {
275
+ throw new Error(`Agent execution failed: ${response.error}`);
276
+ }
277
+ result = {
278
+ response: response.response ?? "",
279
+ sessionId: response.sessionId ?? sessionId,
280
+ };
281
+ }
282
+ catch (err) {
283
+ hostPeer.stop();
284
+ sandboxProcess.kill();
285
+ await sandboxProcess.waitForExit();
286
+ throw err;
287
+ }
288
+ hostPeer.stop();
289
+ sandboxProcess.kill();
290
+ await sandboxProcess.waitForExit();
291
+ return {
292
+ ...result,
293
+ provider,
294
+ model: modelId,
295
+ };
296
+ }
297
+ if (uniqueKeys.length > 1) {
298
+ return executeWithApiKeyRotation({
299
+ provider,
300
+ apiKeys: uniqueKeys,
301
+ execute: runWithKey,
302
+ });
303
+ }
304
+ return runWithKey(uniqueKeys[0] ?? "");
137
305
  }
138
- hostPeer.stop();
139
- sandboxProcess.kill();
140
- await sandboxProcess.waitForExit();
306
+ const fallbackResult = await runWithModelFallback({
307
+ provider: requestedProvider,
308
+ model: requestedModelId,
309
+ fallbacks: options.fallbacks,
310
+ run: executeWithModel,
311
+ cooldownStore,
312
+ });
141
313
  await ctx.hooks.run("after_agent_end", {
142
314
  sessionId,
143
- result: result.response,
315
+ result: fallbackResult.result.response,
144
316
  });
145
317
  await ctx.hooks.run("session_end", { sessionId });
146
- updateSession(ctx.workspaceDir, sessionId, { model: modelId });
147
- return result;
318
+ await updateSession(ctx.workspaceDir, sessionId, { model: fallbackResult.model });
319
+ return {
320
+ ...fallbackResult.result,
321
+ fallbackUsed: fallbackResult.attempts.length > 0,
322
+ };
148
323
  };
149
324
  }
150
325
  function resolveWorkerPath() {
151
326
  const thisFile = url.fileURLToPath(import.meta.url);
152
327
  const srcDir = path.dirname(path.dirname(thisFile));
328
+ const jsPath = path.join(srcDir, "sandbox", "worker.js");
329
+ if (fs.existsSync(jsPath))
330
+ return jsPath;
153
331
  const tsPath = path.join(srcDir, "sandbox", "worker.ts");
154
332
  if (fs.existsSync(tsPath))
155
333
  return tsPath;
156
- return path.join(srcDir, "sandbox", "worker.js");
334
+ return jsPath;
335
+ }
336
+ function findSkillExecutable(skillDir) {
337
+ for (const name of ["execute.js", "execute.mjs", "execute.sh"]) {
338
+ const full = path.join(skillDir, name);
339
+ if (fs.existsSync(full))
340
+ return full;
341
+ }
342
+ return undefined;
343
+ }
344
+ const BLOCKED_CREDENTIAL_ENV_KEYS = new Set([
345
+ "NODE_OPTIONS",
346
+ "NODE_EXTRA_CA_CERTS",
347
+ "NODE_TLS_REJECT_UNAUTHORIZED",
348
+ "NODE_DEBUG",
349
+ "NODE_REDIRECT_WARNINGS",
350
+ "LD_PRELOAD",
351
+ "LD_LIBRARY_PATH",
352
+ "DYLD_INSERT_LIBRARIES",
353
+ "DYLD_LIBRARY_PATH",
354
+ "BASH_ENV",
355
+ "ENV",
356
+ "CDPATH",
357
+ "PYTHONSTARTUP",
358
+ "PYTHONPATH",
359
+ "RUBYOPT",
360
+ "PERL5OPT",
361
+ "PERL5LIB",
362
+ "JAVA_TOOL_OPTIONS",
363
+ "_JAVA_OPTIONS",
364
+ "CLASSPATH",
365
+ "IFS",
366
+ "MAIL",
367
+ "MAILPATH",
368
+ "PROMPT_COMMAND",
369
+ "PS4",
370
+ "SHELLOPTS",
371
+ "BASHOPTS",
372
+ "GLOBIGNORE",
373
+ "HISTFILE",
374
+ "HISTCONTROL",
375
+ "SSH_AUTH_SOCK",
376
+ "GPG_AGENT_INFO",
377
+ "TMPDIR",
378
+ "TEMP",
379
+ "TMP",
380
+ "http_proxy",
381
+ "HTTP_PROXY",
382
+ "https_proxy",
383
+ "HTTPS_PROXY",
384
+ "no_proxy",
385
+ "NO_PROXY",
386
+ "ALL_PROXY",
387
+ ]);
388
+ const MAX_SKILL_OUTPUT_BYTES = 10 * 1024 * 1024; // 10 MB
389
+ async function executeSkillScript(scriptPath, params, credentials, cwd, timeoutMs = 30_000) {
390
+ const ext = path.extname(scriptPath);
391
+ let command;
392
+ let args;
393
+ switch (ext) {
394
+ case ".js":
395
+ case ".mjs":
396
+ command = process.execPath;
397
+ args = ["--no-warnings", scriptPath];
398
+ break;
399
+ case ".sh":
400
+ command = "/bin/bash";
401
+ args = [scriptPath];
402
+ break;
403
+ default:
404
+ throw new Error(`Unsupported skill script type: ${ext}`);
405
+ }
406
+ const env = {};
407
+ for (const key of PROTECTED_SYSTEM_ENV_KEYS) {
408
+ if (process.env[key])
409
+ env[key] = process.env[key];
410
+ }
411
+ for (const [key, value] of Object.entries(credentials)) {
412
+ if (PROTECTED_SYSTEM_ENV_KEYS.has(key))
413
+ continue;
414
+ if (BLOCKED_CREDENTIAL_ENV_KEYS.has(key.toUpperCase())) {
415
+ log.warn(`blocked dangerous credential key "${key}" for skill execution`);
416
+ continue;
417
+ }
418
+ env[key] = value;
419
+ }
420
+ return new Promise((resolve, reject) => {
421
+ const child = spawn(command, args, {
422
+ cwd,
423
+ env,
424
+ stdio: ["pipe", "pipe", "pipe"],
425
+ timeout: timeoutMs,
426
+ });
427
+ let stdout = "";
428
+ let stderr = "";
429
+ let outputLimitExceeded = false;
430
+ child.stdout.on("data", (data) => {
431
+ if (outputLimitExceeded)
432
+ return;
433
+ stdout += data.toString();
434
+ if (stdout.length > MAX_SKILL_OUTPUT_BYTES) {
435
+ outputLimitExceeded = true;
436
+ try {
437
+ child.kill("SIGKILL");
438
+ }
439
+ catch { /* already dead */ }
440
+ }
441
+ });
442
+ child.stderr.on("data", (data) => {
443
+ if (outputLimitExceeded)
444
+ return;
445
+ stderr += data.toString();
446
+ if (stderr.length > MAX_SKILL_OUTPUT_BYTES) {
447
+ outputLimitExceeded = true;
448
+ try {
449
+ child.kill("SIGKILL");
450
+ }
451
+ catch { /* already dead */ }
452
+ }
453
+ });
454
+ child.on("close", (code) => {
455
+ if (outputLimitExceeded) {
456
+ reject(new Error(`Skill script output exceeded ${MAX_SKILL_OUTPUT_BYTES} bytes limit`));
457
+ }
458
+ else if (code !== 0) {
459
+ reject(new Error(`Skill script exited with code ${code}: ${stderr.slice(0, 1000)}`));
460
+ }
461
+ else {
462
+ resolve(stdout);
463
+ }
464
+ });
465
+ child.on("error", reject);
466
+ child.stdin.write(JSON.stringify(params));
467
+ child.stdin.end();
468
+ });
469
+ }
470
+ const IPC_RATE_LIMIT = 200;
471
+ const IPC_RATE_WINDOW_MS = 1000;
472
+ function createIpcRateLimiter() {
473
+ const counters = new Map();
474
+ return {
475
+ check(method) {
476
+ const now = Date.now();
477
+ let entry = counters.get(method);
478
+ if (!entry || now >= entry.resetAt) {
479
+ entry = { count: 0, resetAt: now + IPC_RATE_WINDOW_MS };
480
+ counters.set(method, entry);
481
+ }
482
+ entry.count++;
483
+ if (entry.count > IPC_RATE_LIMIT) {
484
+ throw new Error(`IPC rate limit exceeded for "${method}": max ${IPC_RATE_LIMIT} calls per ${IPC_RATE_WINDOW_MS}ms`);
485
+ }
486
+ },
487
+ };
157
488
  }
158
489
  //# sourceMappingURL=runner.js.map