@cmetech/otto 1.1.1 → 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 (423) 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 +71 -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/package.json +25 -10
  47. package/packages/contracts/package.json +1 -1
  48. package/packages/coworker-artifacts/dist/artifact-store.d.ts +25 -0
  49. package/packages/coworker-artifacts/dist/artifact-store.js +187 -0
  50. package/packages/coworker-artifacts/dist/dir-snapshot.d.ts +7 -0
  51. package/packages/coworker-artifacts/dist/dir-snapshot.js +54 -0
  52. package/packages/coworker-artifacts/dist/errors.d.ts +18 -0
  53. package/packages/coworker-artifacts/dist/errors.js +37 -0
  54. package/packages/coworker-artifacts/dist/index.d.ts +7 -0
  55. package/packages/coworker-artifacts/dist/index.js +7 -0
  56. package/packages/coworker-artifacts/dist/readme-renderer.d.ts +5 -0
  57. package/packages/coworker-artifacts/dist/readme-renderer.js +47 -0
  58. package/packages/coworker-artifacts/dist/resolve-uri.d.ts +3 -0
  59. package/packages/coworker-artifacts/dist/resolve-uri.js +29 -0
  60. package/packages/coworker-artifacts/dist/slug.d.ts +4 -0
  61. package/packages/coworker-artifacts/dist/slug.js +32 -0
  62. package/packages/coworker-artifacts/dist/types.d.ts +52 -0
  63. package/packages/coworker-artifacts/dist/types.js +1 -0
  64. package/packages/coworker-artifacts/package.json +20 -0
  65. package/packages/coworker-artifacts/src/artifact-store.test.ts +188 -0
  66. package/packages/coworker-artifacts/src/artifact-store.ts +206 -0
  67. package/packages/coworker-artifacts/src/artifacts-integration.test.ts +109 -0
  68. package/packages/coworker-artifacts/src/dir-snapshot.test.ts +71 -0
  69. package/packages/coworker-artifacts/src/dir-snapshot.ts +52 -0
  70. package/packages/coworker-artifacts/src/errors.test.ts +37 -0
  71. package/packages/coworker-artifacts/src/errors.ts +28 -0
  72. package/packages/coworker-artifacts/src/index.test.ts +22 -0
  73. package/packages/coworker-artifacts/src/index.ts +7 -0
  74. package/packages/coworker-artifacts/src/readme-renderer.test.ts +72 -0
  75. package/packages/coworker-artifacts/src/readme-renderer.ts +56 -0
  76. package/packages/coworker-artifacts/src/resolve-uri.test.ts +46 -0
  77. package/packages/coworker-artifacts/src/resolve-uri.ts +29 -0
  78. package/packages/coworker-artifacts/src/slug.test.ts +47 -0
  79. package/packages/coworker-artifacts/src/slug.ts +31 -0
  80. package/packages/coworker-artifacts/src/types.ts +61 -0
  81. package/packages/coworker-artifacts/tsconfig.json +15 -0
  82. package/packages/coworker-artifacts/tsconfig.publish.json +4 -0
  83. package/packages/coworker-memory/dist/context-injection.d.ts +9 -0
  84. package/packages/coworker-memory/dist/context-injection.js +41 -0
  85. package/packages/coworker-memory/dist/errors.d.ts +25 -0
  86. package/packages/coworker-memory/dist/errors.js +51 -0
  87. package/packages/coworker-memory/dist/index.d.ts +12 -0
  88. package/packages/coworker-memory/dist/index.js +12 -0
  89. package/packages/coworker-memory/dist/layer-a-store.d.ts +16 -0
  90. package/packages/coworker-memory/dist/layer-a-store.js +78 -0
  91. package/packages/coworker-memory/dist/local-sqlite-backend.d.ts +28 -0
  92. package/packages/coworker-memory/dist/local-sqlite-backend.js +167 -0
  93. package/packages/coworker-memory/dist/memory-backend.d.ts +14 -0
  94. package/packages/coworker-memory/dist/memory-backend.js +1 -0
  95. package/packages/coworker-memory/dist/memory-recorder.d.ts +50 -0
  96. package/packages/coworker-memory/dist/memory-recorder.js +69 -0
  97. package/packages/coworker-memory/dist/migrations/001-init.sql +38 -0
  98. package/packages/coworker-memory/dist/migrations/002-artifact-kind.sql +50 -0
  99. package/packages/coworker-memory/dist/paste-detector.d.ts +5 -0
  100. package/packages/coworker-memory/dist/paste-detector.js +14 -0
  101. package/packages/coworker-memory/dist/persona-seed.d.ts +10 -0
  102. package/packages/coworker-memory/dist/persona-seed.js +38 -0
  103. package/packages/coworker-memory/dist/recall-formatter.d.ts +2 -0
  104. package/packages/coworker-memory/dist/recall-formatter.js +14 -0
  105. package/packages/coworker-memory/dist/scope-resolver.d.ts +9 -0
  106. package/packages/coworker-memory/dist/scope-resolver.js +10 -0
  107. package/packages/coworker-memory/dist/types.d.ts +51 -0
  108. package/packages/coworker-memory/dist/types.js +2 -0
  109. package/packages/coworker-memory/dist/workspace-id.d.ts +3 -0
  110. package/packages/coworker-memory/dist/workspace-id.js +54 -0
  111. package/packages/coworker-memory/package.json +35 -0
  112. package/packages/coworker-memory/src/activator-integration.test.ts +141 -0
  113. package/packages/coworker-memory/src/context-injection.test.ts +72 -0
  114. package/packages/coworker-memory/src/context-injection.ts +57 -0
  115. package/packages/coworker-memory/src/errors.test.ts +45 -0
  116. package/packages/coworker-memory/src/errors.ts +42 -0
  117. package/packages/coworker-memory/src/index.test.ts +21 -0
  118. package/packages/coworker-memory/src/index.ts +12 -0
  119. package/packages/coworker-memory/src/layer-a-store.test.ts +85 -0
  120. package/packages/coworker-memory/src/layer-a-store.ts +88 -0
  121. package/packages/coworker-memory/src/local-sqlite-backend.test.ts +110 -0
  122. package/packages/coworker-memory/src/local-sqlite-backend.ts +185 -0
  123. package/packages/coworker-memory/src/memory-backend.ts +10 -0
  124. package/packages/coworker-memory/src/memory-integration.test.ts +89 -0
  125. package/packages/coworker-memory/src/memory-recorder.test.ts +101 -0
  126. package/packages/coworker-memory/src/memory-recorder.ts +95 -0
  127. package/packages/coworker-memory/src/migrations/001-init.sql +38 -0
  128. package/packages/coworker-memory/src/migrations/002-artifact-kind.sql +50 -0
  129. package/packages/coworker-memory/src/paste-detector.test.ts +23 -0
  130. package/packages/coworker-memory/src/paste-detector.ts +18 -0
  131. package/packages/coworker-memory/src/persona-seed.test.ts +57 -0
  132. package/packages/coworker-memory/src/persona-seed.ts +46 -0
  133. package/packages/coworker-memory/src/recall-formatter.test.ts +34 -0
  134. package/packages/coworker-memory/src/recall-formatter.ts +15 -0
  135. package/packages/coworker-memory/src/scope-resolver.test.ts +23 -0
  136. package/packages/coworker-memory/src/scope-resolver.ts +18 -0
  137. package/packages/coworker-memory/src/types.ts +61 -0
  138. package/packages/coworker-memory/src/workspace-id.test.ts +48 -0
  139. package/packages/coworker-memory/src/workspace-id.ts +56 -0
  140. package/packages/coworker-memory/tsconfig.json +15 -0
  141. package/packages/coworker-memory/tsconfig.publish.json +4 -0
  142. package/packages/coworker-persona/dist/commands.d.ts +7 -0
  143. package/packages/coworker-persona/dist/commands.js +35 -0
  144. package/packages/coworker-persona/dist/defaults/manifest.yaml +12 -0
  145. package/packages/coworker-persona/dist/defaults/steering/identity.md +3 -0
  146. package/packages/coworker-persona/dist/index.d.ts +3 -0
  147. package/packages/coworker-persona/dist/index.js +3 -0
  148. package/packages/coworker-persona/dist/manifest.d.ts +24 -0
  149. package/packages/coworker-persona/dist/manifest.js +21 -0
  150. package/packages/coworker-persona/dist/registry.d.ts +22 -0
  151. package/packages/coworker-persona/dist/registry.js +142 -0
  152. package/packages/coworker-persona/package.json +28 -0
  153. package/packages/coworker-persona/scripts/copy-defaults.cjs +17 -0
  154. package/packages/coworker-persona/src/commands.ts +47 -0
  155. package/packages/coworker-persona/src/defaults/manifest.yaml +12 -0
  156. package/packages/coworker-persona/src/defaults/steering/identity.md +3 -0
  157. package/packages/coworker-persona/src/index.ts +3 -0
  158. package/packages/coworker-persona/src/manifest.test.ts +67 -0
  159. package/packages/coworker-persona/src/manifest.ts +49 -0
  160. package/packages/coworker-persona/src/registry.test.ts +89 -0
  161. package/packages/coworker-persona/src/registry.ts +147 -0
  162. package/packages/coworker-persona/tsconfig.json +15 -0
  163. package/packages/coworker-persona/tsconfig.publish.json +4 -0
  164. package/packages/coworker-scratchpad/dist/cell-archive.d.ts +39 -0
  165. package/packages/coworker-scratchpad/dist/cell-archive.js +77 -0
  166. package/packages/coworker-scratchpad/dist/cell-tree.d.ts +14 -0
  167. package/packages/coworker-scratchpad/dist/cell-tree.js +72 -0
  168. package/packages/coworker-scratchpad/dist/child-process-runtime.d.ts +129 -0
  169. package/packages/coworker-scratchpad/dist/child-process-runtime.js +427 -0
  170. package/packages/coworker-scratchpad/dist/collector-registry.d.ts +12 -0
  171. package/packages/coworker-scratchpad/dist/collector-registry.js +29 -0
  172. package/packages/coworker-scratchpad/dist/detect-kind.d.ts +3 -0
  173. package/packages/coworker-scratchpad/dist/detect-kind.js +19 -0
  174. package/packages/coworker-scratchpad/dist/file-collector.d.ts +15 -0
  175. package/packages/coworker-scratchpad/dist/file-collector.js +99 -0
  176. package/packages/coworker-scratchpad/dist/index.d.ts +13 -0
  177. package/packages/coworker-scratchpad/dist/index.js +13 -0
  178. package/packages/coworker-scratchpad/dist/kernel-bindings.d.ts +49 -0
  179. package/packages/coworker-scratchpad/dist/kernel-bindings.js +220 -0
  180. package/packages/coworker-scratchpad/dist/kernel-entry.d.ts +1 -0
  181. package/packages/coworker-scratchpad/dist/kernel-entry.js +355 -0
  182. package/packages/coworker-scratchpad/dist/kernel-protocol.d.ts +171 -0
  183. package/packages/coworker-scratchpad/dist/kernel-protocol.js +48 -0
  184. package/packages/coworker-scratchpad/dist/kernel-spawn.d.ts +3 -0
  185. package/packages/coworker-scratchpad/dist/kernel-spawn.js +54 -0
  186. package/packages/coworker-scratchpad/dist/namespace-codec.d.ts +22 -0
  187. package/packages/coworker-scratchpad/dist/namespace-codec.js +61 -0
  188. package/packages/coworker-scratchpad/dist/scratchpad-lock.d.ts +24 -0
  189. package/packages/coworker-scratchpad/dist/scratchpad-lock.js +86 -0
  190. package/packages/coworker-scratchpad/dist/scratchpad-manager.d.ts +193 -0
  191. package/packages/coworker-scratchpad/dist/scratchpad-manager.js +866 -0
  192. package/packages/coworker-scratchpad/dist/staleness-banner.d.ts +12 -0
  193. package/packages/coworker-scratchpad/dist/staleness-banner.js +27 -0
  194. package/packages/coworker-scratchpad/package.json +31 -0
  195. package/packages/coworker-scratchpad/src/cell-archive.test.ts +150 -0
  196. package/packages/coworker-scratchpad/src/cell-archive.ts +97 -0
  197. package/packages/coworker-scratchpad/src/cell-tree.test.ts +105 -0
  198. package/packages/coworker-scratchpad/src/cell-tree.ts +90 -0
  199. package/packages/coworker-scratchpad/src/child-process-runtime.test.ts +413 -0
  200. package/packages/coworker-scratchpad/src/child-process-runtime.ts +493 -0
  201. package/packages/coworker-scratchpad/src/collector-registry.test.ts +69 -0
  202. package/packages/coworker-scratchpad/src/collector-registry.ts +33 -0
  203. package/packages/coworker-scratchpad/src/detect-kind.test.ts +33 -0
  204. package/packages/coworker-scratchpad/src/detect-kind.ts +22 -0
  205. package/packages/coworker-scratchpad/src/file-collector.test.ts +109 -0
  206. package/packages/coworker-scratchpad/src/file-collector.ts +114 -0
  207. package/packages/coworker-scratchpad/src/index.ts +74 -0
  208. package/packages/coworker-scratchpad/src/kernel-bindings.test.ts +188 -0
  209. package/packages/coworker-scratchpad/src/kernel-bindings.ts +279 -0
  210. package/packages/coworker-scratchpad/src/kernel-entry.test.ts +123 -0
  211. package/packages/coworker-scratchpad/src/kernel-entry.ts +390 -0
  212. package/packages/coworker-scratchpad/src/kernel-protocol.test.ts +105 -0
  213. package/packages/coworker-scratchpad/src/kernel-protocol.ts +230 -0
  214. package/packages/coworker-scratchpad/src/kernel-spawn.test.ts +60 -0
  215. package/packages/coworker-scratchpad/src/kernel-spawn.ts +54 -0
  216. package/packages/coworker-scratchpad/src/namespace-codec.test.ts +102 -0
  217. package/packages/coworker-scratchpad/src/namespace-codec.ts +90 -0
  218. package/packages/coworker-scratchpad/src/scratchpad-lock.test.ts +98 -0
  219. package/packages/coworker-scratchpad/src/scratchpad-lock.ts +102 -0
  220. package/packages/coworker-scratchpad/src/scratchpad-manager.test.ts +1343 -0
  221. package/packages/coworker-scratchpad/src/scratchpad-manager.ts +891 -0
  222. package/packages/coworker-scratchpad/src/staleness-banner.test.ts +53 -0
  223. package/packages/coworker-scratchpad/src/staleness-banner.ts +33 -0
  224. package/packages/coworker-scratchpad/src/vault-integration.test.ts +221 -0
  225. package/packages/coworker-scratchpad/tsconfig.json +15 -0
  226. package/packages/coworker-scratchpad/tsconfig.publish.json +4 -0
  227. package/packages/coworker-types/dist/artifacts.d.ts +31 -0
  228. package/packages/coworker-types/dist/artifacts.js +2 -0
  229. package/packages/coworker-types/dist/contracts.d.ts +32 -0
  230. package/packages/coworker-types/dist/contracts.js +1 -0
  231. package/packages/coworker-types/dist/index.d.ts +5 -0
  232. package/packages/coworker-types/dist/index.js +5 -0
  233. package/packages/coworker-types/dist/memory.d.ts +61 -0
  234. package/packages/coworker-types/dist/memory.js +3 -0
  235. package/packages/coworker-types/dist/scratchpad.d.ts +43 -0
  236. package/packages/coworker-types/dist/scratchpad.js +2 -0
  237. package/packages/coworker-types/dist/vault.d.ts +34 -0
  238. package/packages/coworker-types/dist/vault.js +2 -0
  239. package/packages/coworker-types/package.json +24 -0
  240. package/packages/coworker-types/src/artifacts.test.ts +52 -0
  241. package/packages/coworker-types/src/artifacts.ts +35 -0
  242. package/packages/coworker-types/src/contracts.test.ts +43 -0
  243. package/packages/coworker-types/src/contracts.ts +36 -0
  244. package/packages/coworker-types/src/index.ts +5 -0
  245. package/packages/coworker-types/src/memory.test.ts +50 -0
  246. package/packages/coworker-types/src/memory.ts +79 -0
  247. package/packages/coworker-types/src/scratchpad.test.ts +46 -0
  248. package/packages/coworker-types/src/scratchpad.ts +51 -0
  249. package/packages/coworker-types/src/smoke.test.ts +34 -0
  250. package/packages/coworker-types/src/vault.test.ts +49 -0
  251. package/packages/coworker-types/src/vault.ts +40 -0
  252. package/packages/coworker-types/tsconfig.json +15 -0
  253. package/packages/coworker-types/tsconfig.publish.json +4 -0
  254. package/packages/coworker-utils/dist/audit-log.d.ts +34 -0
  255. package/packages/coworker-utils/dist/audit-log.js +88 -0
  256. package/packages/coworker-utils/dist/index.d.ts +6 -0
  257. package/packages/coworker-utils/dist/index.js +6 -0
  258. package/packages/coworker-utils/dist/lease.d.ts +7 -0
  259. package/packages/coworker-utils/dist/lease.js +67 -0
  260. package/packages/coworker-utils/dist/logger.d.ts +13 -0
  261. package/packages/coworker-utils/dist/logger.js +26 -0
  262. package/packages/coworker-utils/dist/migration-runner.d.ts +7 -0
  263. package/packages/coworker-utils/dist/migration-runner.js +36 -0
  264. package/packages/coworker-utils/dist/ndjson-channel.d.ts +3 -0
  265. package/packages/coworker-utils/dist/ndjson-channel.js +38 -0
  266. package/packages/coworker-utils/dist/secret-scanner.d.ts +10 -0
  267. package/packages/coworker-utils/dist/secret-scanner.js +42 -0
  268. package/packages/coworker-utils/package.json +24 -0
  269. package/packages/coworker-utils/src/audit-log.test.ts +140 -0
  270. package/packages/coworker-utils/src/audit-log.ts +107 -0
  271. package/packages/coworker-utils/src/index.ts +6 -0
  272. package/packages/coworker-utils/src/lease.test.ts +64 -0
  273. package/packages/coworker-utils/src/lease.ts +76 -0
  274. package/packages/coworker-utils/src/logger.test.ts +50 -0
  275. package/packages/coworker-utils/src/logger.ts +45 -0
  276. package/packages/coworker-utils/src/migration-runner.test.ts +65 -0
  277. package/packages/coworker-utils/src/migration-runner.ts +50 -0
  278. package/packages/coworker-utils/src/ndjson-channel.test.ts +76 -0
  279. package/packages/coworker-utils/src/ndjson-channel.ts +41 -0
  280. package/packages/coworker-utils/src/secret-scanner.test.ts +61 -0
  281. package/packages/coworker-utils/src/secret-scanner.ts +56 -0
  282. package/packages/coworker-utils/tsconfig.json +15 -0
  283. package/packages/coworker-utils/tsconfig.publish.json +4 -0
  284. package/packages/coworker-vault/dist/data-vault.d.ts +41 -0
  285. package/packages/coworker-vault/dist/data-vault.js +223 -0
  286. package/packages/coworker-vault/dist/engine-registry.d.ts +34 -0
  287. package/packages/coworker-vault/dist/engine-registry.js +90 -0
  288. package/packages/coworker-vault/dist/engines/jira.yaml +17 -0
  289. package/packages/coworker-vault/dist/errors.d.ts +28 -0
  290. package/packages/coworker-vault/dist/errors.js +57 -0
  291. package/packages/coworker-vault/dist/index.d.ts +6 -0
  292. package/packages/coworker-vault/dist/index.js +6 -0
  293. package/packages/coworker-vault/dist/injector.d.ts +19 -0
  294. package/packages/coworker-vault/dist/injector.js +77 -0
  295. package/packages/coworker-vault/dist/types.d.ts +28 -0
  296. package/packages/coworker-vault/dist/types.js +1 -0
  297. package/packages/coworker-vault/dist/vault-keep.d.ts +4 -0
  298. package/packages/coworker-vault/dist/vault-keep.js +21 -0
  299. package/packages/coworker-vault/package.json +29 -0
  300. package/packages/coworker-vault/src/data-vault.test.ts +199 -0
  301. package/packages/coworker-vault/src/data-vault.ts +257 -0
  302. package/packages/coworker-vault/src/engine-registry.test.ts +120 -0
  303. package/packages/coworker-vault/src/engine-registry.ts +107 -0
  304. package/packages/coworker-vault/src/engines/jira.yaml +17 -0
  305. package/packages/coworker-vault/src/errors.test.ts +58 -0
  306. package/packages/coworker-vault/src/errors.ts +50 -0
  307. package/packages/coworker-vault/src/index.test.ts +24 -0
  308. package/packages/coworker-vault/src/index.ts +6 -0
  309. package/packages/coworker-vault/src/injector.test.ts +109 -0
  310. package/packages/coworker-vault/src/injector.ts +98 -0
  311. package/packages/coworker-vault/src/types.ts +33 -0
  312. package/packages/coworker-vault/src/vault-keep.test.ts +49 -0
  313. package/packages/coworker-vault/src/vault-keep.ts +31 -0
  314. package/packages/coworker-vault/tsconfig.json +15 -0
  315. package/packages/coworker-vault/tsconfig.publish.json +4 -0
  316. package/packages/daemon/package.json +3 -3
  317. package/packages/mcp-server/package.json +3 -3
  318. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  319. package/packages/native/package.json +1 -1
  320. package/packages/native/tsconfig.tsbuildinfo +1 -1
  321. package/packages/pi-agent-core/package.json +1 -1
  322. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  323. package/packages/pi-ai/package.json +1 -1
  324. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  325. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +6 -1
  326. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  327. package/packages/pi-coding-agent/dist/core/extensions/runner.js +22 -3
  328. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  329. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +11 -0
  330. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  331. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.d.ts +47 -0
  332. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.d.ts.map +1 -0
  333. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.js +107 -0
  334. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.js.map +1 -0
  335. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.d.ts +19 -0
  336. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.d.ts.map +1 -0
  337. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.js +121 -0
  338. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.js.map +1 -0
  339. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  340. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +17 -1
  341. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  342. package/packages/pi-coding-agent/package.json +2 -2
  343. package/packages/pi-coding-agent/src/core/extensions/runner.ts +22 -3
  344. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +11 -0
  345. package/packages/pi-coding-agent/src/modes/rpc/raw-stdout.regression.test.ts +129 -0
  346. package/packages/pi-coding-agent/src/modes/rpc/raw-stdout.ts +117 -0
  347. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +18 -1
  348. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  349. package/packages/pi-tui/package.json +1 -1
  350. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  351. package/packages/rpc-client/package.json +2 -2
  352. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  353. package/pkg/package.json +1 -1
  354. package/scripts/install.js +6 -5
  355. package/src/resources/extensions/_coworker-paths.test.ts +40 -0
  356. package/src/resources/extensions/_coworker-paths.ts +10 -0
  357. package/src/resources/extensions/coworker-artifacts/artifacts-command.test.ts +54 -0
  358. package/src/resources/extensions/coworker-artifacts/artifacts-command.ts +43 -0
  359. package/src/resources/extensions/coworker-artifacts/artifacts-singleton.test.ts +25 -0
  360. package/src/resources/extensions/coworker-artifacts/artifacts-singleton.ts +29 -0
  361. package/src/resources/extensions/coworker-artifacts/extension-manifest.json +13 -0
  362. package/src/resources/extensions/coworker-artifacts/index.test.ts +46 -0
  363. package/src/resources/extensions/coworker-artifacts/index.ts +154 -0
  364. package/src/resources/extensions/coworker-artifacts/list-tool.test.ts +29 -0
  365. package/src/resources/extensions/coworker-artifacts/list-tool.ts +53 -0
  366. package/src/resources/extensions/coworker-artifacts/open-tool.test.ts +30 -0
  367. package/src/resources/extensions/coworker-artifacts/open-tool.ts +43 -0
  368. package/src/resources/extensions/coworker-memory/extension-manifest.json +13 -0
  369. package/src/resources/extensions/coworker-memory/index.test.ts +137 -0
  370. package/src/resources/extensions/coworker-memory/index.ts +257 -0
  371. package/src/resources/extensions/coworker-memory/memorize-tool.test.ts +41 -0
  372. package/src/resources/extensions/coworker-memory/memorize-tool.ts +20 -0
  373. package/src/resources/extensions/coworker-memory/memory-command.test.ts +134 -0
  374. package/src/resources/extensions/coworker-memory/memory-command.ts +131 -0
  375. package/src/resources/extensions/coworker-memory/memory-singleton.test.ts +41 -0
  376. package/src/resources/extensions/coworker-memory/memory-singleton.ts +89 -0
  377. package/src/resources/extensions/coworker-memory/recall-tool.test.ts +50 -0
  378. package/src/resources/extensions/coworker-memory/recall-tool.ts +35 -0
  379. package/src/resources/extensions/coworker-memory/session-hooks.test.ts +77 -0
  380. package/src/resources/extensions/coworker-memory/session-hooks.ts +61 -0
  381. package/src/resources/extensions/coworker-scratchpad/attach-banners.test.ts +124 -0
  382. package/src/resources/extensions/coworker-scratchpad/attach-banners.ts +67 -0
  383. package/src/resources/extensions/coworker-scratchpad/extension-manifest.json +13 -0
  384. package/src/resources/extensions/coworker-scratchpad/format-age.test.ts +30 -0
  385. package/src/resources/extensions/coworker-scratchpad/format-age.ts +6 -0
  386. package/src/resources/extensions/coworker-scratchpad/helpers.test.ts +93 -0
  387. package/src/resources/extensions/coworker-scratchpad/helpers.ts +42 -0
  388. package/src/resources/extensions/coworker-scratchpad/index.test.ts +514 -0
  389. package/src/resources/extensions/coworker-scratchpad/index.ts +207 -0
  390. package/src/resources/extensions/coworker-scratchpad/mime-bundle.test.ts +61 -0
  391. package/src/resources/extensions/coworker-scratchpad/mime-bundle.ts +23 -0
  392. package/src/resources/extensions/coworker-scratchpad/scratchpad-tool.test.ts +137 -0
  393. package/src/resources/extensions/coworker-scratchpad/scratchpad-tool.ts +165 -0
  394. package/src/resources/extensions/coworker-scratchpad/session-sidecar.test.ts +133 -0
  395. package/src/resources/extensions/coworker-scratchpad/session-sidecar.ts +68 -0
  396. package/src/resources/extensions/coworker-scratchpad/sp-command.test.ts +836 -0
  397. package/src/resources/extensions/coworker-scratchpad/sp-command.ts +602 -0
  398. package/src/resources/extensions/coworker-scratchpad/workspace-pointer.test.ts +74 -0
  399. package/src/resources/extensions/coworker-scratchpad/workspace-pointer.ts +55 -0
  400. package/src/resources/extensions/coworker-scratchpad/workspace-root.test.ts +51 -0
  401. package/src/resources/extensions/coworker-scratchpad/workspace-root.ts +16 -0
  402. package/src/resources/extensions/coworker-vault/audit-command.test.ts +109 -0
  403. package/src/resources/extensions/coworker-vault/audit-command.ts +56 -0
  404. package/src/resources/extensions/coworker-vault/connect-command.test.ts +103 -0
  405. package/src/resources/extensions/coworker-vault/connect-command.ts +69 -0
  406. package/src/resources/extensions/coworker-vault/datasource-command.test.ts +80 -0
  407. package/src/resources/extensions/coworker-vault/datasource-command.ts +81 -0
  408. package/src/resources/extensions/coworker-vault/extension-manifest.json +12 -0
  409. package/src/resources/extensions/coworker-vault/index.test.ts +82 -0
  410. package/src/resources/extensions/coworker-vault/index.ts +181 -0
  411. package/src/resources/extensions/coworker-vault/test-helpers.ts +120 -0
  412. package/src/resources/extensions/coworker-vault/vault-singleton.test.ts +27 -0
  413. package/src/resources/extensions/coworker-vault/vault-singleton.ts +40 -0
  414. package/src/resources/extensions/otto/commands/release-notes/_data.ts +85 -0
  415. package/src/resources/extensions/otto/commands/release-notes/command.ts +16 -3
  416. package/src/resources/extensions/subagent/index.ts +9 -0
  417. package/src/resources/extensions/subagent/launch.test.ts +97 -0
  418. package/src/resources/extensions/subagent/launch.ts +42 -5
  419. package/src/resources/extensions/subagent/run-store.ts +3 -1
  420. package/src/resources/extensions/workflow/bootstrap/register-extension.ts +2 -0
  421. package/src/resources/extensions/workflow/bootstrap/register-hooks.ts +10 -0
  422. package/src/resources/extensions/workflow/persona-status.ts +109 -0
  423. package/src/resources/extensions/workflow/tests/auto-recovery.test.ts +34 -0
@@ -0,0 +1,38 @@
1
+ -- packages/coworker-memory/src/migrations/001-init.sql
2
+ PRAGMA journal_mode = WAL;
3
+ PRAGMA user_version = 1;
4
+ PRAGMA foreign_keys = ON;
5
+
6
+ CREATE TABLE IF NOT EXISTS drawers (
7
+ id TEXT PRIMARY KEY,
8
+ wing TEXT NOT NULL,
9
+ room TEXT NOT NULL,
10
+ kind TEXT NOT NULL CHECK (kind IN ('turn','paste','file_load','ticket','email','rca','note')),
11
+ content TEXT NOT NULL,
12
+ metadata_json TEXT NOT NULL DEFAULT '{}',
13
+ parent_id TEXT REFERENCES drawers(id) ON DELETE SET NULL,
14
+ redacted INTEGER NOT NULL DEFAULT 0,
15
+ created_at TEXT NOT NULL
16
+ );
17
+
18
+ CREATE INDEX IF NOT EXISTS idx_drawers_wing_room ON drawers (wing, room);
19
+ CREATE INDEX IF NOT EXISTS idx_drawers_kind ON drawers (kind);
20
+ CREATE INDEX IF NOT EXISTS idx_drawers_created_at ON drawers (created_at);
21
+
22
+ CREATE VIRTUAL TABLE IF NOT EXISTS drawers_fts USING fts5 (
23
+ content,
24
+ content='drawers',
25
+ content_rowid='rowid',
26
+ tokenize='unicode61 remove_diacritics 2'
27
+ );
28
+
29
+ CREATE TRIGGER IF NOT EXISTS drawers_ai AFTER INSERT ON drawers BEGIN
30
+ INSERT INTO drawers_fts (rowid, content) VALUES (new.rowid, new.content);
31
+ END;
32
+ CREATE TRIGGER IF NOT EXISTS drawers_ad AFTER DELETE ON drawers BEGIN
33
+ INSERT INTO drawers_fts (drawers_fts, rowid, content) VALUES ('delete', old.rowid, old.content);
34
+ END;
35
+ CREATE TRIGGER IF NOT EXISTS drawers_au AFTER UPDATE ON drawers BEGIN
36
+ INSERT INTO drawers_fts (drawers_fts, rowid, content) VALUES ('delete', old.rowid, old.content);
37
+ INSERT INTO drawers_fts (rowid, content) VALUES (new.rowid, new.content);
38
+ END;
@@ -0,0 +1,50 @@
1
+ -- packages/coworker-memory/src/migrations/002-artifact-kind.sql
2
+ -- Adds 'artifact' to the drawers.kind CHECK constraint. SQLite can't ALTER
3
+ -- constraints, so the table is rebuilt. Dropping the old table also drops
4
+ -- its triggers, so we recreate them. The FTS index is rebuilt from the
5
+ -- new drawers table to keep rowid alignment.
6
+
7
+ BEGIN;
8
+
9
+ CREATE TABLE drawers_new (
10
+ id TEXT PRIMARY KEY,
11
+ wing TEXT NOT NULL,
12
+ room TEXT NOT NULL,
13
+ kind TEXT NOT NULL CHECK (kind IN
14
+ ('turn','paste','file_load','ticket','email','rca','note','artifact')),
15
+ content TEXT NOT NULL,
16
+ metadata_json TEXT NOT NULL DEFAULT '{}',
17
+ parent_id TEXT REFERENCES drawers_new(id) ON DELETE SET NULL,
18
+ redacted INTEGER NOT NULL DEFAULT 0,
19
+ created_at TEXT NOT NULL
20
+ );
21
+
22
+ INSERT INTO drawers_new (id, wing, room, kind, content, metadata_json, parent_id, redacted, created_at)
23
+ SELECT id, wing, room, kind, content, metadata_json, parent_id, redacted, created_at FROM drawers;
24
+
25
+ DROP TABLE drawers;
26
+ ALTER TABLE drawers_new RENAME TO drawers;
27
+
28
+ -- Recreate indexes (the rename preserves rowids but not separately-named indexes).
29
+ CREATE INDEX IF NOT EXISTS idx_drawers_wing_room ON drawers (wing, room);
30
+ CREATE INDEX IF NOT EXISTS idx_drawers_kind ON drawers (kind);
31
+ CREATE INDEX IF NOT EXISTS idx_drawers_created_at ON drawers (created_at);
32
+
33
+ -- Recreate triggers (dropped when the old drawers table was dropped).
34
+ CREATE TRIGGER IF NOT EXISTS drawers_ai AFTER INSERT ON drawers BEGIN
35
+ INSERT INTO drawers_fts (rowid, content) VALUES (new.rowid, new.content);
36
+ END;
37
+ CREATE TRIGGER IF NOT EXISTS drawers_ad AFTER DELETE ON drawers BEGIN
38
+ INSERT INTO drawers_fts (drawers_fts, rowid, content) VALUES ('delete', old.rowid, old.content);
39
+ END;
40
+ CREATE TRIGGER IF NOT EXISTS drawers_au AFTER UPDATE ON drawers BEGIN
41
+ INSERT INTO drawers_fts (drawers_fts, rowid, content) VALUES ('delete', old.rowid, old.content);
42
+ INSERT INTO drawers_fts (rowid, content) VALUES (new.rowid, new.content);
43
+ END;
44
+
45
+ -- Rebuild FTS index so rowids align with the new drawers table.
46
+ INSERT INTO drawers_fts (drawers_fts) VALUES ('rebuild');
47
+
48
+ PRAGMA user_version = 2;
49
+
50
+ COMMIT;
@@ -0,0 +1,5 @@
1
+ export interface PasteDetectorOptions {
2
+ lengthThreshold?: number;
3
+ newlineThreshold?: number;
4
+ }
5
+ export declare function detectPaste(text: string, opts?: PasteDetectorOptions): 'turn' | 'paste';
@@ -0,0 +1,14 @@
1
+ const DEFAULT_LENGTH = 500;
2
+ const DEFAULT_NEWLINES = 10;
3
+ export function detectPaste(text, opts = {}) {
4
+ const lengthThreshold = opts.lengthThreshold ?? DEFAULT_LENGTH;
5
+ const newlineThreshold = opts.newlineThreshold ?? DEFAULT_NEWLINES;
6
+ if (/```/.test(text))
7
+ return 'paste';
8
+ if (text.length >= lengthThreshold)
9
+ return 'paste';
10
+ const newlines = (text.match(/\n/g) ?? []).length;
11
+ if (newlines > newlineThreshold)
12
+ return 'paste';
13
+ return 'turn';
14
+ }
@@ -0,0 +1,10 @@
1
+ import type { LayerAStore } from './layer-a-store.js';
2
+ export interface SeedResult {
3
+ copied: string[];
4
+ blocked: string[];
5
+ }
6
+ export declare function applyPersonaSeed(args: {
7
+ personaId: string;
8
+ personaDir: string;
9
+ store: LayerAStore;
10
+ }): Promise<SeedResult>;
@@ -0,0 +1,38 @@
1
+ // packages/coworker-memory/src/persona-seed.ts
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { LayerAWriteBlocked } from './errors.js';
5
+ const FILE_MAP = [
6
+ { name: 'profile.md', kind: 'profile' },
7
+ { name: 'rules.md', kind: 'rule' },
8
+ { name: 'lessons.md', kind: 'lesson' },
9
+ ];
10
+ export async function applyPersonaSeed(args) {
11
+ const seedDir = join(args.personaDir, 'memory-seed');
12
+ if (!existsSync(seedDir))
13
+ return { copied: [], blocked: [] };
14
+ const ts = new Date().toISOString();
15
+ const copied = [];
16
+ const blocked = [];
17
+ for (const entry of FILE_MAP) {
18
+ const path = join(seedDir, entry.name);
19
+ if (!existsSync(path))
20
+ continue;
21
+ const text = readFileSync(path, 'utf8').trim();
22
+ if (!text)
23
+ continue;
24
+ try {
25
+ await args.store.append({ kind: entry.kind, text, source: 'persona-seed', ts });
26
+ copied.push(entry.name);
27
+ }
28
+ catch (err) {
29
+ if (err instanceof LayerAWriteBlocked) {
30
+ blocked.push(entry.name);
31
+ }
32
+ else {
33
+ throw err;
34
+ }
35
+ }
36
+ }
37
+ return { copied, blocked };
38
+ }
@@ -0,0 +1,2 @@
1
+ import type { RecallResult } from './types.js';
2
+ export declare function formatRecall(results: RecallResult[]): string;
@@ -0,0 +1,14 @@
1
+ export function formatRecall(results) {
2
+ const header = `### Memory recall (${results.length} matches)\n`;
3
+ if (results.length === 0)
4
+ return header;
5
+ const lines = [header];
6
+ results.forEach((r, i) => {
7
+ const ts = r.drawer.created_at.replace('T', ' ').slice(0, 16);
8
+ const redacted = r.drawer.redacted ? ' (redacted)' : '';
9
+ lines.push(`\n${i + 1}. [${r.drawer.wing}/${r.drawer.room}/${r.drawer.kind} · ${ts}] (score ${r.score.toFixed(2)})${redacted}`);
10
+ lines.push(` > ${r.snippet}`);
11
+ lines.push(` drawer://${r.drawer.id}`);
12
+ });
13
+ return lines.join('\n');
14
+ }
@@ -0,0 +1,9 @@
1
+ import type { ScopeMode, Wing } from './types.js';
2
+ export interface ResolvedScope {
3
+ writeWing: Wing;
4
+ readWings: Wing[];
5
+ }
6
+ export declare function resolveScope(args: {
7
+ mode: ScopeMode;
8
+ workspaceId: Wing;
9
+ }): ResolvedScope;
@@ -0,0 +1,10 @@
1
+ export function resolveScope(args) {
2
+ switch (args.mode) {
3
+ case 'global':
4
+ return { writeWing: 'global', readWings: ['global'] };
5
+ case 'per-project':
6
+ return { writeWing: args.workspaceId, readWings: [args.workspaceId] };
7
+ case 'per-project-tagged':
8
+ return { writeWing: args.workspaceId, readWings: [args.workspaceId, 'global'] };
9
+ }
10
+ }
@@ -0,0 +1,51 @@
1
+ export type Wing = string;
2
+ export type Room = string;
3
+ export declare const DRAWER_KINDS: readonly ["turn", "paste", "file_load", "ticket", "email", "rca", "note", "artifact"];
4
+ export type DrawerKind = typeof DRAWER_KINDS[number];
5
+ export declare const LAYER_A_KINDS: readonly ["profile", "rule", "lesson"];
6
+ export type LayerAKind = typeof LAYER_A_KINDS[number];
7
+ export type ScopeMode = 'global' | 'per-project' | 'per-project-tagged';
8
+ export interface Drawer {
9
+ id: string;
10
+ wing: Wing;
11
+ room: Room;
12
+ kind: DrawerKind;
13
+ content: string;
14
+ metadata: Record<string, unknown>;
15
+ created_at: string;
16
+ parent_id?: string;
17
+ redacted: boolean;
18
+ }
19
+ export interface RecallQuery {
20
+ query: string;
21
+ wing?: Wing | Wing[];
22
+ room?: Room;
23
+ kind?: DrawerKind | DrawerKind[];
24
+ days_back?: number;
25
+ max_results?: number;
26
+ }
27
+ export interface RecallResult {
28
+ drawer: Drawer;
29
+ score: number;
30
+ snippet: string;
31
+ }
32
+ export interface BackendStatus {
33
+ ready: boolean;
34
+ workspace_wing: Wing;
35
+ drawer_count: number;
36
+ layer_b_db_path: string;
37
+ schema_version: number;
38
+ }
39
+ export interface LayerAEntry {
40
+ kind: LayerAKind;
41
+ text: string;
42
+ source: 'user' | 'persona-seed';
43
+ ts: string;
44
+ }
45
+ export interface WorkspaceIdRecord {
46
+ _schema: 1;
47
+ id: string;
48
+ created_at: string;
49
+ memory_seed_applied: boolean;
50
+ memory_seed_persona: string | null;
51
+ }
@@ -0,0 +1,2 @@
1
+ export const DRAWER_KINDS = ['turn', 'paste', 'file_load', 'ticket', 'email', 'rca', 'note', 'artifact'];
2
+ export const LAYER_A_KINDS = ['profile', 'rule', 'lesson'];
@@ -0,0 +1,3 @@
1
+ import type { WorkspaceIdRecord } from './types.js';
2
+ export declare function resolveWorkspaceId(workspaceDir: string): Promise<WorkspaceIdRecord>;
3
+ export declare function writeWorkspaceId(workspaceDir: string, rec: WorkspaceIdRecord): Promise<void>;
@@ -0,0 +1,54 @@
1
+ // packages/coworker-memory/src/workspace-id.ts
2
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, chmodSync } from 'node:fs';
3
+ import { basename, join } from 'node:path';
4
+ import { createHash } from 'node:crypto';
5
+ function pathHash6(absolutePath) {
6
+ return createHash('sha256').update(absolutePath).digest('hex').slice(0, 6);
7
+ }
8
+ function deriveSlug(workspaceDir) {
9
+ let base = basename(workspaceDir).replace(/[^a-z0-9-]/gi, '-').replace(/^-+|-+$/g, '').toLowerCase();
10
+ if (!base)
11
+ base = 'workspace';
12
+ return `${base}-${pathHash6(workspaceDir)}`;
13
+ }
14
+ export async function resolveWorkspaceId(workspaceDir) {
15
+ const memDir = join(workspaceDir, '.otto', 'memory');
16
+ const path = join(memDir, 'workspace.json');
17
+ mkdirSync(memDir, { recursive: true, mode: 0o700 });
18
+ if (existsSync(path)) {
19
+ try {
20
+ const data = JSON.parse(readFileSync(path, 'utf8'));
21
+ if (data && data._schema === 1 && typeof data.id === 'string' && data.id.length > 0) {
22
+ return data;
23
+ }
24
+ }
25
+ catch { /* fall through to recreate */ }
26
+ // Move broken aside.
27
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
28
+ try {
29
+ renameSync(path, `${path}.broken-${stamp}`);
30
+ }
31
+ catch { /* best effort */ }
32
+ }
33
+ const fresh = {
34
+ _schema: 1,
35
+ id: deriveSlug(workspaceDir),
36
+ created_at: new Date().toISOString(),
37
+ memory_seed_applied: false,
38
+ memory_seed_persona: null,
39
+ };
40
+ const tmp = `${path}.tmp`;
41
+ writeFileSync(tmp, JSON.stringify(fresh, null, 2), { mode: 0o600 });
42
+ chmodSync(tmp, 0o600);
43
+ renameSync(tmp, path);
44
+ return fresh;
45
+ }
46
+ export async function writeWorkspaceId(workspaceDir, rec) {
47
+ const memDir = join(workspaceDir, '.otto', 'memory');
48
+ mkdirSync(memDir, { recursive: true, mode: 0o700 });
49
+ const path = join(memDir, 'workspace.json');
50
+ const tmp = `${path}.tmp`;
51
+ writeFileSync(tmp, JSON.stringify(rec, null, 2), { mode: 0o600 });
52
+ chmodSync(tmp, 0o600);
53
+ renameSync(tmp, path);
54
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@otto/coworker-memory",
3
+ "version": "0.0.1",
4
+ "description": "Otto co-worker package: coworker-memory",
5
+ "type": "module",
6
+ "otto": {
7
+ "linkable": true,
8
+ "scope": "@otto",
9
+ "name": "coworker-memory"
10
+ },
11
+ "main": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js"
17
+ }
18
+ },
19
+ "dependencies": {
20
+ "@otto/coworker-utils": "*",
21
+ "better-sqlite3": "^11.7.0",
22
+ "uuid": "^11.0.0",
23
+ "yaml": "^2.8.2"
24
+ },
25
+ "scripts": {
26
+ "build": "tsc -p tsconfig.json && node -e \"const fs=require('fs');const path=require('path');const src='src/migrations';const dst='dist/migrations';fs.mkdirSync(dst,{recursive:true});for(const f of fs.readdirSync(src))if(f.endsWith('.sql'))fs.copyFileSync(path.join(src,f),path.join(dst,f));\"",
27
+ "build:publish": "tsc -p tsconfig.publish.json && node -e \"const fs=require('fs');const path=require('path');const src='src/migrations';const dst='dist/migrations';fs.mkdirSync(dst,{recursive:true});for(const f of fs.readdirSync(src))if(f.endsWith('.sql'))fs.copyFileSync(path.join(src,f),path.join(dst,f));\""
28
+ },
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "devDependencies": {
33
+ "@types/better-sqlite3": "^7.6.13"
34
+ }
35
+ }
@@ -0,0 +1,141 @@
1
+ // packages/coworker-memory/src/activator-integration.test.ts
2
+ import { describe, it } from 'node:test';
3
+ import assert from 'node:assert/strict';
4
+ import { mkdtempSync, mkdirSync } from 'node:fs';
5
+ import { tmpdir } from 'node:os';
6
+ import { join } from 'node:path';
7
+ import type { DataLoadDrawer } from '@otto/coworker-scratchpad';
8
+
9
+ import coworkerVaultExtension from '../../../src/resources/extensions/coworker-vault/index.js';
10
+ import coworkerMemoryExtension, {
11
+ getMemoryRecorder, createMemoryBundle,
12
+ } from '../../../src/resources/extensions/coworker-memory/index.js';
13
+ import coworkerScratchpadExtension from '../../../src/resources/extensions/coworker-scratchpad/index.js';
14
+ import {
15
+ makeFakeApi, fireSessionStart, fireSessionShutdown,
16
+ fireBeforeAgentStart, fireAgentStart,
17
+ } from '../../../src/resources/extensions/coworker-vault/test-helpers.js';
18
+
19
+ describe('Phase 3.1 — cross-extension activator integration', () => {
20
+ it('vault + memory + scratchpad activate, recordTurn fires, recall returns drawer', async () => {
21
+ const global = mkdtempSync(join(tmpdir(), 'p31-g-'));
22
+ const ws = mkdtempSync(join(tmpdir(), 'p31-w-'));
23
+ const sp = mkdtempSync(join(tmpdir(), 'p31-sp-'));
24
+ mkdirSync(ws, { recursive: true });
25
+ process.env.OTTO_COWORKER_GLOBAL_DIR = global;
26
+ process.env.OTTO_SCRATCHPAD_ROOT = sp;
27
+ try {
28
+ // Each ext gets its own fake API in this integration shape — they don't
29
+ // need to share an API instance; what they share is filesystem state.
30
+ const vaultApi = makeFakeApi();
31
+ const memApi = makeFakeApi();
32
+ const spApi = makeFakeApi();
33
+
34
+ coworkerVaultExtension(vaultApi.api);
35
+ coworkerMemoryExtension(memApi.api);
36
+ coworkerScratchpadExtension(spApi.api);
37
+
38
+ await fireSessionStart(vaultApi, { cwd: ws });
39
+ await fireSessionStart(memApi, { cwd: ws });
40
+ await fireSessionStart(spApi, { cwd: ws });
41
+
42
+ assert.equal(vaultApi.notifyCalls.find(c => /unavailable/.test(c.message)), undefined);
43
+ assert.equal(memApi.notifyCalls.find(c => /unavailable/.test(c.message)), undefined);
44
+ assert.equal(spApi.notifyCalls.find(c => /unavailable/.test(c.message)), undefined);
45
+
46
+ assert.ok(getMemoryRecorder(), 'memory recorder live after session_start');
47
+
48
+ // before_agent_start + agent_start round-trip
49
+ await fireBeforeAgentStart(memApi, 'load balancer started returning 503s', 'BASE');
50
+ await fireAgentStart(memApi, 'sess-1', 'turn-1');
51
+
52
+ // Verify the drawer landed via a fresh read.
53
+ const peek = await createMemoryBundle({
54
+ globalDir: global, workspaceDir: ws,
55
+ scopeMode: 'per-project-tagged', currentScratchpadName: () => null,
56
+ });
57
+ try {
58
+ const r = await peek.backend.recall({ query: 'load balancer' });
59
+ assert.equal(r.length, 1);
60
+ assert.equal(r[0]!.drawer.kind, 'turn');
61
+ assert.match(r[0]!.drawer.content, /load balancer started returning 503s/);
62
+ } finally { await peek.dispose(); }
63
+
64
+ await fireSessionShutdown(memApi);
65
+ await fireSessionShutdown(spApi);
66
+ await fireSessionShutdown(vaultApi);
67
+ assert.equal(getMemoryRecorder(), null, 'recorder cleared');
68
+ } finally {
69
+ delete process.env.OTTO_COWORKER_GLOBAL_DIR;
70
+ delete process.env.OTTO_SCRATCHPAD_ROOT;
71
+ }
72
+ });
73
+
74
+ it('onDataLoad closure produces a file_load drawer when memory is live', async () => {
75
+ const global = mkdtempSync(join(tmpdir(), 'p31-fl-g-'));
76
+ const ws = mkdtempSync(join(tmpdir(), 'p31-fl-w-'));
77
+ const sp = mkdtempSync(join(tmpdir(), 'p31-fl-sp-'));
78
+ process.env.OTTO_COWORKER_GLOBAL_DIR = global;
79
+ process.env.OTTO_SCRATCHPAD_ROOT = sp;
80
+ try {
81
+ const memApi = makeFakeApi();
82
+ coworkerMemoryExtension(memApi.api);
83
+ await fireSessionStart(memApi, { cwd: ws });
84
+ const recorder = getMemoryRecorder()!;
85
+ assert.ok(recorder);
86
+
87
+ // Simulate the kernel emitting a data_load event by directly invoking
88
+ // recorder.recordFileLoad with the shape the closure would translate to.
89
+ // (Driving an actual kernel subprocess is overkill for this integration
90
+ // test; the closure logic is locked in Task 4's unit test.)
91
+ await recorder.recordFileLoad({
92
+ scratchpadName: 'p1', collector: 'file', uri: 'file:///x.csv',
93
+ bytes: 1024, rows_loaded: 50, turnId: '',
94
+ });
95
+
96
+ const peek = await createMemoryBundle({
97
+ globalDir: global, workspaceDir: ws,
98
+ scopeMode: 'per-project-tagged', currentScratchpadName: () => null,
99
+ });
100
+ try {
101
+ const r = await peek.backend.recall({ query: 'file', kind: 'file_load' });
102
+ assert.equal(r.length, 1);
103
+ const parsed = JSON.parse(r[0]!.drawer.content);
104
+ assert.equal(parsed.collector, 'file');
105
+ assert.equal(parsed.uri, 'file:///x.csv');
106
+ assert.equal(parsed.rows_loaded, 50);
107
+ assert.equal(r[0]!.drawer.room, 'p1');
108
+ } finally { await peek.dispose(); }
109
+
110
+ await fireSessionShutdown(memApi);
111
+ } finally {
112
+ delete process.env.OTTO_COWORKER_GLOBAL_DIR;
113
+ delete process.env.OTTO_SCRATCHPAD_ROOT;
114
+ }
115
+ });
116
+
117
+ it('memory init failure does not break vault or scratchpad', async () => {
118
+ // Point only memory at a bad path; vault + scratchpad get good paths.
119
+ const goodGlobal = mkdtempSync(join(tmpdir(), 'p31-mix-'));
120
+ const ws = mkdtempSync(join(tmpdir(), 'p31-mix-ws-'));
121
+ const sp = mkdtempSync(join(tmpdir(), 'p31-mix-sp-'));
122
+ process.env.OTTO_COWORKER_GLOBAL_DIR = goodGlobal;
123
+ process.env.OTTO_SCRATCHPAD_ROOT = sp;
124
+ try {
125
+ const vaultApi = makeFakeApi();
126
+ const spApi = makeFakeApi();
127
+ coworkerVaultExtension(vaultApi.api);
128
+ coworkerScratchpadExtension(spApi.api);
129
+ await fireSessionStart(vaultApi, { cwd: ws });
130
+ await fireSessionStart(spApi, { cwd: ws });
131
+ assert.equal(vaultApi.notifyCalls.find(c => /unavailable/.test(c.message)), undefined);
132
+ assert.equal(spApi.notifyCalls.find(c => /unavailable/.test(c.message)), undefined);
133
+ // Memory's onDataLoad path returns null recorder → scratchpad swallows. No crash.
134
+ await fireSessionShutdown(spApi);
135
+ await fireSessionShutdown(vaultApi);
136
+ } finally {
137
+ delete process.env.OTTO_COWORKER_GLOBAL_DIR;
138
+ delete process.env.OTTO_SCRATCHPAD_ROOT;
139
+ }
140
+ });
141
+ });
@@ -0,0 +1,72 @@
1
+ // packages/coworker-memory/src/context-injection.test.ts
2
+ import { describe, it } from 'node:test';
3
+ import assert from 'node:assert/strict';
4
+ import { mkdtempSync } from 'node:fs';
5
+ import { tmpdir } from 'node:os';
6
+ import { join } from 'node:path';
7
+ import { AuditLog, SecretScanner } from '@otto/coworker-utils';
8
+ import { LayerAStore } from './layer-a-store.js';
9
+ import { buildLayerAContext } from './context-injection.js';
10
+
11
+ async function makeStores() {
12
+ const root = mkdtempSync(join(tmpdir(), 'ci-'));
13
+ const audit = new AuditLog({ path: join(root, 'audit.jsonl') });
14
+ const scanner = new SecretScanner();
15
+ const globalStore = new LayerAStore({ scopeDir: join(root, 'g'), scope: 'global', audit, scanner });
16
+ const workspaceStore = new LayerAStore({ scopeDir: join(root, 'w'), scope: 'workspace', audit, scanner });
17
+ return { root, globalStore, workspaceStore };
18
+ }
19
+
20
+ describe('buildLayerAContext', () => {
21
+ it('returns empty when no Layer A files exist', async () => {
22
+ const { globalStore, workspaceStore } = await makeStores();
23
+ const md = await buildLayerAContext({
24
+ mode: 'per-project-tagged', globalStore, workspaceStore, tokenLimit: 3000,
25
+ });
26
+ assert.equal(md, '');
27
+ });
28
+ it('global mode reads global only', async () => {
29
+ const { globalStore, workspaceStore } = await makeStores();
30
+ await globalStore.append({ kind: 'profile', text: 'global profile', source: 'user', ts: '2026-06-02T00:00:00Z' });
31
+ await workspaceStore.append({ kind: 'profile', text: 'workspace profile', source: 'user', ts: '2026-06-02T00:00:00Z' });
32
+ const md = await buildLayerAContext({
33
+ mode: 'global', globalStore, workspaceStore, tokenLimit: 3000,
34
+ });
35
+ assert.match(md, /global profile/);
36
+ assert.equal(md.includes('workspace profile'), false);
37
+ });
38
+ it('per-project mode reads workspace only', async () => {
39
+ const { globalStore, workspaceStore } = await makeStores();
40
+ await globalStore.append({ kind: 'profile', text: 'global profile', source: 'user', ts: '2026-06-02T00:00:00Z' });
41
+ await workspaceStore.append({ kind: 'profile', text: 'workspace profile', source: 'user', ts: '2026-06-02T00:00:00Z' });
42
+ const md = await buildLayerAContext({
43
+ mode: 'per-project', globalStore, workspaceStore, tokenLimit: 3000,
44
+ });
45
+ assert.match(md, /workspace profile/);
46
+ assert.equal(md.includes('global profile'), false);
47
+ });
48
+ it('per-project-tagged includes both with workspace first', async () => {
49
+ const { globalStore, workspaceStore } = await makeStores();
50
+ await globalStore.append({ kind: 'rule', text: 'global rule', source: 'user', ts: '2026-06-02T00:00:00Z' });
51
+ await workspaceStore.append({ kind: 'rule', text: 'workspace rule', source: 'user', ts: '2026-06-02T00:00:00Z' });
52
+ const md = await buildLayerAContext({
53
+ mode: 'per-project-tagged', globalStore, workspaceStore, tokenLimit: 3000,
54
+ });
55
+ const wsIdx = md.indexOf('workspace rule');
56
+ const gIdx = md.indexOf('global rule');
57
+ assert.ok(wsIdx > 0 && gIdx > 0);
58
+ assert.ok(wsIdx < gIdx, 'workspace should appear before global');
59
+ });
60
+ it('truncates lower-priority files when token limit exceeded', async () => {
61
+ const { globalStore, workspaceStore } = await makeStores();
62
+ await workspaceStore.append({ kind: 'profile', text: 'p'.repeat(1000), source: 'user', ts: '2026-06-02T00:00:00Z' });
63
+ await workspaceStore.append({ kind: 'rule', text: 'r'.repeat(1000), source: 'user', ts: '2026-06-02T00:00:00Z' });
64
+ await workspaceStore.append({ kind: 'lesson', text: 'l'.repeat(1000), source: 'user', ts: '2026-06-02T00:00:00Z' });
65
+ const md = await buildLayerAContext({
66
+ mode: 'per-project', globalStore, workspaceStore, tokenLimit: 300, // ~1200 chars
67
+ });
68
+ assert.ok(md.includes('p'.repeat(50)));
69
+ // lessons should be dropped because lowest priority
70
+ assert.equal(md.includes('l'.repeat(900)), false);
71
+ });
72
+ });
@@ -0,0 +1,57 @@
1
+ // packages/coworker-memory/src/context-injection.ts
2
+ import type { ScopeMode } from './types.js';
3
+ import type { LayerAStore } from './layer-a-store.js';
4
+
5
+ export interface ContextInjectionArgs {
6
+ mode: ScopeMode;
7
+ globalStore: LayerAStore;
8
+ workspaceStore: LayerAStore;
9
+ tokenLimit: number; // approx 4 chars per token
10
+ }
11
+
12
+ const CHARS_PER_TOKEN = 4;
13
+
14
+ export async function buildLayerAContext(args: ContextInjectionArgs): Promise<string> {
15
+ const charLimit = args.tokenLimit * CHARS_PER_TOKEN;
16
+
17
+ const readScopes: Array<'workspace' | 'global'> = args.mode === 'global'
18
+ ? ['global']
19
+ : args.mode === 'per-project'
20
+ ? ['workspace']
21
+ : ['workspace', 'global'];
22
+
23
+ type Section = { title: string; body: string; priority: number };
24
+ const sections: Section[] = [];
25
+
26
+ const PRIORITIES: Record<string, number> = { profile: 0, rules: 1, lessons: 2 };
27
+
28
+ for (const scope of readScopes) {
29
+ const store = scope === 'global' ? args.globalStore : args.workspaceStore;
30
+ const profile = await store.read('profile');
31
+ const rules = await store.read('rule');
32
+ const lessons = await store.read('lesson');
33
+ if (profile) sections.push({ title: `Profile (${scope})`, body: profile, priority: PRIORITIES.profile! });
34
+ if (rules) sections.push({ title: `Rules (${scope})`, body: rules, priority: PRIORITIES.rules! });
35
+ if (lessons) sections.push({ title: `Recent lessons (${scope})`, body: lessons, priority: PRIORITIES.lessons! });
36
+ }
37
+
38
+ if (sections.length === 0) return '';
39
+
40
+ // workspace comes first because we iterated readScopes in that order; stable sort by priority.
41
+ sections.sort((a, b) => a.priority - b.priority);
42
+
43
+ let total = 0;
44
+ const include: Section[] = [];
45
+ for (const s of sections) {
46
+ const cost = s.title.length + s.body.length + 10;
47
+ if (total + cost > charLimit && include.length > 0) break;
48
+ include.push(s);
49
+ total += cost;
50
+ }
51
+
52
+ const lines = ['## Memory (Layer A)\n'];
53
+ for (const s of include) {
54
+ lines.push(`### ${s.title}\n${s.body}\n`);
55
+ }
56
+ return lines.join('\n');
57
+ }