@cmetech/otto 1.1.0 → 1.2.4

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 (425) hide show
  1. package/dist/coworker/persona-commands.d.ts +1 -0
  2. package/dist/coworker/persona-commands.js +5 -0
  3. package/dist/coworker/persona-commands.test.d.ts +1 -0
  4. package/dist/coworker/persona-commands.test.js +45 -0
  5. package/dist/resources/.managed-resources-content-hash +1 -1
  6. package/dist/resources/extensions/_coworker-paths.js +8 -0
  7. package/dist/resources/extensions/coworker-artifacts/artifacts-command.js +31 -0
  8. package/dist/resources/extensions/coworker-artifacts/artifacts-singleton.js +17 -0
  9. package/dist/resources/extensions/coworker-artifacts/extension-manifest.json +13 -0
  10. package/dist/resources/extensions/coworker-artifacts/index.js +125 -0
  11. package/dist/resources/extensions/coworker-artifacts/list-tool.js +27 -0
  12. package/dist/resources/extensions/coworker-artifacts/open-tool.js +25 -0
  13. package/dist/resources/extensions/coworker-memory/extension-manifest.json +13 -0
  14. package/dist/resources/extensions/coworker-memory/index.js +219 -0
  15. package/dist/resources/extensions/coworker-memory/memorize-tool.js +10 -0
  16. package/dist/resources/extensions/coworker-memory/memory-command.js +157 -0
  17. package/dist/resources/extensions/coworker-memory/memory-singleton.js +55 -0
  18. package/dist/resources/extensions/coworker-memory/recall-tool.js +18 -0
  19. package/dist/resources/extensions/coworker-memory/session-hooks.js +45 -0
  20. package/dist/resources/extensions/coworker-scratchpad/attach-banners.js +53 -0
  21. package/dist/resources/extensions/coworker-scratchpad/extension-manifest.json +13 -0
  22. package/dist/resources/extensions/coworker-scratchpad/format-age.js +9 -0
  23. package/dist/resources/extensions/coworker-scratchpad/helpers.js +38 -0
  24. package/dist/resources/extensions/coworker-scratchpad/index.js +199 -0
  25. package/dist/resources/extensions/coworker-scratchpad/mime-bundle.js +20 -0
  26. package/dist/resources/extensions/coworker-scratchpad/scratchpad-tool.js +118 -0
  27. package/dist/resources/extensions/coworker-scratchpad/session-sidecar.js +60 -0
  28. package/dist/resources/extensions/coworker-scratchpad/sp-command.js +597 -0
  29. package/dist/resources/extensions/coworker-scratchpad/workspace-pointer.js +41 -0
  30. package/dist/resources/extensions/coworker-scratchpad/workspace-root.js +17 -0
  31. package/dist/resources/extensions/coworker-vault/audit-command.js +35 -0
  32. package/dist/resources/extensions/coworker-vault/connect-command.js +42 -0
  33. package/dist/resources/extensions/coworker-vault/datasource-command.js +50 -0
  34. package/dist/resources/extensions/coworker-vault/extension-manifest.json +12 -0
  35. package/dist/resources/extensions/coworker-vault/index.js +171 -0
  36. package/dist/resources/extensions/coworker-vault/test-helpers.js +86 -0
  37. package/dist/resources/extensions/coworker-vault/vault-singleton.js +24 -0
  38. package/dist/resources/extensions/otto/commands/release-notes/_data.js +82 -0
  39. package/dist/resources/extensions/otto/commands/release-notes/command.js +15 -4
  40. package/dist/resources/extensions/subagent/index.js +8 -1
  41. package/dist/resources/extensions/subagent/launch.js +37 -5
  42. package/dist/resources/extensions/subagent/run-store.js +1 -0
  43. package/dist/resources/extensions/workflow/bootstrap/register-extension.js +2 -0
  44. package/dist/resources/extensions/workflow/bootstrap/register-hooks.js +10 -0
  45. package/dist/resources/extensions/workflow/persona-status.js +87 -0
  46. package/dist/update-cmd.d.ts +19 -0
  47. package/dist/update-cmd.js +177 -6
  48. package/package.json +25 -10
  49. package/packages/contracts/package.json +1 -1
  50. package/packages/coworker-artifacts/dist/artifact-store.d.ts +25 -0
  51. package/packages/coworker-artifacts/dist/artifact-store.js +187 -0
  52. package/packages/coworker-artifacts/dist/dir-snapshot.d.ts +7 -0
  53. package/packages/coworker-artifacts/dist/dir-snapshot.js +54 -0
  54. package/packages/coworker-artifacts/dist/errors.d.ts +18 -0
  55. package/packages/coworker-artifacts/dist/errors.js +37 -0
  56. package/packages/coworker-artifacts/dist/index.d.ts +7 -0
  57. package/packages/coworker-artifacts/dist/index.js +7 -0
  58. package/packages/coworker-artifacts/dist/readme-renderer.d.ts +5 -0
  59. package/packages/coworker-artifacts/dist/readme-renderer.js +47 -0
  60. package/packages/coworker-artifacts/dist/resolve-uri.d.ts +3 -0
  61. package/packages/coworker-artifacts/dist/resolve-uri.js +29 -0
  62. package/packages/coworker-artifacts/dist/slug.d.ts +4 -0
  63. package/packages/coworker-artifacts/dist/slug.js +32 -0
  64. package/packages/coworker-artifacts/dist/types.d.ts +52 -0
  65. package/packages/coworker-artifacts/dist/types.js +1 -0
  66. package/packages/coworker-artifacts/package.json +20 -0
  67. package/packages/coworker-artifacts/src/artifact-store.test.ts +188 -0
  68. package/packages/coworker-artifacts/src/artifact-store.ts +206 -0
  69. package/packages/coworker-artifacts/src/artifacts-integration.test.ts +109 -0
  70. package/packages/coworker-artifacts/src/dir-snapshot.test.ts +71 -0
  71. package/packages/coworker-artifacts/src/dir-snapshot.ts +52 -0
  72. package/packages/coworker-artifacts/src/errors.test.ts +37 -0
  73. package/packages/coworker-artifacts/src/errors.ts +28 -0
  74. package/packages/coworker-artifacts/src/index.test.ts +22 -0
  75. package/packages/coworker-artifacts/src/index.ts +7 -0
  76. package/packages/coworker-artifacts/src/readme-renderer.test.ts +72 -0
  77. package/packages/coworker-artifacts/src/readme-renderer.ts +56 -0
  78. package/packages/coworker-artifacts/src/resolve-uri.test.ts +46 -0
  79. package/packages/coworker-artifacts/src/resolve-uri.ts +29 -0
  80. package/packages/coworker-artifacts/src/slug.test.ts +47 -0
  81. package/packages/coworker-artifacts/src/slug.ts +31 -0
  82. package/packages/coworker-artifacts/src/types.ts +61 -0
  83. package/packages/coworker-artifacts/tsconfig.json +15 -0
  84. package/packages/coworker-artifacts/tsconfig.publish.json +4 -0
  85. package/packages/coworker-memory/dist/context-injection.d.ts +9 -0
  86. package/packages/coworker-memory/dist/context-injection.js +41 -0
  87. package/packages/coworker-memory/dist/errors.d.ts +25 -0
  88. package/packages/coworker-memory/dist/errors.js +51 -0
  89. package/packages/coworker-memory/dist/index.d.ts +12 -0
  90. package/packages/coworker-memory/dist/index.js +12 -0
  91. package/packages/coworker-memory/dist/layer-a-store.d.ts +16 -0
  92. package/packages/coworker-memory/dist/layer-a-store.js +78 -0
  93. package/packages/coworker-memory/dist/local-sqlite-backend.d.ts +28 -0
  94. package/packages/coworker-memory/dist/local-sqlite-backend.js +167 -0
  95. package/packages/coworker-memory/dist/memory-backend.d.ts +14 -0
  96. package/packages/coworker-memory/dist/memory-backend.js +1 -0
  97. package/packages/coworker-memory/dist/memory-recorder.d.ts +50 -0
  98. package/packages/coworker-memory/dist/memory-recorder.js +69 -0
  99. package/packages/coworker-memory/dist/migrations/001-init.sql +38 -0
  100. package/packages/coworker-memory/dist/migrations/002-artifact-kind.sql +50 -0
  101. package/packages/coworker-memory/dist/paste-detector.d.ts +5 -0
  102. package/packages/coworker-memory/dist/paste-detector.js +14 -0
  103. package/packages/coworker-memory/dist/persona-seed.d.ts +10 -0
  104. package/packages/coworker-memory/dist/persona-seed.js +38 -0
  105. package/packages/coworker-memory/dist/recall-formatter.d.ts +2 -0
  106. package/packages/coworker-memory/dist/recall-formatter.js +14 -0
  107. package/packages/coworker-memory/dist/scope-resolver.d.ts +9 -0
  108. package/packages/coworker-memory/dist/scope-resolver.js +10 -0
  109. package/packages/coworker-memory/dist/types.d.ts +51 -0
  110. package/packages/coworker-memory/dist/types.js +2 -0
  111. package/packages/coworker-memory/dist/workspace-id.d.ts +3 -0
  112. package/packages/coworker-memory/dist/workspace-id.js +54 -0
  113. package/packages/coworker-memory/package.json +35 -0
  114. package/packages/coworker-memory/src/activator-integration.test.ts +141 -0
  115. package/packages/coworker-memory/src/context-injection.test.ts +72 -0
  116. package/packages/coworker-memory/src/context-injection.ts +57 -0
  117. package/packages/coworker-memory/src/errors.test.ts +45 -0
  118. package/packages/coworker-memory/src/errors.ts +42 -0
  119. package/packages/coworker-memory/src/index.test.ts +21 -0
  120. package/packages/coworker-memory/src/index.ts +12 -0
  121. package/packages/coworker-memory/src/layer-a-store.test.ts +85 -0
  122. package/packages/coworker-memory/src/layer-a-store.ts +88 -0
  123. package/packages/coworker-memory/src/local-sqlite-backend.test.ts +110 -0
  124. package/packages/coworker-memory/src/local-sqlite-backend.ts +185 -0
  125. package/packages/coworker-memory/src/memory-backend.ts +10 -0
  126. package/packages/coworker-memory/src/memory-integration.test.ts +89 -0
  127. package/packages/coworker-memory/src/memory-recorder.test.ts +101 -0
  128. package/packages/coworker-memory/src/memory-recorder.ts +95 -0
  129. package/packages/coworker-memory/src/migrations/001-init.sql +38 -0
  130. package/packages/coworker-memory/src/migrations/002-artifact-kind.sql +50 -0
  131. package/packages/coworker-memory/src/paste-detector.test.ts +23 -0
  132. package/packages/coworker-memory/src/paste-detector.ts +18 -0
  133. package/packages/coworker-memory/src/persona-seed.test.ts +57 -0
  134. package/packages/coworker-memory/src/persona-seed.ts +46 -0
  135. package/packages/coworker-memory/src/recall-formatter.test.ts +34 -0
  136. package/packages/coworker-memory/src/recall-formatter.ts +15 -0
  137. package/packages/coworker-memory/src/scope-resolver.test.ts +23 -0
  138. package/packages/coworker-memory/src/scope-resolver.ts +18 -0
  139. package/packages/coworker-memory/src/types.ts +61 -0
  140. package/packages/coworker-memory/src/workspace-id.test.ts +48 -0
  141. package/packages/coworker-memory/src/workspace-id.ts +56 -0
  142. package/packages/coworker-memory/tsconfig.json +15 -0
  143. package/packages/coworker-memory/tsconfig.publish.json +4 -0
  144. package/packages/coworker-persona/dist/commands.d.ts +7 -0
  145. package/packages/coworker-persona/dist/commands.js +35 -0
  146. package/packages/coworker-persona/dist/defaults/manifest.yaml +12 -0
  147. package/packages/coworker-persona/dist/defaults/steering/identity.md +3 -0
  148. package/packages/coworker-persona/dist/index.d.ts +3 -0
  149. package/packages/coworker-persona/dist/index.js +3 -0
  150. package/packages/coworker-persona/dist/manifest.d.ts +24 -0
  151. package/packages/coworker-persona/dist/manifest.js +21 -0
  152. package/packages/coworker-persona/dist/registry.d.ts +22 -0
  153. package/packages/coworker-persona/dist/registry.js +142 -0
  154. package/packages/coworker-persona/package.json +28 -0
  155. package/packages/coworker-persona/scripts/copy-defaults.cjs +17 -0
  156. package/packages/coworker-persona/src/commands.ts +47 -0
  157. package/packages/coworker-persona/src/defaults/manifest.yaml +12 -0
  158. package/packages/coworker-persona/src/defaults/steering/identity.md +3 -0
  159. package/packages/coworker-persona/src/index.ts +3 -0
  160. package/packages/coworker-persona/src/manifest.test.ts +67 -0
  161. package/packages/coworker-persona/src/manifest.ts +49 -0
  162. package/packages/coworker-persona/src/registry.test.ts +89 -0
  163. package/packages/coworker-persona/src/registry.ts +147 -0
  164. package/packages/coworker-persona/tsconfig.json +15 -0
  165. package/packages/coworker-persona/tsconfig.publish.json +4 -0
  166. package/packages/coworker-scratchpad/dist/cell-archive.d.ts +39 -0
  167. package/packages/coworker-scratchpad/dist/cell-archive.js +77 -0
  168. package/packages/coworker-scratchpad/dist/cell-tree.d.ts +14 -0
  169. package/packages/coworker-scratchpad/dist/cell-tree.js +72 -0
  170. package/packages/coworker-scratchpad/dist/child-process-runtime.d.ts +129 -0
  171. package/packages/coworker-scratchpad/dist/child-process-runtime.js +427 -0
  172. package/packages/coworker-scratchpad/dist/collector-registry.d.ts +12 -0
  173. package/packages/coworker-scratchpad/dist/collector-registry.js +29 -0
  174. package/packages/coworker-scratchpad/dist/detect-kind.d.ts +3 -0
  175. package/packages/coworker-scratchpad/dist/detect-kind.js +19 -0
  176. package/packages/coworker-scratchpad/dist/file-collector.d.ts +15 -0
  177. package/packages/coworker-scratchpad/dist/file-collector.js +99 -0
  178. package/packages/coworker-scratchpad/dist/index.d.ts +13 -0
  179. package/packages/coworker-scratchpad/dist/index.js +13 -0
  180. package/packages/coworker-scratchpad/dist/kernel-bindings.d.ts +49 -0
  181. package/packages/coworker-scratchpad/dist/kernel-bindings.js +220 -0
  182. package/packages/coworker-scratchpad/dist/kernel-entry.d.ts +1 -0
  183. package/packages/coworker-scratchpad/dist/kernel-entry.js +355 -0
  184. package/packages/coworker-scratchpad/dist/kernel-protocol.d.ts +171 -0
  185. package/packages/coworker-scratchpad/dist/kernel-protocol.js +48 -0
  186. package/packages/coworker-scratchpad/dist/kernel-spawn.d.ts +3 -0
  187. package/packages/coworker-scratchpad/dist/kernel-spawn.js +54 -0
  188. package/packages/coworker-scratchpad/dist/namespace-codec.d.ts +22 -0
  189. package/packages/coworker-scratchpad/dist/namespace-codec.js +61 -0
  190. package/packages/coworker-scratchpad/dist/scratchpad-lock.d.ts +24 -0
  191. package/packages/coworker-scratchpad/dist/scratchpad-lock.js +86 -0
  192. package/packages/coworker-scratchpad/dist/scratchpad-manager.d.ts +193 -0
  193. package/packages/coworker-scratchpad/dist/scratchpad-manager.js +866 -0
  194. package/packages/coworker-scratchpad/dist/staleness-banner.d.ts +12 -0
  195. package/packages/coworker-scratchpad/dist/staleness-banner.js +27 -0
  196. package/packages/coworker-scratchpad/package.json +31 -0
  197. package/packages/coworker-scratchpad/src/cell-archive.test.ts +150 -0
  198. package/packages/coworker-scratchpad/src/cell-archive.ts +97 -0
  199. package/packages/coworker-scratchpad/src/cell-tree.test.ts +105 -0
  200. package/packages/coworker-scratchpad/src/cell-tree.ts +90 -0
  201. package/packages/coworker-scratchpad/src/child-process-runtime.test.ts +413 -0
  202. package/packages/coworker-scratchpad/src/child-process-runtime.ts +493 -0
  203. package/packages/coworker-scratchpad/src/collector-registry.test.ts +69 -0
  204. package/packages/coworker-scratchpad/src/collector-registry.ts +33 -0
  205. package/packages/coworker-scratchpad/src/detect-kind.test.ts +33 -0
  206. package/packages/coworker-scratchpad/src/detect-kind.ts +22 -0
  207. package/packages/coworker-scratchpad/src/file-collector.test.ts +109 -0
  208. package/packages/coworker-scratchpad/src/file-collector.ts +114 -0
  209. package/packages/coworker-scratchpad/src/index.ts +74 -0
  210. package/packages/coworker-scratchpad/src/kernel-bindings.test.ts +188 -0
  211. package/packages/coworker-scratchpad/src/kernel-bindings.ts +279 -0
  212. package/packages/coworker-scratchpad/src/kernel-entry.test.ts +123 -0
  213. package/packages/coworker-scratchpad/src/kernel-entry.ts +390 -0
  214. package/packages/coworker-scratchpad/src/kernel-protocol.test.ts +105 -0
  215. package/packages/coworker-scratchpad/src/kernel-protocol.ts +230 -0
  216. package/packages/coworker-scratchpad/src/kernel-spawn.test.ts +60 -0
  217. package/packages/coworker-scratchpad/src/kernel-spawn.ts +54 -0
  218. package/packages/coworker-scratchpad/src/namespace-codec.test.ts +102 -0
  219. package/packages/coworker-scratchpad/src/namespace-codec.ts +90 -0
  220. package/packages/coworker-scratchpad/src/scratchpad-lock.test.ts +98 -0
  221. package/packages/coworker-scratchpad/src/scratchpad-lock.ts +102 -0
  222. package/packages/coworker-scratchpad/src/scratchpad-manager.test.ts +1343 -0
  223. package/packages/coworker-scratchpad/src/scratchpad-manager.ts +891 -0
  224. package/packages/coworker-scratchpad/src/staleness-banner.test.ts +53 -0
  225. package/packages/coworker-scratchpad/src/staleness-banner.ts +33 -0
  226. package/packages/coworker-scratchpad/src/vault-integration.test.ts +221 -0
  227. package/packages/coworker-scratchpad/tsconfig.json +15 -0
  228. package/packages/coworker-scratchpad/tsconfig.publish.json +4 -0
  229. package/packages/coworker-types/dist/artifacts.d.ts +31 -0
  230. package/packages/coworker-types/dist/artifacts.js +2 -0
  231. package/packages/coworker-types/dist/contracts.d.ts +32 -0
  232. package/packages/coworker-types/dist/contracts.js +1 -0
  233. package/packages/coworker-types/dist/index.d.ts +5 -0
  234. package/packages/coworker-types/dist/index.js +5 -0
  235. package/packages/coworker-types/dist/memory.d.ts +61 -0
  236. package/packages/coworker-types/dist/memory.js +3 -0
  237. package/packages/coworker-types/dist/scratchpad.d.ts +43 -0
  238. package/packages/coworker-types/dist/scratchpad.js +2 -0
  239. package/packages/coworker-types/dist/vault.d.ts +34 -0
  240. package/packages/coworker-types/dist/vault.js +2 -0
  241. package/packages/coworker-types/package.json +24 -0
  242. package/packages/coworker-types/src/artifacts.test.ts +52 -0
  243. package/packages/coworker-types/src/artifacts.ts +35 -0
  244. package/packages/coworker-types/src/contracts.test.ts +43 -0
  245. package/packages/coworker-types/src/contracts.ts +36 -0
  246. package/packages/coworker-types/src/index.ts +5 -0
  247. package/packages/coworker-types/src/memory.test.ts +50 -0
  248. package/packages/coworker-types/src/memory.ts +79 -0
  249. package/packages/coworker-types/src/scratchpad.test.ts +46 -0
  250. package/packages/coworker-types/src/scratchpad.ts +51 -0
  251. package/packages/coworker-types/src/smoke.test.ts +34 -0
  252. package/packages/coworker-types/src/vault.test.ts +49 -0
  253. package/packages/coworker-types/src/vault.ts +40 -0
  254. package/packages/coworker-types/tsconfig.json +15 -0
  255. package/packages/coworker-types/tsconfig.publish.json +4 -0
  256. package/packages/coworker-utils/dist/audit-log.d.ts +34 -0
  257. package/packages/coworker-utils/dist/audit-log.js +88 -0
  258. package/packages/coworker-utils/dist/index.d.ts +6 -0
  259. package/packages/coworker-utils/dist/index.js +6 -0
  260. package/packages/coworker-utils/dist/lease.d.ts +7 -0
  261. package/packages/coworker-utils/dist/lease.js +67 -0
  262. package/packages/coworker-utils/dist/logger.d.ts +13 -0
  263. package/packages/coworker-utils/dist/logger.js +26 -0
  264. package/packages/coworker-utils/dist/migration-runner.d.ts +7 -0
  265. package/packages/coworker-utils/dist/migration-runner.js +36 -0
  266. package/packages/coworker-utils/dist/ndjson-channel.d.ts +3 -0
  267. package/packages/coworker-utils/dist/ndjson-channel.js +38 -0
  268. package/packages/coworker-utils/dist/secret-scanner.d.ts +10 -0
  269. package/packages/coworker-utils/dist/secret-scanner.js +42 -0
  270. package/packages/coworker-utils/package.json +24 -0
  271. package/packages/coworker-utils/src/audit-log.test.ts +140 -0
  272. package/packages/coworker-utils/src/audit-log.ts +107 -0
  273. package/packages/coworker-utils/src/index.ts +6 -0
  274. package/packages/coworker-utils/src/lease.test.ts +64 -0
  275. package/packages/coworker-utils/src/lease.ts +76 -0
  276. package/packages/coworker-utils/src/logger.test.ts +50 -0
  277. package/packages/coworker-utils/src/logger.ts +45 -0
  278. package/packages/coworker-utils/src/migration-runner.test.ts +65 -0
  279. package/packages/coworker-utils/src/migration-runner.ts +50 -0
  280. package/packages/coworker-utils/src/ndjson-channel.test.ts +76 -0
  281. package/packages/coworker-utils/src/ndjson-channel.ts +41 -0
  282. package/packages/coworker-utils/src/secret-scanner.test.ts +61 -0
  283. package/packages/coworker-utils/src/secret-scanner.ts +56 -0
  284. package/packages/coworker-utils/tsconfig.json +15 -0
  285. package/packages/coworker-utils/tsconfig.publish.json +4 -0
  286. package/packages/coworker-vault/dist/data-vault.d.ts +41 -0
  287. package/packages/coworker-vault/dist/data-vault.js +223 -0
  288. package/packages/coworker-vault/dist/engine-registry.d.ts +34 -0
  289. package/packages/coworker-vault/dist/engine-registry.js +90 -0
  290. package/packages/coworker-vault/dist/engines/jira.yaml +17 -0
  291. package/packages/coworker-vault/dist/errors.d.ts +28 -0
  292. package/packages/coworker-vault/dist/errors.js +57 -0
  293. package/packages/coworker-vault/dist/index.d.ts +6 -0
  294. package/packages/coworker-vault/dist/index.js +6 -0
  295. package/packages/coworker-vault/dist/injector.d.ts +19 -0
  296. package/packages/coworker-vault/dist/injector.js +77 -0
  297. package/packages/coworker-vault/dist/types.d.ts +28 -0
  298. package/packages/coworker-vault/dist/types.js +1 -0
  299. package/packages/coworker-vault/dist/vault-keep.d.ts +4 -0
  300. package/packages/coworker-vault/dist/vault-keep.js +21 -0
  301. package/packages/coworker-vault/package.json +29 -0
  302. package/packages/coworker-vault/src/data-vault.test.ts +199 -0
  303. package/packages/coworker-vault/src/data-vault.ts +257 -0
  304. package/packages/coworker-vault/src/engine-registry.test.ts +120 -0
  305. package/packages/coworker-vault/src/engine-registry.ts +107 -0
  306. package/packages/coworker-vault/src/engines/jira.yaml +17 -0
  307. package/packages/coworker-vault/src/errors.test.ts +58 -0
  308. package/packages/coworker-vault/src/errors.ts +50 -0
  309. package/packages/coworker-vault/src/index.test.ts +24 -0
  310. package/packages/coworker-vault/src/index.ts +6 -0
  311. package/packages/coworker-vault/src/injector.test.ts +109 -0
  312. package/packages/coworker-vault/src/injector.ts +98 -0
  313. package/packages/coworker-vault/src/types.ts +33 -0
  314. package/packages/coworker-vault/src/vault-keep.test.ts +49 -0
  315. package/packages/coworker-vault/src/vault-keep.ts +31 -0
  316. package/packages/coworker-vault/tsconfig.json +15 -0
  317. package/packages/coworker-vault/tsconfig.publish.json +4 -0
  318. package/packages/daemon/package.json +3 -3
  319. package/packages/mcp-server/package.json +3 -3
  320. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  321. package/packages/native/package.json +1 -1
  322. package/packages/native/tsconfig.tsbuildinfo +1 -1
  323. package/packages/pi-agent-core/package.json +1 -1
  324. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  325. package/packages/pi-ai/package.json +1 -1
  326. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  327. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +6 -1
  328. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  329. package/packages/pi-coding-agent/dist/core/extensions/runner.js +22 -3
  330. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  331. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +11 -0
  332. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  333. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.d.ts +47 -0
  334. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.d.ts.map +1 -0
  335. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.js +107 -0
  336. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.js.map +1 -0
  337. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.d.ts +19 -0
  338. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.d.ts.map +1 -0
  339. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.js +121 -0
  340. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.js.map +1 -0
  341. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  342. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +17 -1
  343. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  344. package/packages/pi-coding-agent/package.json +2 -2
  345. package/packages/pi-coding-agent/src/core/extensions/runner.ts +22 -3
  346. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +11 -0
  347. package/packages/pi-coding-agent/src/modes/rpc/raw-stdout.regression.test.ts +129 -0
  348. package/packages/pi-coding-agent/src/modes/rpc/raw-stdout.ts +117 -0
  349. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +18 -1
  350. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  351. package/packages/pi-tui/package.json +1 -1
  352. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  353. package/packages/rpc-client/package.json +2 -2
  354. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  355. package/pkg/package.json +1 -1
  356. package/scripts/install.js +6 -5
  357. package/src/resources/extensions/_coworker-paths.test.ts +40 -0
  358. package/src/resources/extensions/_coworker-paths.ts +10 -0
  359. package/src/resources/extensions/coworker-artifacts/artifacts-command.test.ts +54 -0
  360. package/src/resources/extensions/coworker-artifacts/artifacts-command.ts +43 -0
  361. package/src/resources/extensions/coworker-artifacts/artifacts-singleton.test.ts +25 -0
  362. package/src/resources/extensions/coworker-artifacts/artifacts-singleton.ts +29 -0
  363. package/src/resources/extensions/coworker-artifacts/extension-manifest.json +13 -0
  364. package/src/resources/extensions/coworker-artifacts/index.test.ts +46 -0
  365. package/src/resources/extensions/coworker-artifacts/index.ts +154 -0
  366. package/src/resources/extensions/coworker-artifacts/list-tool.test.ts +29 -0
  367. package/src/resources/extensions/coworker-artifacts/list-tool.ts +53 -0
  368. package/src/resources/extensions/coworker-artifacts/open-tool.test.ts +30 -0
  369. package/src/resources/extensions/coworker-artifacts/open-tool.ts +43 -0
  370. package/src/resources/extensions/coworker-memory/extension-manifest.json +13 -0
  371. package/src/resources/extensions/coworker-memory/index.test.ts +137 -0
  372. package/src/resources/extensions/coworker-memory/index.ts +257 -0
  373. package/src/resources/extensions/coworker-memory/memorize-tool.test.ts +41 -0
  374. package/src/resources/extensions/coworker-memory/memorize-tool.ts +20 -0
  375. package/src/resources/extensions/coworker-memory/memory-command.test.ts +134 -0
  376. package/src/resources/extensions/coworker-memory/memory-command.ts +131 -0
  377. package/src/resources/extensions/coworker-memory/memory-singleton.test.ts +41 -0
  378. package/src/resources/extensions/coworker-memory/memory-singleton.ts +89 -0
  379. package/src/resources/extensions/coworker-memory/recall-tool.test.ts +50 -0
  380. package/src/resources/extensions/coworker-memory/recall-tool.ts +35 -0
  381. package/src/resources/extensions/coworker-memory/session-hooks.test.ts +77 -0
  382. package/src/resources/extensions/coworker-memory/session-hooks.ts +61 -0
  383. package/src/resources/extensions/coworker-scratchpad/attach-banners.test.ts +124 -0
  384. package/src/resources/extensions/coworker-scratchpad/attach-banners.ts +67 -0
  385. package/src/resources/extensions/coworker-scratchpad/extension-manifest.json +13 -0
  386. package/src/resources/extensions/coworker-scratchpad/format-age.test.ts +30 -0
  387. package/src/resources/extensions/coworker-scratchpad/format-age.ts +6 -0
  388. package/src/resources/extensions/coworker-scratchpad/helpers.test.ts +93 -0
  389. package/src/resources/extensions/coworker-scratchpad/helpers.ts +42 -0
  390. package/src/resources/extensions/coworker-scratchpad/index.test.ts +514 -0
  391. package/src/resources/extensions/coworker-scratchpad/index.ts +207 -0
  392. package/src/resources/extensions/coworker-scratchpad/mime-bundle.test.ts +61 -0
  393. package/src/resources/extensions/coworker-scratchpad/mime-bundle.ts +23 -0
  394. package/src/resources/extensions/coworker-scratchpad/scratchpad-tool.test.ts +137 -0
  395. package/src/resources/extensions/coworker-scratchpad/scratchpad-tool.ts +165 -0
  396. package/src/resources/extensions/coworker-scratchpad/session-sidecar.test.ts +133 -0
  397. package/src/resources/extensions/coworker-scratchpad/session-sidecar.ts +68 -0
  398. package/src/resources/extensions/coworker-scratchpad/sp-command.test.ts +836 -0
  399. package/src/resources/extensions/coworker-scratchpad/sp-command.ts +602 -0
  400. package/src/resources/extensions/coworker-scratchpad/workspace-pointer.test.ts +74 -0
  401. package/src/resources/extensions/coworker-scratchpad/workspace-pointer.ts +55 -0
  402. package/src/resources/extensions/coworker-scratchpad/workspace-root.test.ts +51 -0
  403. package/src/resources/extensions/coworker-scratchpad/workspace-root.ts +16 -0
  404. package/src/resources/extensions/coworker-vault/audit-command.test.ts +109 -0
  405. package/src/resources/extensions/coworker-vault/audit-command.ts +56 -0
  406. package/src/resources/extensions/coworker-vault/connect-command.test.ts +103 -0
  407. package/src/resources/extensions/coworker-vault/connect-command.ts +69 -0
  408. package/src/resources/extensions/coworker-vault/datasource-command.test.ts +80 -0
  409. package/src/resources/extensions/coworker-vault/datasource-command.ts +81 -0
  410. package/src/resources/extensions/coworker-vault/extension-manifest.json +12 -0
  411. package/src/resources/extensions/coworker-vault/index.test.ts +82 -0
  412. package/src/resources/extensions/coworker-vault/index.ts +181 -0
  413. package/src/resources/extensions/coworker-vault/test-helpers.ts +120 -0
  414. package/src/resources/extensions/coworker-vault/vault-singleton.test.ts +27 -0
  415. package/src/resources/extensions/coworker-vault/vault-singleton.ts +40 -0
  416. package/src/resources/extensions/otto/commands/release-notes/_data.ts +96 -0
  417. package/src/resources/extensions/otto/commands/release-notes/command.ts +16 -3
  418. package/src/resources/extensions/subagent/index.ts +9 -0
  419. package/src/resources/extensions/subagent/launch.test.ts +97 -0
  420. package/src/resources/extensions/subagent/launch.ts +42 -5
  421. package/src/resources/extensions/subagent/run-store.ts +3 -1
  422. package/src/resources/extensions/workflow/bootstrap/register-extension.ts +2 -0
  423. package/src/resources/extensions/workflow/bootstrap/register-hooks.ts +10 -0
  424. package/src/resources/extensions/workflow/persona-status.ts +109 -0
  425. package/src/resources/extensions/workflow/tests/auto-recovery.test.ts +34 -0
@@ -0,0 +1,157 @@
1
+ import { runMemorize } from './memorize-tool.js';
2
+ import { runRecall } from './recall-tool.js';
3
+ export async function runMemoryCommand(bundle, argv) {
4
+ const [sub, ...rest] = argv;
5
+ switch (sub) {
6
+ case 'note': {
7
+ const text = rest.join(' ').trim();
8
+ if (!text)
9
+ throw new Error('Usage: /memory note <text>');
10
+ await runMemorize(bundle, { text, kind: 'lesson', scope: 'workspace' });
11
+ return { message: `lesson stored in workspace.` };
12
+ }
13
+ case 'status': {
14
+ const status = await bundle.backend.status();
15
+ return {
16
+ message: [
17
+ `scope_mode: ${bundle.scopeMode}`,
18
+ `workspace_wing: ${bundle.workspaceWing}`,
19
+ `drawer_count: ${status.drawer_count}`,
20
+ `layer_b_db_path: ${status.layer_b_db_path}`,
21
+ `schema_version: ${status.schema_version}`,
22
+ ].join('\n'),
23
+ };
24
+ }
25
+ case 'clear': {
26
+ let wing;
27
+ let confirm = false;
28
+ for (let i = 0; i < rest.length; i++) {
29
+ if (rest[i] === '--wing' && rest[i + 1]) {
30
+ wing = rest[++i];
31
+ }
32
+ else if (rest[i] === '--confirm') {
33
+ confirm = true;
34
+ }
35
+ }
36
+ if (!confirm)
37
+ throw new Error('Usage: /memory clear --wing <wing> --confirm');
38
+ const out = await bundle.backend.clear({ wing, confirm: true });
39
+ return { message: `deleted: ${out.deleted}` };
40
+ }
41
+ case 'wing':
42
+ case 'room': {
43
+ // Session overrides — caller manages state; this is a placeholder return acknowledging.
44
+ const target = rest.join(' ').trim();
45
+ if (!target)
46
+ throw new Error(`Usage: /memory ${sub} <name>`);
47
+ return { message: `${sub} override: ${target}` };
48
+ }
49
+ case 'seed': {
50
+ // Caller wires this to applyPersonaSeed via session-hooks; here we just acknowledge the request.
51
+ return { message: 're-seed will run on next session_start; flip workspace.json.memory_seed_applied=false and reattach.' };
52
+ }
53
+ case 'show': {
54
+ let target = 'all';
55
+ let scopeOverride = null;
56
+ for (let i = 0; i < rest.length; i++) {
57
+ const t = rest[i];
58
+ if (t === '--scope' && rest[i + 1]) {
59
+ const s = rest[++i];
60
+ if (s !== 'workspace' && s !== 'global')
61
+ throw new Error(`Usage: /memory show [profile|rules|lessons] [--scope workspace|global]`);
62
+ scopeOverride = s;
63
+ }
64
+ else if (t === 'profile') {
65
+ target = 'profile';
66
+ }
67
+ else if (t === 'rule' || t === 'rules') {
68
+ target = 'rule';
69
+ }
70
+ else if (t === 'lesson' || t === 'lessons') {
71
+ target = 'lesson';
72
+ }
73
+ else if (t === 'all') {
74
+ target = 'all';
75
+ }
76
+ else {
77
+ throw new Error(`Usage: /memory show [profile|rules|lessons] [--scope workspace|global]`);
78
+ }
79
+ }
80
+ const kinds = target === 'all' ? ['profile', 'rule', 'lesson'] : [target];
81
+ // Determine scopes: --scope override wins; otherwise derive from bundle.scopeMode.
82
+ const scopes = [];
83
+ if (scopeOverride === 'workspace') {
84
+ scopes.push({ name: 'workspace', store: bundle.workspaceLayerA });
85
+ }
86
+ else if (scopeOverride === 'global') {
87
+ scopes.push({ name: 'global', store: bundle.globalLayerA });
88
+ }
89
+ else if (bundle.scopeMode === 'global') {
90
+ scopes.push({ name: 'global', store: bundle.globalLayerA });
91
+ }
92
+ else if (bundle.scopeMode === 'per-project') {
93
+ scopes.push({ name: 'workspace', store: bundle.workspaceLayerA });
94
+ }
95
+ else {
96
+ // per-project-tagged: workspace first, then global
97
+ scopes.push({ name: 'workspace', store: bundle.workspaceLayerA });
98
+ scopes.push({ name: 'global', store: bundle.globalLayerA });
99
+ }
100
+ const TITLE_FOR = { profile: 'Profile', rule: 'Rules', lesson: 'Lessons' };
101
+ const sections = [];
102
+ for (const k of kinds) {
103
+ for (const scope of scopes) {
104
+ const body = await scope.store.read(k);
105
+ sections.push(`## ${TITLE_FOR[k]} (${scope.name})\n${body || '(none)'}`);
106
+ }
107
+ }
108
+ return { message: sections.join('\n\n') };
109
+ }
110
+ case 'recall': {
111
+ // First flag/positional split: gather positional tokens for the query;
112
+ // recognize --kind, --room, --wing, --limit, --days_back.
113
+ const flags = {};
114
+ const queryTokens = [];
115
+ for (let i = 0; i < rest.length; i++) {
116
+ const t = rest[i];
117
+ if (t === '--kind' && rest[i + 1]) {
118
+ flags.kind = rest[++i];
119
+ }
120
+ else if (t === '--room' && rest[i + 1]) {
121
+ flags.room = rest[++i];
122
+ }
123
+ else if (t === '--wing' && rest[i + 1]) {
124
+ flags.wing = rest[++i];
125
+ }
126
+ else if (t === '--limit' && rest[i + 1]) {
127
+ const n = parseInt(rest[++i], 10);
128
+ if (Number.isFinite(n))
129
+ flags.limit = n;
130
+ }
131
+ else if (t === '--days_back' && rest[i + 1]) {
132
+ const n = parseInt(rest[++i], 10);
133
+ if (Number.isFinite(n))
134
+ flags.days_back = n;
135
+ }
136
+ else {
137
+ queryTokens.push(t);
138
+ }
139
+ }
140
+ const query = queryTokens.join(' ').trim();
141
+ if (!query)
142
+ throw new Error('Usage: /memory recall <query> [--kind <k>] [--room <r>] [--wing <w>] [--limit N] [--days_back N]');
143
+ // Defer to the same runRecall the LLM tool uses for parity.
144
+ const out = await runRecall(bundle, {
145
+ query,
146
+ kind: flags.kind, // runRecall accepts string; backend validates
147
+ room: flags.room,
148
+ wing: flags.wing,
149
+ max_results: flags.limit,
150
+ days_back: flags.days_back,
151
+ });
152
+ return { message: out.markdown };
153
+ }
154
+ default:
155
+ throw new Error(`Unknown /memory subcommand: ${sub}. Try: note, status, clear, wing, room, seed, show, recall.`);
156
+ }
157
+ }
@@ -0,0 +1,55 @@
1
+ // src/resources/extensions/coworker-memory/memory-singleton.ts
2
+ //
3
+ // Factory that constructs the layered memory collaborators (LayerAStore x2,
4
+ // LocalSqliteBackend, MemoryRecorder) plus shared audit + secret scanner for
5
+ // given roots. Mirrors the coworker-vault singleton pattern. Pure beyond what
6
+ // the underlying constructors already perform (dir creation, db open, etc.).
7
+ import { join } from 'node:path';
8
+ import { AuditLog, SecretScanner } from '@otto/coworker-utils';
9
+ import { LayerAStore, LocalSqliteBackend, MemoryRecorder, resolveScope, resolveWorkspaceId, } from '@otto/coworker-memory';
10
+ export async function createMemoryBundle(opts) {
11
+ const audit = new AuditLog({ path: join(opts.globalDir, 'audit.jsonl') });
12
+ const scanner = new SecretScanner();
13
+ const wsRecord = await resolveWorkspaceId(opts.workspaceDir);
14
+ const scope = resolveScope({ mode: opts.scopeMode, workspaceId: wsRecord.id });
15
+ const globalLayerA = new LayerAStore({
16
+ scopeDir: join(opts.globalDir, 'memory'),
17
+ scope: 'global',
18
+ audit,
19
+ scanner,
20
+ });
21
+ const workspaceLayerA = new LayerAStore({
22
+ scopeDir: join(opts.workspaceDir, '.otto', 'memory'),
23
+ scope: 'workspace',
24
+ audit,
25
+ scanner,
26
+ });
27
+ const backend = new LocalSqliteBackend({
28
+ dbPath: join(opts.workspaceDir, '.otto', 'memory', 'layer-b.db'),
29
+ });
30
+ await backend.open();
31
+ const recorder = new MemoryRecorder({
32
+ backend,
33
+ scanner,
34
+ audit,
35
+ writeWing: scope.writeWing,
36
+ currentScratchpadName: opts.currentScratchpadName,
37
+ });
38
+ return {
39
+ globalLayerA,
40
+ workspaceLayerA,
41
+ backend,
42
+ recorder,
43
+ audit,
44
+ scanner,
45
+ workspaceWing: wsRecord.id,
46
+ writeWing: scope.writeWing,
47
+ readWings: scope.readWings,
48
+ scopeMode: opts.scopeMode,
49
+ workspaceRecord: wsRecord,
50
+ workspaceDir: opts.workspaceDir,
51
+ async dispose() {
52
+ await backend.close();
53
+ },
54
+ };
55
+ }
@@ -0,0 +1,18 @@
1
+ import { formatRecall } from '@otto/coworker-memory';
2
+ export async function runRecall(bundle, args) {
3
+ const wings = args.wing ? [args.wing, ...bundle.readWings.filter(w => w !== args.wing)] : bundle.readWings;
4
+ const limit = args.max_results === undefined ? 8 : Math.min(Math.max(args.max_results, 1), 64);
5
+ const results = await bundle.backend.recall({
6
+ query: args.query, wing: wings, room: args.room, kind: args.kind,
7
+ days_back: args.days_back, max_results: limit,
8
+ });
9
+ bundle.audit.append({
10
+ _schema: 1, ts: new Date().toISOString(), producer: 'memory', action: 'recall',
11
+ detail: {
12
+ wing_filter: wings, room_filter: args.room ?? null,
13
+ kind_filter: args.kind ?? null, days_back: args.days_back ?? null,
14
+ result_count: results.length,
15
+ },
16
+ });
17
+ return { results, markdown: formatRecall(results) };
18
+ }
@@ -0,0 +1,45 @@
1
+ // src/resources/extensions/coworker-memory/session-hooks.ts
2
+ //
3
+ // Session lifecycle seams for the coworker-memory extension.
4
+ // - onSessionStart: applies a one-shot persona seed (if pending) and returns
5
+ // the Layer A context block ready for system-prompt injection.
6
+ // - onSessionShutdown: closes the backend (Layer B). Safe to double-call —
7
+ // LocalSqliteBackend.close() nulls its db handle so a second close is a no-op.
8
+ import { buildLayerAContext, applyPersonaSeed, writeWorkspaceId } from '@otto/coworker-memory';
9
+ export async function onSessionStart(bundle, opts = {}) {
10
+ let seed = { copied: [], blocked: [] };
11
+ if (opts.persona && !bundle.workspaceRecord.memory_seed_applied) {
12
+ seed = await applyPersonaSeed({
13
+ personaId: opts.persona.id,
14
+ personaDir: opts.persona.personaDir,
15
+ store: bundle.workspaceLayerA,
16
+ });
17
+ if (seed.copied.length > 0 || seed.blocked.length > 0) {
18
+ bundle.workspaceRecord.memory_seed_applied = true;
19
+ bundle.workspaceRecord.memory_seed_persona = opts.persona.id;
20
+ await writeWorkspaceId(bundle.workspaceDir, bundle.workspaceRecord);
21
+ bundle.audit.append({
22
+ _schema: 1,
23
+ ts: new Date().toISOString(),
24
+ producer: 'memory',
25
+ action: 'seed-applied',
26
+ detail: {
27
+ persona_id: opts.persona.id,
28
+ files_copied: seed.copied,
29
+ files_blocked: seed.blocked,
30
+ },
31
+ });
32
+ }
33
+ }
34
+ const contextBlock = await buildLayerAContext({
35
+ mode: bundle.scopeMode,
36
+ globalStore: bundle.globalLayerA,
37
+ workspaceStore: bundle.workspaceLayerA,
38
+ tokenLimit: opts.tokenLimit ?? 3000,
39
+ });
40
+ return { contextBlock, seed };
41
+ }
42
+ export async function onSessionShutdown(bundle) {
43
+ // WAL checkpoint happens on backend close; this is the seam.
44
+ await bundle.dispose();
45
+ }
@@ -0,0 +1,53 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ export function showRecoveryNotesBanner(name, rootDir, ui) {
4
+ const metaPath = join(rootDir, name, 'meta.json');
5
+ if (!existsSync(metaPath))
6
+ return { unseenCount: 0, markSeen: false };
7
+ let meta;
8
+ try {
9
+ meta = JSON.parse(readFileSync(metaPath, 'utf8'));
10
+ }
11
+ catch {
12
+ return { unseenCount: 0, markSeen: false };
13
+ }
14
+ const notes = Array.isArray(meta.recovery_notes) ? meta.recovery_notes : [];
15
+ if (notes.length === 0)
16
+ return { unseenCount: 0, markSeen: false };
17
+ const seenAt = typeof meta.recovery_notes_seen_at === 'string' ? meta.recovery_notes_seen_at : null;
18
+ const unseen = notes.filter((n) => seenAt === null || n.at > seenAt);
19
+ if (unseen.length === 0)
20
+ return { unseenCount: 0, markSeen: false };
21
+ const head = unseen.slice(0, 5).map(formatNoteLine).join('\n');
22
+ const tail = unseen.length > 5 ? `\n+ ${unseen.length - 5} more (run /sp notes)` : '';
23
+ ui.notify(`⚠ ${unseen.length} unread recovery notes:\n${head}${tail}`, 'warning');
24
+ return { unseenCount: unseen.length, markSeen: true };
25
+ }
26
+ export function showDivergenceBanner(name, rootDir, ui) {
27
+ const metaPath = join(rootDir, name, 'meta.json');
28
+ if (!existsSync(metaPath))
29
+ return { diverged: false };
30
+ let meta;
31
+ try {
32
+ meta = JSON.parse(readFileSync(metaPath, 'utf8'));
33
+ }
34
+ catch {
35
+ return { diverged: false };
36
+ }
37
+ const leaf = typeof meta.cell_leaf_id === 'number' ? meta.cell_leaf_id : null;
38
+ const kernel = typeof meta.kernel_at_cell_id === 'number' ? meta.kernel_at_cell_id : null;
39
+ if (leaf === null || kernel === null || leaf === kernel)
40
+ return { diverged: false };
41
+ ui.notify(`ℹ kernel state is at cell #${kernel}; view is at cell #${leaf} (run /sp tree to inspect)`, 'info');
42
+ return { diverged: true };
43
+ }
44
+ export function formatNoteLine(n) {
45
+ const ts = n.at.slice(0, 19);
46
+ switch (n.kind) {
47
+ case 'snapshot-failed': return ` • [${ts}] snapshot-failed: ${n.message}`;
48
+ case 'cells-since-snapshot': return ` • [${ts}] ${n.n} cells since last snapshot`;
49
+ case 'namespace-corrupt': return ` • [${ts}] namespace-corrupt: ${n.message}`;
50
+ case 'namespace-absent': return ` • [${ts}] namespace-absent`;
51
+ default: return ` • [${ts}] ${n.kind}`;
52
+ }
53
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "id": "coworker-scratchpad",
3
+ "name": "Co-worker Scratchpad",
4
+ "version": "1.0.0",
5
+ "description": "Persistent TypeScript scratchpad kernel: /sp slash commands and cw_scratchpad tool with MIME bundle responses",
6
+ "tier": "bundled",
7
+ "requires": { "platform": ">=2.29.0" },
8
+ "provides": {
9
+ "tools": ["cw_scratchpad"],
10
+ "commands": ["sp"],
11
+ "hooks": ["session_start", "session_shutdown"]
12
+ }
13
+ }
@@ -0,0 +1,9 @@
1
+ export function formatRelativeAge(ageMs) {
2
+ if (ageMs < 30_000)
3
+ return 'active';
4
+ if (ageMs < 60 * 60_000)
5
+ return `idle ${Math.floor(ageMs / 60_000)}m`;
6
+ if (ageMs < 24 * 60 * 60_000)
7
+ return `idle ${Math.floor(ageMs / (60 * 60_000))}h`;
8
+ return `idle ${Math.floor(ageMs / (24 * 60 * 60_000))}d`;
9
+ }
@@ -0,0 +1,38 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ export const SCRATCHPAD_NAME_REGEX = /^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/;
4
+ export function validateName(name) {
5
+ if (!SCRATCHPAD_NAME_REGEX.test(name)) {
6
+ throw new Error(`invalid scratchpad name: ${JSON.stringify(name)} (must match ${SCRATCHPAD_NAME_REGEX})`);
7
+ }
8
+ }
9
+ export function readCellsJsonl(dir) {
10
+ const path = join(dir, 'cells.jsonl');
11
+ if (!existsSync(path))
12
+ return { cells: [], total_cells: 0 };
13
+ const cells = [];
14
+ for (const line of readFileSync(path, 'utf8').split('\n')) {
15
+ if (!line.trim())
16
+ continue;
17
+ try {
18
+ const obj = JSON.parse(line);
19
+ if (typeof obj.id === 'number')
20
+ cells.push(obj);
21
+ }
22
+ catch {
23
+ // header line or trailing corrupt line -> skip (same tolerance as CellArchive.scan)
24
+ }
25
+ }
26
+ return { cells, total_cells: cells.length };
27
+ }
28
+ export function readPersistedLeaf(metaPath) {
29
+ if (!existsSync(metaPath))
30
+ return null;
31
+ try {
32
+ const meta = JSON.parse(readFileSync(metaPath, 'utf8'));
33
+ return typeof meta.cell_leaf_id === 'number' ? meta.cell_leaf_id : null;
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
@@ -0,0 +1,199 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
+ import { join, basename } from 'node:path';
3
+ import { ScratchpadManager } from '@otto/coworker-scratchpad';
4
+ import { getScratchpadsRoot } from '../_coworker-paths.js';
5
+ import { getMemoryRecorder } from '../coworker-memory/index.js';
6
+ import { getArtifactStore } from '../coworker-artifacts/index.js';
7
+ import { registerSpCommand } from './sp-command.js';
8
+ import { registerScratchpadTool } from './scratchpad-tool.js';
9
+ import { sessionSidecarPath, readSessionSidecar, deleteSessionSidecar, sweepStaleSidecars } from './session-sidecar.js';
10
+ import { detectWorkspaceRoot } from './workspace-root.js';
11
+ import { workspaceHash, workspacePointerPath, readWorkspacePointer, isPointerFresh } from './workspace-pointer.js';
12
+ import { formatRelativeAge } from './format-age.js';
13
+ function deriveSessionId(ctx) {
14
+ const file = ctx.sessionManager.getSessionFile();
15
+ if (!file)
16
+ return 'default';
17
+ const base = basename(file);
18
+ return base.endsWith('.jsonl') ? base.slice(0, -6) : base;
19
+ }
20
+ /**
21
+ * Computes restore precedence with sidecar-fallback. Returns the scratchpad to restore
22
+ * (if any) and the notice string to surface to the user. Does NOT call ctx.ui.notify —
23
+ * the caller does.
24
+ *
25
+ * Side-effect: deletes a broken sidecar (one whose referenced scratchpad no longer
26
+ * exists) so subsequent fresh sessions don't repeatedly re-check it. Not a pure
27
+ * function — a cleanup-closure refactor was considered and rejected as over-engineering
28
+ * for a single in-process call site.
29
+ *
30
+ * Precedence:
31
+ * (a) per-session sidecar — wins when its scratchpad still exists on disk.
32
+ * (b) broken sidecar — silently deleted, falls through to (c).
33
+ * (c) workspace pointer — fresh (≤ 7d) AND its scratchpad still exists on disk.
34
+ * (d) no restore.
35
+ */
36
+ export function tryRestoreCurrentName(root, sessionId, cwd, now) {
37
+ // (a) Sidecar restore
38
+ const sidecarPath = sessionSidecarPath(root, sessionId);
39
+ const sidecar = readSessionSidecar(sidecarPath);
40
+ if (sidecar) {
41
+ const meta = join(root, sidecar.current_name, 'meta.json');
42
+ if (existsSync(meta)) {
43
+ return { name: sidecar.current_name, notice: `attached to ${sidecar.current_name} (restored)` };
44
+ }
45
+ deleteSessionSidecar(sidecarPath); // broken sidecar — clean up silently and fall through
46
+ }
47
+ // (b) Workspace-pointer fallback
48
+ const wsRoot = detectWorkspaceRoot(cwd);
49
+ const ptr = readWorkspacePointer(workspacePointerPath(root, workspaceHash(wsRoot)));
50
+ if (ptr && isPointerFresh(ptr, now)) {
51
+ const meta = join(root, ptr.last_current_name, 'meta.json');
52
+ if (existsSync(meta)) {
53
+ const rel = formatRelativeAge(now - Date.parse(ptr.last_attached_at));
54
+ return {
55
+ name: ptr.last_current_name,
56
+ notice: `attached to ${ptr.last_current_name} (from workspace, last used ${rel})`,
57
+ };
58
+ }
59
+ }
60
+ // (c) No restore
61
+ return { name: null, notice: null };
62
+ }
63
+ const SUBAGENT_NAME_REGEX = /^subagent-[a-z0-9-]+$/;
64
+ /**
65
+ * Phase 4.5: When the OTTO_SUBAGENT_SCRATCHPAD env var is set, force-attach
66
+ * to the named scratchpad (creating it if needed) BEFORE the sidecar/pointer
67
+ * restore path runs. Idempotent: re-running with same name attaches without
68
+ * overwriting existing state.
69
+ */
70
+ export function forceSubagentAttach(name, scratchpadsRoot) {
71
+ if (!SUBAGENT_NAME_REGEX.test(name) || name.length > 80) {
72
+ return { ok: false, reason: `invalid subagent scratchpad name: ${name}` };
73
+ }
74
+ const dir = join(scratchpadsRoot, name);
75
+ try {
76
+ if (!existsSync(dir)) {
77
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
78
+ }
79
+ const metaPath = join(dir, 'meta.json');
80
+ if (!existsSync(metaPath)) {
81
+ const meta = {
82
+ name,
83
+ schema_version: 1,
84
+ created_at: new Date().toISOString(),
85
+ source: 'subagent-dispatch',
86
+ };
87
+ writeFileSync(metaPath, JSON.stringify(meta, null, 2), { mode: 0o600 });
88
+ }
89
+ return { ok: true };
90
+ }
91
+ catch (err) {
92
+ return { ok: false, reason: err.message };
93
+ }
94
+ }
95
+ export default function coworkerScratchpadExtension(pi) {
96
+ let manager = null;
97
+ let workspaceCwd = null;
98
+ let sessionId = null;
99
+ let currentName = null;
100
+ const root = getScratchpadsRoot();
101
+ const getManager = () => {
102
+ if (!manager) {
103
+ if (!workspaceCwd)
104
+ throw new Error('scratchpad: manager requested before session_start');
105
+ manager = new ScratchpadManager({
106
+ workspace: workspaceCwd,
107
+ root,
108
+ sessionId: sessionId ?? 'default',
109
+ // Phase 4 Task 11/12: lazy getter for the artifact store — returns null
110
+ // before artifacts session_start or after session_shutdown. Cross-pillar
111
+ // wiring is one-way (scratchpad → artifacts), so activator load order
112
+ // is indifferent.
113
+ getArtifactStore,
114
+ // Phase 3.1 Task 4: production hop into memory. `getMemoryRecorder()` is
115
+ // called lazily on each event so activator load order is indifferent —
116
+ // returns null before memory session_start or after session_shutdown,
117
+ // which is the correct no-op signal. Failures are swallowed silently
118
+ // (spec §3.8): file loads are frequent and a memory backend hiccup must
119
+ // not break the kernel runtime; surfaces in /audit instead.
120
+ onDataLoad: (drawer, scratchpadName) => {
121
+ const recorder = getMemoryRecorder();
122
+ if (!recorder)
123
+ return;
124
+ void recorder.recordFileLoad({
125
+ scratchpadName,
126
+ collector: drawer.collector,
127
+ uri: drawer.uri,
128
+ bytes: drawer.bytes ?? 0,
129
+ rows_loaded: drawer.rows_loaded ?? undefined,
130
+ schema: drawer.schema ?? undefined,
131
+ turnId: '',
132
+ }).catch(() => { });
133
+ },
134
+ // Phase 4 Task 12: production hop into memory for artifact-create events.
135
+ // Mirrors the onDataLoad pattern: lazy recorder lookup, silent failures
136
+ // (spec §3.9). drawer.artifact_kind is the actual artifact kind
137
+ // (e.g. 'report'); drawer.kind is the discriminator literal 'artifact'.
138
+ onArtifactCreate: (drawer, scratchpadName) => {
139
+ const recorder = getMemoryRecorder();
140
+ if (!recorder)
141
+ return;
142
+ void recorder.recordArtifact({
143
+ scratchpadName,
144
+ slug: drawer.slug,
145
+ kind: drawer.artifact_kind,
146
+ uri: drawer.uri,
147
+ turnId: '',
148
+ }).catch(() => { });
149
+ },
150
+ });
151
+ }
152
+ return manager;
153
+ };
154
+ const getCurrentName = () => currentName;
155
+ const setCurrentName = (n) => { currentName = n; };
156
+ const rootDir = () => root;
157
+ const getSessionId = () => sessionId ?? 'default';
158
+ const getWorkspaceCwd = () => workspaceCwd ?? process.cwd();
159
+ registerSpCommand(pi, { getManager, getCurrentName, setCurrentName, rootDir, getSessionId, getWorkspaceCwd });
160
+ registerScratchpadTool(pi, { getManager, getCurrentName, setCurrentName, rootDir });
161
+ pi.on('session_start', async (_event, ctx) => {
162
+ workspaceCwd = ctx.cwd;
163
+ sessionId = deriveSessionId(ctx);
164
+ // Phase 4.5: force-attach to subagent scratchpad if OTTO_SUBAGENT_SCRATCHPAD is set.
165
+ const subagentName = process.env.OTTO_SUBAGENT_SCRATCHPAD;
166
+ if (subagentName) {
167
+ const result = forceSubagentAttach(subagentName, root);
168
+ if (result.ok) {
169
+ currentName = subagentName;
170
+ ctx.ui.notify(`attached to ${subagentName} (subagent dispatch)`, 'info');
171
+ try {
172
+ sweepStaleSidecars(root, sessionId, Date.now());
173
+ }
174
+ catch { /* silent */ }
175
+ return;
176
+ }
177
+ ctx.ui.notify(`subagent scratchpad attach failed: ${result.reason}; continuing without`, 'warning');
178
+ // fall through to normal restore
179
+ }
180
+ const restore = tryRestoreCurrentName(root, sessionId, ctx.cwd ?? process.cwd(), Date.now());
181
+ if (restore.name) {
182
+ currentName = restore.name;
183
+ ctx.ui.notify(restore.notice, 'info');
184
+ }
185
+ // When neither sidecar nor fresh pointer resolves to an existing scratchpad,
186
+ // we stay silent — the user gets a clean session_start with no noise.
187
+ try {
188
+ sweepStaleSidecars(root, sessionId, Date.now());
189
+ }
190
+ catch { /* sweep failures are silent */ }
191
+ });
192
+ pi.on('session_shutdown', async () => {
193
+ if (manager) {
194
+ await manager.disposeAll();
195
+ manager = null;
196
+ }
197
+ // Sidecar deliberately NOT deleted here — survives so /resume restores.
198
+ });
199
+ }
@@ -0,0 +1,20 @@
1
+ export function deriveMimeBundle(value, stdout) {
2
+ const bundle = {};
3
+ if (stdout.length > 0)
4
+ bundle['text/plain'] = stdout;
5
+ if (value !== undefined && value !== null)
6
+ bundle['application/json'] = value;
7
+ if (typeof value === 'string' && looksLikeMarkdown(value)) {
8
+ bundle['text/markdown'] = value;
9
+ }
10
+ return bundle;
11
+ }
12
+ function looksLikeMarkdown(s) {
13
+ const trimmed = s.trimStart();
14
+ if (trimmed.startsWith('#') || trimmed.startsWith('|'))
15
+ return true;
16
+ // GFM table separator row: a line that is just |---|---|...
17
+ if (/\n\s*\|[-:|\s]+\|\s*\n/.test(s))
18
+ return true;
19
+ return false;
20
+ }