@cmetech/otto 1.1.0 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (425) hide show
  1. package/dist/coworker/persona-commands.d.ts +1 -0
  2. package/dist/coworker/persona-commands.js +5 -0
  3. package/dist/coworker/persona-commands.test.d.ts +1 -0
  4. package/dist/coworker/persona-commands.test.js +45 -0
  5. package/dist/resources/.managed-resources-content-hash +1 -1
  6. package/dist/resources/extensions/_coworker-paths.js +8 -0
  7. package/dist/resources/extensions/coworker-artifacts/artifacts-command.js +31 -0
  8. package/dist/resources/extensions/coworker-artifacts/artifacts-singleton.js +17 -0
  9. package/dist/resources/extensions/coworker-artifacts/extension-manifest.json +13 -0
  10. package/dist/resources/extensions/coworker-artifacts/index.js +125 -0
  11. package/dist/resources/extensions/coworker-artifacts/list-tool.js +27 -0
  12. package/dist/resources/extensions/coworker-artifacts/open-tool.js +25 -0
  13. package/dist/resources/extensions/coworker-memory/extension-manifest.json +13 -0
  14. package/dist/resources/extensions/coworker-memory/index.js +219 -0
  15. package/dist/resources/extensions/coworker-memory/memorize-tool.js +10 -0
  16. package/dist/resources/extensions/coworker-memory/memory-command.js +157 -0
  17. package/dist/resources/extensions/coworker-memory/memory-singleton.js +55 -0
  18. package/dist/resources/extensions/coworker-memory/recall-tool.js +18 -0
  19. package/dist/resources/extensions/coworker-memory/session-hooks.js +45 -0
  20. package/dist/resources/extensions/coworker-scratchpad/attach-banners.js +53 -0
  21. package/dist/resources/extensions/coworker-scratchpad/extension-manifest.json +13 -0
  22. package/dist/resources/extensions/coworker-scratchpad/format-age.js +9 -0
  23. package/dist/resources/extensions/coworker-scratchpad/helpers.js +38 -0
  24. package/dist/resources/extensions/coworker-scratchpad/index.js +199 -0
  25. package/dist/resources/extensions/coworker-scratchpad/mime-bundle.js +20 -0
  26. package/dist/resources/extensions/coworker-scratchpad/scratchpad-tool.js +118 -0
  27. package/dist/resources/extensions/coworker-scratchpad/session-sidecar.js +60 -0
  28. package/dist/resources/extensions/coworker-scratchpad/sp-command.js +597 -0
  29. package/dist/resources/extensions/coworker-scratchpad/workspace-pointer.js +41 -0
  30. package/dist/resources/extensions/coworker-scratchpad/workspace-root.js +17 -0
  31. package/dist/resources/extensions/coworker-vault/audit-command.js +35 -0
  32. package/dist/resources/extensions/coworker-vault/connect-command.js +42 -0
  33. package/dist/resources/extensions/coworker-vault/datasource-command.js +50 -0
  34. package/dist/resources/extensions/coworker-vault/extension-manifest.json +12 -0
  35. package/dist/resources/extensions/coworker-vault/index.js +171 -0
  36. package/dist/resources/extensions/coworker-vault/test-helpers.js +86 -0
  37. package/dist/resources/extensions/coworker-vault/vault-singleton.js +24 -0
  38. package/dist/resources/extensions/otto/commands/release-notes/_data.js +82 -0
  39. package/dist/resources/extensions/otto/commands/release-notes/command.js +15 -4
  40. package/dist/resources/extensions/subagent/index.js +8 -1
  41. package/dist/resources/extensions/subagent/launch.js +37 -5
  42. package/dist/resources/extensions/subagent/run-store.js +1 -0
  43. package/dist/resources/extensions/workflow/bootstrap/register-extension.js +2 -0
  44. package/dist/resources/extensions/workflow/bootstrap/register-hooks.js +10 -0
  45. package/dist/resources/extensions/workflow/persona-status.js +87 -0
  46. package/dist/update-cmd.d.ts +19 -0
  47. package/dist/update-cmd.js +177 -6
  48. package/package.json +25 -10
  49. package/packages/contracts/package.json +1 -1
  50. package/packages/coworker-artifacts/dist/artifact-store.d.ts +25 -0
  51. package/packages/coworker-artifacts/dist/artifact-store.js +187 -0
  52. package/packages/coworker-artifacts/dist/dir-snapshot.d.ts +7 -0
  53. package/packages/coworker-artifacts/dist/dir-snapshot.js +54 -0
  54. package/packages/coworker-artifacts/dist/errors.d.ts +18 -0
  55. package/packages/coworker-artifacts/dist/errors.js +37 -0
  56. package/packages/coworker-artifacts/dist/index.d.ts +7 -0
  57. package/packages/coworker-artifacts/dist/index.js +7 -0
  58. package/packages/coworker-artifacts/dist/readme-renderer.d.ts +5 -0
  59. package/packages/coworker-artifacts/dist/readme-renderer.js +47 -0
  60. package/packages/coworker-artifacts/dist/resolve-uri.d.ts +3 -0
  61. package/packages/coworker-artifacts/dist/resolve-uri.js +29 -0
  62. package/packages/coworker-artifacts/dist/slug.d.ts +4 -0
  63. package/packages/coworker-artifacts/dist/slug.js +32 -0
  64. package/packages/coworker-artifacts/dist/types.d.ts +52 -0
  65. package/packages/coworker-artifacts/dist/types.js +1 -0
  66. package/packages/coworker-artifacts/package.json +20 -0
  67. package/packages/coworker-artifacts/src/artifact-store.test.ts +188 -0
  68. package/packages/coworker-artifacts/src/artifact-store.ts +206 -0
  69. package/packages/coworker-artifacts/src/artifacts-integration.test.ts +109 -0
  70. package/packages/coworker-artifacts/src/dir-snapshot.test.ts +71 -0
  71. package/packages/coworker-artifacts/src/dir-snapshot.ts +52 -0
  72. package/packages/coworker-artifacts/src/errors.test.ts +37 -0
  73. package/packages/coworker-artifacts/src/errors.ts +28 -0
  74. package/packages/coworker-artifacts/src/index.test.ts +22 -0
  75. package/packages/coworker-artifacts/src/index.ts +7 -0
  76. package/packages/coworker-artifacts/src/readme-renderer.test.ts +72 -0
  77. package/packages/coworker-artifacts/src/readme-renderer.ts +56 -0
  78. package/packages/coworker-artifacts/src/resolve-uri.test.ts +46 -0
  79. package/packages/coworker-artifacts/src/resolve-uri.ts +29 -0
  80. package/packages/coworker-artifacts/src/slug.test.ts +47 -0
  81. package/packages/coworker-artifacts/src/slug.ts +31 -0
  82. package/packages/coworker-artifacts/src/types.ts +61 -0
  83. package/packages/coworker-artifacts/tsconfig.json +15 -0
  84. package/packages/coworker-artifacts/tsconfig.publish.json +4 -0
  85. package/packages/coworker-memory/dist/context-injection.d.ts +9 -0
  86. package/packages/coworker-memory/dist/context-injection.js +41 -0
  87. package/packages/coworker-memory/dist/errors.d.ts +25 -0
  88. package/packages/coworker-memory/dist/errors.js +51 -0
  89. package/packages/coworker-memory/dist/index.d.ts +12 -0
  90. package/packages/coworker-memory/dist/index.js +12 -0
  91. package/packages/coworker-memory/dist/layer-a-store.d.ts +16 -0
  92. package/packages/coworker-memory/dist/layer-a-store.js +78 -0
  93. package/packages/coworker-memory/dist/local-sqlite-backend.d.ts +28 -0
  94. package/packages/coworker-memory/dist/local-sqlite-backend.js +167 -0
  95. package/packages/coworker-memory/dist/memory-backend.d.ts +14 -0
  96. package/packages/coworker-memory/dist/memory-backend.js +1 -0
  97. package/packages/coworker-memory/dist/memory-recorder.d.ts +50 -0
  98. package/packages/coworker-memory/dist/memory-recorder.js +69 -0
  99. package/packages/coworker-memory/dist/migrations/001-init.sql +38 -0
  100. package/packages/coworker-memory/dist/migrations/002-artifact-kind.sql +50 -0
  101. package/packages/coworker-memory/dist/paste-detector.d.ts +5 -0
  102. package/packages/coworker-memory/dist/paste-detector.js +14 -0
  103. package/packages/coworker-memory/dist/persona-seed.d.ts +10 -0
  104. package/packages/coworker-memory/dist/persona-seed.js +38 -0
  105. package/packages/coworker-memory/dist/recall-formatter.d.ts +2 -0
  106. package/packages/coworker-memory/dist/recall-formatter.js +14 -0
  107. package/packages/coworker-memory/dist/scope-resolver.d.ts +9 -0
  108. package/packages/coworker-memory/dist/scope-resolver.js +10 -0
  109. package/packages/coworker-memory/dist/types.d.ts +51 -0
  110. package/packages/coworker-memory/dist/types.js +2 -0
  111. package/packages/coworker-memory/dist/workspace-id.d.ts +3 -0
  112. package/packages/coworker-memory/dist/workspace-id.js +54 -0
  113. package/packages/coworker-memory/package.json +35 -0
  114. package/packages/coworker-memory/src/activator-integration.test.ts +141 -0
  115. package/packages/coworker-memory/src/context-injection.test.ts +72 -0
  116. package/packages/coworker-memory/src/context-injection.ts +57 -0
  117. package/packages/coworker-memory/src/errors.test.ts +45 -0
  118. package/packages/coworker-memory/src/errors.ts +42 -0
  119. package/packages/coworker-memory/src/index.test.ts +21 -0
  120. package/packages/coworker-memory/src/index.ts +12 -0
  121. package/packages/coworker-memory/src/layer-a-store.test.ts +85 -0
  122. package/packages/coworker-memory/src/layer-a-store.ts +88 -0
  123. package/packages/coworker-memory/src/local-sqlite-backend.test.ts +110 -0
  124. package/packages/coworker-memory/src/local-sqlite-backend.ts +185 -0
  125. package/packages/coworker-memory/src/memory-backend.ts +10 -0
  126. package/packages/coworker-memory/src/memory-integration.test.ts +89 -0
  127. package/packages/coworker-memory/src/memory-recorder.test.ts +101 -0
  128. package/packages/coworker-memory/src/memory-recorder.ts +95 -0
  129. package/packages/coworker-memory/src/migrations/001-init.sql +38 -0
  130. package/packages/coworker-memory/src/migrations/002-artifact-kind.sql +50 -0
  131. package/packages/coworker-memory/src/paste-detector.test.ts +23 -0
  132. package/packages/coworker-memory/src/paste-detector.ts +18 -0
  133. package/packages/coworker-memory/src/persona-seed.test.ts +57 -0
  134. package/packages/coworker-memory/src/persona-seed.ts +46 -0
  135. package/packages/coworker-memory/src/recall-formatter.test.ts +34 -0
  136. package/packages/coworker-memory/src/recall-formatter.ts +15 -0
  137. package/packages/coworker-memory/src/scope-resolver.test.ts +23 -0
  138. package/packages/coworker-memory/src/scope-resolver.ts +18 -0
  139. package/packages/coworker-memory/src/types.ts +61 -0
  140. package/packages/coworker-memory/src/workspace-id.test.ts +48 -0
  141. package/packages/coworker-memory/src/workspace-id.ts +56 -0
  142. package/packages/coworker-memory/tsconfig.json +15 -0
  143. package/packages/coworker-memory/tsconfig.publish.json +4 -0
  144. package/packages/coworker-persona/dist/commands.d.ts +7 -0
  145. package/packages/coworker-persona/dist/commands.js +35 -0
  146. package/packages/coworker-persona/dist/defaults/manifest.yaml +12 -0
  147. package/packages/coworker-persona/dist/defaults/steering/identity.md +3 -0
  148. package/packages/coworker-persona/dist/index.d.ts +3 -0
  149. package/packages/coworker-persona/dist/index.js +3 -0
  150. package/packages/coworker-persona/dist/manifest.d.ts +24 -0
  151. package/packages/coworker-persona/dist/manifest.js +21 -0
  152. package/packages/coworker-persona/dist/registry.d.ts +22 -0
  153. package/packages/coworker-persona/dist/registry.js +142 -0
  154. package/packages/coworker-persona/package.json +28 -0
  155. package/packages/coworker-persona/scripts/copy-defaults.cjs +17 -0
  156. package/packages/coworker-persona/src/commands.ts +47 -0
  157. package/packages/coworker-persona/src/defaults/manifest.yaml +12 -0
  158. package/packages/coworker-persona/src/defaults/steering/identity.md +3 -0
  159. package/packages/coworker-persona/src/index.ts +3 -0
  160. package/packages/coworker-persona/src/manifest.test.ts +67 -0
  161. package/packages/coworker-persona/src/manifest.ts +49 -0
  162. package/packages/coworker-persona/src/registry.test.ts +89 -0
  163. package/packages/coworker-persona/src/registry.ts +147 -0
  164. package/packages/coworker-persona/tsconfig.json +15 -0
  165. package/packages/coworker-persona/tsconfig.publish.json +4 -0
  166. package/packages/coworker-scratchpad/dist/cell-archive.d.ts +39 -0
  167. package/packages/coworker-scratchpad/dist/cell-archive.js +77 -0
  168. package/packages/coworker-scratchpad/dist/cell-tree.d.ts +14 -0
  169. package/packages/coworker-scratchpad/dist/cell-tree.js +72 -0
  170. package/packages/coworker-scratchpad/dist/child-process-runtime.d.ts +129 -0
  171. package/packages/coworker-scratchpad/dist/child-process-runtime.js +427 -0
  172. package/packages/coworker-scratchpad/dist/collector-registry.d.ts +12 -0
  173. package/packages/coworker-scratchpad/dist/collector-registry.js +29 -0
  174. package/packages/coworker-scratchpad/dist/detect-kind.d.ts +3 -0
  175. package/packages/coworker-scratchpad/dist/detect-kind.js +19 -0
  176. package/packages/coworker-scratchpad/dist/file-collector.d.ts +15 -0
  177. package/packages/coworker-scratchpad/dist/file-collector.js +99 -0
  178. package/packages/coworker-scratchpad/dist/index.d.ts +13 -0
  179. package/packages/coworker-scratchpad/dist/index.js +13 -0
  180. package/packages/coworker-scratchpad/dist/kernel-bindings.d.ts +49 -0
  181. package/packages/coworker-scratchpad/dist/kernel-bindings.js +220 -0
  182. package/packages/coworker-scratchpad/dist/kernel-entry.d.ts +1 -0
  183. package/packages/coworker-scratchpad/dist/kernel-entry.js +355 -0
  184. package/packages/coworker-scratchpad/dist/kernel-protocol.d.ts +171 -0
  185. package/packages/coworker-scratchpad/dist/kernel-protocol.js +48 -0
  186. package/packages/coworker-scratchpad/dist/kernel-spawn.d.ts +3 -0
  187. package/packages/coworker-scratchpad/dist/kernel-spawn.js +54 -0
  188. package/packages/coworker-scratchpad/dist/namespace-codec.d.ts +22 -0
  189. package/packages/coworker-scratchpad/dist/namespace-codec.js +61 -0
  190. package/packages/coworker-scratchpad/dist/scratchpad-lock.d.ts +24 -0
  191. package/packages/coworker-scratchpad/dist/scratchpad-lock.js +86 -0
  192. package/packages/coworker-scratchpad/dist/scratchpad-manager.d.ts +193 -0
  193. package/packages/coworker-scratchpad/dist/scratchpad-manager.js +866 -0
  194. package/packages/coworker-scratchpad/dist/staleness-banner.d.ts +12 -0
  195. package/packages/coworker-scratchpad/dist/staleness-banner.js +27 -0
  196. package/packages/coworker-scratchpad/package.json +31 -0
  197. package/packages/coworker-scratchpad/src/cell-archive.test.ts +150 -0
  198. package/packages/coworker-scratchpad/src/cell-archive.ts +97 -0
  199. package/packages/coworker-scratchpad/src/cell-tree.test.ts +105 -0
  200. package/packages/coworker-scratchpad/src/cell-tree.ts +90 -0
  201. package/packages/coworker-scratchpad/src/child-process-runtime.test.ts +413 -0
  202. package/packages/coworker-scratchpad/src/child-process-runtime.ts +493 -0
  203. package/packages/coworker-scratchpad/src/collector-registry.test.ts +69 -0
  204. package/packages/coworker-scratchpad/src/collector-registry.ts +33 -0
  205. package/packages/coworker-scratchpad/src/detect-kind.test.ts +33 -0
  206. package/packages/coworker-scratchpad/src/detect-kind.ts +22 -0
  207. package/packages/coworker-scratchpad/src/file-collector.test.ts +109 -0
  208. package/packages/coworker-scratchpad/src/file-collector.ts +114 -0
  209. package/packages/coworker-scratchpad/src/index.ts +74 -0
  210. package/packages/coworker-scratchpad/src/kernel-bindings.test.ts +188 -0
  211. package/packages/coworker-scratchpad/src/kernel-bindings.ts +279 -0
  212. package/packages/coworker-scratchpad/src/kernel-entry.test.ts +123 -0
  213. package/packages/coworker-scratchpad/src/kernel-entry.ts +390 -0
  214. package/packages/coworker-scratchpad/src/kernel-protocol.test.ts +105 -0
  215. package/packages/coworker-scratchpad/src/kernel-protocol.ts +230 -0
  216. package/packages/coworker-scratchpad/src/kernel-spawn.test.ts +60 -0
  217. package/packages/coworker-scratchpad/src/kernel-spawn.ts +54 -0
  218. package/packages/coworker-scratchpad/src/namespace-codec.test.ts +102 -0
  219. package/packages/coworker-scratchpad/src/namespace-codec.ts +90 -0
  220. package/packages/coworker-scratchpad/src/scratchpad-lock.test.ts +98 -0
  221. package/packages/coworker-scratchpad/src/scratchpad-lock.ts +102 -0
  222. package/packages/coworker-scratchpad/src/scratchpad-manager.test.ts +1343 -0
  223. package/packages/coworker-scratchpad/src/scratchpad-manager.ts +891 -0
  224. package/packages/coworker-scratchpad/src/staleness-banner.test.ts +53 -0
  225. package/packages/coworker-scratchpad/src/staleness-banner.ts +33 -0
  226. package/packages/coworker-scratchpad/src/vault-integration.test.ts +221 -0
  227. package/packages/coworker-scratchpad/tsconfig.json +15 -0
  228. package/packages/coworker-scratchpad/tsconfig.publish.json +4 -0
  229. package/packages/coworker-types/dist/artifacts.d.ts +31 -0
  230. package/packages/coworker-types/dist/artifacts.js +2 -0
  231. package/packages/coworker-types/dist/contracts.d.ts +32 -0
  232. package/packages/coworker-types/dist/contracts.js +1 -0
  233. package/packages/coworker-types/dist/index.d.ts +5 -0
  234. package/packages/coworker-types/dist/index.js +5 -0
  235. package/packages/coworker-types/dist/memory.d.ts +61 -0
  236. package/packages/coworker-types/dist/memory.js +3 -0
  237. package/packages/coworker-types/dist/scratchpad.d.ts +43 -0
  238. package/packages/coworker-types/dist/scratchpad.js +2 -0
  239. package/packages/coworker-types/dist/vault.d.ts +34 -0
  240. package/packages/coworker-types/dist/vault.js +2 -0
  241. package/packages/coworker-types/package.json +24 -0
  242. package/packages/coworker-types/src/artifacts.test.ts +52 -0
  243. package/packages/coworker-types/src/artifacts.ts +35 -0
  244. package/packages/coworker-types/src/contracts.test.ts +43 -0
  245. package/packages/coworker-types/src/contracts.ts +36 -0
  246. package/packages/coworker-types/src/index.ts +5 -0
  247. package/packages/coworker-types/src/memory.test.ts +50 -0
  248. package/packages/coworker-types/src/memory.ts +79 -0
  249. package/packages/coworker-types/src/scratchpad.test.ts +46 -0
  250. package/packages/coworker-types/src/scratchpad.ts +51 -0
  251. package/packages/coworker-types/src/smoke.test.ts +34 -0
  252. package/packages/coworker-types/src/vault.test.ts +49 -0
  253. package/packages/coworker-types/src/vault.ts +40 -0
  254. package/packages/coworker-types/tsconfig.json +15 -0
  255. package/packages/coworker-types/tsconfig.publish.json +4 -0
  256. package/packages/coworker-utils/dist/audit-log.d.ts +34 -0
  257. package/packages/coworker-utils/dist/audit-log.js +88 -0
  258. package/packages/coworker-utils/dist/index.d.ts +6 -0
  259. package/packages/coworker-utils/dist/index.js +6 -0
  260. package/packages/coworker-utils/dist/lease.d.ts +7 -0
  261. package/packages/coworker-utils/dist/lease.js +67 -0
  262. package/packages/coworker-utils/dist/logger.d.ts +13 -0
  263. package/packages/coworker-utils/dist/logger.js +26 -0
  264. package/packages/coworker-utils/dist/migration-runner.d.ts +7 -0
  265. package/packages/coworker-utils/dist/migration-runner.js +36 -0
  266. package/packages/coworker-utils/dist/ndjson-channel.d.ts +3 -0
  267. package/packages/coworker-utils/dist/ndjson-channel.js +38 -0
  268. package/packages/coworker-utils/dist/secret-scanner.d.ts +10 -0
  269. package/packages/coworker-utils/dist/secret-scanner.js +42 -0
  270. package/packages/coworker-utils/package.json +24 -0
  271. package/packages/coworker-utils/src/audit-log.test.ts +140 -0
  272. package/packages/coworker-utils/src/audit-log.ts +107 -0
  273. package/packages/coworker-utils/src/index.ts +6 -0
  274. package/packages/coworker-utils/src/lease.test.ts +64 -0
  275. package/packages/coworker-utils/src/lease.ts +76 -0
  276. package/packages/coworker-utils/src/logger.test.ts +50 -0
  277. package/packages/coworker-utils/src/logger.ts +45 -0
  278. package/packages/coworker-utils/src/migration-runner.test.ts +65 -0
  279. package/packages/coworker-utils/src/migration-runner.ts +50 -0
  280. package/packages/coworker-utils/src/ndjson-channel.test.ts +76 -0
  281. package/packages/coworker-utils/src/ndjson-channel.ts +41 -0
  282. package/packages/coworker-utils/src/secret-scanner.test.ts +61 -0
  283. package/packages/coworker-utils/src/secret-scanner.ts +56 -0
  284. package/packages/coworker-utils/tsconfig.json +15 -0
  285. package/packages/coworker-utils/tsconfig.publish.json +4 -0
  286. package/packages/coworker-vault/dist/data-vault.d.ts +41 -0
  287. package/packages/coworker-vault/dist/data-vault.js +223 -0
  288. package/packages/coworker-vault/dist/engine-registry.d.ts +34 -0
  289. package/packages/coworker-vault/dist/engine-registry.js +90 -0
  290. package/packages/coworker-vault/dist/engines/jira.yaml +17 -0
  291. package/packages/coworker-vault/dist/errors.d.ts +28 -0
  292. package/packages/coworker-vault/dist/errors.js +57 -0
  293. package/packages/coworker-vault/dist/index.d.ts +6 -0
  294. package/packages/coworker-vault/dist/index.js +6 -0
  295. package/packages/coworker-vault/dist/injector.d.ts +19 -0
  296. package/packages/coworker-vault/dist/injector.js +77 -0
  297. package/packages/coworker-vault/dist/types.d.ts +28 -0
  298. package/packages/coworker-vault/dist/types.js +1 -0
  299. package/packages/coworker-vault/dist/vault-keep.d.ts +4 -0
  300. package/packages/coworker-vault/dist/vault-keep.js +21 -0
  301. package/packages/coworker-vault/package.json +29 -0
  302. package/packages/coworker-vault/src/data-vault.test.ts +199 -0
  303. package/packages/coworker-vault/src/data-vault.ts +257 -0
  304. package/packages/coworker-vault/src/engine-registry.test.ts +120 -0
  305. package/packages/coworker-vault/src/engine-registry.ts +107 -0
  306. package/packages/coworker-vault/src/engines/jira.yaml +17 -0
  307. package/packages/coworker-vault/src/errors.test.ts +58 -0
  308. package/packages/coworker-vault/src/errors.ts +50 -0
  309. package/packages/coworker-vault/src/index.test.ts +24 -0
  310. package/packages/coworker-vault/src/index.ts +6 -0
  311. package/packages/coworker-vault/src/injector.test.ts +109 -0
  312. package/packages/coworker-vault/src/injector.ts +98 -0
  313. package/packages/coworker-vault/src/types.ts +33 -0
  314. package/packages/coworker-vault/src/vault-keep.test.ts +49 -0
  315. package/packages/coworker-vault/src/vault-keep.ts +31 -0
  316. package/packages/coworker-vault/tsconfig.json +15 -0
  317. package/packages/coworker-vault/tsconfig.publish.json +4 -0
  318. package/packages/daemon/package.json +3 -3
  319. package/packages/mcp-server/package.json +3 -3
  320. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  321. package/packages/native/package.json +1 -1
  322. package/packages/native/tsconfig.tsbuildinfo +1 -1
  323. package/packages/pi-agent-core/package.json +1 -1
  324. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  325. package/packages/pi-ai/package.json +1 -1
  326. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  327. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +6 -1
  328. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  329. package/packages/pi-coding-agent/dist/core/extensions/runner.js +22 -3
  330. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  331. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +11 -0
  332. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  333. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.d.ts +47 -0
  334. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.d.ts.map +1 -0
  335. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.js +107 -0
  336. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.js.map +1 -0
  337. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.d.ts +19 -0
  338. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.d.ts.map +1 -0
  339. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.js +121 -0
  340. package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.js.map +1 -0
  341. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  342. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +17 -1
  343. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  344. package/packages/pi-coding-agent/package.json +2 -2
  345. package/packages/pi-coding-agent/src/core/extensions/runner.ts +22 -3
  346. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +11 -0
  347. package/packages/pi-coding-agent/src/modes/rpc/raw-stdout.regression.test.ts +129 -0
  348. package/packages/pi-coding-agent/src/modes/rpc/raw-stdout.ts +117 -0
  349. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +18 -1
  350. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  351. package/packages/pi-tui/package.json +1 -1
  352. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  353. package/packages/rpc-client/package.json +2 -2
  354. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  355. package/pkg/package.json +1 -1
  356. package/scripts/install.js +6 -5
  357. package/src/resources/extensions/_coworker-paths.test.ts +40 -0
  358. package/src/resources/extensions/_coworker-paths.ts +10 -0
  359. package/src/resources/extensions/coworker-artifacts/artifacts-command.test.ts +54 -0
  360. package/src/resources/extensions/coworker-artifacts/artifacts-command.ts +43 -0
  361. package/src/resources/extensions/coworker-artifacts/artifacts-singleton.test.ts +25 -0
  362. package/src/resources/extensions/coworker-artifacts/artifacts-singleton.ts +29 -0
  363. package/src/resources/extensions/coworker-artifacts/extension-manifest.json +13 -0
  364. package/src/resources/extensions/coworker-artifacts/index.test.ts +46 -0
  365. package/src/resources/extensions/coworker-artifacts/index.ts +154 -0
  366. package/src/resources/extensions/coworker-artifacts/list-tool.test.ts +29 -0
  367. package/src/resources/extensions/coworker-artifacts/list-tool.ts +53 -0
  368. package/src/resources/extensions/coworker-artifacts/open-tool.test.ts +30 -0
  369. package/src/resources/extensions/coworker-artifacts/open-tool.ts +43 -0
  370. package/src/resources/extensions/coworker-memory/extension-manifest.json +13 -0
  371. package/src/resources/extensions/coworker-memory/index.test.ts +137 -0
  372. package/src/resources/extensions/coworker-memory/index.ts +257 -0
  373. package/src/resources/extensions/coworker-memory/memorize-tool.test.ts +41 -0
  374. package/src/resources/extensions/coworker-memory/memorize-tool.ts +20 -0
  375. package/src/resources/extensions/coworker-memory/memory-command.test.ts +134 -0
  376. package/src/resources/extensions/coworker-memory/memory-command.ts +131 -0
  377. package/src/resources/extensions/coworker-memory/memory-singleton.test.ts +41 -0
  378. package/src/resources/extensions/coworker-memory/memory-singleton.ts +89 -0
  379. package/src/resources/extensions/coworker-memory/recall-tool.test.ts +50 -0
  380. package/src/resources/extensions/coworker-memory/recall-tool.ts +35 -0
  381. package/src/resources/extensions/coworker-memory/session-hooks.test.ts +77 -0
  382. package/src/resources/extensions/coworker-memory/session-hooks.ts +61 -0
  383. package/src/resources/extensions/coworker-scratchpad/attach-banners.test.ts +124 -0
  384. package/src/resources/extensions/coworker-scratchpad/attach-banners.ts +67 -0
  385. package/src/resources/extensions/coworker-scratchpad/extension-manifest.json +13 -0
  386. package/src/resources/extensions/coworker-scratchpad/format-age.test.ts +30 -0
  387. package/src/resources/extensions/coworker-scratchpad/format-age.ts +6 -0
  388. package/src/resources/extensions/coworker-scratchpad/helpers.test.ts +93 -0
  389. package/src/resources/extensions/coworker-scratchpad/helpers.ts +42 -0
  390. package/src/resources/extensions/coworker-scratchpad/index.test.ts +514 -0
  391. package/src/resources/extensions/coworker-scratchpad/index.ts +207 -0
  392. package/src/resources/extensions/coworker-scratchpad/mime-bundle.test.ts +61 -0
  393. package/src/resources/extensions/coworker-scratchpad/mime-bundle.ts +23 -0
  394. package/src/resources/extensions/coworker-scratchpad/scratchpad-tool.test.ts +137 -0
  395. package/src/resources/extensions/coworker-scratchpad/scratchpad-tool.ts +165 -0
  396. package/src/resources/extensions/coworker-scratchpad/session-sidecar.test.ts +133 -0
  397. package/src/resources/extensions/coworker-scratchpad/session-sidecar.ts +68 -0
  398. package/src/resources/extensions/coworker-scratchpad/sp-command.test.ts +836 -0
  399. package/src/resources/extensions/coworker-scratchpad/sp-command.ts +602 -0
  400. package/src/resources/extensions/coworker-scratchpad/workspace-pointer.test.ts +74 -0
  401. package/src/resources/extensions/coworker-scratchpad/workspace-pointer.ts +55 -0
  402. package/src/resources/extensions/coworker-scratchpad/workspace-root.test.ts +51 -0
  403. package/src/resources/extensions/coworker-scratchpad/workspace-root.ts +16 -0
  404. package/src/resources/extensions/coworker-vault/audit-command.test.ts +109 -0
  405. package/src/resources/extensions/coworker-vault/audit-command.ts +56 -0
  406. package/src/resources/extensions/coworker-vault/connect-command.test.ts +103 -0
  407. package/src/resources/extensions/coworker-vault/connect-command.ts +69 -0
  408. package/src/resources/extensions/coworker-vault/datasource-command.test.ts +80 -0
  409. package/src/resources/extensions/coworker-vault/datasource-command.ts +81 -0
  410. package/src/resources/extensions/coworker-vault/extension-manifest.json +12 -0
  411. package/src/resources/extensions/coworker-vault/index.test.ts +82 -0
  412. package/src/resources/extensions/coworker-vault/index.ts +181 -0
  413. package/src/resources/extensions/coworker-vault/test-helpers.ts +120 -0
  414. package/src/resources/extensions/coworker-vault/vault-singleton.test.ts +27 -0
  415. package/src/resources/extensions/coworker-vault/vault-singleton.ts +40 -0
  416. package/src/resources/extensions/otto/commands/release-notes/_data.ts +96 -0
  417. package/src/resources/extensions/otto/commands/release-notes/command.ts +16 -3
  418. package/src/resources/extensions/subagent/index.ts +9 -0
  419. package/src/resources/extensions/subagent/launch.test.ts +97 -0
  420. package/src/resources/extensions/subagent/launch.ts +42 -5
  421. package/src/resources/extensions/subagent/run-store.ts +3 -1
  422. package/src/resources/extensions/workflow/bootstrap/register-extension.ts +2 -0
  423. package/src/resources/extensions/workflow/bootstrap/register-hooks.ts +10 -0
  424. package/src/resources/extensions/workflow/persona-status.ts +109 -0
  425. package/src/resources/extensions/workflow/tests/auto-recovery.test.ts +34 -0
@@ -0,0 +1,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
+ }
@@ -0,0 +1,61 @@
1
+ // packages/coworker-memory/src/types.ts
2
+ export type Wing = string;
3
+ export type Room = string;
4
+
5
+ export const DRAWER_KINDS = ['turn', 'paste', 'file_load', 'ticket', 'email', 'rca', 'note', 'artifact'] as const;
6
+ export type DrawerKind = typeof DRAWER_KINDS[number];
7
+
8
+ export const LAYER_A_KINDS = ['profile', 'rule', 'lesson'] as const;
9
+ export type LayerAKind = typeof LAYER_A_KINDS[number];
10
+
11
+ export type ScopeMode = 'global' | 'per-project' | 'per-project-tagged';
12
+
13
+ export interface Drawer {
14
+ id: string;
15
+ wing: Wing;
16
+ room: Room;
17
+ kind: DrawerKind;
18
+ content: string;
19
+ metadata: Record<string, unknown>;
20
+ created_at: string;
21
+ parent_id?: string;
22
+ redacted: boolean;
23
+ }
24
+
25
+ export interface RecallQuery {
26
+ query: string;
27
+ wing?: Wing | Wing[];
28
+ room?: Room;
29
+ kind?: DrawerKind | DrawerKind[];
30
+ days_back?: number;
31
+ max_results?: number;
32
+ }
33
+
34
+ export interface RecallResult {
35
+ drawer: Drawer;
36
+ score: number;
37
+ snippet: string;
38
+ }
39
+
40
+ export interface BackendStatus {
41
+ ready: boolean;
42
+ workspace_wing: Wing;
43
+ drawer_count: number;
44
+ layer_b_db_path: string;
45
+ schema_version: number;
46
+ }
47
+
48
+ export interface LayerAEntry {
49
+ kind: LayerAKind;
50
+ text: string;
51
+ source: 'user' | 'persona-seed';
52
+ ts: string;
53
+ }
54
+
55
+ export interface WorkspaceIdRecord {
56
+ _schema: 1;
57
+ id: string;
58
+ created_at: string;
59
+ memory_seed_applied: boolean;
60
+ memory_seed_persona: string | null;
61
+ }
@@ -0,0 +1,48 @@
1
+ // packages/coworker-memory/src/workspace-id.test.ts
2
+ import { describe, it } from 'node:test';
3
+ import assert from 'node:assert/strict';
4
+ import { mkdtempSync, mkdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
5
+ import { tmpdir } from 'node:os';
6
+ import { join } from 'node:path';
7
+ import { resolveWorkspaceId } from './workspace-id.js';
8
+
9
+ function tmp(): string { return mkdtempSync(join(tmpdir(), 'wsid-')); }
10
+
11
+ describe('resolveWorkspaceId', () => {
12
+ it('creates workspace.json on first call with basename + 6-hex slug', async () => {
13
+ const root = tmp();
14
+ const ws = join(root, 'acme-noc');
15
+ mkdirSync(ws, { recursive: true });
16
+ const rec = await resolveWorkspaceId(ws);
17
+ assert.match(rec.id, /^acme-noc-[0-9a-f]{6}$/);
18
+ assert.equal(rec.memory_seed_applied, false);
19
+ assert.equal(rec.memory_seed_persona, null);
20
+ assert.ok(existsSync(join(ws, '.otto', 'memory', 'workspace.json')));
21
+ });
22
+ it('returns existing id on second call (idempotent)', async () => {
23
+ const root = tmp();
24
+ const ws = join(root, 'acme-noc');
25
+ mkdirSync(ws, { recursive: true });
26
+ const a = await resolveWorkspaceId(ws);
27
+ const b = await resolveWorkspaceId(ws);
28
+ assert.equal(a.id, b.id);
29
+ assert.equal(a.created_at, b.created_at);
30
+ });
31
+ it('falls back to path-hash when workspace.json is corrupted', async () => {
32
+ const root = tmp();
33
+ const ws = join(root, 'broken');
34
+ mkdirSync(join(ws, '.otto', 'memory'), { recursive: true });
35
+ writeFileSync(join(ws, '.otto', 'memory', 'workspace.json'), 'not json');
36
+ const rec = await resolveWorkspaceId(ws);
37
+ assert.match(rec.id, /^broken-[0-9a-f]{6}$/);
38
+ assert.ok(existsSync(join(ws, '.otto', 'memory', 'workspace.json.broken-')) ||
39
+ readFileSync(join(ws, '.otto', 'memory', 'workspace.json'), 'utf8').includes('"_schema"'));
40
+ });
41
+ it('uses workspace fallback when basename is empty', async () => {
42
+ // synthesize via an explicit path with weird basename
43
+ const root = tmp();
44
+ const ws = root; // root-of-tmp has a basename like 'wsid-xxxxxx', fine
45
+ const rec = await resolveWorkspaceId(ws);
46
+ assert.ok(rec.id.length > 0);
47
+ });
48
+ });
@@ -0,0 +1,56 @@
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
+ import type { WorkspaceIdRecord } from './types.js';
6
+
7
+ function pathHash6(absolutePath: string): string {
8
+ return createHash('sha256').update(absolutePath).digest('hex').slice(0, 6);
9
+ }
10
+
11
+ function deriveSlug(workspaceDir: string): string {
12
+ let base = basename(workspaceDir).replace(/[^a-z0-9-]/gi, '-').replace(/^-+|-+$/g, '').toLowerCase();
13
+ if (!base) base = 'workspace';
14
+ return `${base}-${pathHash6(workspaceDir)}`;
15
+ }
16
+
17
+ export async function resolveWorkspaceId(workspaceDir: string): Promise<WorkspaceIdRecord> {
18
+ const memDir = join(workspaceDir, '.otto', 'memory');
19
+ const path = join(memDir, 'workspace.json');
20
+ mkdirSync(memDir, { recursive: true, mode: 0o700 });
21
+
22
+ if (existsSync(path)) {
23
+ try {
24
+ const data = JSON.parse(readFileSync(path, 'utf8')) as WorkspaceIdRecord;
25
+ if (data && data._schema === 1 && typeof data.id === 'string' && data.id.length > 0) {
26
+ return data;
27
+ }
28
+ } catch { /* fall through to recreate */ }
29
+ // Move broken aside.
30
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
31
+ try { renameSync(path, `${path}.broken-${stamp}`); } catch { /* best effort */ }
32
+ }
33
+
34
+ const fresh: WorkspaceIdRecord = {
35
+ _schema: 1,
36
+ id: deriveSlug(workspaceDir),
37
+ created_at: new Date().toISOString(),
38
+ memory_seed_applied: false,
39
+ memory_seed_persona: null,
40
+ };
41
+ const tmp = `${path}.tmp`;
42
+ writeFileSync(tmp, JSON.stringify(fresh, null, 2), { mode: 0o600 });
43
+ chmodSync(tmp, 0o600);
44
+ renameSync(tmp, path);
45
+ return fresh;
46
+ }
47
+
48
+ export async function writeWorkspaceId(workspaceDir: string, rec: WorkspaceIdRecord): Promise<void> {
49
+ const memDir = join(workspaceDir, '.otto', 'memory');
50
+ mkdirSync(memDir, { recursive: true, mode: 0o700 });
51
+ const path = join(memDir, 'workspace.json');
52
+ const tmp = `${path}.tmp`;
53
+ writeFileSync(tmp, JSON.stringify(rec, null, 2), { mode: 0o600 });
54
+ chmodSync(tmp, 0o600);
55
+ renameSync(tmp, path);
56
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "NodeNext",
4
+ "moduleResolution": "NodeNext",
5
+ "target": "ES2022",
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "strict": true,
9
+ "declaration": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true
12
+ },
13
+ "include": ["src"],
14
+ "exclude": ["src/**/*.test.ts"]
15
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": { "incremental": false }
4
+ }
@@ -0,0 +1,7 @@
1
+ import { PersonaRegistry } from './registry.js';
2
+ export declare function handleList(registry: PersonaRegistry, workspaceRoot: string): Promise<string[]>;
3
+ export declare function handleCurrent(registry: PersonaRegistry, workspaceRoot: string): Promise<string[]>;
4
+ export declare function handleSwitch(registry: PersonaRegistry, workspaceRoot: string, name: string): Promise<string[]>;
5
+ export declare function handleReset(registry: PersonaRegistry, workspaceRoot: string): Promise<string[]>;
6
+ export declare function handleInstall(registry: PersonaRegistry, source: string): Promise<string[]>;
7
+ export declare function handleUninstall(registry: PersonaRegistry, name: string, trackedWorkspaces: string[]): Promise<string[]>;
@@ -0,0 +1,35 @@
1
+ export async function handleList(registry, workspaceRoot) {
2
+ const installed = await registry.list();
3
+ const active = await registry.activeInWorkspace(workspaceRoot);
4
+ return installed.map(p => `${p.name === active.name ? '*' : ' '} ${p.name} — ${p.display_name} (v${p.version})`);
5
+ }
6
+ export async function handleCurrent(registry, workspaceRoot) {
7
+ const active = await registry.activeInWorkspace(workspaceRoot);
8
+ return [
9
+ `Active persona: ${active.name}`,
10
+ `Display name: ${active.display_name}`,
11
+ `Version: ${active.version}`,
12
+ `Description: ${active.description}`,
13
+ `Author: ${active.author}`,
14
+ ];
15
+ }
16
+ export async function handleSwitch(registry, workspaceRoot, name) {
17
+ const persona = await registry.get(name);
18
+ if (!persona)
19
+ throw new Error(`persona "${name}" is not installed; run /persona list to see installed personas`);
20
+ await registry.activateInWorkspace(workspaceRoot, name);
21
+ return [`Switched to persona: ${name} (${persona.display_name})`];
22
+ }
23
+ export async function handleReset(registry, workspaceRoot) {
24
+ await registry.activateInWorkspace(workspaceRoot, 'default');
25
+ return ['Persona reset to default'];
26
+ }
27
+ export async function handleInstall(registry, source) {
28
+ // For Phase 0 we only support local-path install. Npm + git install come in Phase 6.
29
+ const manifest = await registry.installFromPath(source);
30
+ return [`Installed persona: ${manifest.name} (${manifest.display_name})`];
31
+ }
32
+ export async function handleUninstall(registry, name, trackedWorkspaces) {
33
+ await registry.uninstall(name, { trackedWorkspaces });
34
+ return [`Uninstalled persona: ${name}`];
35
+ }
@@ -0,0 +1,12 @@
1
+ name: default
2
+ display_name: "Default Co-Worker"
3
+ version: 1.0.0
4
+ description: "Generic co-worker with no domain specialization. Auto-activates when no persona is set."
5
+ author: "@cmetech"
6
+ otto_version_required: ">=2.0.0"
7
+ steering:
8
+ - steering/identity.md
9
+ status_line:
10
+ label: "default"
11
+ color: "#6B6B7C"
12
+ icon: "⚙"
@@ -0,0 +1,3 @@
1
+ You are Otto — a co-worker that helps the user run analyses, build reports, query their data,
2
+ and remember context across sessions. Be direct, accurate, and concise. When in doubt, ask
3
+ the user rather than guess. Cite memory and data sources verbatim when relevant.
@@ -0,0 +1,3 @@
1
+ export * from './manifest.js';
2
+ export * from './registry.js';
3
+ export * from './commands.js';
@@ -0,0 +1,3 @@
1
+ export * from './manifest.js';
2
+ export * from './registry.js';
3
+ export * from './commands.js';
@@ -0,0 +1,24 @@
1
+ export interface PersonaStatusLine {
2
+ label: string;
3
+ color: string;
4
+ icon: string;
5
+ }
6
+ export interface PersonaMemorySeed {
7
+ apply_on_first_activation: boolean;
8
+ scope?: 'global' | 'per-project' | 'per-project-tagged';
9
+ }
10
+ export interface PersonaManifest {
11
+ name: string;
12
+ display_name: string;
13
+ version: string;
14
+ description: string;
15
+ author: string;
16
+ otto_version_required: string;
17
+ steering: string[];
18
+ memory_seed?: PersonaMemorySeed;
19
+ engines?: string;
20
+ artifact_kinds?: string[];
21
+ skills_path?: string;
22
+ status_line: PersonaStatusLine;
23
+ }
24
+ export declare function parsePersonaManifest(yamlText: string): PersonaManifest;
@@ -0,0 +1,21 @@
1
+ // Persona manifest parser. See spec §2.5.
2
+ import { parse as parseYaml } from 'yaml';
3
+ // Order matters: tests assert the FIRST missing field reported, and the
4
+ // "missing version" case supplies only `name`, so `version` must be checked
5
+ // before `display_name`.
6
+ const REQUIRED = [
7
+ 'name', 'version', 'display_name', 'description', 'author',
8
+ 'otto_version_required', 'steering', 'status_line',
9
+ ];
10
+ export function parsePersonaManifest(yamlText) {
11
+ const raw = parseYaml(yamlText);
12
+ if (!raw || typeof raw !== 'object') {
13
+ throw new Error('persona manifest must be a YAML object');
14
+ }
15
+ for (const field of REQUIRED) {
16
+ if (!(field in raw)) {
17
+ throw new Error(`persona manifest missing required field: ${field}`);
18
+ }
19
+ }
20
+ return raw;
21
+ }
@@ -0,0 +1,22 @@
1
+ import { type PersonaManifest } from './manifest.js';
2
+ export interface RegistryOptions {
3
+ ottoHome: string;
4
+ }
5
+ export interface ActiveRecord {
6
+ active: string;
7
+ activated_at: string;
8
+ memory_seed_applied: boolean;
9
+ }
10
+ export declare class PersonaRegistry {
11
+ #private;
12
+ constructor(opts: RegistryOptions);
13
+ ensureDefaultInstalled(): Promise<void>;
14
+ list(): Promise<PersonaManifest[]>;
15
+ installFromPath(bundlePath: string): Promise<PersonaManifest>;
16
+ activateInWorkspace(workspaceRoot: string, name: string): Promise<void>;
17
+ activeInWorkspace(workspaceRoot: string): Promise<PersonaManifest>;
18
+ get(name: string): Promise<PersonaManifest | null>;
19
+ uninstall(name: string, opts: {
20
+ trackedWorkspaces: string[];
21
+ }): Promise<void>;
22
+ }