@selesai/code 0.12.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (466) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/core/agent-session.d.ts +1 -1
  3. package/dist/core/agent-session.js +1 -1
  4. package/dist/core/sdk.d.ts +2 -2
  5. package/dist/core/sdk.js +1 -1
  6. package/dist/core/system-prompt.d.ts +1 -1
  7. package/dist/core/system-prompt.js +1 -1
  8. package/dist/core/usage-totals.d.ts +2 -0
  9. package/dist/core/usage-totals.js +10 -0
  10. package/dist/extensions/ascii.txt +9 -0
  11. package/dist/extensions/package.json +1 -2
  12. package/dist/extensions/pi-hermes-memory/CHANGELOG.md +371 -0
  13. package/dist/extensions/pi-hermes-memory/LICENSE +21 -0
  14. package/dist/extensions/pi-hermes-memory/README.md +643 -0
  15. package/dist/extensions/pi-hermes-memory/docs/0.1/TASKS.md +197 -0
  16. package/dist/extensions/pi-hermes-memory/docs/0.2/PLAN.md +290 -0
  17. package/dist/extensions/pi-hermes-memory/docs/0.2/TASKS.md +134 -0
  18. package/dist/extensions/pi-hermes-memory/docs/0.2/TEST-PLAN.md +216 -0
  19. package/dist/extensions/pi-hermes-memory/docs/0.3/PLAN.md +330 -0
  20. package/dist/extensions/pi-hermes-memory/docs/0.3/TASKS.md +125 -0
  21. package/dist/extensions/pi-hermes-memory/docs/0.4/PLAN.md +160 -0
  22. package/dist/extensions/pi-hermes-memory/docs/0.4/TASKS.md +113 -0
  23. package/dist/extensions/pi-hermes-memory/docs/0.6/CHANGELOG.md +57 -0
  24. package/dist/extensions/pi-hermes-memory/docs/0.6/PLAN.md +202 -0
  25. package/dist/extensions/pi-hermes-memory/docs/0.6/TASKS.md +144 -0
  26. package/dist/extensions/pi-hermes-memory/docs/0.7/PLAN.md +349 -0
  27. package/dist/extensions/pi-hermes-memory/docs/0.7/TASKS.md +110 -0
  28. package/dist/extensions/pi-hermes-memory/docs/PUBLISHING.md +149 -0
  29. package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +468 -0
  30. package/dist/extensions/pi-hermes-memory/docs/images/memory-architecture.svg +1 -0
  31. package/dist/extensions/pi-hermes-memory/docs/images/pi-logo.svg +22 -0
  32. package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.png +0 -0
  33. package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.svg +19 -0
  34. package/dist/extensions/pi-hermes-memory/docs/images/security-flow.svg +1 -0
  35. package/dist/extensions/pi-hermes-memory/docs/images/session-lifecycle.svg +1 -0
  36. package/dist/extensions/pi-hermes-memory/docs/images/source-architecture.svg +1 -0
  37. package/dist/extensions/pi-hermes-memory/docs/mermaid/memory-architecture.mmd +49 -0
  38. package/dist/extensions/pi-hermes-memory/docs/mermaid/security-flow.mmd +40 -0
  39. package/dist/extensions/pi-hermes-memory/docs/mermaid/session-lifecycle.mmd +56 -0
  40. package/dist/extensions/pi-hermes-memory/docs/mermaid/source-architecture.mmd +54 -0
  41. package/dist/extensions/pi-hermes-memory/package-lock.json +3799 -0
  42. package/dist/extensions/pi-hermes-memory/package.json +66 -0
  43. package/dist/extensions/pi-hermes-memory/scripts/check-min-sdk.mjs +99 -0
  44. package/dist/extensions/pi-hermes-memory/scripts/ensure-dev.mjs +57 -0
  45. package/dist/extensions/pi-hermes-memory/src/auto-consolidation-warning.ts +6 -0
  46. package/dist/extensions/pi-hermes-memory/src/config.ts +178 -0
  47. package/dist/extensions/pi-hermes-memory/src/constants.ts +424 -0
  48. package/dist/extensions/pi-hermes-memory/src/extension-root-migration.ts +639 -0
  49. package/dist/extensions/pi-hermes-memory/src/handlers/auto-consolidate.ts +401 -0
  50. package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +297 -0
  51. package/dist/extensions/pi-hermes-memory/src/handlers/child-process-watchdog.mjs +90 -0
  52. package/dist/extensions/pi-hermes-memory/src/handlers/correction-detector.ts +300 -0
  53. package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +86 -0
  54. package/dist/extensions/pi-hermes-memory/src/handlers/insights.ts +78 -0
  55. package/dist/extensions/pi-hermes-memory/src/handlers/interview.ts +37 -0
  56. package/dist/extensions/pi-hermes-memory/src/handlers/learn-memory.ts +186 -0
  57. package/dist/extensions/pi-hermes-memory/src/handlers/message-parts.ts +27 -0
  58. package/dist/extensions/pi-hermes-memory/src/handlers/pi-child-process.ts +485 -0
  59. package/dist/extensions/pi-hermes-memory/src/handlers/preview-context.ts +105 -0
  60. package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +506 -0
  61. package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +145 -0
  62. package/dist/extensions/pi-hermes-memory/src/handlers/session-flush.ts +151 -0
  63. package/dist/extensions/pi-hermes-memory/src/handlers/session-live-index.ts +94 -0
  64. package/dist/extensions/pi-hermes-memory/src/handlers/skills-command.ts +1334 -0
  65. package/dist/extensions/pi-hermes-memory/src/handlers/standing-pin.ts +111 -0
  66. package/dist/extensions/pi-hermes-memory/src/handlers/switch-project.ts +75 -0
  67. package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +266 -0
  68. package/dist/extensions/pi-hermes-memory/src/index.ts +378 -0
  69. package/dist/extensions/pi-hermes-memory/src/lifecycle-timing.ts +54 -0
  70. package/dist/extensions/pi-hermes-memory/src/paths.ts +61 -0
  71. package/dist/extensions/pi-hermes-memory/src/project-context.ts +13 -0
  72. package/dist/extensions/pi-hermes-memory/src/project-memory-migration.ts +93 -0
  73. package/dist/extensions/pi-hermes-memory/src/project.ts +157 -0
  74. package/dist/extensions/pi-hermes-memory/src/prompt-context.ts +55 -0
  75. package/dist/extensions/pi-hermes-memory/src/store/atomic-lock-coordinator.ts +375 -0
  76. package/dist/extensions/pi-hermes-memory/src/store/canonical-storage-path.ts +54 -0
  77. package/dist/extensions/pi-hermes-memory/src/store/content-scanner.ts +101 -0
  78. package/dist/extensions/pi-hermes-memory/src/store/db.ts +1180 -0
  79. package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +101 -0
  80. package/dist/extensions/pi-hermes-memory/src/store/markdown-mutation-lock.ts +38 -0
  81. package/dist/extensions/pi-hermes-memory/src/store/memory-lookup.ts +24 -0
  82. package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +1191 -0
  83. package/dist/extensions/pi-hermes-memory/src/store/recovery-maintenance.ts +66 -0
  84. package/dist/extensions/pi-hermes-memory/src/store/schema.ts +118 -0
  85. package/dist/extensions/pi-hermes-memory/src/store/session-anchor-search.ts +472 -0
  86. package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +511 -0
  87. package/dist/extensions/pi-hermes-memory/src/store/session-parser.ts +213 -0
  88. package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +234 -0
  89. package/dist/extensions/pi-hermes-memory/src/store/skill-store.ts +950 -0
  90. package/dist/extensions/pi-hermes-memory/src/store/skill-utils.ts +133 -0
  91. package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +1002 -0
  92. package/dist/extensions/pi-hermes-memory/src/store/sqlite-native.ts +241 -0
  93. package/dist/extensions/pi-hermes-memory/src/store/standing-instructions.ts +247 -0
  94. package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +95 -0
  95. package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +469 -0
  96. package/dist/extensions/pi-hermes-memory/src/tools/session-search-tool.ts +246 -0
  97. package/dist/extensions/pi-hermes-memory/src/tools/shared-output-view.ts +170 -0
  98. package/dist/extensions/pi-hermes-memory/src/tools/skill-tool.ts +333 -0
  99. package/dist/extensions/pi-hermes-memory/src/tools/tool-result-views.ts +105 -0
  100. package/dist/extensions/pi-hermes-memory/src/types.ts +214 -0
  101. package/dist/extensions/pi-hermes-memory/tests/config.test.ts +434 -0
  102. package/dist/extensions/pi-hermes-memory/tests/extension-root-migration.test.ts +368 -0
  103. package/dist/extensions/pi-hermes-memory/tests/handlers/auto-consolidate.test.ts +984 -0
  104. package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +950 -0
  105. package/dist/extensions/pi-hermes-memory/tests/handlers/correction-detector.test.ts +671 -0
  106. package/dist/extensions/pi-hermes-memory/tests/handlers/insights.test.ts +157 -0
  107. package/dist/extensions/pi-hermes-memory/tests/handlers/interview.test.ts +127 -0
  108. package/dist/extensions/pi-hermes-memory/tests/handlers/pi-child-process.test.ts +905 -0
  109. package/dist/extensions/pi-hermes-memory/tests/handlers/preview-context.test.ts +152 -0
  110. package/dist/extensions/pi-hermes-memory/tests/handlers/prompt-context.test.ts +166 -0
  111. package/dist/extensions/pi-hermes-memory/tests/handlers/resources-discover.test.ts +71 -0
  112. package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +614 -0
  113. package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +192 -0
  114. package/dist/extensions/pi-hermes-memory/tests/handlers/session-flush.test.ts +677 -0
  115. package/dist/extensions/pi-hermes-memory/tests/handlers/session-live-index.test.ts +225 -0
  116. package/dist/extensions/pi-hermes-memory/tests/handlers/skills-command.test.ts +827 -0
  117. package/dist/extensions/pi-hermes-memory/tests/handlers/standing-pin.test.ts +134 -0
  118. package/dist/extensions/pi-hermes-memory/tests/handlers/sync-markdown-memories.test.ts +491 -0
  119. package/dist/extensions/pi-hermes-memory/tests/handlers/system-prompt.test.ts +199 -0
  120. package/dist/extensions/pi-hermes-memory/tests/handlers/thinking-max.test.ts +64 -0
  121. package/dist/extensions/pi-hermes-memory/tests/index.test.ts +30 -0
  122. package/dist/extensions/pi-hermes-memory/tests/integration/flow.test.ts +126 -0
  123. package/dist/extensions/pi-hermes-memory/tests/lifecycle-timing.test.ts +76 -0
  124. package/dist/extensions/pi-hermes-memory/tests/paths.test.ts +23 -0
  125. package/dist/extensions/pi-hermes-memory/tests/project-memory-migration.test.ts +80 -0
  126. package/dist/extensions/pi-hermes-memory/tests/project-rebinding.test.ts +115 -0
  127. package/dist/extensions/pi-hermes-memory/tests/project.test.ts +120 -0
  128. package/dist/extensions/pi-hermes-memory/tests/run-all-timeout.test.ts +107 -0
  129. package/dist/extensions/pi-hermes-memory/tests/run-all.sh +45 -0
  130. package/dist/extensions/pi-hermes-memory/tests/store/atomic-lock-coordinator.test.ts +381 -0
  131. package/dist/extensions/pi-hermes-memory/tests/store/content-scanner.test.ts +388 -0
  132. package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +1046 -0
  133. package/dist/extensions/pi-hermes-memory/tests/store/markdown-mutation-lock.test.ts +34 -0
  134. package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +2150 -0
  135. package/dist/extensions/pi-hermes-memory/tests/store/recovery-maintenance.test.ts +170 -0
  136. package/dist/extensions/pi-hermes-memory/tests/store/session-anchor-search.test.ts +210 -0
  137. package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +643 -0
  138. package/dist/extensions/pi-hermes-memory/tests/store/session-parser.test.ts +282 -0
  139. package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +287 -0
  140. package/dist/extensions/pi-hermes-memory/tests/store/skill-store.test.ts +678 -0
  141. package/dist/extensions/pi-hermes-memory/tests/store/skill-utils.test.ts +21 -0
  142. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-lazy-load.test.ts +78 -0
  143. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +543 -0
  144. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +164 -0
  145. package/dist/extensions/pi-hermes-memory/tests/store/standing-instructions.test.ts +171 -0
  146. package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +84 -0
  147. package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +709 -0
  148. package/dist/extensions/pi-hermes-memory/tests/tools/session-search-tool.test.ts +345 -0
  149. package/dist/extensions/pi-hermes-memory/tests/tools/shared-output-view.test.ts +362 -0
  150. package/dist/extensions/pi-hermes-memory/tests/tools/skill-tool.test.ts +478 -0
  151. package/dist/extensions/pi-hermes-memory/tests/tools/tool-result-renderer-wiring.test.ts +40 -0
  152. package/dist/extensions/pi-hermes-memory/tsconfig.json +19 -0
  153. package/dist/extensions/pi-subagents/.oxlintrc.json +43 -0
  154. package/dist/extensions/pi-subagents/AGENTS.md +5 -0
  155. package/dist/extensions/pi-subagents/CHANGELOG.md +157 -0
  156. package/dist/extensions/pi-subagents/README.md +12 -6
  157. package/dist/extensions/pi-subagents/VISION.md +81 -0
  158. package/dist/extensions/pi-subagents/agents/claude-code-writer.md +15 -0
  159. package/dist/extensions/pi-subagents/agents/claude-code.md +15 -0
  160. package/dist/extensions/pi-subagents/agents/codex-exec-writer.md +15 -0
  161. package/dist/extensions/pi-subagents/agents/codex-exec.md +15 -0
  162. package/dist/extensions/pi-subagents/agents/cursor-agent-writer.md +14 -0
  163. package/dist/extensions/pi-subagents/agents/cursor-agent.md +14 -0
  164. package/dist/extensions/pi-subagents/agents/reviewer.md +1 -3
  165. package/dist/extensions/pi-subagents/docs/agents.md +488 -0
  166. package/dist/extensions/pi-subagents/docs/configuration.md +46 -9
  167. package/dist/extensions/pi-subagents/docs/extension-api.md +42 -3
  168. package/dist/extensions/pi-subagents/docs/models.md +27 -4
  169. package/dist/extensions/pi-subagents/docs/observability.md +10 -9
  170. package/dist/extensions/pi-subagents/docs/tool-reference.md +102 -6
  171. package/dist/extensions/pi-subagents/docs/workflows.md +135 -0
  172. package/dist/extensions/pi-subagents/package-lock.json +389 -2
  173. package/dist/extensions/pi-subagents/package.json +3 -1
  174. package/dist/extensions/pi-subagents/prompts/review-loop.md +2 -2
  175. package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +1 -1
  176. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +3 -1
  177. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +7 -3
  178. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
  179. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +26 -5
  180. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -22
  181. package/dist/extensions/pi-subagents/src/agents/agent-refinements.ts +4 -4
  182. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +9 -2
  183. package/dist/extensions/pi-subagents/src/agents/agents.ts +322 -75
  184. package/dist/extensions/pi-subagents/src/agents/builtin-names.ts +6 -0
  185. package/dist/extensions/pi-subagents/src/agents/runtime-agent-events.ts +70 -0
  186. package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +27 -20
  187. package/dist/extensions/pi-subagents/src/api/agents.ts +10 -5
  188. package/dist/extensions/pi-subagents/src/api/background-work.ts +5 -1
  189. package/dist/extensions/pi-subagents/src/api/delegation.ts +0 -7
  190. package/dist/extensions/pi-subagents/src/api/preflight.ts +34 -12
  191. package/dist/extensions/pi-subagents/src/extension/config.ts +70 -0
  192. package/dist/extensions/pi-subagents/src/extension/doctor.ts +3 -3
  193. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +5 -3
  194. package/dist/extensions/pi-subagents/src/extension/index.ts +83 -30
  195. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +44 -15
  196. package/dist/extensions/pi-subagents/src/extension/rpc.ts +55 -19
  197. package/dist/extensions/pi-subagents/src/extension/schemas.ts +41 -15
  198. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -31
  199. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +2 -1
  200. package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +2 -10
  201. package/dist/extensions/pi-subagents/src/inspectors/herdr/session-roots-codec.ts +42 -0
  202. package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +51 -3
  203. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +2 -0
  204. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +5 -6
  205. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +77 -10
  206. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +176 -53
  207. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +19 -12
  208. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +29 -27
  209. package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +20 -3
  210. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +23 -261
  211. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +73 -7
  212. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +8 -3
  213. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +74 -8
  214. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +37 -21
  215. package/dist/extensions/pi-subagents/src/runs/background/inspect-rpc.ts +8 -8
  216. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +184 -9
  217. package/dist/extensions/pi-subagents/src/runs/background/result-delivery-ownership.ts +45 -0
  218. package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +28 -14
  219. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +38 -15
  220. package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +1 -1
  221. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +1 -1
  222. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +50 -13
  223. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +93 -8
  224. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +10 -4
  225. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -14
  226. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +549 -377
  227. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +58 -10
  228. package/dist/extensions/pi-subagents/src/runs/background/terminal-run-index.ts +1 -1
  229. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +26 -1
  230. package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +23 -9
  231. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +9 -2
  232. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +2 -2
  233. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +204 -129
  234. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +9 -0
  235. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +23 -1
  236. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +773 -311
  237. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +140 -115
  238. package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +119 -0
  239. package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +463 -0
  240. package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +19 -4
  241. package/dist/extensions/pi-subagents/src/runs/shared/child-launch-plan.ts +151 -0
  242. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +21 -7
  243. package/dist/extensions/pi-subagents/src/runs/shared/claude-code-adapter.ts +129 -0
  244. package/dist/extensions/pi-subagents/src/runs/shared/codex-exec-adapter.ts +129 -0
  245. package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +89 -0
  246. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +5 -4
  247. package/dist/extensions/pi-subagents/src/runs/shared/cursor-agent-adapter.ts +114 -0
  248. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +3 -3
  249. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-contract.ts +167 -0
  250. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +122 -0
  251. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +348 -55
  252. package/dist/extensions/pi-subagents/src/runs/shared/fast-mode-extension.ts +5 -5
  253. package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +230 -0
  254. package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +105 -0
  255. package/dist/extensions/pi-subagents/src/runs/shared/launch-cwd.ts +16 -0
  256. package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +2 -1
  257. package/dist/extensions/pi-subagents/src/runs/shared/mcp-config-sources.ts +422 -0
  258. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +221 -158
  259. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-grant.ts +197 -0
  260. package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +69 -7
  261. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +47 -6
  262. package/dist/extensions/pi-subagents/src/runs/shared/mutation-evidence.ts +7 -2
  263. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +9 -3
  264. package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +9 -5
  265. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +419 -7
  266. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +15 -1
  267. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +81 -8
  268. package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +13 -0
  269. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +21 -1
  270. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +44 -4
  271. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +98 -13
  272. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +1 -1
  273. package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +847 -0
  274. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +18 -0
  275. package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +46 -0
  276. package/dist/extensions/pi-subagents/src/shared/extension-context.ts +24 -0
  277. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +21 -0
  278. package/dist/extensions/pi-subagents/src/shared/formatters.ts +18 -3
  279. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +5 -1
  280. package/dist/extensions/pi-subagents/src/shared/pruned-fork.ts +450 -0
  281. package/dist/extensions/pi-subagents/src/shared/session-file-trust.ts +19 -0
  282. package/dist/extensions/pi-subagents/src/shared/session-tokens.ts +14 -3
  283. package/dist/extensions/pi-subagents/src/shared/settings.ts +19 -105
  284. package/dist/extensions/pi-subagents/src/shared/shortcuts.ts +17 -0
  285. package/dist/extensions/pi-subagents/src/shared/types.ts +359 -40
  286. package/dist/extensions/pi-subagents/src/shared/utils.ts +41 -84
  287. package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +116 -0
  288. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +1 -9
  289. package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +0 -4
  290. package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +1 -2
  291. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +378 -88
  292. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +22 -11
  293. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +3 -0
  294. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +147 -79
  295. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +11 -5
  296. package/dist/extensions/pi-subagents/src/tui/fleet.ts +39 -18
  297. package/dist/extensions/pi-subagents/src/tui/render.ts +369 -46
  298. package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +1 -1
  299. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +6 -3
  300. package/dist/extensions/pi-subagents/src/workflows/host-command.ts +230 -0
  301. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +740 -58
  302. package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +121 -0
  303. package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +270 -0
  304. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +195 -6
  305. package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +246 -0
  306. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
  307. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1114 -260
  308. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +34 -0
  309. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +150 -3
  310. package/dist/extensions/pi-subagents/test/integration/claude-code-smoke.test.ts +52 -0
  311. package/dist/extensions/pi-subagents/test/integration/claude-code-writer-smoke.test.ts +55 -0
  312. package/dist/extensions/pi-subagents/test/integration/codex-exec-smoke.test.ts +53 -0
  313. package/dist/extensions/pi-subagents/test/integration/codex-exec-writer-smoke.test.ts +57 -0
  314. package/dist/extensions/pi-subagents/test/integration/cursor-agent-smoke.test.ts +59 -0
  315. package/dist/extensions/pi-subagents/test/integration/cursor-agent-writer-smoke.test.ts +62 -0
  316. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +57 -1
  317. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
  318. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +20 -0
  319. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +4 -3
  320. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +2 -2
  321. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +54 -4
  322. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +482 -4
  323. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +157 -9
  324. package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +5 -5
  325. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1508 -291
  326. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +423 -1
  327. package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +8 -0
  328. package/dist/extensions/pi-subagents/test/support/cursor-smoke-inputs.ts +51 -0
  329. package/dist/extensions/pi-subagents/test/support/helpers.ts +2 -0
  330. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +5 -5
  331. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +6 -6
  332. package/dist/extensions/pi-subagents/test/unit/abort-recovery.test.ts +152 -0
  333. package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +48 -0
  334. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +184 -6
  335. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +66 -6
  336. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +77 -0
  337. package/dist/extensions/pi-subagents/test/unit/agent-refinements.test.ts +18 -0
  338. package/dist/extensions/pi-subagents/test/unit/anti-slop-oxlint.test.ts +86 -0
  339. package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +1 -1
  340. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +101 -1
  341. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +121 -0
  342. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +28 -0
  343. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +14 -7
  344. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +53 -0
  345. package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +190 -0
  346. package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +42 -0
  347. package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +8 -4
  348. package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +23 -0
  349. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +78 -1
  350. package/dist/extensions/pi-subagents/test/unit/child-launch-plan.test.ts +94 -0
  351. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +21 -0
  352. package/dist/extensions/pi-subagents/test/unit/child-session-name.test.ts +50 -0
  353. package/dist/extensions/pi-subagents/test/unit/claude-code-adapter.test.ts +245 -0
  354. package/dist/extensions/pi-subagents/test/unit/codex-exec-adapter.test.ts +192 -0
  355. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +27 -0
  356. package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +150 -0
  357. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +21 -0
  358. package/dist/extensions/pi-subagents/test/unit/cursor-agent-adapter.test.ts +259 -0
  359. package/dist/extensions/pi-subagents/test/unit/cursor-smoke-inputs.test.ts +77 -0
  360. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +1 -4
  361. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +1 -1
  362. package/dist/extensions/pi-subagents/test/unit/extension-context.test.ts +27 -0
  363. package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +223 -0
  364. package/dist/extensions/pi-subagents/test/unit/fast-mode-extension.test.ts +27 -0
  365. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +208 -2
  366. package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +29 -1
  367. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +110 -1
  368. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +10 -0
  369. package/dist/extensions/pi-subagents/test/unit/formatters.test.ts +14 -0
  370. package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +18 -9
  371. package/dist/extensions/pi-subagents/test/unit/herdr-session-roots-codec.test.ts +63 -0
  372. package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +2 -2
  373. package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +132 -0
  374. package/dist/extensions/pi-subagents/test/unit/host-command.test.ts +156 -0
  375. package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +98 -0
  376. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +178 -26
  377. package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +2 -1
  378. package/dist/extensions/pi-subagents/test/unit/inspect-rpc.test.ts +21 -0
  379. package/dist/extensions/pi-subagents/test/unit/mcp-direct-tool-grant.test.ts +138 -0
  380. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
  381. package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +91 -0
  382. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +72 -7
  383. package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +21 -0
  384. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +1 -0
  385. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +20 -2
  386. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +129 -1
  387. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +2 -2
  388. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +4 -0
  389. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +358 -0
  390. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +574 -21
  391. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +109 -1
  392. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +71 -1
  393. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +7 -7
  394. package/dist/extensions/pi-subagents/test/unit/pruned-fork.test.ts +214 -0
  395. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +29 -8
  396. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +53 -0
  397. package/dist/extensions/pi-subagents/test/unit/result-delivery-ownership.test.ts +34 -0
  398. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +101 -3
  399. package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +1 -0
  400. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +10 -8
  401. package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +71 -1
  402. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +41 -7
  403. package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +136 -2
  404. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +137 -0
  405. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +33 -12
  406. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +504 -1
  407. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +8 -0
  408. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
  409. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +12 -1
  410. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +3 -5
  411. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +5 -6
  412. package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +0 -8
  413. package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +5 -0
  414. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +157 -2
  415. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +1 -1
  416. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +63 -4
  417. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +27 -23
  418. package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +44 -0
  419. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -0
  420. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +7 -5
  421. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +106 -0
  422. package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +484 -9
  423. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +38 -4
  424. package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +155 -0
  425. package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +318 -0
  426. package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +365 -0
  427. package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +349 -0
  428. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +21 -0
  429. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +2 -7
  430. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/index.ts +13 -0
  431. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/rules/no-service-constructor-imports.ts +52 -0
  432. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/index.ts +41 -0
  433. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-chained-type-assertions.ts +77 -0
  434. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-conditional-empty-object-spread.ts +49 -0
  435. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-known-value-widening.ts +247 -0
  436. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-module-mocking.ts +91 -0
  437. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-object-parameters.ts +126 -0
  438. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-apply.ts +28 -0
  439. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-get.ts +28 -0
  440. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-runtime-typeof.ts +67 -0
  441. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-shape-in-symbol-names.ts +39 -0
  442. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-parameters.ts +83 -0
  443. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-returns.ts +115 -0
  444. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-type-aliases.ts +72 -0
  445. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unsafe-dictionary-type.ts +134 -0
  446. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-widen-then-assert.ts +366 -0
  447. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/require-safety-comment-for-type-assertion.ts +62 -0
  448. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/dictionary-types.ts +502 -0
  449. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/lexical-type-parameters.ts +60 -0
  450. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/reflect-method.ts +35 -0
  451. package/dist/extensions/pi-tool-display/tests/capabilities.test.ts +1 -1
  452. package/dist/extensions/pi-tool-display/tests/settings-inspector.test.ts +2 -2
  453. package/dist/extensions/pi-tool-display/tests/tool-overrides-mcp.test.ts +1 -1
  454. package/dist/extensions/pi-zentui/extensions/zentui/format.ts +2 -1
  455. package/dist/extensions/pi-zentui/test/format.test.ts +15 -1
  456. package/dist/modes/interactive/components/footer.js +2 -2
  457. package/dist/modes/interactive/interactive-mode.js +11 -11
  458. package/dist/skills/workflow/SKILL.md +65 -50
  459. package/package.json +112 -110
  460. package/dist/extensions/enable-readonly-tools.test.ts +0 -63
  461. package/dist/extensions/enable-readonly-tools.ts +0 -24
  462. package/dist/extensions/model-prompt-injector/config.json +0 -16
  463. package/dist/extensions/model-prompt-injector/index.test.ts +0 -102
  464. package/dist/extensions/model-prompt-injector/index.ts +0 -148
  465. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +0 -98
  466. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +0 -250
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Project detection — determines whether the current working directory
3
+ * represents a project and resolves its name.
4
+ */
5
+
6
+ import * as fs from "node:fs";
7
+ import * as path from "node:path";
8
+ import * as os from "node:os";
9
+ import { resolveProjectsRoot } from "./paths.js";
10
+
11
+ export interface ProjectInfo {
12
+ /** Project name (directory basename), or null if not in a project. */
13
+ name: string | null;
14
+ /** Path to the project-scoped memory directory, or null. */
15
+ memoryDir: string | null;
16
+ }
17
+
18
+ export interface ProjectSkillInfo extends ProjectInfo {
19
+ /** Path to the project-scoped skills directory, or null. */
20
+ skillsDir: string | null;
21
+ }
22
+
23
+ /**
24
+ * Resolve the repository root shared by every linked worktree of `dir`'s repo.
25
+ *
26
+ * Mirrors what `git rev-parse --git-common-dir` reports, without spawning git:
27
+ * a linked worktree's `.git` is a file pointing at
28
+ * `<main>/.git/worktrees/<name>`, and that directory carries a `commondir`
29
+ * file pointing back at the shared `<main>/.git`. Returns null outside a
30
+ * repository, or for a bare/detached layout with no obvious working root.
31
+ */
32
+ function findGitRepoRoot(dir: string): string | null {
33
+ let current = path.resolve(dir);
34
+
35
+ while (true) {
36
+ const dotGit = path.join(current, ".git");
37
+ let stat: fs.Stats | undefined;
38
+ try {
39
+ stat = fs.statSync(dotGit);
40
+ } catch {
41
+ stat = undefined;
42
+ }
43
+
44
+ if (stat?.isDirectory()) return current;
45
+
46
+ if (stat?.isFile()) {
47
+ const commonDir = resolveWorktreeCommonDir(current, dotGit);
48
+ if (!commonDir) return current;
49
+ return path.basename(commonDir) === ".git" ? path.dirname(commonDir) : commonDir;
50
+ }
51
+
52
+ const parent = path.dirname(current);
53
+ if (parent === current) return null;
54
+ current = parent;
55
+ }
56
+ }
57
+
58
+ function resolveWorktreeCommonDir(worktreeRoot: string, dotGitFile: string): string | null {
59
+ let pointer: string;
60
+ try {
61
+ pointer = fs.readFileSync(dotGitFile, "utf-8");
62
+ } catch {
63
+ return null;
64
+ }
65
+
66
+ const match = /^gitdir:\s*(.+)$/m.exec(pointer);
67
+ if (!match) return null;
68
+
69
+ const gitDir = path.resolve(worktreeRoot, match[1].trim());
70
+ try {
71
+ const commonDir = fs.readFileSync(path.join(gitDir, "commondir"), "utf-8").trim();
72
+ if (commonDir) return path.resolve(gitDir, commonDir);
73
+ } catch {
74
+ // Not a linked worktree, or an older layout without `commondir`.
75
+ }
76
+
77
+ // `<main>/.git/worktrees/<name>` — two levels up is the shared git dir.
78
+ const parent = path.dirname(gitDir);
79
+ return path.basename(parent) === "worktrees" ? path.dirname(parent) : null;
80
+ }
81
+
82
+ const repoRootCache = new Map<string, string | null>();
83
+
84
+ /**
85
+ * Detect project from the current working directory.
86
+ *
87
+ * A "project" is any directory that is not the user's home directory. Inside a
88
+ * Git repository the project name is the *repository* root's basename, so every
89
+ * linked worktree shares one identity instead of stranding its memory and
90
+ * skills under the worktree directory name (#120). Outside Git it stays the
91
+ * working directory's basename.
92
+ *
93
+ * An existing `projects-memory/<cwd-basename>/` directory still wins over a
94
+ * newly derived repository name, so upgrading never orphans memory that was
95
+ * written under the old cwd-basename identity.
96
+ *
97
+ * Project-scoped memory is stored at ~/.selesai/agent/<projectsMemoryDir>/<projectName>/.
98
+ */
99
+ export function detectProject(projectsMemoryDir = "projects-memory", cwd?: string): ProjectInfo {
100
+ const dir = cwd ?? process.cwd();
101
+ const homeDir = os.homedir();
102
+
103
+ // Normalize paths for comparison
104
+ const resolved = path.resolve(dir);
105
+ const resolvedHome = path.resolve(homeDir);
106
+
107
+ if (resolved === resolvedHome || resolved === "/" || !resolved || resolved === resolvedHome + "/") {
108
+ return { name: null, memoryDir: null };
109
+ }
110
+
111
+ const cwdName = path.basename(resolved);
112
+ if (!cwdName || cwdName === "." || cwdName === "..") {
113
+ return { name: null, memoryDir: null };
114
+ }
115
+
116
+ const projectsRoot = resolveProjectsRoot(projectsMemoryDir);
117
+ const name = resolveProjectName(resolved, resolvedHome, cwdName, projectsRoot);
118
+
119
+ return {
120
+ name,
121
+ memoryDir: path.join(projectsRoot, name),
122
+ };
123
+ }
124
+
125
+ function resolveProjectName(
126
+ resolved: string,
127
+ resolvedHome: string,
128
+ cwdName: string,
129
+ projectsRoot: string,
130
+ ): string {
131
+ let repoRoot = repoRootCache.get(resolved);
132
+ if (repoRoot === undefined) {
133
+ repoRoot = findGitRepoRoot(resolved);
134
+ repoRootCache.set(resolved, repoRoot);
135
+ }
136
+
137
+ if (!repoRoot || repoRoot === resolved || repoRoot === resolvedHome) return cwdName;
138
+
139
+ const repoName = path.basename(repoRoot);
140
+ if (!repoName || repoName === cwdName) return cwdName;
141
+
142
+ // Migration bridge: a store already written under the old cwd-basename
143
+ // identity keeps working. Only fresh directories adopt the repository name.
144
+ if (!fs.existsSync(path.join(projectsRoot, repoName)) && fs.existsSync(path.join(projectsRoot, cwdName))) {
145
+ return cwdName;
146
+ }
147
+
148
+ return repoName;
149
+ }
150
+
151
+ export function detectProjectSkills(projectsMemoryDir = "projects-memory", cwd?: string): ProjectSkillInfo {
152
+ const project = detectProject(projectsMemoryDir, cwd);
153
+ return {
154
+ ...project,
155
+ skillsDir: project.memoryDir ? path.join(project.memoryDir, "skills") : null,
156
+ };
157
+ }
@@ -0,0 +1,55 @@
1
+ import { MEMORY_POLICY_PROMPT, MEMORY_POLICY_PROMPT_COMPACT } from "./constants.js";
2
+ import type { MemoryConfig } from "./types.js";
3
+ import type { MemoryStore } from "./store/memory-store.js";
4
+ import type { StandingInstructions } from "./store/standing-instructions.js";
5
+
6
+ type MemoryPolicyConfig = Pick<MemoryConfig, "memoryPolicyStyle" | "memoryPolicyCustomText">;
7
+
8
+ export function resolveMemoryPolicyPrompt(config: MemoryPolicyConfig): string {
9
+ const style = config.memoryPolicyStyle ?? "full";
10
+
11
+ switch (style) {
12
+ case "compact":
13
+ return MEMORY_POLICY_PROMPT_COMPACT;
14
+ case "custom":
15
+ return config.memoryPolicyCustomText && config.memoryPolicyCustomText.trim().length > 0
16
+ ? config.memoryPolicyCustomText
17
+ : MEMORY_POLICY_PROMPT_COMPACT;
18
+ case "none":
19
+ return "";
20
+ case "full":
21
+ default:
22
+ return MEMORY_POLICY_PROMPT;
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Standing instructions are appended in *every* mode, including policy-only
28
+ * and policy style "none". That is the whole point of the store: a rule the
29
+ * user pinned must not depend on the model choosing to run memory_search
30
+ * before the action it forbids (#121). They go last so they read as the
31
+ * operative directive rather than as recalled context.
32
+ */
33
+ export async function buildPromptContext(
34
+ config: Pick<MemoryConfig, "memoryMode" | "memoryPolicyStyle" | "memoryPolicyCustomText">,
35
+ store: MemoryStore,
36
+ projectStore: MemoryStore | null,
37
+ projectName: string,
38
+ standing: StandingInstructions | null = null,
39
+ ): Promise<string> {
40
+ const standingBlock = standing?.formatForSystemPrompt() ?? "";
41
+
42
+ if (config.memoryMode === "policy-only") {
43
+ return [resolveMemoryPolicyPrompt(config), standingBlock].filter(Boolean).join("\n\n");
44
+ }
45
+
46
+ const memoryBlock = store.formatForSystemPrompt();
47
+ const projectBlock = projectStore ? projectStore.formatProjectBlock(projectName) : "";
48
+
49
+ const parts: string[] = [];
50
+ if (memoryBlock) parts.push(memoryBlock);
51
+ if (projectBlock) parts.push(projectBlock);
52
+ if (standingBlock) parts.push(standingBlock);
53
+
54
+ return parts.join("\n\n");
55
+ }
@@ -0,0 +1,375 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { createRequire } from 'node:module';
5
+ import { spawnSync } from 'node:child_process';
6
+ import { isBunRuntime, loadBetterSqlite3 } from './sqlite-native.js';
7
+
8
+ type StatementLike = {
9
+ run: (...args: unknown[]) => unknown;
10
+ get: (...args: unknown[]) => unknown;
11
+ all: (...args: unknown[]) => unknown[];
12
+ };
13
+
14
+ type DatabaseLike = {
15
+ prepare: (sql: string) => StatementLike;
16
+ exec: (sql: string) => void;
17
+ close: () => void;
18
+ };
19
+
20
+ type DatabaseCtor = new (dbPath: string) => DatabaseLike;
21
+
22
+ export interface AtomicLockOptions {
23
+ staleMs: number;
24
+ }
25
+
26
+ export interface AtomicLockLease {
27
+ token: string;
28
+ release: () => void;
29
+ /**
30
+ * Refresh the lease timestamp so a long-running-but-healthy holder is never
31
+ * mistaken for a wedged one. Returns false once the lease has been taken
32
+ * over, which is the holder's signal that it no longer owns the resource.
33
+ */
34
+ renew: () => boolean;
35
+ }
36
+
37
+ export interface AtomicLockCoordinatorOptions {
38
+ pid?: number;
39
+ incarnation?: string;
40
+ probeIncarnation?: (pid: number) => string | null;
41
+ }
42
+
43
+ let cachedDatabaseCtor: DatabaseCtor | null = null;
44
+
45
+ /**
46
+ * Resolved on first use, never at import time — see the same note in db.ts.
47
+ * Compiled Pi cannot resolve better-sqlite3 at all, so Bun must take bun:sqlite.
48
+ */
49
+ function getDatabaseCtor(): DatabaseCtor {
50
+ if (!cachedDatabaseCtor) {
51
+ const require = createRequire(import.meta.url);
52
+ if (isBunRuntime()) {
53
+ const bunSqlite = require('bun:sqlite') as { Database: DatabaseCtor };
54
+ cachedDatabaseCtor = bunSqlite.Database;
55
+ } else {
56
+ cachedDatabaseCtor = loadBetterSqlite3({ requireImpl: require }) as DatabaseCtor;
57
+ }
58
+ }
59
+ return cachedDatabaseCtor;
60
+ }
61
+
62
+ function processIsAlive(pid: number): boolean {
63
+ try {
64
+ process.kill(pid, 0);
65
+ return true;
66
+ } catch (error) {
67
+ return (error as NodeJS.ErrnoException).code !== 'ESRCH';
68
+ }
69
+ }
70
+
71
+ function probeProcessIncarnation(pid: number): string | null {
72
+ if (!Number.isSafeInteger(pid) || pid <= 0) return null;
73
+ if (process.platform === 'linux') {
74
+ try {
75
+ const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf-8');
76
+ const end = stat.lastIndexOf(')');
77
+ const fields = stat.slice(end + 2).split(' ');
78
+ return fields[19] || null;
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+
84
+ if (process.platform !== 'win32') {
85
+ const result = spawnSync('ps', ['-o', 'lstart=', '-p', String(pid)], {
86
+ encoding: 'utf-8',
87
+ timeout: 250,
88
+ });
89
+ return result.status === 0 ? result.stdout.trim() || null : null;
90
+ }
91
+
92
+ const result = spawnSync(
93
+ 'powershell.exe',
94
+ ['-NoProfile', '-NonInteractive', '-Command', `(Get-Process -Id ${pid} -ErrorAction SilentlyContinue).StartTime.ToUniversalTime().Ticks`],
95
+ { encoding: 'utf-8', timeout: 500 },
96
+ );
97
+ return result.status === 0 ? result.stdout.trim() || null : null;
98
+ }
99
+
100
+ const currentProcessIncarnation = probeProcessIncarnation(process.pid);
101
+ const RELEASE_ATTEMPTS = 3;
102
+ // Opportunistic dead-row GC: a row must be older than the grace period before
103
+ // a dead pid makes it collectable (guards against a pid we cannot observe, and
104
+ // against racing a holder that has just inserted its row), and each coordinator
105
+ // sweeps at most once per interval so acquisition stays cheap.
106
+ const DEAD_LOCK_SWEEP_GRACE_MS = 60_000;
107
+ const DEAD_LOCK_SWEEP_INTERVAL_MS = 60_000;
108
+ const pendingReleases = new Map<string, () => void>();
109
+ const sharedCoordinators = new Map<string, AtomicLockCoordinator>();
110
+
111
+ export class AtomicLockCoordinator {
112
+ private readonly pid: number;
113
+ private readonly incarnation: string | null;
114
+ private readonly probeIncarnation: (pid: number) => string | null;
115
+ private cachedDb: DatabaseLike | null = null;
116
+ private lastSweepMs = 0;
117
+
118
+ constructor(private readonly dbPath: string, options: AtomicLockCoordinatorOptions = {}) {
119
+ this.pid = options.pid ?? process.pid;
120
+ this.probeIncarnation = options.probeIncarnation
121
+ ?? ((pid) => pid === process.pid ? currentProcessIncarnation : probeProcessIncarnation(pid));
122
+ this.incarnation = options.incarnation
123
+ ?? this.probeIncarnation(this.pid)
124
+ ?? null;
125
+ }
126
+
127
+ tryAcquire(key: string, options: AtomicLockOptions): AtomicLockLease | null {
128
+ this.retryPendingReleases(key);
129
+ const token = randomUUID();
130
+ const now = Date.now();
131
+ const db = this.open();
132
+ this.sweepDeadLocks(db, now);
133
+ let acquired = false;
134
+
135
+ db.exec('BEGIN IMMEDIATE');
136
+ try {
137
+ const owner = db.prepare(`
138
+ SELECT token, pid, incarnation, acquired_at
139
+ FROM locks
140
+ WHERE lock_key = ?
141
+ `).get(key) as { token: string; pid: number; incarnation: string | null; acquired_at: number } | undefined;
142
+
143
+ if (!owner) {
144
+ db.prepare(`
145
+ INSERT INTO locks (lock_key, token, pid, incarnation, acquired_at)
146
+ VALUES (?, ?, ?, ?, ?)
147
+ `).run(key, token, this.pid, this.incarnation, now);
148
+ acquired = true;
149
+ } else {
150
+ const observedIncarnation = this.probeIncarnation(owner.pid);
151
+ const alive = observedIncarnation !== null || processIsAlive(owner.pid);
152
+ const sameIncarnation = alive
153
+ && owner.incarnation !== null
154
+ && observedIncarnation !== null
155
+ && owner.incarnation === observedIncarnation;
156
+ const unknownIncarnation = alive && (owner.incarnation === null || observedIncarnation === null);
157
+ // A lease is reclaimable once it has been held for longer than staleMs,
158
+ // regardless of whether the owning process is still alive — it may be
159
+ // making no progress (blocked I/O, wedged, suspended) rather than dead.
160
+ // This is the sole backstop for that case: liveness/incarnation checks
161
+ // alone cannot distinguish "alive and working" from "alive and stuck".
162
+ // staleMs <= 0 disables time-based takeover (liveness checks only).
163
+ const stale = options.staleMs > 0 && now - owner.acquired_at >= options.staleMs;
164
+ if (stale || (!sameIncarnation && !unknownIncarnation)) {
165
+ db.prepare(`
166
+ UPDATE locks
167
+ SET token = ?, pid = ?, incarnation = ?, acquired_at = ?
168
+ WHERE lock_key = ? AND token = ?
169
+ `).run(token, this.pid, this.incarnation, now, key, owner.token);
170
+ acquired = true;
171
+ }
172
+ }
173
+
174
+ db.exec('COMMIT');
175
+ } catch (error) {
176
+ try {
177
+ db.exec('ROLLBACK');
178
+ } catch {
179
+ // The transaction is still open on a connection we are about to hand
180
+ // to the next caller, whose BEGIN IMMEDIATE would then fail forever.
181
+ // Drop the handle so open() rebuilds it.
182
+ this.discardCachedDb();
183
+ }
184
+ throw error;
185
+ }
186
+
187
+ if (!acquired) return null;
188
+ return {
189
+ token,
190
+ release: () => this.release(key, token),
191
+ renew: () => this.renew(key, token),
192
+ };
193
+ }
194
+
195
+ /**
196
+ * Fencing check for destructive operations that lack their own independent
197
+ * compare-and-swap (e.g. a plain fs.renameSync with no content/inode
198
+ * verification). A lease can be legitimately stolen from a stale-but-alive
199
+ * holder (see tryAcquire); a holder resuming after being stuck must verify
200
+ * it is still the current owner immediately before publishing, or abort.
201
+ * This narrows — it cannot fully close — the check-then-act race, since
202
+ * synchronous work between this call and the actual write is not atomic
203
+ * with it.
204
+ */
205
+ isCurrentOwner(key: string, token: string): boolean {
206
+ const db = this.open();
207
+ const row = db.prepare('SELECT token FROM locks WHERE lock_key = ?').get(key) as { token: string } | undefined;
208
+ return row?.token === token;
209
+ }
210
+
211
+ /**
212
+ * Extend a held lease. A holder whose work legitimately outlives staleMs
213
+ * (a consolidation child can run for minutes) must beat periodically or a
214
+ * peer will reclaim the lease out from under it. Beating also lets staleMs
215
+ * stay short, so a holder that stops making progress is reclaimed in
216
+ * seconds instead of after its worst-case runtime.
217
+ *
218
+ * Token-fenced: a lease that has already been taken over renews nothing and
219
+ * returns false.
220
+ */
221
+ renew(key: string, token: string): boolean {
222
+ const db = this.open();
223
+ db.exec('BEGIN IMMEDIATE');
224
+ try {
225
+ const owner = db.prepare('SELECT token FROM locks WHERE lock_key = ?').get(key) as { token: string } | undefined;
226
+ const owned = owner?.token === token;
227
+ if (owned) {
228
+ db.prepare('UPDATE locks SET acquired_at = ? WHERE lock_key = ? AND token = ?').run(Date.now(), key, token);
229
+ }
230
+ db.exec('COMMIT');
231
+ return owned;
232
+ } catch (error) {
233
+ try {
234
+ db.exec('ROLLBACK');
235
+ } catch {
236
+ this.discardCachedDb();
237
+ }
238
+ throw error;
239
+ }
240
+ }
241
+
242
+ release(key: string, token: string): void {
243
+ const pendingKey = this.pendingReleaseKey(key, token);
244
+ for (let attempt = 0; attempt < RELEASE_ATTEMPTS; attempt++) {
245
+ try {
246
+ this.deleteOwnedLock(key, token);
247
+ pendingReleases.delete(pendingKey);
248
+ return;
249
+ } catch {
250
+ }
251
+ }
252
+ pendingReleases.set(pendingKey, () => this.release(key, token));
253
+ }
254
+
255
+ private deleteOwnedLock(key: string, token: string): void {
256
+ const db = this.open();
257
+ db.prepare('DELETE FROM locks WHERE lock_key = ? AND token = ?').run(key, token);
258
+ }
259
+
260
+ /**
261
+ * Delete rows whose holder process is gone.
262
+ *
263
+ * Without this, a row is only ever reclaimed by a peer that asks for the
264
+ * same lock key again. A key belonging to an identity that never returns
265
+ * (a deleted project, a one-shot `pi -p` run) leaks its row forever, and a
266
+ * much later session using that identity pays a spurious wait.
267
+ *
268
+ * A dead process can never release or renew its lease, so deleting its row
269
+ * is always safe. Probing happens outside any transaction and the DELETE is
270
+ * fenced on (token, acquired_at) so a row re-taken in between is left alone.
271
+ */
272
+ private sweepDeadLocks(db: DatabaseLike, now: number): void {
273
+ if (now - this.lastSweepMs < DEAD_LOCK_SWEEP_INTERVAL_MS) return;
274
+ this.lastSweepMs = now;
275
+ try {
276
+ const rows = db
277
+ .prepare('SELECT lock_key, token, pid, acquired_at FROM locks WHERE acquired_at <= ?')
278
+ .all(now - DEAD_LOCK_SWEEP_GRACE_MS) as Array<{
279
+ lock_key: string;
280
+ token: string;
281
+ pid: number;
282
+ acquired_at: number;
283
+ }>;
284
+ const dead = rows.filter((row) => this.holderIsGone(row.pid));
285
+ if (dead.length === 0) return;
286
+ const remove = db.prepare('DELETE FROM locks WHERE lock_key = ? AND token = ? AND acquired_at = ?');
287
+ for (const row of dead) {
288
+ remove.run(row.lock_key, row.token, row.acquired_at);
289
+ }
290
+ } catch {
291
+ // GC is best-effort — never fail an acquisition because of it.
292
+ }
293
+ }
294
+
295
+ /** Same liveness test tryAcquire steals on, cheapest check first. */
296
+ private holderIsGone(pid: number): boolean {
297
+ if (!Number.isSafeInteger(pid) || pid <= 0) return false;
298
+ if (pid === this.pid) return false;
299
+ if (processIsAlive(pid)) return false;
300
+ return this.probeIncarnation(pid) === null;
301
+ }
302
+
303
+ private retryPendingReleases(key: string): void {
304
+ const prefix = `${path.resolve(this.dbPath)}\0${key}\0`;
305
+ for (const [pendingKey, release] of [...pendingReleases.entries()]) {
306
+ if (pendingKey.startsWith(prefix)) release();
307
+ }
308
+ }
309
+
310
+ private pendingReleaseKey(key: string, token: string): string {
311
+ return `${path.resolve(this.dbPath)}\0${key}\0${token}`;
312
+ }
313
+
314
+ private discardCachedDb(): void {
315
+ const db = this.cachedDb;
316
+ this.cachedDb = null;
317
+ if (db) {
318
+ try { db.close(); } catch {}
319
+ }
320
+ }
321
+
322
+ private open(): DatabaseLike {
323
+ if (this.cachedDb) return this.cachedDb;
324
+ fs.mkdirSync(path.dirname(this.dbPath), { recursive: true });
325
+ const existed = fs.existsSync(this.dbPath);
326
+ const db = new (getDatabaseCtor())(this.dbPath);
327
+ try {
328
+ db.exec(`
329
+ PRAGMA busy_timeout = 5000;
330
+ PRAGMA journal_mode = WAL;
331
+ CREATE TABLE IF NOT EXISTS locks (
332
+ lock_key TEXT PRIMARY KEY,
333
+ token TEXT NOT NULL,
334
+ pid INTEGER NOT NULL,
335
+ incarnation TEXT,
336
+ acquired_at INTEGER NOT NULL
337
+ );
338
+ `);
339
+ const columns = db.prepare('PRAGMA table_info(locks)').all() as Array<{ name: string }>;
340
+ if (!columns.some(({ name }) => name === 'incarnation')) {
341
+ try {
342
+ db.exec('ALTER TABLE locks ADD COLUMN incarnation TEXT');
343
+ } catch (error) {
344
+ const refreshed = db.prepare('PRAGMA table_info(locks)').all() as Array<{ name: string }>;
345
+ if (!refreshed.some(({ name }) => name === 'incarnation')) throw error;
346
+ }
347
+ }
348
+ if (!existed) fs.chmodSync(this.dbPath, 0o600);
349
+ this.cachedDb = db;
350
+ return db;
351
+ } catch (error) {
352
+ db.close();
353
+ throw error;
354
+ }
355
+ }
356
+
357
+ /**
358
+ * Process-wide coordinator for `dbPath`.
359
+ *
360
+ * Each instance now pins its SQLite connection for its own lifetime, so a
361
+ * caller that constructs a fresh coordinator per operation would leak one
362
+ * open WAL connection per call. Every default-options caller must share.
363
+ * The option-carrying constructor stays public for tests, which pass a
364
+ * synthetic pid/incarnation that a dbPath-keyed cache would silently ignore.
365
+ */
366
+ static shared(dbPath: string): AtomicLockCoordinator {
367
+ const key = path.resolve(dbPath);
368
+ let coordinator = sharedCoordinators.get(key);
369
+ if (!coordinator) {
370
+ coordinator = new AtomicLockCoordinator(dbPath);
371
+ sharedCoordinators.set(key, coordinator);
372
+ }
373
+ return coordinator;
374
+ }
375
+ }
@@ -0,0 +1,54 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ const MAX_SYMLINK_DEPTH = 40;
5
+
6
+ function pathParts(absolutePath: string): { root: string; parts: string[] } {
7
+ const root = path.parse(absolutePath).root;
8
+ return {
9
+ root,
10
+ parts: absolutePath.slice(root.length).split(path.sep).filter(Boolean),
11
+ };
12
+ }
13
+
14
+ export function canonicalStoragePathSync(filePath: string): string {
15
+ const input = path.resolve(filePath);
16
+ let { root, parts } = pathParts(input);
17
+ let current = root;
18
+ let depth = 0;
19
+
20
+ while (parts.length > 0) {
21
+ const part = parts.shift()!;
22
+ const candidate = path.join(current, part);
23
+ let state: fs.Stats;
24
+ try {
25
+ state = fs.lstatSync(candidate);
26
+ } catch (error) {
27
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
28
+ return path.join(fs.realpathSync.native(current), part, ...parts);
29
+ }
30
+
31
+ if (!state.isSymbolicLink()) {
32
+ current = candidate;
33
+ continue;
34
+ }
35
+
36
+ if (depth++ >= MAX_SYMLINK_DEPTH) {
37
+ const error = new Error(`Symbolic link loop detected while resolving ${input}`) as NodeJS.ErrnoException;
38
+ error.code = "ELOOP";
39
+ throw error;
40
+ }
41
+ const target = fs.readlinkSync(candidate);
42
+ const targetPath = path.resolve(path.dirname(candidate), target);
43
+ const targetParts = pathParts(targetPath);
44
+ root = targetParts.root;
45
+ current = root;
46
+ parts = [...targetParts.parts, ...parts];
47
+ }
48
+
49
+ return fs.realpathSync.native(current);
50
+ }
51
+
52
+ export async function canonicalStoragePath(filePath: string): Promise<string> {
53
+ return canonicalStoragePathSync(filePath);
54
+ }