@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,89 @@
1
+ // packages/coworker-memory/src/memory-integration.test.ts
2
+ import { describe, it } from 'node:test';
3
+ import assert from 'node:assert/strict';
4
+ import { mkdtempSync, mkdirSync, rmSync } from 'node:fs';
5
+ import { tmpdir } from 'node:os';
6
+ import { join } from 'node:path';
7
+ import { createMemoryBundle } from '../../../src/resources/extensions/coworker-memory/memory-singleton.js';
8
+ import { runMemorize } from '../../../src/resources/extensions/coworker-memory/memorize-tool.js';
9
+ import { runRecall } from '../../../src/resources/extensions/coworker-memory/recall-tool.js';
10
+ import { onSessionStart, onSessionShutdown } from '../../../src/resources/extensions/coworker-memory/session-hooks.js';
11
+
12
+ describe('Memory integration — Day-2 verbatim recall', () => {
13
+ it('paste Monday → recall Tuesday in a fresh session', async () => {
14
+ const homeMon = mkdtempSync(join(tmpdir(), 'mem-home-mon-'));
15
+ const ws = mkdtempSync(join(tmpdir(), 'mem-ws-mon-'));
16
+ mkdirSync(ws, { recursive: true });
17
+
18
+ // ===== MONDAY =====
19
+ const monBundle = await createMemoryBundle({
20
+ globalDir: homeMon, workspaceDir: ws,
21
+ scopeMode: 'per-project-tagged',
22
+ currentScratchpadName: () => 'p1-1234',
23
+ });
24
+ await monBundle.recorder.recordPaste({
25
+ sessionId: 'sess-mon',
26
+ content: 'customer says the load balancer started returning 503s around 14:00 UTC; the on-call escalated to the network team at 14:18',
27
+ turnId: 't1',
28
+ });
29
+ await onSessionShutdown(monBundle);
30
+
31
+ // Simulate a different Otto process by re-creating the bundle with the same workspace.
32
+ // ===== TUESDAY =====
33
+ const tueBundle = await createMemoryBundle({
34
+ globalDir: homeMon /* same as Monday */, workspaceDir: ws,
35
+ scopeMode: 'per-project-tagged',
36
+ currentScratchpadName: () => null,
37
+ });
38
+ const start = await onSessionStart(tueBundle, { tokenLimit: 3000 });
39
+ // Layer A was empty Monday, so the inject is empty on Tuesday.
40
+ assert.equal(start.contextBlock, '');
41
+ const r = await runRecall(tueBundle, { query: 'load balancer' });
42
+ assert.equal(r.results.length, 1);
43
+ assert.match(r.results[0]!.drawer.content, /load balancer started returning 503s around 14:00 UTC/);
44
+ assert.equal(r.results[0]!.drawer.room, 'p1-1234');
45
+ assert.match(r.markdown, /drawer:\/\//);
46
+ await onSessionShutdown(tueBundle);
47
+ });
48
+ it('memorize lessons → next session_start injects them', async () => {
49
+ const home = mkdtempSync(join(tmpdir(), 'mem-home-A-'));
50
+ const ws = mkdtempSync(join(tmpdir(), 'mem-ws-A-'));
51
+ mkdirSync(ws, { recursive: true });
52
+ const b1 = await createMemoryBundle({
53
+ globalDir: home, workspaceDir: ws,
54
+ scopeMode: 'per-project-tagged', currentScratchpadName: () => null,
55
+ });
56
+ await runMemorize(b1, { text: 'MTTR target is 30 minutes for P1', kind: 'lesson' });
57
+ await runMemorize(b1, { text: 'Always escalate to mgr within 5 min on customer-facing P1', kind: 'rule' });
58
+ await onSessionShutdown(b1);
59
+
60
+ const b2 = await createMemoryBundle({
61
+ globalDir: home, workspaceDir: ws,
62
+ scopeMode: 'per-project-tagged', currentScratchpadName: () => null,
63
+ });
64
+ const start = await onSessionStart(b2, { tokenLimit: 3000 });
65
+ assert.match(start.contextBlock, /MTTR target is 30 minutes/);
66
+ assert.match(start.contextBlock, /Always escalate to mgr/);
67
+ await onSessionShutdown(b2);
68
+ });
69
+ it('secret in paste is redacted; recall surrounding context still works', async () => {
70
+ const home = mkdtempSync(join(tmpdir(), 'mem-home-B-'));
71
+ const ws = mkdtempSync(join(tmpdir(), 'mem-ws-B-'));
72
+ mkdirSync(ws, { recursive: true });
73
+ const b = await createMemoryBundle({
74
+ globalDir: home, workspaceDir: ws,
75
+ scopeMode: 'per-project-tagged', currentScratchpadName: () => null,
76
+ });
77
+ await b.recorder.recordPaste({
78
+ sessionId: 's',
79
+ content: 'login token AKIAABCDEFGHIJKLMNOP, used for Datadog API',
80
+ turnId: 't',
81
+ });
82
+ const r = await runRecall(b, { query: 'Datadog' });
83
+ assert.equal(r.results.length, 1);
84
+ assert.match(r.results[0]!.drawer.content, /\[REDACTED:aws_access_key_id\]/);
85
+ assert.equal(r.results[0]!.drawer.content.includes('AKIAABCDEFGHIJKLMNOP'), false);
86
+ assert.equal(r.results[0]!.drawer.redacted, true);
87
+ await onSessionShutdown(b);
88
+ });
89
+ });
@@ -0,0 +1,101 @@
1
+ // packages/coworker-memory/src/memory-recorder.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 { LocalSqliteBackend } from './local-sqlite-backend.js';
9
+ import { MemoryRecorder } from './memory-recorder.js';
10
+
11
+ async function ctx() {
12
+ const dir = mkdtempSync(join(tmpdir(), 'mr-'));
13
+ const audit = new AuditLog({ path: join(dir, 'audit.jsonl') });
14
+ const scanner = new SecretScanner();
15
+ const backend = new LocalSqliteBackend({ dbPath: join(dir, 'layer-b.db') });
16
+ await backend.open();
17
+ return { dir, audit, scanner, backend };
18
+ }
19
+
20
+ describe('MemoryRecorder', () => {
21
+ it('recordTurn writes kind:turn drawer for short text', async () => {
22
+ const c = await ctx();
23
+ const recorder = new MemoryRecorder({
24
+ backend: c.backend, scanner: c.scanner, audit: c.audit,
25
+ writeWing: 'global', currentScratchpadName: () => null,
26
+ });
27
+ await recorder.recordTurn({ sessionId: 's', userText: 'hi', turnId: 't1' });
28
+ const wings = await c.backend.listWings();
29
+ assert.deepEqual(wings, ['global']);
30
+ const rooms = await c.backend.listRooms('global');
31
+ assert.deepEqual(rooms, ['inbox']);
32
+ await c.backend.close();
33
+ });
34
+ it('recordTurn writes kind:paste drawer for long text', async () => {
35
+ const c = await ctx();
36
+ const recorder = new MemoryRecorder({
37
+ backend: c.backend, scanner: c.scanner, audit: c.audit,
38
+ writeWing: 'global', currentScratchpadName: () => 'p1',
39
+ });
40
+ // Use whitespace-separated tokens so FTS5 can index/match (single huge token wouldn't match 'x').
41
+ const longText = 'lorem ipsum '.repeat(60); // > 500 chars, tokenizable
42
+ await recorder.recordTurn({ sessionId: 's', userText: longText, turnId: 't1' });
43
+ const r = await c.backend.recall({ query: 'lorem' });
44
+ assert.equal(r[0]!.drawer.kind, 'paste');
45
+ assert.equal(r[0]!.drawer.room, 'p1');
46
+ await c.backend.close();
47
+ });
48
+ it('redacts secret content and sets redacted=true', async () => {
49
+ const c = await ctx();
50
+ const recorder = new MemoryRecorder({
51
+ backend: c.backend, scanner: c.scanner, audit: c.audit,
52
+ writeWing: 'global', currentScratchpadName: () => null,
53
+ });
54
+ await recorder.recordPaste({ sessionId: 's', content: 'token AKIAABCDEFGHIJKLMNOP', turnId: 't1' });
55
+ const r = await c.backend.recall({ query: 'token' });
56
+ assert.equal(r[0]!.drawer.redacted, true);
57
+ assert.match(r[0]!.drawer.content, /\[REDACTED:aws_access_key_id\]/);
58
+ const rows: { action: string }[] = [];
59
+ for await (const x of c.audit.read({ producer: 'memory', action: 'redact' })) rows.push(x as never);
60
+ assert.equal(rows.length, 1);
61
+ await c.backend.close();
62
+ });
63
+ it('recordFileLoad stores structured JSON in content', async () => {
64
+ const c = await ctx();
65
+ const recorder = new MemoryRecorder({
66
+ backend: c.backend, scanner: c.scanner, audit: c.audit,
67
+ writeWing: 'global', currentScratchpadName: () => 'p1',
68
+ });
69
+ await recorder.recordFileLoad({
70
+ scratchpadName: 'p1', collector: 'file', uri: 'file:///x.csv',
71
+ bytes: 1000, rows_loaded: 50, schema: { cols: ['a','b'] }, turnId: 't1',
72
+ });
73
+ const r = await c.backend.recall({ query: 'file' });
74
+ assert.equal(r[0]!.drawer.kind, 'file_load');
75
+ const parsed = JSON.parse(r[0]!.drawer.content);
76
+ assert.equal(parsed.uri, 'file:///x.csv');
77
+ assert.equal(parsed.rows_loaded, 50);
78
+ await c.backend.close();
79
+ });
80
+ });
81
+
82
+ describe('MemoryRecorder.recordArtifact (Phase 4 Task 8)', () => {
83
+ it('writes a kind:artifact drawer with slug/kind/uri JSON content', async () => {
84
+ const c = await ctx();
85
+ const recorder = new MemoryRecorder({
86
+ backend: c.backend, scanner: c.scanner, audit: c.audit,
87
+ writeWing: 'global', currentScratchpadName: () => null,
88
+ });
89
+ await recorder.recordArtifact({
90
+ scratchpadName: 'p1', slug: 'rca-1', kind: 'report',
91
+ uri: 'artifact://rca-1', turnId: 't1',
92
+ });
93
+ const r = await c.backend.recall({ query: 'rca', kind: 'artifact' });
94
+ assert.equal(r.length, 1);
95
+ const parsed = JSON.parse(r[0]!.drawer.content);
96
+ assert.equal(parsed.slug, 'rca-1');
97
+ assert.equal(parsed.uri, 'artifact://rca-1');
98
+ assert.equal(r[0]!.drawer.room, 'p1');
99
+ await c.backend.close();
100
+ });
101
+ });
@@ -0,0 +1,95 @@
1
+ // packages/coworker-memory/src/memory-recorder.ts
2
+ import { AuditLog, SecretScanner } from '@otto/coworker-utils';
3
+ import type { MemoryBackend } from './memory-backend.js';
4
+ import type { Wing, Room, Drawer } from './types.js';
5
+ import { detectPaste } from './paste-detector.js';
6
+
7
+ export interface CurrentScratchpadProvider {
8
+ (sessionId: string): string | null;
9
+ }
10
+
11
+ export interface MemoryRecorderOptions {
12
+ backend: MemoryBackend;
13
+ scanner: SecretScanner;
14
+ audit: AuditLog;
15
+ writeWing: Wing;
16
+ currentScratchpadName: CurrentScratchpadProvider;
17
+ pasteOptions?: { lengthThreshold?: number; newlineThreshold?: number };
18
+ }
19
+
20
+ export class MemoryRecorder {
21
+ constructor(private readonly opts: MemoryRecorderOptions) {}
22
+
23
+ async recordTurn(args: { sessionId: string; userText: string; turnId: string; room?: Room }): Promise<Drawer> {
24
+ const kind = detectPaste(args.userText, this.opts.pasteOptions);
25
+ const room = args.room ?? this.opts.currentScratchpadName(args.sessionId) ?? 'inbox';
26
+ return this.writeDrawer({
27
+ wing: this.opts.writeWing, room, kind,
28
+ content: args.userText, metadata: { turn_id: args.turnId, session_id: args.sessionId },
29
+ });
30
+ }
31
+
32
+ async recordPaste(args: { sessionId: string; content: string; turnId: string; room?: Room }): Promise<Drawer> {
33
+ const room = args.room ?? this.opts.currentScratchpadName(args.sessionId) ?? 'inbox';
34
+ return this.writeDrawer({
35
+ wing: this.opts.writeWing, room, kind: 'paste',
36
+ content: args.content, metadata: { turn_id: args.turnId, session_id: args.sessionId },
37
+ });
38
+ }
39
+
40
+ async recordFileLoad(args: {
41
+ scratchpadName: string; collector: string; uri: string;
42
+ bytes: number; rows_loaded?: number; schema?: object; turnId: string;
43
+ }): Promise<Drawer> {
44
+ const content = JSON.stringify({
45
+ collector: args.collector, uri: args.uri, bytes: args.bytes,
46
+ rows_loaded: args.rows_loaded, schema: args.schema,
47
+ });
48
+ return this.writeDrawer({
49
+ wing: this.opts.writeWing, room: args.scratchpadName, kind: 'file_load',
50
+ content, metadata: { turn_id: args.turnId, scratchpad: args.scratchpadName },
51
+ });
52
+ }
53
+
54
+ async recordArtifact(args: {
55
+ scratchpadName: string; slug: string; kind: string; uri: string;
56
+ turnId: string;
57
+ }): Promise<Drawer> {
58
+ const content = JSON.stringify({
59
+ slug: args.slug, kind: args.kind, uri: args.uri,
60
+ });
61
+ return this.writeDrawer({
62
+ wing: this.opts.writeWing, room: args.scratchpadName, kind: 'artifact',
63
+ content, metadata: { turn_id: args.turnId, scratchpad: args.scratchpadName },
64
+ });
65
+ }
66
+
67
+ private async writeDrawer(input: Omit<Drawer, 'id' | 'created_at' | 'redacted'>): Promise<Drawer> {
68
+ const hits = this.opts.scanner.scan(input.content);
69
+ let content = input.content;
70
+ let redacted = false;
71
+ if (hits.length > 0) {
72
+ content = this.opts.scanner.redact(input.content);
73
+ redacted = true;
74
+ const ts = new Date().toISOString();
75
+ for (const h of hits) {
76
+ this.opts.audit.append({
77
+ _schema: 1, ts, producer: 'memory', action: 'redact', severity: 'warn',
78
+ detail: {
79
+ wing: input.wing, room: input.room, kind: input.kind,
80
+ secret_kind: h.kind, offset: h.start, length: h.end - h.start,
81
+ },
82
+ });
83
+ }
84
+ }
85
+ const drawer = await this.opts.backend.retain({ ...input, content, redacted });
86
+ this.opts.audit.append({
87
+ _schema: 1, ts: new Date().toISOString(), producer: 'memory', action: 'write-drawer',
88
+ detail: {
89
+ wing: drawer.wing, room: drawer.room, kind: drawer.kind,
90
+ byte_count: Buffer.byteLength(content, 'utf8'), redacted,
91
+ },
92
+ });
93
+ return drawer;
94
+ }
95
+ }
@@ -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,23 @@
1
+ // packages/coworker-memory/src/paste-detector.test.ts
2
+ import { describe, it } from 'node:test';
3
+ import assert from 'node:assert/strict';
4
+ import { detectPaste } from './paste-detector.js';
5
+
6
+ describe('detectPaste', () => {
7
+ it('returns turn for short single-line', () => {
8
+ assert.equal(detectPaste('what servers had alerts last night?'), 'turn');
9
+ });
10
+ it('returns paste when length >= 500', () => {
11
+ assert.equal(detectPaste('x'.repeat(500)), 'paste');
12
+ });
13
+ it('returns paste on triple-backtick fence', () => {
14
+ assert.equal(detectPaste('look at this:\n```ts\nconst x = 1;\n```'), 'paste');
15
+ });
16
+ it('returns paste on > 10 newlines', () => {
17
+ assert.equal(detectPaste('a\n'.repeat(11)), 'paste');
18
+ });
19
+ it('respects custom thresholds', () => {
20
+ assert.equal(detectPaste('x'.repeat(100), { lengthThreshold: 50 }), 'paste');
21
+ assert.equal(detectPaste('a\n'.repeat(5), { newlineThreshold: 3 }), 'paste');
22
+ });
23
+ });
@@ -0,0 +1,18 @@
1
+ // packages/coworker-memory/src/paste-detector.ts
2
+ export interface PasteDetectorOptions {
3
+ lengthThreshold?: number; // default 500
4
+ newlineThreshold?: number; // default 10
5
+ }
6
+
7
+ const DEFAULT_LENGTH = 500;
8
+ const DEFAULT_NEWLINES = 10;
9
+
10
+ export function detectPaste(text: string, opts: PasteDetectorOptions = {}): 'turn' | 'paste' {
11
+ const lengthThreshold = opts.lengthThreshold ?? DEFAULT_LENGTH;
12
+ const newlineThreshold = opts.newlineThreshold ?? DEFAULT_NEWLINES;
13
+ if (/```/.test(text)) return 'paste';
14
+ if (text.length >= lengthThreshold) return 'paste';
15
+ const newlines = (text.match(/\n/g) ?? []).length;
16
+ if (newlines > newlineThreshold) return 'paste';
17
+ return 'turn';
18
+ }
@@ -0,0 +1,57 @@
1
+ // packages/coworker-memory/src/persona-seed.test.ts
2
+ import { describe, it } from 'node:test';
3
+ import assert from 'node:assert/strict';
4
+ import { mkdtempSync, mkdirSync, writeFileSync, readFileSync } 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 { applyPersonaSeed } from './persona-seed.js';
10
+
11
+ function setup() {
12
+ const root = mkdtempSync(join(tmpdir(), 'ps-'));
13
+ const audit = new AuditLog({ path: join(root, 'audit.jsonl') });
14
+ const scanner = new SecretScanner();
15
+ const scopeDir = join(root, 'memory');
16
+ const personaDir = join(root, 'persona-noc');
17
+ const seedDir = join(personaDir, 'memory-seed');
18
+ mkdirSync(seedDir, { recursive: true });
19
+ const store = new LayerAStore({ scopeDir, scope: 'workspace', audit, scanner });
20
+ return { root, audit, store, personaDir, seedDir };
21
+ }
22
+
23
+ describe('applyPersonaSeed', () => {
24
+ it('copies profile.md/rules.md/lessons.md from persona memory-seed', async () => {
25
+ const c = setup();
26
+ writeFileSync(join(c.seedDir, 'profile.md'), 'Persona NOC profile baseline.');
27
+ writeFileSync(join(c.seedDir, 'rules.md'), 'Always escalate P1 to mgr.');
28
+ writeFileSync(join(c.seedDir, 'lessons.md'), 'Datadog API uses pagination.');
29
+ const out = await applyPersonaSeed({
30
+ personaId: 'noc-ops', personaDir: c.personaDir, store: c.store,
31
+ });
32
+ assert.deepEqual(out.copied.sort(), ['lessons.md', 'profile.md', 'rules.md']);
33
+ assert.match(await c.store.read('profile'), /Persona NOC profile baseline/);
34
+ assert.match(await c.store.read('rule'), /Always escalate P1 to mgr/);
35
+ assert.match(await c.store.read('lesson'), /Datadog API uses pagination/);
36
+ });
37
+ it('blocks files containing secrets but copies remaining', async () => {
38
+ const c = setup();
39
+ writeFileSync(join(c.seedDir, 'profile.md'), 'Persona baseline.');
40
+ writeFileSync(join(c.seedDir, 'rules.md'), 'use AKIAABCDEFGHIJKLMNOP for telemetry');
41
+ const out = await applyPersonaSeed({
42
+ personaId: 'noc-ops', personaDir: c.personaDir, store: c.store,
43
+ });
44
+ assert.deepEqual(out.copied, ['profile.md']);
45
+ assert.deepEqual(out.blocked, ['rules.md']);
46
+ assert.equal(await c.store.read('rule'), '');
47
+ });
48
+ it('returns empty result when persona has no memory-seed dir', async () => {
49
+ const c = setup();
50
+ // No files written; remove seedDir
51
+ const out = await applyPersonaSeed({
52
+ personaId: 'plain', personaDir: join(c.root, 'no-such-persona'), store: c.store,
53
+ });
54
+ assert.deepEqual(out.copied, []);
55
+ assert.deepEqual(out.blocked, []);
56
+ });
57
+ });
@@ -0,0 +1,46 @@
1
+ // packages/coworker-memory/src/persona-seed.ts
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import type { LayerAStore } from './layer-a-store.js';
5
+ import type { LayerAKind } from './types.js';
6
+ import { LayerAWriteBlocked } from './errors.js';
7
+
8
+ const FILE_MAP: Array<{ name: string; kind: LayerAKind }> = [
9
+ { name: 'profile.md', kind: 'profile' },
10
+ { name: 'rules.md', kind: 'rule' },
11
+ { name: 'lessons.md', kind: 'lesson' },
12
+ ];
13
+
14
+ export interface SeedResult {
15
+ copied: string[];
16
+ blocked: string[];
17
+ }
18
+
19
+ export async function applyPersonaSeed(args: {
20
+ personaId: string;
21
+ personaDir: string;
22
+ store: LayerAStore;
23
+ }): Promise<SeedResult> {
24
+ const seedDir = join(args.personaDir, 'memory-seed');
25
+ if (!existsSync(seedDir)) return { copied: [], blocked: [] };
26
+ const ts = new Date().toISOString();
27
+ const copied: string[] = [];
28
+ const blocked: string[] = [];
29
+ for (const entry of FILE_MAP) {
30
+ const path = join(seedDir, entry.name);
31
+ if (!existsSync(path)) continue;
32
+ const text = readFileSync(path, 'utf8').trim();
33
+ if (!text) continue;
34
+ try {
35
+ await args.store.append({ kind: entry.kind, text, source: 'persona-seed', ts });
36
+ copied.push(entry.name);
37
+ } catch (err) {
38
+ if (err instanceof LayerAWriteBlocked) {
39
+ blocked.push(entry.name);
40
+ } else {
41
+ throw err;
42
+ }
43
+ }
44
+ }
45
+ return { copied, blocked };
46
+ }
@@ -0,0 +1,34 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { formatRecall } from './recall-formatter.js';
4
+
5
+ describe('formatRecall', () => {
6
+ it('returns empty header for zero results', () => {
7
+ const md = formatRecall([]);
8
+ assert.match(md, /### Memory recall \(0 matches\)/);
9
+ assert.equal(md.includes('drawer://'), false);
10
+ });
11
+ it('renders match metadata + snippet + drawer URI', () => {
12
+ const md = formatRecall([{
13
+ drawer: {
14
+ id: 'abc123', wing: 'global', room: 'inbox', kind: 'paste',
15
+ content: 'full content', metadata: {}, created_at: '2026-06-01T14:22:00Z',
16
+ redacted: false,
17
+ },
18
+ score: 5.21, snippet: 'paste content <mark>matched</mark> terms',
19
+ }]);
20
+ assert.match(md, /\[global\/inbox\/paste · 2026-06-01 14:22\] \(score 5\.21\)/);
21
+ assert.match(md, /<mark>matched<\/mark>/);
22
+ assert.match(md, /drawer:\/\/abc123/);
23
+ });
24
+ it('flags redacted drawers', () => {
25
+ const md = formatRecall([{
26
+ drawer: {
27
+ id: 'r1', wing: 'g', room: 'inbox', kind: 'paste', content: 'x',
28
+ metadata: {}, created_at: '2026-06-01T00:00:00Z', redacted: true,
29
+ },
30
+ score: 1, snippet: 'x',
31
+ }]);
32
+ assert.match(md, /\(redacted\)/);
33
+ });
34
+ });
@@ -0,0 +1,15 @@
1
+ import type { RecallResult } from './types.js';
2
+
3
+ export function formatRecall(results: RecallResult[]): string {
4
+ const header = `### Memory recall (${results.length} matches)\n`;
5
+ if (results.length === 0) return header;
6
+ const lines: string[] = [header];
7
+ results.forEach((r, i) => {
8
+ const ts = r.drawer.created_at.replace('T', ' ').slice(0, 16);
9
+ const redacted = r.drawer.redacted ? ' (redacted)' : '';
10
+ lines.push(`\n${i + 1}. [${r.drawer.wing}/${r.drawer.room}/${r.drawer.kind} · ${ts}] (score ${r.score.toFixed(2)})${redacted}`);
11
+ lines.push(` > ${r.snippet}`);
12
+ lines.push(` drawer://${r.drawer.id}`);
13
+ });
14
+ return lines.join('\n');
15
+ }
@@ -0,0 +1,23 @@
1
+ // packages/coworker-memory/src/scope-resolver.test.ts
2
+ import { describe, it } from 'node:test';
3
+ import assert from 'node:assert/strict';
4
+ import { resolveScope } from './scope-resolver.js';
5
+
6
+ describe('resolveScope', () => {
7
+ const wing = 'acme-noc-7f3a9c';
8
+ it('global → write global, read [global]', () => {
9
+ const r = resolveScope({ mode: 'global', workspaceId: wing });
10
+ assert.equal(r.writeWing, 'global');
11
+ assert.deepEqual(r.readWings, ['global']);
12
+ });
13
+ it('per-project → write workspace, read [workspace]', () => {
14
+ const r = resolveScope({ mode: 'per-project', workspaceId: wing });
15
+ assert.equal(r.writeWing, wing);
16
+ assert.deepEqual(r.readWings, [wing]);
17
+ });
18
+ it('per-project-tagged → write workspace, read [workspace, global]', () => {
19
+ const r = resolveScope({ mode: 'per-project-tagged', workspaceId: wing });
20
+ assert.equal(r.writeWing, wing);
21
+ assert.deepEqual(r.readWings, [wing, 'global']);
22
+ });
23
+ });
@@ -0,0 +1,18 @@
1
+ // packages/coworker-memory/src/scope-resolver.ts
2
+ import type { ScopeMode, Wing } from './types.js';
3
+
4
+ export interface ResolvedScope {
5
+ writeWing: Wing;
6
+ readWings: Wing[];
7
+ }
8
+
9
+ export function resolveScope(args: { mode: ScopeMode; workspaceId: Wing }): ResolvedScope {
10
+ switch (args.mode) {
11
+ case 'global':
12
+ return { writeWing: 'global', readWings: ['global'] };
13
+ case 'per-project':
14
+ return { writeWing: args.workspaceId, readWings: [args.workspaceId] };
15
+ case 'per-project-tagged':
16
+ return { writeWing: args.workspaceId, readWings: [args.workspaceId, 'global'] };
17
+ }
18
+ }