@farazirfan/costar-server-executor 1.7.37 → 1.7.39

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 (253) hide show
  1. package/dist/agent/agent.d.ts +90 -0
  2. package/dist/agent/agent.d.ts.map +1 -1
  3. package/dist/agent/agent.js +606 -0
  4. package/dist/agent/agent.js.map +1 -1
  5. package/dist/agent/pi-embedded-runner/run.d.ts.map +1 -1
  6. package/dist/agent/pi-embedded-runner/run.js +2 -1
  7. package/dist/agent/pi-embedded-runner/run.js.map +1 -1
  8. package/dist/agent/pi-embedded-runner/system-prompt.d.ts.map +1 -1
  9. package/dist/agent/pi-embedded-runner/system-prompt.js +16 -37
  10. package/dist/agent/pi-embedded-runner/system-prompt.js.map +1 -1
  11. package/dist/agent/pi-embedded-runner/tools.d.ts +4 -1
  12. package/dist/agent/pi-embedded-runner/tools.d.ts.map +1 -1
  13. package/dist/agent/pi-embedded-runner/tools.js +3 -1
  14. package/dist/agent/pi-embedded-runner/tools.js.map +1 -1
  15. package/dist/agent/pi-embedded-runner/types.d.ts +4 -0
  16. package/dist/agent/pi-embedded-runner/types.d.ts.map +1 -1
  17. package/dist/cli/env-loader.d.ts.map +1 -1
  18. package/dist/cli/env-loader.js +1 -0
  19. package/dist/cli/env-loader.js.map +1 -1
  20. package/dist/cli/setup.js +2 -2
  21. package/dist/cli/setup.js.map +1 -1
  22. package/dist/cron/normalize.d.ts +31 -0
  23. package/dist/cron/normalize.d.ts.map +1 -0
  24. package/dist/cron/normalize.js +211 -0
  25. package/dist/cron/normalize.js.map +1 -0
  26. package/dist/cron/scheduler.d.ts +33 -3
  27. package/dist/cron/scheduler.d.ts.map +1 -1
  28. package/dist/cron/scheduler.js +253 -48
  29. package/dist/cron/scheduler.js.map +1 -1
  30. package/dist/heartbeat/runner.d.ts +27 -12
  31. package/dist/heartbeat/runner.d.ts.map +1 -1
  32. package/dist/heartbeat/runner.js +82 -104
  33. package/dist/heartbeat/runner.js.map +1 -1
  34. package/dist/infra/heartbeat-events-filter.d.ts +29 -0
  35. package/dist/infra/heartbeat-events-filter.d.ts.map +1 -0
  36. package/dist/infra/heartbeat-events-filter.js +80 -0
  37. package/dist/infra/heartbeat-events-filter.js.map +1 -0
  38. package/dist/infra/index.d.ts +9 -0
  39. package/dist/infra/index.d.ts.map +1 -0
  40. package/dist/infra/index.js +9 -0
  41. package/dist/infra/index.js.map +1 -0
  42. package/dist/infra/system-events.d.ts +58 -2
  43. package/dist/infra/system-events.d.ts.map +1 -1
  44. package/dist/infra/system-events.js +80 -14
  45. package/dist/infra/system-events.js.map +1 -1
  46. package/dist/server.d.ts.map +1 -1
  47. package/dist/server.js +6 -1
  48. package/dist/server.js.map +1 -1
  49. package/dist/services/platform-keys.d.ts +19 -0
  50. package/dist/services/platform-keys.d.ts.map +1 -0
  51. package/dist/services/platform-keys.js +74 -0
  52. package/dist/services/platform-keys.js.map +1 -0
  53. package/dist/subagent/registry.d.ts +96 -0
  54. package/dist/subagent/registry.d.ts.map +1 -0
  55. package/dist/subagent/registry.js +180 -0
  56. package/dist/subagent/registry.js.map +1 -0
  57. package/dist/tools/complete-turn.d.ts +2 -2
  58. package/dist/tools/complete-turn.js +10 -10
  59. package/dist/tools/complete-turn.js.map +1 -1
  60. package/dist/tools/contacts.d.ts +13 -0
  61. package/dist/tools/contacts.d.ts.map +1 -0
  62. package/dist/tools/contacts.js +80 -0
  63. package/dist/tools/contacts.js.map +1 -0
  64. package/dist/tools/cron.d.ts +17 -2
  65. package/dist/tools/cron.d.ts.map +1 -1
  66. package/dist/tools/cron.js +117 -35
  67. package/dist/tools/cron.js.map +1 -1
  68. package/dist/tools/google-maps.d.ts +6 -6
  69. package/dist/tools/google-maps.d.ts.map +1 -1
  70. package/dist/tools/google-maps.js +207 -262
  71. package/dist/tools/google-maps.js.map +1 -1
  72. package/dist/tools/index.d.ts +17 -7
  73. package/dist/tools/index.d.ts.map +1 -1
  74. package/dist/tools/index.js +40 -9
  75. package/dist/tools/index.js.map +1 -1
  76. package/dist/tools/phone-call.d.ts +11 -0
  77. package/dist/tools/phone-call.d.ts.map +1 -0
  78. package/dist/tools/phone-call.js +151 -0
  79. package/dist/tools/phone-call.js.map +1 -0
  80. package/dist/tools/sessions-spawn.d.ts +33 -0
  81. package/dist/tools/sessions-spawn.d.ts.map +1 -0
  82. package/dist/tools/sessions-spawn.js +164 -0
  83. package/dist/tools/sessions-spawn.js.map +1 -0
  84. package/dist/tools/spotify.d.ts +12 -0
  85. package/dist/tools/spotify.d.ts.map +1 -0
  86. package/dist/tools/spotify.js +251 -0
  87. package/dist/tools/spotify.js.map +1 -0
  88. package/dist/tools/subagents.d.ts +23 -0
  89. package/dist/tools/subagents.d.ts.map +1 -0
  90. package/dist/tools/subagents.js +209 -0
  91. package/dist/tools/subagents.js.map +1 -0
  92. package/dist/tools/whatsapp.d.ts +13 -0
  93. package/dist/tools/whatsapp.d.ts.map +1 -0
  94. package/dist/tools/whatsapp.js +215 -0
  95. package/dist/tools/whatsapp.js.map +1 -0
  96. package/dist/tools/youtube.d.ts +12 -0
  97. package/dist/tools/youtube.d.ts.map +1 -0
  98. package/dist/tools/youtube.js +218 -0
  99. package/dist/tools/youtube.js.map +1 -0
  100. package/dist/utils/asterizk-auth.d.ts +43 -0
  101. package/dist/utils/asterizk-auth.d.ts.map +1 -0
  102. package/dist/utils/asterizk-auth.js +125 -0
  103. package/dist/utils/asterizk-auth.js.map +1 -0
  104. package/dist/web-server.d.ts.map +1 -1
  105. package/dist/web-server.js +132 -0
  106. package/dist/web-server.js.map +1 -1
  107. package/dist/workspace/index.d.ts +3 -4
  108. package/dist/workspace/index.d.ts.map +1 -1
  109. package/dist/workspace/index.js +3 -4
  110. package/dist/workspace/index.js.map +1 -1
  111. package/dist/workspace/templates.d.ts +8 -7
  112. package/dist/workspace/templates.d.ts.map +1 -1
  113. package/dist/workspace/templates.js +18 -127
  114. package/dist/workspace/templates.js.map +1 -1
  115. package/dist/workspace/workspace.d.ts +2 -4
  116. package/dist/workspace/workspace.d.ts.map +1 -1
  117. package/dist/workspace/workspace.js +7 -16
  118. package/dist/workspace/workspace.js.map +1 -1
  119. package/package.json +1 -1
  120. package/public/index.html +231 -0
  121. package/skills/docx/SKILL.md +468 -0
  122. package/skills/docx/scripts/__init__.py +1 -0
  123. package/skills/docx/scripts/accept_changes.py +181 -0
  124. package/skills/docx/scripts/comment.py +347 -0
  125. package/skills/docx/scripts/helpers/__init__.py +0 -0
  126. package/skills/docx/scripts/helpers/merge_runs.py +231 -0
  127. package/skills/docx/scripts/helpers/simplify_redlines.py +240 -0
  128. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  129. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  130. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  131. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  132. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  133. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  134. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  135. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  136. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  137. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  138. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  139. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  140. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  141. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  142. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  143. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  144. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  145. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  146. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  147. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  148. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  149. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  150. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  151. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  152. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  153. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  154. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  155. package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  156. package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  157. package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  158. package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  159. package/skills/docx/scripts/ooxml/schemas/mce/mc.xsd +75 -0
  160. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  161. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  162. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  163. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  164. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  165. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  166. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  167. package/skills/docx/scripts/ooxml/scripts/pack.py +159 -0
  168. package/skills/docx/scripts/ooxml/scripts/unpack.py +29 -0
  169. package/skills/docx/scripts/ooxml/scripts/validate.py +106 -0
  170. package/skills/docx/scripts/ooxml/scripts/validation/__init__.py +15 -0
  171. package/skills/docx/scripts/ooxml/scripts/validation/base.py +1023 -0
  172. package/skills/docx/scripts/ooxml/scripts/validation/docx.py +519 -0
  173. package/skills/docx/scripts/ooxml/scripts/validation/pptx.py +315 -0
  174. package/skills/docx/scripts/ooxml/scripts/validation/redlining.py +284 -0
  175. package/skills/docx/scripts/pack.py +166 -0
  176. package/skills/docx/scripts/templates/comments.xml +3 -0
  177. package/skills/docx/scripts/templates/commentsExtended.xml +3 -0
  178. package/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
  179. package/skills/docx/scripts/templates/commentsIds.xml +3 -0
  180. package/skills/docx/scripts/templates/people.xml +3 -0
  181. package/skills/docx/scripts/unpack.py +134 -0
  182. package/skills/longform-video-generation/SKILL.md +298 -0
  183. package/skills/longform-video-generation/references/advanced_techniques.md +474 -0
  184. package/skills/longform-video-generation/references/google_api_guide.md +288 -0
  185. package/skills/longform-video-generation/scripts/video_generator.py +579 -0
  186. package/skills/pdf/FORMS.md +305 -0
  187. package/skills/pdf/REFERENCE.md +612 -0
  188. package/skills/pdf/SKILL.md +293 -0
  189. package/skills/pdf/scripts/check_bounding_boxes.py +70 -0
  190. package/skills/pdf/scripts/check_fillable_fields.py +12 -0
  191. package/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
  192. package/skills/pdf/scripts/create_validation_image.py +41 -0
  193. package/skills/pdf/scripts/extract_form_field_info.py +152 -0
  194. package/skills/pdf/scripts/extract_form_structure.py +124 -0
  195. package/skills/pdf/scripts/fill_fillable_fields.py +116 -0
  196. package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +136 -0
  197. package/skills/pptx/SKILL.md +171 -0
  198. package/skills/pptx/editing.md +205 -0
  199. package/skills/pptx/pptxgenjs.md +377 -0
  200. package/skills/pptx/scripts/add_slide.py +225 -0
  201. package/skills/pptx/scripts/clean.py +309 -0
  202. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  203. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  204. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  205. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  206. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  207. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  208. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  209. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  210. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  211. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  212. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  213. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  214. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  215. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  216. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  217. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  218. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  219. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  220. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  221. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  222. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  223. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  224. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  225. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  226. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  227. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  228. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  229. package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  230. package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  231. package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  232. package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  233. package/skills/pptx/scripts/ooxml/schemas/mce/mc.xsd +75 -0
  234. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  235. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  236. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  237. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  238. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  239. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  240. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  241. package/skills/pptx/scripts/ooxml/scripts/pack.py +159 -0
  242. package/skills/pptx/scripts/ooxml/scripts/unpack.py +29 -0
  243. package/skills/pptx/scripts/ooxml/scripts/validate.py +106 -0
  244. package/skills/pptx/scripts/ooxml/scripts/validation/__init__.py +15 -0
  245. package/skills/pptx/scripts/ooxml/scripts/validation/base.py +1023 -0
  246. package/skills/pptx/scripts/ooxml/scripts/validation/docx.py +519 -0
  247. package/skills/pptx/scripts/ooxml/scripts/validation/pptx.py +315 -0
  248. package/skills/pptx/scripts/ooxml/scripts/validation/redlining.py +284 -0
  249. package/skills/pptx/scripts/pack.py +168 -0
  250. package/skills/pptx/scripts/thumbnail.py +318 -0
  251. package/skills/pptx/scripts/unpack.py +86 -0
  252. package/skills/xlsx/SKILL.md +291 -0
  253. package/skills/xlsx/recalc.py +247 -0
@@ -6,6 +6,7 @@ import { runEmbeddedPiAgent } from "./pi-embedded-runner/run.js";
6
6
  import { runWithModelFallback } from "./model-fallback.js";
7
7
  import { ensureAgentWorkspace, } from "../workspace/index.js";
8
8
  import { compactEmbeddedPiSessionDirect } from "./pi-embedded-runner/compact.js";
9
+ import { SessionManager } from "@mariozechner/pi-coding-agent";
9
10
  import path from "node:path";
10
11
  import os from "node:os";
11
12
  import fs from "node:fs";
@@ -274,6 +275,13 @@ export class Agent {
274
275
  // Run embedded pi agent (core OpenClaw function)
275
276
  // cronDeps is passed through so tools are created inside the runner
276
277
  // with full context (OpenClaw pattern: no pre-built tool arrays)
278
+ // subagentDeps provides sessions_spawn + subagents tools to main session
279
+ const subagentDeps = {
280
+ userId: this.userId,
281
+ agent: this,
282
+ maxSpawnDepth: 1,
283
+ maxChildren: 5,
284
+ };
277
285
  return await runEmbeddedPiAgent({
278
286
  sessionId,
279
287
  sessionFile,
@@ -290,6 +298,7 @@ export class Agent {
290
298
  runId: sessionId,
291
299
  skillEntries,
292
300
  cronDeps: this.cronDeps,
301
+ subagentDeps,
293
302
  onPartialReply: options.onPartialReply,
294
303
  onToolResult: options.onPartialReply,
295
304
  onToolStart: options.onToolStart,
@@ -564,6 +573,153 @@ export class Agent {
564
573
  this._busy = false;
565
574
  }
566
575
  }
576
+ // ═══════════════════════════════════════════════════════════════════
577
+ // SUB-AGENT SESSION SUPPORT
578
+ // Pattern: OpenClaw's subagent-spawn.ts — runs child worker in isolated
579
+ // session. Unlike runIsolatedAgentTurn, this does NOT set _busy, uses
580
+ // unique session per spawn, and strips spawn/cron/message tools.
581
+ // ═══════════════════════════════════════════════════════════════════
582
+ /**
583
+ * Run a sub-agent in an isolated session (fire-and-forget pattern).
584
+ * Similar to runIsolatedAgentTurn but designed for concurrent spawning:
585
+ *
586
+ * Key differences from runIsolatedAgentTurn:
587
+ * 1. Does NOT set this._busy — multiple sub-agents can run concurrently
588
+ * 2. Unique session per spawn — no session reuse/freshness logic
589
+ * 3. No cronDeps — sub-agents can't create cron jobs
590
+ * 4. No subagentDeps — sub-agents can't spawn (depth limit enforcement)
591
+ * 5. Passes abortSignal for kill support
592
+ * 6. deleteAfterRun defaults to true (one-shot tasks)
593
+ *
594
+ * Pattern: OpenClaw's spawnSubagentDirect + runCronIsolatedAgentTurn
595
+ */
596
+ async runSubagentTurn(prompt, options = {}) {
597
+ // NOTE: Does NOT set this._busy — sub-agents run concurrently
598
+ // Unique session per sub-agent (no reuse, no store file)
599
+ const subagentSessionId = `${this.userId}-subagent-${crypto.randomUUID()}`;
600
+ const subagentSessionFile = path.join(this.sessionDir, `${subagentSessionId}.json`);
601
+ const deleteAfterRun = options.deleteAfterRun !== false; // Default: true
602
+ const timeoutMs = options.timeoutMs ?? 600_000;
603
+ try {
604
+ console.log(`\n${"─".repeat(60)}`);
605
+ console.log(`[AGENT] Starting SUB-AGENT turn`);
606
+ console.log(`[AGENT] Sub-agent session: ${subagentSessionId}`);
607
+ console.log(`[AGENT] Sub-agent file: ${subagentSessionFile}`);
608
+ console.log(`[AGENT] Provider/Model: ${this.getProviderModel()}`);
609
+ if (options.sessionLabel) {
610
+ console.log(`[AGENT] Label: ${options.sessionLabel}`);
611
+ }
612
+ // Ensure workspace files exist (same as regular turn)
613
+ if (!this.workspaceSynced) {
614
+ await this.syncWorkspaceFromStorage();
615
+ }
616
+ const [provider, model] = this.parseProviderModel();
617
+ const skillEntries = this.buildSkillEntries();
618
+ // Build special system prompt for sub-agent sessions
619
+ const subagentSystemPrompt = buildIsolatedSessionSystemPrompt({
620
+ label: options.sessionLabel,
621
+ });
622
+ // Run with model fallback using the SUB-AGENT session file
623
+ const fallbackResult = await runWithModelFallback({
624
+ cfg: this.config,
625
+ provider,
626
+ model,
627
+ agentDir: process.cwd(),
628
+ run: async (fallbackProvider, fallbackModel) => {
629
+ console.log(`[AGENT] Sub-agent run with ${fallbackProvider}/${fallbackModel}`);
630
+ return await runEmbeddedPiAgent({
631
+ sessionId: subagentSessionId,
632
+ sessionFile: subagentSessionFile,
633
+ workspaceDir: this.workspaceDir,
634
+ agentDir: process.cwd(),
635
+ config: this.config,
636
+ prompt,
637
+ provider: fallbackProvider,
638
+ model: fallbackModel,
639
+ thinkLevel: "off",
640
+ verboseLevel: "normal",
641
+ reasoningLevel: "off",
642
+ timeoutMs,
643
+ runId: subagentSessionId,
644
+ skillEntries,
645
+ // NOTE: No cronDeps — sub-agents can't create cron jobs
646
+ // NOTE: No subagentDeps — sub-agents can't spawn (tool stripping)
647
+ // Lane isolation
648
+ lane: "subagent",
649
+ // Disable message tool — output goes through announce pipeline
650
+ disableMessageTool: true,
651
+ // Special system prompt for sub-agent sessions
652
+ extraSystemPrompt: subagentSystemPrompt,
653
+ // Abort signal for kill support
654
+ abortSignal: options.abortSignal,
655
+ });
656
+ },
657
+ });
658
+ const result = fallbackResult.result;
659
+ console.log(`[AGENT] Sub-agent run completed with ${fallbackResult.provider}/${fallbackResult.model}`);
660
+ console.log(`[AGENT] Duration: ${result.meta.durationMs}ms`);
661
+ console.log(`[AGENT] Tokens: ${result.meta.agentMeta.usage?.inputTokens}/${result.meta.agentMeta.usage?.outputTokens}`);
662
+ if (result.didSendViaMessagingTool) {
663
+ console.log(`[AGENT] Sub-agent sent via messaging tool — will skip announce to avoid duplicate`);
664
+ }
665
+ // Extract response text
666
+ const payloads = result.payloads ?? [];
667
+ const outputText = pickLastNonEmptyText(payloads) ?? "";
668
+ return {
669
+ success: !result.meta.error,
670
+ response: outputText,
671
+ outputText,
672
+ toolCalls: 0,
673
+ stopReason: result.meta.stopReason,
674
+ turns: 1,
675
+ inputTokens: result.meta.agentMeta.usage?.inputTokens ?? 0,
676
+ outputTokens: result.meta.agentMeta.usage?.outputTokens ?? 0,
677
+ error: result.meta.error?.message,
678
+ didSendViaMessagingTool: result.didSendViaMessagingTool ?? false,
679
+ };
680
+ }
681
+ catch (error) {
682
+ const message = error instanceof Error ? error.message : String(error);
683
+ console.error(`[AGENT] Sub-agent turn error:`, message);
684
+ return {
685
+ success: false,
686
+ error: message,
687
+ turns: 1,
688
+ didSendViaMessagingTool: false,
689
+ };
690
+ }
691
+ finally {
692
+ // Session cleanup — default: DELETE (sub-agent sessions are one-shot)
693
+ if (deleteAfterRun) {
694
+ try {
695
+ if (fs.existsSync(subagentSessionFile)) {
696
+ fs.unlinkSync(subagentSessionFile);
697
+ console.log(`[AGENT] Cleaned up sub-agent session: ${subagentSessionFile}`);
698
+ }
699
+ }
700
+ catch (err) {
701
+ console.warn(`[AGENT] Failed to clean up sub-agent session:`, err);
702
+ }
703
+ // Clean up backup files
704
+ try {
705
+ const dir = path.dirname(subagentSessionFile);
706
+ const base = path.basename(subagentSessionFile);
707
+ const files = fs.readdirSync(dir);
708
+ for (const f of files) {
709
+ if (f.startsWith(base) && f.endsWith(".bak")) {
710
+ fs.unlinkSync(path.join(dir, f));
711
+ }
712
+ }
713
+ }
714
+ catch {
715
+ // best-effort
716
+ }
717
+ }
718
+ else {
719
+ console.log(`[AGENT] Kept sub-agent session file: ${subagentSessionFile}`);
720
+ }
721
+ }
722
+ }
567
723
  /**
568
724
  * Inject an isolated run's result into the MAIN session as a compact
569
725
  * system message. The main agent sees one summary message and decides
@@ -608,6 +764,456 @@ export class Agent {
608
764
  // Execute in the MAIN session — agent sees one compact message
609
765
  return await this.executeTurn(triggerMessage, { maxTurns: 25 });
610
766
  }
767
+ // ═══════════════════════════════════════════════════════════════════
768
+ // CLIENT SESSION API
769
+ // Shared main session between client (Flutter) and server (TypeScript).
770
+ // Client reads/writes generic {role, content} messages; server translates
771
+ // to/from pi-agent JSONL session format. Follows OpenClaw isolated session pattern.
772
+ // ═══════════════════════════════════════════════════════════════════
773
+ _clientTurnActive = false;
774
+ _clientLockTimeout = null;
775
+ /**
776
+ * Acquire the main session for a client turn.
777
+ * Sets _busy = true to prevent server merges (injectIsolatedResult)
778
+ * while client is running its agent loop.
779
+ * Auto-expires after 5 min as safety against client crashes.
780
+ */
781
+ acquireForClient() {
782
+ if (this._busy)
783
+ return false;
784
+ this._busy = true;
785
+ this._clientTurnActive = true;
786
+ // Auto-expire after 5 min (safety: client crash/disconnect)
787
+ this._clientLockTimeout = setTimeout(() => {
788
+ if (this._clientTurnActive) {
789
+ console.warn("[AGENT] Client lock auto-expired after 5 min");
790
+ this._busy = false;
791
+ this._clientTurnActive = false;
792
+ }
793
+ }, 300_000);
794
+ console.log("[AGENT] Client acquired main session");
795
+ return true;
796
+ }
797
+ /**
798
+ * Release the main session after client turn completes.
799
+ */
800
+ releaseClient() {
801
+ this._clientTurnActive = false;
802
+ this._busy = false;
803
+ if (this._clientLockTimeout) {
804
+ clearTimeout(this._clientLockTimeout);
805
+ this._clientLockTimeout = null;
806
+ }
807
+ console.log("[AGENT] Client released main session");
808
+ }
809
+ /**
810
+ * Check if client currently holds the session.
811
+ */
812
+ isClientTurnActive() {
813
+ return this._clientTurnActive;
814
+ }
815
+ /**
816
+ * Read the main session as generic {role, content} messages.
817
+ * Converts pi-agent JSONL → generic message format.
818
+ * Server tool calls are summarized as text (client doesn't have server tool schemas).
819
+ */
820
+ getSessionMessages(tokenLimit = 15000) {
821
+ if (!fs.existsSync(this.sessionFile)) {
822
+ return { messages: [], tokenCount: 0 };
823
+ }
824
+ try {
825
+ // Open session and build context (resolves compaction, tree traversal)
826
+ const manager = SessionManager.open(this.sessionFile);
827
+ const context = manager.buildSessionContext();
828
+ // Convert pi-agent AgentMessages → generic messages
829
+ const genericMessages = this.convertToGenericMessages(context.messages);
830
+ // Trim to tokenLimit from the end (keep most recent)
831
+ const trimmed = this.trimToTokenLimit(genericMessages, tokenLimit);
832
+ // Estimate total token count
833
+ const tokenCount = this.estimateMessageTokens(trimmed);
834
+ return { messages: trimmed, tokenCount };
835
+ }
836
+ catch (err) {
837
+ console.error("[AGENT] Error reading session messages:", err);
838
+ return { messages: [], tokenCount: 0 };
839
+ }
840
+ }
841
+ /**
842
+ * Append generic {role, content} messages to the main session as JSONL entries.
843
+ * Converts generic message format → pi-agent JSONL.
844
+ * Optionally triggers compaction if session grows too large.
845
+ */
846
+ async appendClientMessages(messages) {
847
+ try {
848
+ // Open or create session
849
+ let manager;
850
+ if (fs.existsSync(this.sessionFile)) {
851
+ manager = SessionManager.open(this.sessionFile);
852
+ }
853
+ else {
854
+ manager = SessionManager.create(this.workspaceDir, this.sessionDir);
855
+ // Rename to our expected session file if different
856
+ const createdFile = manager.getSessionFile() || "";
857
+ if (createdFile && createdFile !== this.sessionFile) {
858
+ // Move it to expected location
859
+ fs.renameSync(createdFile, this.sessionFile);
860
+ manager = SessionManager.open(this.sessionFile);
861
+ }
862
+ }
863
+ // Convert generic messages → pi-agent messages and append
864
+ for (const msg of messages) {
865
+ const piMessage = this.convertFromGenericMessage(msg);
866
+ if (piMessage) {
867
+ manager.appendMessage(piMessage);
868
+ }
869
+ }
870
+ // Check if compaction is needed (estimate tokens)
871
+ const context = manager.buildSessionContext();
872
+ let totalTokens = 0;
873
+ for (const m of context.messages) {
874
+ totalTokens += this.estimatePiMessageTokens(m);
875
+ }
876
+ let compacted = false;
877
+ // Trigger compaction if over 150k tokens (leave room for response)
878
+ if (totalTokens > 150_000) {
879
+ console.log(`[AGENT] Session at ${totalTokens} tokens after client append — triggering compaction`);
880
+ try {
881
+ const result = await this.compactSession();
882
+ compacted = result.compacted === true;
883
+ }
884
+ catch (err) {
885
+ console.error("[AGENT] Post-append compaction failed:", err);
886
+ }
887
+ }
888
+ return { compacted };
889
+ }
890
+ catch (err) {
891
+ console.error("[AGENT] Error appending client messages:", err);
892
+ return { compacted: false };
893
+ }
894
+ }
895
+ /**
896
+ * Mid-turn compaction for client overflow recovery.
897
+ * Takes in-memory generic messages, runs pi-agent compaction,
898
+ * returns compacted generic messages sized to fit targetContextTokens.
899
+ * Does NOT write to session file (client is still mid-turn).
900
+ * Uses server's configured model for summarization (model-agnostic).
901
+ */
902
+ async compactClientContext(messages, targetContextTokens) {
903
+ try {
904
+ // Convert generic messages → pi-agent messages
905
+ const piMessages = [];
906
+ for (const msg of messages) {
907
+ const piMsg = this.convertFromGenericMessage(msg);
908
+ if (piMsg)
909
+ piMessages.push(piMsg);
910
+ }
911
+ // Create a temporary in-memory session with these messages
912
+ const tempSessionFile = path.join(this.sessionDir, `${this.userId}-compact-temp-${crypto.randomUUID()}.json`);
913
+ try {
914
+ // Create temp session and populate it
915
+ const tempManager = SessionManager.create(this.workspaceDir, this.sessionDir);
916
+ const tempCreatedFile = tempManager.getSessionFile() || "";
917
+ // Append all messages to temp session
918
+ for (const piMsg of piMessages) {
919
+ tempManager.appendMessage(piMsg);
920
+ }
921
+ // Rename to expected path
922
+ if (tempCreatedFile && tempCreatedFile !== tempSessionFile) {
923
+ fs.renameSync(tempCreatedFile, tempSessionFile);
924
+ }
925
+ // Run pi-agent compaction on the temp session
926
+ const [provider, model] = this.parseProviderModel();
927
+ const compactParams = {
928
+ sessionId: `compact-temp-${Date.now()}`,
929
+ sessionFile: tempSessionFile,
930
+ workspaceDir: this.workspaceDir,
931
+ agentDir: process.cwd(),
932
+ config: this.config,
933
+ provider,
934
+ model,
935
+ thinkLevel: "off",
936
+ customInstructions: `Summarize preserving all important context. Target: fit within ${targetContextTokens} tokens.`,
937
+ };
938
+ await compactEmbeddedPiSessionDirect(compactParams);
939
+ // Read back the compacted session
940
+ const compactedManager = SessionManager.open(tempSessionFile);
941
+ const compactedContext = compactedManager.buildSessionContext();
942
+ // Convert back to generic messages
943
+ const compactedMessages = this.convertToGenericMessages(compactedContext.messages);
944
+ const tokenCount = this.estimateMessageTokens(compactedMessages);
945
+ return { messages: compactedMessages, tokenCount };
946
+ }
947
+ finally {
948
+ // Cleanup temp file
949
+ try {
950
+ if (fs.existsSync(tempSessionFile))
951
+ fs.unlinkSync(tempSessionFile);
952
+ }
953
+ catch {
954
+ // best-effort cleanup
955
+ }
956
+ }
957
+ }
958
+ catch (err) {
959
+ console.error("[AGENT] Client context compaction failed:", err);
960
+ // Return original messages if compaction fails
961
+ return { messages, tokenCount: this.estimateMessageTokens(messages) };
962
+ }
963
+ }
964
+ // ── Generic message ↔ Pi-agent message conversion helpers ──
965
+ /**
966
+ * Convert pi-agent AgentMessages → generic {role, content} messages.
967
+ * Server tool calls are summarized as text to avoid exposing
968
+ * server-only tool schemas to the client.
969
+ */
970
+ convertToGenericMessages(piMessages) {
971
+ const result = [];
972
+ // Known client tool names — tools that exist on the Flutter client.
973
+ // Server-only tools (browser, exec, pty, etc.) get summarized as text.
974
+ // We detect server tools by checking if the message has assistant metadata
975
+ // from the server's pi-agent runs (provider/model fields).
976
+ for (let i = 0; i < piMessages.length; i++) {
977
+ const msg = piMessages[i];
978
+ if (msg.role === "user") {
979
+ // User messages pass through directly
980
+ if (typeof msg.content === "string") {
981
+ result.push({ role: "user", content: msg.content });
982
+ }
983
+ else if (Array.isArray(msg.content)) {
984
+ // Content blocks array
985
+ const blocks = msg.content.map((block) => {
986
+ if (block.type === "text")
987
+ return { type: "text", text: block.text };
988
+ if (block.type === "image")
989
+ return { type: "image", source: { type: "base64", media_type: block.mimeType, data: block.data } };
990
+ return block;
991
+ });
992
+ result.push({ role: "user", content: blocks });
993
+ }
994
+ }
995
+ else if (msg.role === "assistant") {
996
+ // Assistant messages: convert content blocks
997
+ if (!Array.isArray(msg.content))
998
+ continue;
999
+ const blocks = [];
1000
+ for (const block of msg.content) {
1001
+ if (block.type === "text" && block.text) {
1002
+ blocks.push({ type: "text", text: block.text });
1003
+ }
1004
+ else if (block.type === "toolCall") {
1005
+ blocks.push({
1006
+ type: "tool_use",
1007
+ id: block.id,
1008
+ name: block.name,
1009
+ input: block.arguments || {},
1010
+ });
1011
+ }
1012
+ else if (block.type === "thinking" && block.thinking) {
1013
+ // Include thinking blocks for models that support it
1014
+ blocks.push({ type: "thinking", thinking: block.thinking });
1015
+ }
1016
+ }
1017
+ if (blocks.length > 0) {
1018
+ result.push({ role: "assistant", content: blocks });
1019
+ }
1020
+ }
1021
+ else if (msg.role === "toolResult") {
1022
+ // Tool results → Claude API tool_result blocks within user message
1023
+ const textContent = Array.isArray(msg.content)
1024
+ ? msg.content.map((c) => c.type === "text" ? c.text : "").join("")
1025
+ : String(msg.content || "");
1026
+ result.push({
1027
+ role: "user",
1028
+ content: [
1029
+ {
1030
+ type: "tool_result",
1031
+ tool_use_id: msg.toolCallId,
1032
+ content: textContent,
1033
+ is_error: msg.isError || false,
1034
+ },
1035
+ ],
1036
+ });
1037
+ }
1038
+ else if (msg.role === "compactionSummary") {
1039
+ // Compaction summaries → user message with context prefix
1040
+ result.push({
1041
+ role: "user",
1042
+ content: `[Previous conversation summary]\n${msg.summary}`,
1043
+ });
1044
+ }
1045
+ else if (msg.role === "branchSummary") {
1046
+ // Branch summaries → user message
1047
+ result.push({
1048
+ role: "user",
1049
+ content: `[Branch summary]\n${msg.summary}`,
1050
+ });
1051
+ }
1052
+ else if (msg.role === "custom" && msg.display !== false) {
1053
+ // Custom messages that should be displayed
1054
+ const content = typeof msg.content === "string"
1055
+ ? msg.content
1056
+ : Array.isArray(msg.content)
1057
+ ? msg.content.map((c) => c.text || "").join("")
1058
+ : "";
1059
+ if (content) {
1060
+ result.push({ role: "user", content });
1061
+ }
1062
+ }
1063
+ // Skip bashExecution and other non-standard roles
1064
+ }
1065
+ return result;
1066
+ }
1067
+ /**
1068
+ * Convert a single generic {role, content} message → pi-agent message format.
1069
+ * Used when appending client turn messages to the session.
1070
+ */
1071
+ convertFromGenericMessage(msg) {
1072
+ const role = msg.role;
1073
+ const content = msg.content;
1074
+ const timestamp = Date.now();
1075
+ if (role === "user") {
1076
+ if (typeof content === "string") {
1077
+ return { role: "user", content, timestamp };
1078
+ }
1079
+ // Check if it's a tool_result array
1080
+ if (Array.isArray(content)) {
1081
+ const firstBlock = content[0];
1082
+ if (firstBlock?.type === "tool_result") {
1083
+ // This is actually a tool result message
1084
+ return {
1085
+ role: "toolResult",
1086
+ toolCallId: firstBlock.tool_use_id,
1087
+ toolName: "", // Not available in generic message format
1088
+ content: [{ type: "text", text: firstBlock.content || "" }],
1089
+ isError: firstBlock.is_error || false,
1090
+ timestamp,
1091
+ };
1092
+ }
1093
+ // Regular user message with content blocks
1094
+ const blocks = content.map((block) => {
1095
+ if (block.type === "text")
1096
+ return { type: "text", text: block.text };
1097
+ if (block.type === "image") {
1098
+ return {
1099
+ type: "image",
1100
+ data: block.source?.data || "",
1101
+ mimeType: block.source?.media_type || "image/png",
1102
+ };
1103
+ }
1104
+ return block;
1105
+ });
1106
+ return { role: "user", content: blocks, timestamp };
1107
+ }
1108
+ return null;
1109
+ }
1110
+ if (role === "assistant") {
1111
+ if (!Array.isArray(content)) {
1112
+ return { role: "assistant", content: [{ type: "text", text: String(content || "") }], timestamp };
1113
+ }
1114
+ // Convert generic content blocks → pi-agent content blocks
1115
+ const blocks = content.map((block) => {
1116
+ if (block.type === "text")
1117
+ return { type: "text", text: block.text };
1118
+ if (block.type === "tool_use") {
1119
+ return {
1120
+ type: "toolCall",
1121
+ id: block.id,
1122
+ name: block.name,
1123
+ arguments: block.input || {},
1124
+ };
1125
+ }
1126
+ if (block.type === "thinking")
1127
+ return { type: "thinking", thinking: block.thinking };
1128
+ return block;
1129
+ });
1130
+ return {
1131
+ role: "assistant",
1132
+ content: blocks,
1133
+ timestamp,
1134
+ // Minimal metadata — server doesn't need full provider info for client turns
1135
+ stopReason: "stop",
1136
+ };
1137
+ }
1138
+ return null;
1139
+ }
1140
+ /**
1141
+ * Trim generic messages to fit within a token limit.
1142
+ * Keeps the most recent messages.
1143
+ */
1144
+ trimToTokenLimit(messages, tokenLimit) {
1145
+ if (messages.length === 0)
1146
+ return messages;
1147
+ // Estimate total tokens
1148
+ const total = this.estimateMessageTokens(messages);
1149
+ if (total <= tokenLimit)
1150
+ return messages;
1151
+ // Work backwards, accumulating tokens
1152
+ const result = [];
1153
+ let accumulated = 0;
1154
+ for (let i = messages.length - 1; i >= 0; i--) {
1155
+ const msgTokens = this.estimateMessageTokens([messages[i]]);
1156
+ if (accumulated + msgTokens > tokenLimit && result.length > 0)
1157
+ break;
1158
+ result.unshift(messages[i]);
1159
+ accumulated += msgTokens;
1160
+ }
1161
+ return result;
1162
+ }
1163
+ /**
1164
+ * Estimate token count for generic messages.
1165
+ * Uses chars/4 approximation (same as pi-agent).
1166
+ */
1167
+ estimateMessageTokens(messages) {
1168
+ let totalChars = 0;
1169
+ for (const msg of messages) {
1170
+ const m = msg;
1171
+ const content = m.content;
1172
+ if (typeof content === "string") {
1173
+ totalChars += content.length;
1174
+ }
1175
+ else if (Array.isArray(content)) {
1176
+ for (const block of content) {
1177
+ const b = block;
1178
+ if (b.text)
1179
+ totalChars += String(b.text).length;
1180
+ if (b.thinking)
1181
+ totalChars += String(b.thinking).length;
1182
+ if (b.content)
1183
+ totalChars += String(b.content).length; // tool_result content
1184
+ if (b.input)
1185
+ totalChars += JSON.stringify(b.input).length; // tool_use input
1186
+ if (b.name)
1187
+ totalChars += String(b.name).length;
1188
+ }
1189
+ }
1190
+ }
1191
+ return Math.ceil(totalChars / 4);
1192
+ }
1193
+ /**
1194
+ * Estimate token count for a pi-agent message.
1195
+ */
1196
+ estimatePiMessageTokens(msg) {
1197
+ let chars = 0;
1198
+ if (typeof msg.content === "string") {
1199
+ chars = msg.content.length;
1200
+ }
1201
+ else if (Array.isArray(msg.content)) {
1202
+ for (const block of msg.content) {
1203
+ if (block.text)
1204
+ chars += block.text.length;
1205
+ if (block.thinking)
1206
+ chars += block.thinking.length;
1207
+ if (block.arguments)
1208
+ chars += JSON.stringify(block.arguments).length;
1209
+ if (block.name)
1210
+ chars += block.name.length;
1211
+ }
1212
+ }
1213
+ if (msg.summary)
1214
+ chars += msg.summary.length;
1215
+ return Math.ceil(chars / 4);
1216
+ }
611
1217
  /**
612
1218
  * Get conversation history
613
1219
  */