@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.
- package/dist/coworker/persona-commands.d.ts +1 -0
- package/dist/coworker/persona-commands.js +5 -0
- package/dist/coworker/persona-commands.test.d.ts +1 -0
- package/dist/coworker/persona-commands.test.js +45 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/_coworker-paths.js +8 -0
- package/dist/resources/extensions/coworker-artifacts/artifacts-command.js +31 -0
- package/dist/resources/extensions/coworker-artifacts/artifacts-singleton.js +17 -0
- package/dist/resources/extensions/coworker-artifacts/extension-manifest.json +13 -0
- package/dist/resources/extensions/coworker-artifacts/index.js +125 -0
- package/dist/resources/extensions/coworker-artifacts/list-tool.js +27 -0
- package/dist/resources/extensions/coworker-artifacts/open-tool.js +25 -0
- package/dist/resources/extensions/coworker-memory/extension-manifest.json +13 -0
- package/dist/resources/extensions/coworker-memory/index.js +219 -0
- package/dist/resources/extensions/coworker-memory/memorize-tool.js +10 -0
- package/dist/resources/extensions/coworker-memory/memory-command.js +157 -0
- package/dist/resources/extensions/coworker-memory/memory-singleton.js +55 -0
- package/dist/resources/extensions/coworker-memory/recall-tool.js +18 -0
- package/dist/resources/extensions/coworker-memory/session-hooks.js +45 -0
- package/dist/resources/extensions/coworker-scratchpad/attach-banners.js +53 -0
- package/dist/resources/extensions/coworker-scratchpad/extension-manifest.json +13 -0
- package/dist/resources/extensions/coworker-scratchpad/format-age.js +9 -0
- package/dist/resources/extensions/coworker-scratchpad/helpers.js +38 -0
- package/dist/resources/extensions/coworker-scratchpad/index.js +199 -0
- package/dist/resources/extensions/coworker-scratchpad/mime-bundle.js +20 -0
- package/dist/resources/extensions/coworker-scratchpad/scratchpad-tool.js +118 -0
- package/dist/resources/extensions/coworker-scratchpad/session-sidecar.js +60 -0
- package/dist/resources/extensions/coworker-scratchpad/sp-command.js +597 -0
- package/dist/resources/extensions/coworker-scratchpad/workspace-pointer.js +41 -0
- package/dist/resources/extensions/coworker-scratchpad/workspace-root.js +17 -0
- package/dist/resources/extensions/coworker-vault/audit-command.js +35 -0
- package/dist/resources/extensions/coworker-vault/connect-command.js +42 -0
- package/dist/resources/extensions/coworker-vault/datasource-command.js +50 -0
- package/dist/resources/extensions/coworker-vault/extension-manifest.json +12 -0
- package/dist/resources/extensions/coworker-vault/index.js +171 -0
- package/dist/resources/extensions/coworker-vault/test-helpers.js +86 -0
- package/dist/resources/extensions/coworker-vault/vault-singleton.js +24 -0
- package/dist/resources/extensions/otto/commands/release-notes/_data.js +71 -0
- package/dist/resources/extensions/otto/commands/release-notes/command.js +15 -4
- package/dist/resources/extensions/subagent/index.js +8 -1
- package/dist/resources/extensions/subagent/launch.js +37 -5
- package/dist/resources/extensions/subagent/run-store.js +1 -0
- package/dist/resources/extensions/workflow/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/workflow/bootstrap/register-hooks.js +10 -0
- package/dist/resources/extensions/workflow/persona-status.js +87 -0
- package/package.json +25 -10
- package/packages/contracts/package.json +1 -1
- package/packages/coworker-artifacts/dist/artifact-store.d.ts +25 -0
- package/packages/coworker-artifacts/dist/artifact-store.js +187 -0
- package/packages/coworker-artifacts/dist/dir-snapshot.d.ts +7 -0
- package/packages/coworker-artifacts/dist/dir-snapshot.js +54 -0
- package/packages/coworker-artifacts/dist/errors.d.ts +18 -0
- package/packages/coworker-artifacts/dist/errors.js +37 -0
- package/packages/coworker-artifacts/dist/index.d.ts +7 -0
- package/packages/coworker-artifacts/dist/index.js +7 -0
- package/packages/coworker-artifacts/dist/readme-renderer.d.ts +5 -0
- package/packages/coworker-artifacts/dist/readme-renderer.js +47 -0
- package/packages/coworker-artifacts/dist/resolve-uri.d.ts +3 -0
- package/packages/coworker-artifacts/dist/resolve-uri.js +29 -0
- package/packages/coworker-artifacts/dist/slug.d.ts +4 -0
- package/packages/coworker-artifacts/dist/slug.js +32 -0
- package/packages/coworker-artifacts/dist/types.d.ts +52 -0
- package/packages/coworker-artifacts/dist/types.js +1 -0
- package/packages/coworker-artifacts/package.json +20 -0
- package/packages/coworker-artifacts/src/artifact-store.test.ts +188 -0
- package/packages/coworker-artifacts/src/artifact-store.ts +206 -0
- package/packages/coworker-artifacts/src/artifacts-integration.test.ts +109 -0
- package/packages/coworker-artifacts/src/dir-snapshot.test.ts +71 -0
- package/packages/coworker-artifacts/src/dir-snapshot.ts +52 -0
- package/packages/coworker-artifacts/src/errors.test.ts +37 -0
- package/packages/coworker-artifacts/src/errors.ts +28 -0
- package/packages/coworker-artifacts/src/index.test.ts +22 -0
- package/packages/coworker-artifacts/src/index.ts +7 -0
- package/packages/coworker-artifacts/src/readme-renderer.test.ts +72 -0
- package/packages/coworker-artifacts/src/readme-renderer.ts +56 -0
- package/packages/coworker-artifacts/src/resolve-uri.test.ts +46 -0
- package/packages/coworker-artifacts/src/resolve-uri.ts +29 -0
- package/packages/coworker-artifacts/src/slug.test.ts +47 -0
- package/packages/coworker-artifacts/src/slug.ts +31 -0
- package/packages/coworker-artifacts/src/types.ts +61 -0
- package/packages/coworker-artifacts/tsconfig.json +15 -0
- package/packages/coworker-artifacts/tsconfig.publish.json +4 -0
- package/packages/coworker-memory/dist/context-injection.d.ts +9 -0
- package/packages/coworker-memory/dist/context-injection.js +41 -0
- package/packages/coworker-memory/dist/errors.d.ts +25 -0
- package/packages/coworker-memory/dist/errors.js +51 -0
- package/packages/coworker-memory/dist/index.d.ts +12 -0
- package/packages/coworker-memory/dist/index.js +12 -0
- package/packages/coworker-memory/dist/layer-a-store.d.ts +16 -0
- package/packages/coworker-memory/dist/layer-a-store.js +78 -0
- package/packages/coworker-memory/dist/local-sqlite-backend.d.ts +28 -0
- package/packages/coworker-memory/dist/local-sqlite-backend.js +167 -0
- package/packages/coworker-memory/dist/memory-backend.d.ts +14 -0
- package/packages/coworker-memory/dist/memory-backend.js +1 -0
- package/packages/coworker-memory/dist/memory-recorder.d.ts +50 -0
- package/packages/coworker-memory/dist/memory-recorder.js +69 -0
- package/packages/coworker-memory/dist/migrations/001-init.sql +38 -0
- package/packages/coworker-memory/dist/migrations/002-artifact-kind.sql +50 -0
- package/packages/coworker-memory/dist/paste-detector.d.ts +5 -0
- package/packages/coworker-memory/dist/paste-detector.js +14 -0
- package/packages/coworker-memory/dist/persona-seed.d.ts +10 -0
- package/packages/coworker-memory/dist/persona-seed.js +38 -0
- package/packages/coworker-memory/dist/recall-formatter.d.ts +2 -0
- package/packages/coworker-memory/dist/recall-formatter.js +14 -0
- package/packages/coworker-memory/dist/scope-resolver.d.ts +9 -0
- package/packages/coworker-memory/dist/scope-resolver.js +10 -0
- package/packages/coworker-memory/dist/types.d.ts +51 -0
- package/packages/coworker-memory/dist/types.js +2 -0
- package/packages/coworker-memory/dist/workspace-id.d.ts +3 -0
- package/packages/coworker-memory/dist/workspace-id.js +54 -0
- package/packages/coworker-memory/package.json +35 -0
- package/packages/coworker-memory/src/activator-integration.test.ts +141 -0
- package/packages/coworker-memory/src/context-injection.test.ts +72 -0
- package/packages/coworker-memory/src/context-injection.ts +57 -0
- package/packages/coworker-memory/src/errors.test.ts +45 -0
- package/packages/coworker-memory/src/errors.ts +42 -0
- package/packages/coworker-memory/src/index.test.ts +21 -0
- package/packages/coworker-memory/src/index.ts +12 -0
- package/packages/coworker-memory/src/layer-a-store.test.ts +85 -0
- package/packages/coworker-memory/src/layer-a-store.ts +88 -0
- package/packages/coworker-memory/src/local-sqlite-backend.test.ts +110 -0
- package/packages/coworker-memory/src/local-sqlite-backend.ts +185 -0
- package/packages/coworker-memory/src/memory-backend.ts +10 -0
- package/packages/coworker-memory/src/memory-integration.test.ts +89 -0
- package/packages/coworker-memory/src/memory-recorder.test.ts +101 -0
- package/packages/coworker-memory/src/memory-recorder.ts +95 -0
- package/packages/coworker-memory/src/migrations/001-init.sql +38 -0
- package/packages/coworker-memory/src/migrations/002-artifact-kind.sql +50 -0
- package/packages/coworker-memory/src/paste-detector.test.ts +23 -0
- package/packages/coworker-memory/src/paste-detector.ts +18 -0
- package/packages/coworker-memory/src/persona-seed.test.ts +57 -0
- package/packages/coworker-memory/src/persona-seed.ts +46 -0
- package/packages/coworker-memory/src/recall-formatter.test.ts +34 -0
- package/packages/coworker-memory/src/recall-formatter.ts +15 -0
- package/packages/coworker-memory/src/scope-resolver.test.ts +23 -0
- package/packages/coworker-memory/src/scope-resolver.ts +18 -0
- package/packages/coworker-memory/src/types.ts +61 -0
- package/packages/coworker-memory/src/workspace-id.test.ts +48 -0
- package/packages/coworker-memory/src/workspace-id.ts +56 -0
- package/packages/coworker-memory/tsconfig.json +15 -0
- package/packages/coworker-memory/tsconfig.publish.json +4 -0
- package/packages/coworker-persona/dist/commands.d.ts +7 -0
- package/packages/coworker-persona/dist/commands.js +35 -0
- package/packages/coworker-persona/dist/defaults/manifest.yaml +12 -0
- package/packages/coworker-persona/dist/defaults/steering/identity.md +3 -0
- package/packages/coworker-persona/dist/index.d.ts +3 -0
- package/packages/coworker-persona/dist/index.js +3 -0
- package/packages/coworker-persona/dist/manifest.d.ts +24 -0
- package/packages/coworker-persona/dist/manifest.js +21 -0
- package/packages/coworker-persona/dist/registry.d.ts +22 -0
- package/packages/coworker-persona/dist/registry.js +142 -0
- package/packages/coworker-persona/package.json +28 -0
- package/packages/coworker-persona/scripts/copy-defaults.cjs +17 -0
- package/packages/coworker-persona/src/commands.ts +47 -0
- package/packages/coworker-persona/src/defaults/manifest.yaml +12 -0
- package/packages/coworker-persona/src/defaults/steering/identity.md +3 -0
- package/packages/coworker-persona/src/index.ts +3 -0
- package/packages/coworker-persona/src/manifest.test.ts +67 -0
- package/packages/coworker-persona/src/manifest.ts +49 -0
- package/packages/coworker-persona/src/registry.test.ts +89 -0
- package/packages/coworker-persona/src/registry.ts +147 -0
- package/packages/coworker-persona/tsconfig.json +15 -0
- package/packages/coworker-persona/tsconfig.publish.json +4 -0
- package/packages/coworker-scratchpad/dist/cell-archive.d.ts +39 -0
- package/packages/coworker-scratchpad/dist/cell-archive.js +77 -0
- package/packages/coworker-scratchpad/dist/cell-tree.d.ts +14 -0
- package/packages/coworker-scratchpad/dist/cell-tree.js +72 -0
- package/packages/coworker-scratchpad/dist/child-process-runtime.d.ts +129 -0
- package/packages/coworker-scratchpad/dist/child-process-runtime.js +427 -0
- package/packages/coworker-scratchpad/dist/collector-registry.d.ts +12 -0
- package/packages/coworker-scratchpad/dist/collector-registry.js +29 -0
- package/packages/coworker-scratchpad/dist/detect-kind.d.ts +3 -0
- package/packages/coworker-scratchpad/dist/detect-kind.js +19 -0
- package/packages/coworker-scratchpad/dist/file-collector.d.ts +15 -0
- package/packages/coworker-scratchpad/dist/file-collector.js +99 -0
- package/packages/coworker-scratchpad/dist/index.d.ts +13 -0
- package/packages/coworker-scratchpad/dist/index.js +13 -0
- package/packages/coworker-scratchpad/dist/kernel-bindings.d.ts +49 -0
- package/packages/coworker-scratchpad/dist/kernel-bindings.js +220 -0
- package/packages/coworker-scratchpad/dist/kernel-entry.d.ts +1 -0
- package/packages/coworker-scratchpad/dist/kernel-entry.js +355 -0
- package/packages/coworker-scratchpad/dist/kernel-protocol.d.ts +171 -0
- package/packages/coworker-scratchpad/dist/kernel-protocol.js +48 -0
- package/packages/coworker-scratchpad/dist/kernel-spawn.d.ts +3 -0
- package/packages/coworker-scratchpad/dist/kernel-spawn.js +54 -0
- package/packages/coworker-scratchpad/dist/namespace-codec.d.ts +22 -0
- package/packages/coworker-scratchpad/dist/namespace-codec.js +61 -0
- package/packages/coworker-scratchpad/dist/scratchpad-lock.d.ts +24 -0
- package/packages/coworker-scratchpad/dist/scratchpad-lock.js +86 -0
- package/packages/coworker-scratchpad/dist/scratchpad-manager.d.ts +193 -0
- package/packages/coworker-scratchpad/dist/scratchpad-manager.js +866 -0
- package/packages/coworker-scratchpad/dist/staleness-banner.d.ts +12 -0
- package/packages/coworker-scratchpad/dist/staleness-banner.js +27 -0
- package/packages/coworker-scratchpad/package.json +31 -0
- package/packages/coworker-scratchpad/src/cell-archive.test.ts +150 -0
- package/packages/coworker-scratchpad/src/cell-archive.ts +97 -0
- package/packages/coworker-scratchpad/src/cell-tree.test.ts +105 -0
- package/packages/coworker-scratchpad/src/cell-tree.ts +90 -0
- package/packages/coworker-scratchpad/src/child-process-runtime.test.ts +413 -0
- package/packages/coworker-scratchpad/src/child-process-runtime.ts +493 -0
- package/packages/coworker-scratchpad/src/collector-registry.test.ts +69 -0
- package/packages/coworker-scratchpad/src/collector-registry.ts +33 -0
- package/packages/coworker-scratchpad/src/detect-kind.test.ts +33 -0
- package/packages/coworker-scratchpad/src/detect-kind.ts +22 -0
- package/packages/coworker-scratchpad/src/file-collector.test.ts +109 -0
- package/packages/coworker-scratchpad/src/file-collector.ts +114 -0
- package/packages/coworker-scratchpad/src/index.ts +74 -0
- package/packages/coworker-scratchpad/src/kernel-bindings.test.ts +188 -0
- package/packages/coworker-scratchpad/src/kernel-bindings.ts +279 -0
- package/packages/coworker-scratchpad/src/kernel-entry.test.ts +123 -0
- package/packages/coworker-scratchpad/src/kernel-entry.ts +390 -0
- package/packages/coworker-scratchpad/src/kernel-protocol.test.ts +105 -0
- package/packages/coworker-scratchpad/src/kernel-protocol.ts +230 -0
- package/packages/coworker-scratchpad/src/kernel-spawn.test.ts +60 -0
- package/packages/coworker-scratchpad/src/kernel-spawn.ts +54 -0
- package/packages/coworker-scratchpad/src/namespace-codec.test.ts +102 -0
- package/packages/coworker-scratchpad/src/namespace-codec.ts +90 -0
- package/packages/coworker-scratchpad/src/scratchpad-lock.test.ts +98 -0
- package/packages/coworker-scratchpad/src/scratchpad-lock.ts +102 -0
- package/packages/coworker-scratchpad/src/scratchpad-manager.test.ts +1343 -0
- package/packages/coworker-scratchpad/src/scratchpad-manager.ts +891 -0
- package/packages/coworker-scratchpad/src/staleness-banner.test.ts +53 -0
- package/packages/coworker-scratchpad/src/staleness-banner.ts +33 -0
- package/packages/coworker-scratchpad/src/vault-integration.test.ts +221 -0
- package/packages/coworker-scratchpad/tsconfig.json +15 -0
- package/packages/coworker-scratchpad/tsconfig.publish.json +4 -0
- package/packages/coworker-types/dist/artifacts.d.ts +31 -0
- package/packages/coworker-types/dist/artifacts.js +2 -0
- package/packages/coworker-types/dist/contracts.d.ts +32 -0
- package/packages/coworker-types/dist/contracts.js +1 -0
- package/packages/coworker-types/dist/index.d.ts +5 -0
- package/packages/coworker-types/dist/index.js +5 -0
- package/packages/coworker-types/dist/memory.d.ts +61 -0
- package/packages/coworker-types/dist/memory.js +3 -0
- package/packages/coworker-types/dist/scratchpad.d.ts +43 -0
- package/packages/coworker-types/dist/scratchpad.js +2 -0
- package/packages/coworker-types/dist/vault.d.ts +34 -0
- package/packages/coworker-types/dist/vault.js +2 -0
- package/packages/coworker-types/package.json +24 -0
- package/packages/coworker-types/src/artifacts.test.ts +52 -0
- package/packages/coworker-types/src/artifacts.ts +35 -0
- package/packages/coworker-types/src/contracts.test.ts +43 -0
- package/packages/coworker-types/src/contracts.ts +36 -0
- package/packages/coworker-types/src/index.ts +5 -0
- package/packages/coworker-types/src/memory.test.ts +50 -0
- package/packages/coworker-types/src/memory.ts +79 -0
- package/packages/coworker-types/src/scratchpad.test.ts +46 -0
- package/packages/coworker-types/src/scratchpad.ts +51 -0
- package/packages/coworker-types/src/smoke.test.ts +34 -0
- package/packages/coworker-types/src/vault.test.ts +49 -0
- package/packages/coworker-types/src/vault.ts +40 -0
- package/packages/coworker-types/tsconfig.json +15 -0
- package/packages/coworker-types/tsconfig.publish.json +4 -0
- package/packages/coworker-utils/dist/audit-log.d.ts +34 -0
- package/packages/coworker-utils/dist/audit-log.js +88 -0
- package/packages/coworker-utils/dist/index.d.ts +6 -0
- package/packages/coworker-utils/dist/index.js +6 -0
- package/packages/coworker-utils/dist/lease.d.ts +7 -0
- package/packages/coworker-utils/dist/lease.js +67 -0
- package/packages/coworker-utils/dist/logger.d.ts +13 -0
- package/packages/coworker-utils/dist/logger.js +26 -0
- package/packages/coworker-utils/dist/migration-runner.d.ts +7 -0
- package/packages/coworker-utils/dist/migration-runner.js +36 -0
- package/packages/coworker-utils/dist/ndjson-channel.d.ts +3 -0
- package/packages/coworker-utils/dist/ndjson-channel.js +38 -0
- package/packages/coworker-utils/dist/secret-scanner.d.ts +10 -0
- package/packages/coworker-utils/dist/secret-scanner.js +42 -0
- package/packages/coworker-utils/package.json +24 -0
- package/packages/coworker-utils/src/audit-log.test.ts +140 -0
- package/packages/coworker-utils/src/audit-log.ts +107 -0
- package/packages/coworker-utils/src/index.ts +6 -0
- package/packages/coworker-utils/src/lease.test.ts +64 -0
- package/packages/coworker-utils/src/lease.ts +76 -0
- package/packages/coworker-utils/src/logger.test.ts +50 -0
- package/packages/coworker-utils/src/logger.ts +45 -0
- package/packages/coworker-utils/src/migration-runner.test.ts +65 -0
- package/packages/coworker-utils/src/migration-runner.ts +50 -0
- package/packages/coworker-utils/src/ndjson-channel.test.ts +76 -0
- package/packages/coworker-utils/src/ndjson-channel.ts +41 -0
- package/packages/coworker-utils/src/secret-scanner.test.ts +61 -0
- package/packages/coworker-utils/src/secret-scanner.ts +56 -0
- package/packages/coworker-utils/tsconfig.json +15 -0
- package/packages/coworker-utils/tsconfig.publish.json +4 -0
- package/packages/coworker-vault/dist/data-vault.d.ts +41 -0
- package/packages/coworker-vault/dist/data-vault.js +223 -0
- package/packages/coworker-vault/dist/engine-registry.d.ts +34 -0
- package/packages/coworker-vault/dist/engine-registry.js +90 -0
- package/packages/coworker-vault/dist/engines/jira.yaml +17 -0
- package/packages/coworker-vault/dist/errors.d.ts +28 -0
- package/packages/coworker-vault/dist/errors.js +57 -0
- package/packages/coworker-vault/dist/index.d.ts +6 -0
- package/packages/coworker-vault/dist/index.js +6 -0
- package/packages/coworker-vault/dist/injector.d.ts +19 -0
- package/packages/coworker-vault/dist/injector.js +77 -0
- package/packages/coworker-vault/dist/types.d.ts +28 -0
- package/packages/coworker-vault/dist/types.js +1 -0
- package/packages/coworker-vault/dist/vault-keep.d.ts +4 -0
- package/packages/coworker-vault/dist/vault-keep.js +21 -0
- package/packages/coworker-vault/package.json +29 -0
- package/packages/coworker-vault/src/data-vault.test.ts +199 -0
- package/packages/coworker-vault/src/data-vault.ts +257 -0
- package/packages/coworker-vault/src/engine-registry.test.ts +120 -0
- package/packages/coworker-vault/src/engine-registry.ts +107 -0
- package/packages/coworker-vault/src/engines/jira.yaml +17 -0
- package/packages/coworker-vault/src/errors.test.ts +58 -0
- package/packages/coworker-vault/src/errors.ts +50 -0
- package/packages/coworker-vault/src/index.test.ts +24 -0
- package/packages/coworker-vault/src/index.ts +6 -0
- package/packages/coworker-vault/src/injector.test.ts +109 -0
- package/packages/coworker-vault/src/injector.ts +98 -0
- package/packages/coworker-vault/src/types.ts +33 -0
- package/packages/coworker-vault/src/vault-keep.test.ts +49 -0
- package/packages/coworker-vault/src/vault-keep.ts +31 -0
- package/packages/coworker-vault/tsconfig.json +15 -0
- package/packages/coworker-vault/tsconfig.publish.json +4 -0
- package/packages/daemon/package.json +3 -3
- package/packages/mcp-server/package.json +3 -3
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/native/package.json +1 -1
- package/packages/native/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +6 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +22 -3
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +11 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.d.ts +47 -0
- package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.js +107 -0
- package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.d.ts +19 -0
- package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.js +121 -0
- package/packages/pi-coding-agent/dist/modes/rpc/raw-stdout.regression.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +17 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +2 -2
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +22 -3
- package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +11 -0
- package/packages/pi-coding-agent/src/modes/rpc/raw-stdout.regression.test.ts +129 -0
- package/packages/pi-coding-agent/src/modes/rpc/raw-stdout.ts +117 -0
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +18 -1
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
- package/pkg/package.json +1 -1
- package/scripts/install.js +6 -5
- package/src/resources/extensions/_coworker-paths.test.ts +40 -0
- package/src/resources/extensions/_coworker-paths.ts +10 -0
- package/src/resources/extensions/coworker-artifacts/artifacts-command.test.ts +54 -0
- package/src/resources/extensions/coworker-artifacts/artifacts-command.ts +43 -0
- package/src/resources/extensions/coworker-artifacts/artifacts-singleton.test.ts +25 -0
- package/src/resources/extensions/coworker-artifacts/artifacts-singleton.ts +29 -0
- package/src/resources/extensions/coworker-artifacts/extension-manifest.json +13 -0
- package/src/resources/extensions/coworker-artifacts/index.test.ts +46 -0
- package/src/resources/extensions/coworker-artifacts/index.ts +154 -0
- package/src/resources/extensions/coworker-artifacts/list-tool.test.ts +29 -0
- package/src/resources/extensions/coworker-artifacts/list-tool.ts +53 -0
- package/src/resources/extensions/coworker-artifacts/open-tool.test.ts +30 -0
- package/src/resources/extensions/coworker-artifacts/open-tool.ts +43 -0
- package/src/resources/extensions/coworker-memory/extension-manifest.json +13 -0
- package/src/resources/extensions/coworker-memory/index.test.ts +137 -0
- package/src/resources/extensions/coworker-memory/index.ts +257 -0
- package/src/resources/extensions/coworker-memory/memorize-tool.test.ts +41 -0
- package/src/resources/extensions/coworker-memory/memorize-tool.ts +20 -0
- package/src/resources/extensions/coworker-memory/memory-command.test.ts +134 -0
- package/src/resources/extensions/coworker-memory/memory-command.ts +131 -0
- package/src/resources/extensions/coworker-memory/memory-singleton.test.ts +41 -0
- package/src/resources/extensions/coworker-memory/memory-singleton.ts +89 -0
- package/src/resources/extensions/coworker-memory/recall-tool.test.ts +50 -0
- package/src/resources/extensions/coworker-memory/recall-tool.ts +35 -0
- package/src/resources/extensions/coworker-memory/session-hooks.test.ts +77 -0
- package/src/resources/extensions/coworker-memory/session-hooks.ts +61 -0
- package/src/resources/extensions/coworker-scratchpad/attach-banners.test.ts +124 -0
- package/src/resources/extensions/coworker-scratchpad/attach-banners.ts +67 -0
- package/src/resources/extensions/coworker-scratchpad/extension-manifest.json +13 -0
- package/src/resources/extensions/coworker-scratchpad/format-age.test.ts +30 -0
- package/src/resources/extensions/coworker-scratchpad/format-age.ts +6 -0
- package/src/resources/extensions/coworker-scratchpad/helpers.test.ts +93 -0
- package/src/resources/extensions/coworker-scratchpad/helpers.ts +42 -0
- package/src/resources/extensions/coworker-scratchpad/index.test.ts +514 -0
- package/src/resources/extensions/coworker-scratchpad/index.ts +207 -0
- package/src/resources/extensions/coworker-scratchpad/mime-bundle.test.ts +61 -0
- package/src/resources/extensions/coworker-scratchpad/mime-bundle.ts +23 -0
- package/src/resources/extensions/coworker-scratchpad/scratchpad-tool.test.ts +137 -0
- package/src/resources/extensions/coworker-scratchpad/scratchpad-tool.ts +165 -0
- package/src/resources/extensions/coworker-scratchpad/session-sidecar.test.ts +133 -0
- package/src/resources/extensions/coworker-scratchpad/session-sidecar.ts +68 -0
- package/src/resources/extensions/coworker-scratchpad/sp-command.test.ts +836 -0
- package/src/resources/extensions/coworker-scratchpad/sp-command.ts +602 -0
- package/src/resources/extensions/coworker-scratchpad/workspace-pointer.test.ts +74 -0
- package/src/resources/extensions/coworker-scratchpad/workspace-pointer.ts +55 -0
- package/src/resources/extensions/coworker-scratchpad/workspace-root.test.ts +51 -0
- package/src/resources/extensions/coworker-scratchpad/workspace-root.ts +16 -0
- package/src/resources/extensions/coworker-vault/audit-command.test.ts +109 -0
- package/src/resources/extensions/coworker-vault/audit-command.ts +56 -0
- package/src/resources/extensions/coworker-vault/connect-command.test.ts +103 -0
- package/src/resources/extensions/coworker-vault/connect-command.ts +69 -0
- package/src/resources/extensions/coworker-vault/datasource-command.test.ts +80 -0
- package/src/resources/extensions/coworker-vault/datasource-command.ts +81 -0
- package/src/resources/extensions/coworker-vault/extension-manifest.json +12 -0
- package/src/resources/extensions/coworker-vault/index.test.ts +82 -0
- package/src/resources/extensions/coworker-vault/index.ts +181 -0
- package/src/resources/extensions/coworker-vault/test-helpers.ts +120 -0
- package/src/resources/extensions/coworker-vault/vault-singleton.test.ts +27 -0
- package/src/resources/extensions/coworker-vault/vault-singleton.ts +40 -0
- package/src/resources/extensions/otto/commands/release-notes/_data.ts +85 -0
- package/src/resources/extensions/otto/commands/release-notes/command.ts +16 -3
- package/src/resources/extensions/subagent/index.ts +9 -0
- package/src/resources/extensions/subagent/launch.test.ts +97 -0
- package/src/resources/extensions/subagent/launch.ts +42 -5
- package/src/resources/extensions/subagent/run-store.ts +3 -1
- package/src/resources/extensions/workflow/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/workflow/bootstrap/register-hooks.ts +10 -0
- package/src/resources/extensions/workflow/persona-status.ts +109 -0
- package/src/resources/extensions/workflow/tests/auto-recovery.test.ts +34 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// src/resources/extensions/coworker-memory/index.test.ts
|
|
2
|
+
//
|
|
3
|
+
// Unit tests for the coworker-memory production activator.
|
|
4
|
+
// Covers barrel surface preservation (replaces the Phase 3 Task 12 spot-check
|
|
5
|
+
// stub), lifecycle wiring (session_start/shutdown), command + tool registration,
|
|
6
|
+
// before_agent_start + agent_start round-trip recording a turn drawer,
|
|
7
|
+
// Layer A context injection, and init-failure gating.
|
|
8
|
+
import { describe, it } from 'node:test';
|
|
9
|
+
import assert from 'node:assert/strict';
|
|
10
|
+
import { mkdtempSync } from 'node:fs';
|
|
11
|
+
import { tmpdir } from 'node:os';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
import coworkerMemoryExtension, { getMemoryRecorder, createMemoryBundle } from './index.js';
|
|
14
|
+
import {
|
|
15
|
+
makeFakeApi, fireSessionStart, fireSessionShutdown,
|
|
16
|
+
fireBeforeAgentStart, fireAgentStart,
|
|
17
|
+
} from '../coworker-vault/test-helpers.js';
|
|
18
|
+
|
|
19
|
+
describe('coworker-memory activator', () => {
|
|
20
|
+
it('barrel still exports key surface (preserves Task 12 spot-check)', () => {
|
|
21
|
+
assert.equal(typeof createMemoryBundle, 'function');
|
|
22
|
+
assert.equal(typeof getMemoryRecorder, 'function');
|
|
23
|
+
assert.equal(typeof coworkerMemoryExtension, 'function');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('getMemoryRecorder returns null before session_start', () => {
|
|
27
|
+
assert.equal(getMemoryRecorder(), null);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('registers memorize + recall tools and /memory command', () => {
|
|
31
|
+
const api = makeFakeApi();
|
|
32
|
+
coworkerMemoryExtension(api.api);
|
|
33
|
+
assert.ok(api.tools.has('memorize'));
|
|
34
|
+
assert.ok(api.tools.has('recall'));
|
|
35
|
+
assert.ok(api.commands.has('memory'));
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('session_start constructs bundle; getMemoryRecorder returns recorder', async () => {
|
|
39
|
+
const api = makeFakeApi();
|
|
40
|
+
coworkerMemoryExtension(api.api);
|
|
41
|
+
const ws = mkdtempSync(join(tmpdir(), 'mem-act-'));
|
|
42
|
+
const global = mkdtempSync(join(tmpdir(), 'mem-act-global-'));
|
|
43
|
+
process.env.OTTO_COWORKER_GLOBAL_DIR = global;
|
|
44
|
+
process.env.OTTO_SCRATCHPAD_ROOT = mkdtempSync(join(tmpdir(), 'mem-act-sp-'));
|
|
45
|
+
try {
|
|
46
|
+
await fireSessionStart(api, { cwd: ws });
|
|
47
|
+
assert.equal(api.notifyCalls.find(c => /unavailable/.test(c.message)), undefined);
|
|
48
|
+
assert.ok(getMemoryRecorder(), 'recorder should be set after session_start');
|
|
49
|
+
await fireSessionShutdown(api);
|
|
50
|
+
assert.equal(getMemoryRecorder(), null, 'recorder cleared after session_shutdown');
|
|
51
|
+
} finally {
|
|
52
|
+
delete process.env.OTTO_COWORKER_GLOBAL_DIR;
|
|
53
|
+
delete process.env.OTTO_SCRATCHPAD_ROOT;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('before_agent_start + agent_start round-trip records a turn drawer', async () => {
|
|
58
|
+
const api = makeFakeApi();
|
|
59
|
+
coworkerMemoryExtension(api.api);
|
|
60
|
+
const ws = mkdtempSync(join(tmpdir(), 'mem-rt-'));
|
|
61
|
+
process.env.OTTO_COWORKER_GLOBAL_DIR = mkdtempSync(join(tmpdir(), 'mem-rt-g-'));
|
|
62
|
+
process.env.OTTO_SCRATCHPAD_ROOT = mkdtempSync(join(tmpdir(), 'mem-rt-sp-'));
|
|
63
|
+
try {
|
|
64
|
+
await fireSessionStart(api, { cwd: ws });
|
|
65
|
+
const result = await fireBeforeAgentStart(api, 'what happened last night', 'YOU ARE OTTO');
|
|
66
|
+
// No Layer A content yet → no augmentation expected.
|
|
67
|
+
assert.equal(result?.systemPrompt, undefined);
|
|
68
|
+
await fireAgentStart(api, 'sess-1', 'turn-1');
|
|
69
|
+
// Verify via a fresh peek bundle reading the same DB.
|
|
70
|
+
const peek = await createMemoryBundle({
|
|
71
|
+
globalDir: process.env.OTTO_COWORKER_GLOBAL_DIR!,
|
|
72
|
+
workspaceDir: ws,
|
|
73
|
+
scopeMode: 'per-project-tagged',
|
|
74
|
+
currentScratchpadName: () => null,
|
|
75
|
+
});
|
|
76
|
+
try {
|
|
77
|
+
const results = await peek.backend.recall({ query: 'happened' });
|
|
78
|
+
assert.equal(results.length, 1);
|
|
79
|
+
assert.equal(results[0]!.drawer.kind, 'turn');
|
|
80
|
+
assert.match(results[0]!.drawer.content, /what happened last night/);
|
|
81
|
+
} finally {
|
|
82
|
+
await peek.dispose();
|
|
83
|
+
}
|
|
84
|
+
await fireSessionShutdown(api);
|
|
85
|
+
} finally {
|
|
86
|
+
delete process.env.OTTO_COWORKER_GLOBAL_DIR;
|
|
87
|
+
delete process.env.OTTO_SCRATCHPAD_ROOT;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('before_agent_start injects Layer A block when Layer A has content', async () => {
|
|
92
|
+
const api = makeFakeApi();
|
|
93
|
+
coworkerMemoryExtension(api.api);
|
|
94
|
+
const ws = mkdtempSync(join(tmpdir(), 'mem-la-'));
|
|
95
|
+
process.env.OTTO_COWORKER_GLOBAL_DIR = mkdtempSync(join(tmpdir(), 'mem-la-g-'));
|
|
96
|
+
process.env.OTTO_SCRATCHPAD_ROOT = mkdtempSync(join(tmpdir(), 'mem-la-sp-'));
|
|
97
|
+
try {
|
|
98
|
+
await fireSessionStart(api, { cwd: ws });
|
|
99
|
+
// Seed a lesson into the workspace Layer A store through a fresh bundle.
|
|
100
|
+
const seed = await createMemoryBundle({
|
|
101
|
+
globalDir: process.env.OTTO_COWORKER_GLOBAL_DIR!,
|
|
102
|
+
workspaceDir: ws, scopeMode: 'per-project-tagged',
|
|
103
|
+
currentScratchpadName: () => null,
|
|
104
|
+
});
|
|
105
|
+
try {
|
|
106
|
+
await seed.workspaceLayerA.append({
|
|
107
|
+
kind: 'lesson', text: 'always check ttl', source: 'user',
|
|
108
|
+
ts: '2026-06-02T00:00:00Z',
|
|
109
|
+
});
|
|
110
|
+
} finally { await seed.dispose(); }
|
|
111
|
+
const result = await fireBeforeAgentStart(api, 'q', 'BASE PROMPT');
|
|
112
|
+
assert.match(result?.systemPrompt ?? '', /BASE PROMPT/);
|
|
113
|
+
assert.match(result?.systemPrompt ?? '', /always check ttl/);
|
|
114
|
+
await fireSessionShutdown(api);
|
|
115
|
+
} finally {
|
|
116
|
+
delete process.env.OTTO_COWORKER_GLOBAL_DIR;
|
|
117
|
+
delete process.env.OTTO_SCRATCHPAD_ROOT;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('init failure notifies + gates commands', async () => {
|
|
122
|
+
const api = makeFakeApi();
|
|
123
|
+
coworkerMemoryExtension(api.api);
|
|
124
|
+
process.env.OTTO_COWORKER_GLOBAL_DIR = '/no/such/path/should/not/exist';
|
|
125
|
+
process.env.OTTO_SCRATCHPAD_ROOT = '/no/such/path/should/not/exist';
|
|
126
|
+
try {
|
|
127
|
+
await fireSessionStart(api, { cwd: '/no/such/path' });
|
|
128
|
+
assert.ok(api.notifyCalls.find(c => /memory unavailable/.test(c.message)));
|
|
129
|
+
const mem = api.commands.get('memory')!;
|
|
130
|
+
await mem.handler('status', api.commandCtx);
|
|
131
|
+
assert.ok(api.notifyCalls.filter(c => /unavailable/.test(c.message)).length >= 2);
|
|
132
|
+
} finally {
|
|
133
|
+
delete process.env.OTTO_COWORKER_GLOBAL_DIR;
|
|
134
|
+
delete process.env.OTTO_SCRATCHPAD_ROOT;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// src/resources/extensions/coworker-memory/index.ts
|
|
2
|
+
//
|
|
3
|
+
// Coworker Memory production activator. Wires the Phase 3 memory primitives
|
|
4
|
+
// (createMemoryBundle, runMemorize, runRecall, runMemoryCommand, buildLayerAContext)
|
|
5
|
+
// into pi's ExtensionAPI so /memory, memorize+recall tools, before_agent_start
|
|
6
|
+
// Layer A injection, and agent_start auto-retain of user turns all light up in a
|
|
7
|
+
// live Otto session. Closes Phase 3 Task 20 (recordTurn auto-retain).
|
|
8
|
+
//
|
|
9
|
+
// Cross-pillar surface: the scratchpad activator imports getMemoryRecorder()
|
|
10
|
+
// from this module to gate its onDataLoad → recordFileLoad hop (Phase 3.1 Task 4).
|
|
11
|
+
import type { ExtensionAPI } from '@otto/pi-coding-agent';
|
|
12
|
+
import { Type } from '@sinclair/typebox';
|
|
13
|
+
import {
|
|
14
|
+
buildLayerAContext,
|
|
15
|
+
type MemoryRecorder,
|
|
16
|
+
type LayerAKind,
|
|
17
|
+
type DrawerKind,
|
|
18
|
+
} from '@otto/coworker-memory';
|
|
19
|
+
import { createMemoryBundle, type MemoryBundle, type MemoryBundleOptions } from './memory-singleton.js';
|
|
20
|
+
import { runMemorize } from './memorize-tool.js';
|
|
21
|
+
import { runRecall, type RecallToolArgs } from './recall-tool.js';
|
|
22
|
+
import { runMemoryCommand } from './memory-command.js';
|
|
23
|
+
import { onSessionShutdown } from './session-hooks.js';
|
|
24
|
+
import { createCurrentScratchpadProvider } from '../coworker-scratchpad/sp-command.js';
|
|
25
|
+
import { getCoworkerGlobalDir, getScratchpadsRoot } from '../_coworker-paths.js';
|
|
26
|
+
|
|
27
|
+
export { createMemoryBundle };
|
|
28
|
+
export type { MemoryBundle, MemoryBundleOptions };
|
|
29
|
+
|
|
30
|
+
// Cross-pillar export. Scratchpad's onDataLoad closure imports this and calls
|
|
31
|
+
// it lazily — returns null before session_start or after session_shutdown,
|
|
32
|
+
// which is the correct "no-op" signal.
|
|
33
|
+
let activeRecorder: MemoryRecorder | null = null;
|
|
34
|
+
export function getMemoryRecorder(): MemoryRecorder | null {
|
|
35
|
+
return activeRecorder;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const MEMORIZE_PARAMS = Type.Object({
|
|
39
|
+
text: Type.String({ description: 'The note text to store in Layer A memory.' }),
|
|
40
|
+
kind: Type.Union(
|
|
41
|
+
[Type.Literal('profile'), Type.Literal('rule'), Type.Literal('lesson')],
|
|
42
|
+
{ description: "Layer A kind: 'profile' (about the user), 'rule' (must-do), or 'lesson' (learned)." },
|
|
43
|
+
),
|
|
44
|
+
scope: Type.Optional(Type.Union(
|
|
45
|
+
[Type.Literal('global'), Type.Literal('workspace')],
|
|
46
|
+
{ description: "Defaults to 'workspace' when omitted." },
|
|
47
|
+
)),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const RECALL_PARAMS = Type.Object({
|
|
51
|
+
query: Type.String({ description: 'Free-text query to search verbatim drawers.' }),
|
|
52
|
+
kind: Type.Optional(Type.Union([
|
|
53
|
+
Type.Literal('turn'), Type.Literal('paste'), Type.Literal('file_load'),
|
|
54
|
+
Type.Literal('ticket'), Type.Literal('email'), Type.Literal('rca'), Type.Literal('note'),
|
|
55
|
+
])),
|
|
56
|
+
wing: Type.Optional(Type.String()),
|
|
57
|
+
room: Type.Optional(Type.String()),
|
|
58
|
+
days_back: Type.Optional(Type.Number()),
|
|
59
|
+
max_results: Type.Optional(Type.Number()),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
export default function coworkerMemoryExtension(api: ExtensionAPI): void {
|
|
63
|
+
let bundle: MemoryBundle | null = null;
|
|
64
|
+
let unavailable = false;
|
|
65
|
+
let pendingPrompt: string | undefined;
|
|
66
|
+
// One notify per session covers both before_agent_start inject failures and
|
|
67
|
+
// agent_start recordTurn failures — both are best-effort and must not break
|
|
68
|
+
// the chat loop. Spec §3.7.
|
|
69
|
+
let writeFailureNotified = false;
|
|
70
|
+
|
|
71
|
+
api.on('session_start', async (_event, ctx) => {
|
|
72
|
+
try {
|
|
73
|
+
bundle = await createMemoryBundle({
|
|
74
|
+
globalDir: getCoworkerGlobalDir(),
|
|
75
|
+
workspaceDir: ctx.cwd,
|
|
76
|
+
scopeMode: 'per-project-tagged',
|
|
77
|
+
currentScratchpadName: createCurrentScratchpadProvider({
|
|
78
|
+
scratchpadsRoot: getScratchpadsRoot(),
|
|
79
|
+
}),
|
|
80
|
+
});
|
|
81
|
+
activeRecorder = bundle.recorder;
|
|
82
|
+
unavailable = false;
|
|
83
|
+
} catch (err) {
|
|
84
|
+
unavailable = true;
|
|
85
|
+
bundle = null;
|
|
86
|
+
activeRecorder = null;
|
|
87
|
+
ctx.ui.notify(`memory unavailable: ${(err as Error).message}`, 'warning');
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
api.on('before_agent_start', async (event, ctx) => {
|
|
92
|
+
if (!bundle) return undefined;
|
|
93
|
+
// Capture the prompt for the paired agent_start so we can record it even
|
|
94
|
+
// when there is no Layer A content to inject (block.length === 0).
|
|
95
|
+
pendingPrompt = event.prompt;
|
|
96
|
+
try {
|
|
97
|
+
const block = await buildLayerAContext({
|
|
98
|
+
mode: bundle.scopeMode,
|
|
99
|
+
globalStore: bundle.globalLayerA,
|
|
100
|
+
workspaceStore: bundle.workspaceLayerA,
|
|
101
|
+
tokenLimit: 3000,
|
|
102
|
+
});
|
|
103
|
+
if (block.length === 0) return undefined;
|
|
104
|
+
return { systemPrompt: event.systemPrompt + '\n\n' + block };
|
|
105
|
+
} catch (err) {
|
|
106
|
+
if (!writeFailureNotified) {
|
|
107
|
+
ctx.ui.notify(`memory context inject failed: ${(err as Error).message}`, 'warning');
|
|
108
|
+
writeFailureNotified = true;
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
api.on('agent_start', async (event, ctx) => {
|
|
115
|
+
if (!bundle || !pendingPrompt || !event.sessionId || !event.turnId) {
|
|
116
|
+
pendingPrompt = undefined;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const userText = pendingPrompt;
|
|
120
|
+
const sessionId = event.sessionId;
|
|
121
|
+
const turnId = event.turnId;
|
|
122
|
+
pendingPrompt = undefined;
|
|
123
|
+
try {
|
|
124
|
+
await bundle.recorder.recordTurn({ sessionId, userText, turnId });
|
|
125
|
+
} catch (err) {
|
|
126
|
+
if (!writeFailureNotified) {
|
|
127
|
+
ctx.ui.notify(`memory write failed: ${(err as Error).message}`, 'warning');
|
|
128
|
+
writeFailureNotified = true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Union TDetails so TS accepts both error and success branches of execute().
|
|
134
|
+
type MemorizeDetails =
|
|
135
|
+
| { error: string; stored?: false }
|
|
136
|
+
| { error?: undefined; stored: true; layer_a_file: string };
|
|
137
|
+
|
|
138
|
+
api.registerTool<typeof MEMORIZE_PARAMS, MemorizeDetails>({
|
|
139
|
+
name: 'memorize',
|
|
140
|
+
label: 'Memorize',
|
|
141
|
+
description: 'Save a profile note, rule, or lesson into Layer A memory.',
|
|
142
|
+
parameters: MEMORIZE_PARAMS,
|
|
143
|
+
async execute(_toolCallId, params) {
|
|
144
|
+
if (!bundle) {
|
|
145
|
+
return {
|
|
146
|
+
content: [{ type: 'text', text: 'memory unavailable; cannot memorize.' }],
|
|
147
|
+
details: { error: 'memory unavailable' },
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
const out = await runMemorize(bundle, {
|
|
152
|
+
text: params.text,
|
|
153
|
+
kind: params.kind as LayerAKind,
|
|
154
|
+
scope: params.scope as 'global' | 'workspace' | undefined,
|
|
155
|
+
});
|
|
156
|
+
return {
|
|
157
|
+
content: [{ type: 'text', text: `stored in ${out.layer_a_file}` }],
|
|
158
|
+
details: out,
|
|
159
|
+
};
|
|
160
|
+
} catch (err) {
|
|
161
|
+
return {
|
|
162
|
+
content: [{ type: 'text', text: `memorize failed: ${(err as Error).message}` }],
|
|
163
|
+
details: { error: (err as Error).message },
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
type RecallDetails =
|
|
170
|
+
| { error: string; result_count?: undefined }
|
|
171
|
+
| { error?: undefined; result_count: number };
|
|
172
|
+
|
|
173
|
+
api.registerTool<typeof RECALL_PARAMS, RecallDetails>({
|
|
174
|
+
name: 'recall',
|
|
175
|
+
label: 'Recall',
|
|
176
|
+
description: 'Search verbatim drawers in memory (Layer B). Returns markdown with drawer URIs.',
|
|
177
|
+
parameters: RECALL_PARAMS,
|
|
178
|
+
async execute(_toolCallId, params) {
|
|
179
|
+
if (!bundle) {
|
|
180
|
+
return {
|
|
181
|
+
content: [{ type: 'text', text: 'memory unavailable; cannot recall.' }],
|
|
182
|
+
details: { error: 'memory unavailable' },
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
const args: RecallToolArgs = {
|
|
187
|
+
query: params.query,
|
|
188
|
+
kind: params.kind as DrawerKind | undefined,
|
|
189
|
+
wing: params.wing,
|
|
190
|
+
room: params.room,
|
|
191
|
+
days_back: params.days_back,
|
|
192
|
+
max_results: params.max_results,
|
|
193
|
+
};
|
|
194
|
+
const out = await runRecall(bundle, args);
|
|
195
|
+
return {
|
|
196
|
+
content: [{ type: 'text', text: out.markdown }],
|
|
197
|
+
details: { result_count: out.results.length },
|
|
198
|
+
};
|
|
199
|
+
} catch (err) {
|
|
200
|
+
return {
|
|
201
|
+
content: [{ type: 'text', text: `recall failed: ${(err as Error).message}` }],
|
|
202
|
+
details: { error: (err as Error).message },
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
api.registerCommand('memory', {
|
|
209
|
+
description: 'Inspect and manage co-worker memory (Layer A rules/lessons + Layer B drawers).',
|
|
210
|
+
getArgumentCompletions: (prefix) => {
|
|
211
|
+
const subcommands = [
|
|
212
|
+
{ label: 'note', description: 'Append a lesson: /memory note <text>' },
|
|
213
|
+
{ label: 'show', description: 'Show Layer A: /memory show [profile|rules|lessons] [--scope workspace|global]' },
|
|
214
|
+
{ label: 'recall', description: 'Search Layer B drawers: /memory recall <query> [--kind|--room|--wing|--limit|--days_back]' },
|
|
215
|
+
{ label: 'status', description: 'Show workspace_wing, drawer_count, db path' },
|
|
216
|
+
{ label: 'clear', description: 'Clear drawers: /memory clear --wing <wing> --confirm' },
|
|
217
|
+
{ label: 'wing', description: 'Set session wing override: /memory wing <name>' },
|
|
218
|
+
{ label: 'room', description: 'Set session room override: /memory room <name>' },
|
|
219
|
+
{ label: 'seed', description: 'Re-apply persona seed on next session_start' },
|
|
220
|
+
];
|
|
221
|
+
return subcommands
|
|
222
|
+
.filter((s) => s.label.startsWith(prefix))
|
|
223
|
+
.map((s) => ({ value: s.label, label: s.label, description: s.description }));
|
|
224
|
+
},
|
|
225
|
+
handler: async (args, ctx) => {
|
|
226
|
+
if (!bundle) {
|
|
227
|
+
ctx.ui.notify(
|
|
228
|
+
unavailable ? 'memory unavailable; chat continues without it.' : 'memory not ready yet.',
|
|
229
|
+
'warning',
|
|
230
|
+
);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const argv = args.trim().split(/\s+/).filter(Boolean);
|
|
234
|
+
try {
|
|
235
|
+
const result = await runMemoryCommand(bundle, argv);
|
|
236
|
+
ctx.ui.notify(result.message, 'info');
|
|
237
|
+
} catch (err) {
|
|
238
|
+
ctx.ui.notify(`/memory failed: ${(err as Error).message}`, 'error');
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
api.on('session_shutdown', async () => {
|
|
244
|
+
if (bundle) {
|
|
245
|
+
try {
|
|
246
|
+
await onSessionShutdown(bundle);
|
|
247
|
+
} catch {
|
|
248
|
+
// dispose is best-effort; LocalSqliteBackend.close() handles double-close.
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
bundle = null;
|
|
252
|
+
activeRecorder = null;
|
|
253
|
+
pendingPrompt = undefined;
|
|
254
|
+
// writeFailureNotified persists across sessions intentionally — one notify
|
|
255
|
+
// per Otto process lifetime is enough to avoid spam.
|
|
256
|
+
});
|
|
257
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// src/resources/extensions/coworker-memory/memorize-tool.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 { createMemoryBundle } from './memory-singleton.js';
|
|
8
|
+
import { runMemorize } from './memorize-tool.js';
|
|
9
|
+
|
|
10
|
+
async function bundleFor(scope: 'global'|'per-project'|'per-project-tagged') {
|
|
11
|
+
return createMemoryBundle({
|
|
12
|
+
globalDir: mkdtempSync(join(tmpdir(), 'mt-home-')),
|
|
13
|
+
workspaceDir: mkdtempSync(join(tmpdir(), 'mt-ws-')),
|
|
14
|
+
scopeMode: scope, currentScratchpadName: () => null,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe('memorize tool', () => {
|
|
19
|
+
it('writes lesson to workspace by default', async () => {
|
|
20
|
+
const b = await bundleFor('per-project-tagged');
|
|
21
|
+
await runMemorize(b, { text: 'MTTR target 30m', kind: 'lesson' });
|
|
22
|
+
const body = await b.workspaceLayerA.read('lesson');
|
|
23
|
+
assert.match(body, /MTTR target 30m/);
|
|
24
|
+
assert.equal((await b.globalLayerA.read('lesson')), '');
|
|
25
|
+
await b.dispose();
|
|
26
|
+
});
|
|
27
|
+
it('honors scope: global', async () => {
|
|
28
|
+
const b = await bundleFor('per-project-tagged');
|
|
29
|
+
await runMemorize(b, { text: 'use polars', kind: 'profile', scope: 'global' });
|
|
30
|
+
assert.match(await b.globalLayerA.read('profile'), /use polars/);
|
|
31
|
+
await b.dispose();
|
|
32
|
+
});
|
|
33
|
+
it('throws LayerAWriteBlocked on secret content', async () => {
|
|
34
|
+
const b = await bundleFor('per-project-tagged');
|
|
35
|
+
await assert.rejects(
|
|
36
|
+
() => runMemorize(b, { text: 'token AKIAABCDEFGHIJKLMNOP', kind: 'rule' }),
|
|
37
|
+
/VAULT|secret-shaped|Refused/,
|
|
38
|
+
);
|
|
39
|
+
await b.dispose();
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// src/resources/extensions/coworker-memory/memorize-tool.ts
|
|
2
|
+
import type { MemoryBundle } from './memory-singleton.js';
|
|
3
|
+
import type { LayerAKind } from '@otto/coworker-memory';
|
|
4
|
+
|
|
5
|
+
export interface MemorizeArgs {
|
|
6
|
+
text: string;
|
|
7
|
+
kind: LayerAKind;
|
|
8
|
+
scope?: 'global' | 'workspace';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function runMemorize(bundle: MemoryBundle, args: MemorizeArgs): Promise<{ stored: true; layer_a_file: string }> {
|
|
12
|
+
const scope = args.scope ?? 'workspace';
|
|
13
|
+
const store = scope === 'global' ? bundle.globalLayerA : bundle.workspaceLayerA;
|
|
14
|
+
const ts = new Date().toISOString();
|
|
15
|
+
await store.append({ kind: args.kind, text: args.text, source: 'user', ts });
|
|
16
|
+
return {
|
|
17
|
+
stored: true,
|
|
18
|
+
layer_a_file: args.kind === 'lesson' ? 'lessons.md' : args.kind === 'rule' ? 'rules.md' : 'profile.md',
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// src/resources/extensions/coworker-memory/memory-command.test.ts
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
import assert from 'node:assert/strict';
|
|
4
|
+
import { mkdtempSync, mkdirSync } from 'node:fs';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { createMemoryBundle } from './memory-singleton.js';
|
|
8
|
+
import { runMemoryCommand } from './memory-command.js';
|
|
9
|
+
|
|
10
|
+
async function setup() {
|
|
11
|
+
const home = mkdtempSync(join(tmpdir(), 'mc-home-'));
|
|
12
|
+
const ws = mkdtempSync(join(tmpdir(), 'mc-ws-'));
|
|
13
|
+
mkdirSync(ws, { recursive: true });
|
|
14
|
+
return createMemoryBundle({
|
|
15
|
+
globalDir: home, workspaceDir: ws,
|
|
16
|
+
scopeMode: 'per-project-tagged', currentScratchpadName: () => null,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('/memory command', () => {
|
|
21
|
+
it('note appends a lesson', async () => {
|
|
22
|
+
const b = await setup();
|
|
23
|
+
const out = await runMemoryCommand(b, ['note', 'P1 includes MTTR']);
|
|
24
|
+
assert.match(out.message, /lesson stored/i);
|
|
25
|
+
assert.match(await b.workspaceLayerA.read('lesson'), /P1 includes MTTR/);
|
|
26
|
+
await b.dispose();
|
|
27
|
+
});
|
|
28
|
+
it('status reports workspace_wing + drawer_count', async () => {
|
|
29
|
+
const b = await setup();
|
|
30
|
+
await b.recorder.recordTurn({ sessionId: 's', userText: 'x', turnId: 't' });
|
|
31
|
+
const out = await runMemoryCommand(b, ['status']);
|
|
32
|
+
assert.match(out.message, /workspace_wing:/);
|
|
33
|
+
assert.match(out.message, /drawer_count: 1/);
|
|
34
|
+
await b.dispose();
|
|
35
|
+
});
|
|
36
|
+
it('clear --wing deletes drawers', async () => {
|
|
37
|
+
const b = await setup();
|
|
38
|
+
await b.recorder.recordTurn({ sessionId: 's', userText: 'x', turnId: 't' });
|
|
39
|
+
const out = await runMemoryCommand(b, ['clear', '--wing', b.writeWing, '--confirm']);
|
|
40
|
+
assert.match(out.message, /deleted: 1/);
|
|
41
|
+
await b.dispose();
|
|
42
|
+
});
|
|
43
|
+
it('clear without --confirm errors', async () => {
|
|
44
|
+
const b = await setup();
|
|
45
|
+
await assert.rejects(() => runMemoryCommand(b, ['clear', '--wing', 'x']));
|
|
46
|
+
await b.dispose();
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('/memory show (Phase 3.1 follow-up: issue #73)', () => {
|
|
51
|
+
it('show with no args dumps profile + rules + lessons for read scopes (per-project-tagged → workspace first then global)', async () => {
|
|
52
|
+
const b = await setup();
|
|
53
|
+
await b.workspaceLayerA.append({ kind: 'lesson', text: 'mttr is 30m', source: 'user', ts: '2026-06-02T10:00:00Z' });
|
|
54
|
+
await b.workspaceLayerA.append({ kind: 'rule', text: 'escalate P1 to mgr', source: 'user', ts: '2026-06-02T10:00:00Z' });
|
|
55
|
+
await b.globalLayerA.append({ kind: 'profile', text: 'prefer polars', source: 'user', ts: '2026-06-02T10:00:00Z' });
|
|
56
|
+
const out = await runMemoryCommand(b, ['show']);
|
|
57
|
+
assert.match(out.message, /## Lessons \(workspace\)/);
|
|
58
|
+
assert.match(out.message, /mttr is 30m/);
|
|
59
|
+
assert.match(out.message, /## Rules \(workspace\)/);
|
|
60
|
+
assert.match(out.message, /escalate P1 to mgr/);
|
|
61
|
+
assert.match(out.message, /## Profile \(global\)/);
|
|
62
|
+
assert.match(out.message, /prefer polars/);
|
|
63
|
+
// workspace appears before global
|
|
64
|
+
assert.ok(out.message.indexOf('workspace') < out.message.indexOf('global'));
|
|
65
|
+
await b.dispose();
|
|
66
|
+
});
|
|
67
|
+
it('show lessons dumps only lessons.md', async () => {
|
|
68
|
+
const b = await setup();
|
|
69
|
+
await b.workspaceLayerA.append({ kind: 'lesson', text: 'l1', source: 'user', ts: '2026-06-02T00:00:00Z' });
|
|
70
|
+
await b.workspaceLayerA.append({ kind: 'rule', text: 'r1', source: 'user', ts: '2026-06-02T00:00:00Z' });
|
|
71
|
+
const out = await runMemoryCommand(b, ['show', 'lessons']);
|
|
72
|
+
assert.match(out.message, /l1/);
|
|
73
|
+
assert.equal(out.message.includes('r1'), false);
|
|
74
|
+
await b.dispose();
|
|
75
|
+
});
|
|
76
|
+
it('show with --scope global reads global only', async () => {
|
|
77
|
+
const b = await setup();
|
|
78
|
+
await b.workspaceLayerA.append({ kind: 'lesson', text: 'ws-only', source: 'user', ts: '2026-06-02T00:00:00Z' });
|
|
79
|
+
await b.globalLayerA.append({ kind: 'lesson', text: 'global-only', source: 'user', ts: '2026-06-02T00:00:00Z' });
|
|
80
|
+
const out = await runMemoryCommand(b, ['show', 'lessons', '--scope', 'global']);
|
|
81
|
+
assert.match(out.message, /global-only/);
|
|
82
|
+
assert.equal(out.message.includes('ws-only'), false);
|
|
83
|
+
await b.dispose();
|
|
84
|
+
});
|
|
85
|
+
it('show missing layer outputs "(none)"', async () => {
|
|
86
|
+
const b = await setup();
|
|
87
|
+
// No appends — both stores empty
|
|
88
|
+
const out = await runMemoryCommand(b, ['show', 'rules']);
|
|
89
|
+
assert.match(out.message, /\(none\)/);
|
|
90
|
+
await b.dispose();
|
|
91
|
+
});
|
|
92
|
+
it('show with invalid kind errors with usage', async () => {
|
|
93
|
+
const b = await setup();
|
|
94
|
+
await assert.rejects(() => runMemoryCommand(b, ['show', 'banana']));
|
|
95
|
+
await b.dispose();
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('/memory recall (Phase 3.1 follow-up: issue #73)', () => {
|
|
100
|
+
it('recall <query> returns formatted markdown matching the recall LLM tool', async () => {
|
|
101
|
+
const b = await setup();
|
|
102
|
+
await b.recorder.recordPaste({
|
|
103
|
+
sessionId: 's', content: 'load balancer returned 503s around 14:00 UTC', turnId: 't1',
|
|
104
|
+
});
|
|
105
|
+
const out = await runMemoryCommand(b, ['recall', 'load', 'balancer']);
|
|
106
|
+
assert.match(out.message, /Memory recall \(1 matches\)/);
|
|
107
|
+
assert.match(out.message, /<mark>/);
|
|
108
|
+
assert.match(out.message, /drawer:\/\//);
|
|
109
|
+
await b.dispose();
|
|
110
|
+
});
|
|
111
|
+
it('recall with no args errors with usage notice', async () => {
|
|
112
|
+
const b = await setup();
|
|
113
|
+
await assert.rejects(() => runMemoryCommand(b, ['recall']));
|
|
114
|
+
await b.dispose();
|
|
115
|
+
});
|
|
116
|
+
it('recall --kind paste filters to paste drawers', async () => {
|
|
117
|
+
const b = await setup();
|
|
118
|
+
await b.recorder.recordTurn({ sessionId: 's', userText: 'short alpha', turnId: 't1' });
|
|
119
|
+
await b.recorder.recordPaste({ sessionId: 's', content: 'long alpha paste body', turnId: 't2' });
|
|
120
|
+
const out = await runMemoryCommand(b, ['recall', 'alpha', '--kind', 'paste']);
|
|
121
|
+
assert.match(out.message, /Memory recall \(1 matches\)/);
|
|
122
|
+
await b.dispose();
|
|
123
|
+
});
|
|
124
|
+
it('recall --limit clamps to [1, 64]', async () => {
|
|
125
|
+
const b = await setup();
|
|
126
|
+
for (let i = 0; i < 10; i++) {
|
|
127
|
+
await b.recorder.recordTurn({ sessionId: 's', userText: `apple ${i}`, turnId: `t${i}` });
|
|
128
|
+
}
|
|
129
|
+
const out = await runMemoryCommand(b, ['recall', 'apple', '--limit', '3']);
|
|
130
|
+
// Markdown header reports actual count, which should be 3 due to clamp
|
|
131
|
+
assert.match(out.message, /Memory recall \(3 matches\)/);
|
|
132
|
+
await b.dispose();
|
|
133
|
+
});
|
|
134
|
+
});
|