@cmetech/otto 1.1.1 → 1.2.5
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-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 +83 -0
- package/dist/resources/extensions/otto/commands/release-notes/command.js +15 -4
- package/dist/resources/extensions/otto/index.js +31 -6
- package/dist/resources/extensions/shared/coworker-paths.js +8 -0
- package/dist/resources/extensions/slash-commands/{audit.js → audit-codebase.js} +4 -4
- package/dist/resources/extensions/slash-commands/extension-manifest.json +1 -1
- package/dist/resources/extensions/slash-commands/index.js +2 -2
- 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/health-widget-core.js +1 -1
- package/dist/resources/extensions/workflow/persona-status.js +87 -0
- package/package.json +26 -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-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 +97 -0
- package/src/resources/extensions/otto/commands/release-notes/command.ts +16 -3
- package/src/resources/extensions/otto/index.ts +29 -6
- package/src/resources/extensions/shared/coworker-paths.test.ts +40 -0
- package/src/resources/extensions/shared/coworker-paths.ts +10 -0
- package/src/resources/extensions/slash-commands/{audit.ts → audit-codebase.ts} +4 -4
- package/src/resources/extensions/slash-commands/extension-manifest.json +1 -1
- package/src/resources/extensions/slash-commands/index.ts +2 -2
- 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/health-widget-core.ts +1 -1
- 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,82 @@
|
|
|
1
|
+
// src/resources/extensions/coworker-vault/index.test.ts
|
|
2
|
+
//
|
|
3
|
+
// Unit tests for the coworker-vault production activator.
|
|
4
|
+
// Verifies command registration, session_start/shutdown lifecycle, init-failure
|
|
5
|
+
// gating, and that the /connect handler doesn't surface "unavailable" on a happy
|
|
6
|
+
// session_start.
|
|
7
|
+
import { describe, it } from 'node:test';
|
|
8
|
+
import assert from 'node:assert/strict';
|
|
9
|
+
import { mkdtempSync } from 'node:fs';
|
|
10
|
+
import { tmpdir } from 'node:os';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import coworkerVaultExtension from './index.js';
|
|
13
|
+
import { makeFakeApi, fireSessionStart, fireSessionShutdown } from './test-helpers.js';
|
|
14
|
+
|
|
15
|
+
describe('coworker-vault activator', () => {
|
|
16
|
+
it('registers /connect, /datasource, /audit commands at load time', () => {
|
|
17
|
+
const api = makeFakeApi();
|
|
18
|
+
coworkerVaultExtension(api.api);
|
|
19
|
+
assert.ok(api.commands.has('connect'), 'connect command registered');
|
|
20
|
+
assert.ok(api.commands.has('datasource'), 'datasource command registered');
|
|
21
|
+
assert.ok(api.commands.has('audit'), 'audit command registered');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('session_start constructs bundle; session_shutdown clears it', async () => {
|
|
25
|
+
const api = makeFakeApi();
|
|
26
|
+
coworkerVaultExtension(api.api);
|
|
27
|
+
const ws = mkdtempSync(join(tmpdir(), 'vault-act-'));
|
|
28
|
+
process.env.OTTO_COWORKER_GLOBAL_DIR = mkdtempSync(join(tmpdir(), 'otto-global-'));
|
|
29
|
+
try {
|
|
30
|
+
await fireSessionStart(api, { cwd: ws });
|
|
31
|
+
assert.equal(
|
|
32
|
+
api.notifyCalls.filter(c => /unavailable/.test(c.message)).length,
|
|
33
|
+
0,
|
|
34
|
+
'no failure notify on happy path',
|
|
35
|
+
);
|
|
36
|
+
await fireSessionShutdown(api);
|
|
37
|
+
} finally {
|
|
38
|
+
delete process.env.OTTO_COWORKER_GLOBAL_DIR;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('init failure notifies + leaves commands registered (handlers gate on bundle)', async () => {
|
|
43
|
+
const api = makeFakeApi();
|
|
44
|
+
coworkerVaultExtension(api.api);
|
|
45
|
+
// Force createVaultBundle to fail by pointing at a non-writable path.
|
|
46
|
+
process.env.OTTO_COWORKER_GLOBAL_DIR = '/no/such/path/should/not/exist';
|
|
47
|
+
try {
|
|
48
|
+
await fireSessionStart(api, { cwd: '/tmp' });
|
|
49
|
+
const warn = api.notifyCalls.find(c => c.level === 'warning');
|
|
50
|
+
assert.ok(warn, 'expected a warning notify');
|
|
51
|
+
assert.match(warn!.message, /vault unavailable/);
|
|
52
|
+
// Calling the connect handler should notify "vault unavailable" and not throw.
|
|
53
|
+
const connect = api.commands.get('connect')!;
|
|
54
|
+
await connect.handler('', api.commandCtx);
|
|
55
|
+
const unavail = api.notifyCalls.filter(c => /unavailable/.test(c.message));
|
|
56
|
+
assert.ok(unavail.length >= 2, `expected ≥2 unavailable notices, got ${unavail.length}`);
|
|
57
|
+
} finally {
|
|
58
|
+
delete process.env.OTTO_COWORKER_GLOBAL_DIR;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('happy path: /connect handler runs without unavailable notice', async () => {
|
|
63
|
+
const api = makeFakeApi();
|
|
64
|
+
coworkerVaultExtension(api.api);
|
|
65
|
+
const ws = mkdtempSync(join(tmpdir(), 'vault-conn-'));
|
|
66
|
+
process.env.OTTO_COWORKER_GLOBAL_DIR = mkdtempSync(join(tmpdir(), 'otto-conn-'));
|
|
67
|
+
try {
|
|
68
|
+
await fireSessionStart(api, { cwd: ws });
|
|
69
|
+
const connect = api.commands.get('connect')!;
|
|
70
|
+
await connect.handler('', api.commandCtx);
|
|
71
|
+
// No "unavailable" notice should be emitted on a successful session_start;
|
|
72
|
+
// /connect may emit a usage notice (info), which is acceptable.
|
|
73
|
+
assert.equal(
|
|
74
|
+
api.notifyCalls.find(c => /unavailable/.test(c.message)),
|
|
75
|
+
undefined,
|
|
76
|
+
'no "unavailable" notice expected when bundle is healthy',
|
|
77
|
+
);
|
|
78
|
+
} finally {
|
|
79
|
+
delete process.env.OTTO_COWORKER_GLOBAL_DIR;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// src/resources/extensions/coworker-vault/index.ts
|
|
2
|
+
//
|
|
3
|
+
// Coworker Vault production activator. Wires the Phase 2 command runners
|
|
4
|
+
// (runConnect, runDatasourceList/Remove/Test, runAudit) into pi's
|
|
5
|
+
// ExtensionAPI as /connect, /datasource, and /audit slash commands. Closes
|
|
6
|
+
// the Phase 2 "Phase 2.1+ deferral" — vault commands are now reachable from
|
|
7
|
+
// a live Otto session.
|
|
8
|
+
import type { ExtensionAPI } from '@otto/pi-coding-agent';
|
|
9
|
+
import { createVaultBundle, type VaultBundle, type VaultBundleOptions } from './vault-singleton.js';
|
|
10
|
+
import { runConnect, type PromptFieldOptions } from './connect-command.js';
|
|
11
|
+
import {
|
|
12
|
+
runDatasourceList,
|
|
13
|
+
runDatasourceRemove,
|
|
14
|
+
runDatasourceTest,
|
|
15
|
+
} from './datasource-command.js';
|
|
16
|
+
import { runAudit, type AuditQuery } from './audit-command.js';
|
|
17
|
+
import { getCoworkerGlobalDir } from '../shared/coworker-paths.js';
|
|
18
|
+
|
|
19
|
+
export { createVaultBundle };
|
|
20
|
+
export type { VaultBundle, VaultBundleOptions };
|
|
21
|
+
|
|
22
|
+
export default function coworkerVaultExtension(api: ExtensionAPI): void {
|
|
23
|
+
let bundle: VaultBundle | null = null;
|
|
24
|
+
let unavailable = false;
|
|
25
|
+
|
|
26
|
+
api.on('session_start', async (_event, ctx) => {
|
|
27
|
+
try {
|
|
28
|
+
bundle = await createVaultBundle({
|
|
29
|
+
globalDir: getCoworkerGlobalDir(),
|
|
30
|
+
workspaceDir: ctx.cwd,
|
|
31
|
+
});
|
|
32
|
+
unavailable = false;
|
|
33
|
+
} catch (err) {
|
|
34
|
+
unavailable = true;
|
|
35
|
+
bundle = null;
|
|
36
|
+
ctx.ui.notify(`vault unavailable: ${(err as Error).message}`, 'warning');
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
api.registerCommand('connect', {
|
|
41
|
+
description: 'Add or edit a credential entry. Usage: /connect <engine> <name> [--workspace]',
|
|
42
|
+
handler: async (args, ctx) => {
|
|
43
|
+
if (!bundle) {
|
|
44
|
+
ctx.ui.notify(
|
|
45
|
+
unavailable ? 'vault unavailable; chat continues without it.' : 'vault not ready yet.',
|
|
46
|
+
'warning',
|
|
47
|
+
);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const tokens = args.trim().split(/\s+/).filter(Boolean);
|
|
51
|
+
const flags = new Set(tokens.filter(t => t.startsWith('--')));
|
|
52
|
+
const positional = tokens.filter(t => !t.startsWith('--'));
|
|
53
|
+
const [engineId, entryName] = positional;
|
|
54
|
+
if (!engineId || !entryName) {
|
|
55
|
+
ctx.ui.notify('Usage: /connect <engine> <name> [--workspace]', 'info');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const forceWorkspace = flags.has('--workspace');
|
|
59
|
+
// Wire ctx.ui.input as the prompt provider. If hasUI is false (print/RPC
|
|
60
|
+
// mode), surface a non-error notice and bail rather than throwing.
|
|
61
|
+
if (!ctx.hasUI) {
|
|
62
|
+
ctx.ui.notify('interactive /connect requires a TTY session.', 'warning');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const promptProvider = async (field: string, opts: PromptFieldOptions): Promise<string> => {
|
|
66
|
+
const placeholder = opts.defaultValue ?? '';
|
|
67
|
+
const title = `${opts.label}${opts.required ? ' *' : ''}${opts.secret ? ' (hidden)' : ''}`;
|
|
68
|
+
const result = await ctx.ui.input(title, placeholder);
|
|
69
|
+
return result ?? '';
|
|
70
|
+
};
|
|
71
|
+
try {
|
|
72
|
+
await runConnect(bundle, { engineId, entryName, forceWorkspace, promptProvider });
|
|
73
|
+
ctx.ui.notify(`saved ${engineId}:${entryName}`, 'success');
|
|
74
|
+
} catch (err) {
|
|
75
|
+
ctx.ui.notify(`/connect failed: ${(err as Error).message}`, 'error');
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
api.registerCommand('datasource', {
|
|
81
|
+
description: '/datasource list [engine] | remove <engine:name> | test <engine:name>',
|
|
82
|
+
handler: async (args, ctx) => {
|
|
83
|
+
if (!bundle) {
|
|
84
|
+
ctx.ui.notify(
|
|
85
|
+
unavailable ? 'vault unavailable; chat continues without it.' : 'vault not ready yet.',
|
|
86
|
+
'warning',
|
|
87
|
+
);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const tokens = args.trim().split(/\s+/).filter(Boolean);
|
|
91
|
+
const [sub, ...rest] = tokens;
|
|
92
|
+
try {
|
|
93
|
+
switch (sub) {
|
|
94
|
+
case undefined:
|
|
95
|
+
case 'list': {
|
|
96
|
+
const engineFilter = rest[0];
|
|
97
|
+
const rows = await runDatasourceList(bundle, engineFilter ? { engine: engineFilter } : {});
|
|
98
|
+
const lines = rows.length === 0
|
|
99
|
+
? ['no datasources configured']
|
|
100
|
+
: rows.map(r => ` ${r.engine}:${r.name} (${r.scope})`);
|
|
101
|
+
ctx.ui.notify(lines.join('\n'), 'info');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
case 'remove': {
|
|
105
|
+
const ref = rest.join(' ').trim();
|
|
106
|
+
if (!ref) {
|
|
107
|
+
ctx.ui.notify('Usage: /datasource remove <engine:name>', 'info');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
await runDatasourceRemove(bundle, { ref });
|
|
111
|
+
ctx.ui.notify(`removed: ${ref}`, 'success');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
case 'test': {
|
|
115
|
+
const ref = rest.join(' ').trim();
|
|
116
|
+
if (!ref) {
|
|
117
|
+
ctx.ui.notify('Usage: /datasource test <engine:name>', 'info');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const preview = await runDatasourceTest(bundle, { ref });
|
|
121
|
+
ctx.ui.notify(`Would inject: ${preview.envVarNames.join(', ')}`, 'info');
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
default:
|
|
125
|
+
ctx.ui.notify(`Unknown /datasource subcommand: ${sub}. Try: list, remove, test.`, 'warning');
|
|
126
|
+
}
|
|
127
|
+
} catch (err) {
|
|
128
|
+
ctx.ui.notify(`/datasource failed: ${(err as Error).message}`, 'error');
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
api.registerCommand('audit', {
|
|
134
|
+
description: '/audit [--since 1h|24h|7d|ISO] [--producer P] [--engine E] [--action A] [--severity info|warn] [--limit N]',
|
|
135
|
+
handler: async (args, ctx) => {
|
|
136
|
+
if (!bundle) {
|
|
137
|
+
ctx.ui.notify(
|
|
138
|
+
unavailable ? 'vault unavailable; chat continues without it.' : 'vault not ready yet.',
|
|
139
|
+
'warning',
|
|
140
|
+
);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const tokens = args.trim().split(/\s+/).filter(Boolean);
|
|
144
|
+
const q: AuditQuery = {};
|
|
145
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
146
|
+
const t = tokens[i];
|
|
147
|
+
const next = tokens[i + 1];
|
|
148
|
+
if (t === '--since' && next) { q.since = next; i++; }
|
|
149
|
+
else if (t === '--producer' && next) { q.producer = next; i++; }
|
|
150
|
+
else if (t === '--engine' && next) { q.engine = next; i++; }
|
|
151
|
+
else if (t === '--action' && next) { q.action = next; i++; }
|
|
152
|
+
else if (t === '--severity' && next) {
|
|
153
|
+
if (next === 'info' || next === 'warn') { q.severity = next; }
|
|
154
|
+
i++;
|
|
155
|
+
}
|
|
156
|
+
else if (t === '--limit' && next) {
|
|
157
|
+
const n = parseInt(next, 10);
|
|
158
|
+
if (Number.isFinite(n) && n > 0) q.limit = n;
|
|
159
|
+
i++;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
const rows = await runAudit(bundle, q);
|
|
164
|
+
const lines = rows.length === 0
|
|
165
|
+
? ['no audit records match']
|
|
166
|
+
: rows.map(r => {
|
|
167
|
+
const engine = typeof r.detail?.engine === 'string' ? r.detail.engine : undefined;
|
|
168
|
+
return ` [${r.ts}] ${r.producer}/${r.action}${engine ? ` engine=${engine}` : ''}`;
|
|
169
|
+
});
|
|
170
|
+
ctx.ui.notify(lines.join('\n'), 'info');
|
|
171
|
+
} catch (err) {
|
|
172
|
+
ctx.ui.notify(`/audit failed: ${(err as Error).message}`, 'error');
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
api.on('session_shutdown', async () => {
|
|
178
|
+
// Phase 2 VaultBundle has no async dispose path; clearing the reference is enough.
|
|
179
|
+
bundle = null;
|
|
180
|
+
});
|
|
181
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// src/resources/extensions/coworker-vault/test-helpers.ts
|
|
2
|
+
//
|
|
3
|
+
// Shared fake ExtensionAPI used by the coworker-vault, coworker-memory, and
|
|
4
|
+
// (eventually) cross-extension integration tests. Captures every command +
|
|
5
|
+
// tool registration + event handler so tests can fire lifecycle events and
|
|
6
|
+
// inspect ui.notify calls.
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import type {
|
|
10
|
+
ExtensionAPI, ExtensionContext, ExtensionCommandContext, RegisteredCommand,
|
|
11
|
+
SessionStartEvent, SessionShutdownEvent, BeforeAgentStartEvent, AgentStartEvent,
|
|
12
|
+
ToolDefinition,
|
|
13
|
+
} from '@otto/pi-coding-agent';
|
|
14
|
+
|
|
15
|
+
export interface NotifyCall {
|
|
16
|
+
message: string;
|
|
17
|
+
level: 'info' | 'warning' | 'error' | 'success';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface FakeApi {
|
|
21
|
+
api: ExtensionAPI;
|
|
22
|
+
commands: Map<string, RegisteredCommand>;
|
|
23
|
+
tools: Map<string, ToolDefinition>;
|
|
24
|
+
handlers: Map<string, Array<(event: unknown, ctx: ExtensionContext) => Promise<unknown> | unknown>>;
|
|
25
|
+
notifyCalls: NotifyCall[];
|
|
26
|
+
ctx: ExtensionContext;
|
|
27
|
+
commandCtx: ExtensionCommandContext;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function makeFakeApi(): FakeApi {
|
|
31
|
+
const commands = new Map<string, RegisteredCommand>();
|
|
32
|
+
const tools = new Map<string, ToolDefinition>();
|
|
33
|
+
const handlers = new Map<string, Array<(event: unknown, ctx: ExtensionContext) => Promise<unknown> | unknown>>();
|
|
34
|
+
const notifyCalls: NotifyCall[] = [];
|
|
35
|
+
|
|
36
|
+
const ui = {
|
|
37
|
+
notify: (message: string, level?: 'info' | 'warning' | 'error' | 'success'): void => {
|
|
38
|
+
notifyCalls.push({ message, level: level ?? 'info' });
|
|
39
|
+
},
|
|
40
|
+
confirm: async (): Promise<boolean> => true,
|
|
41
|
+
input: async (): Promise<string | undefined> => '',
|
|
42
|
+
select: async (): Promise<string | string[] | undefined> => undefined,
|
|
43
|
+
} as unknown as ExtensionContext['ui'];
|
|
44
|
+
|
|
45
|
+
const ctx = {
|
|
46
|
+
cwd: tmpdir(),
|
|
47
|
+
hasUI: true,
|
|
48
|
+
ui,
|
|
49
|
+
sessionManager: {
|
|
50
|
+
getSessionFile: (): string | undefined => join(tmpdir(), 'session.jsonl'),
|
|
51
|
+
},
|
|
52
|
+
isIdle: (): boolean => true,
|
|
53
|
+
abort: (): void => {},
|
|
54
|
+
hasPendingMessages: (): boolean => false,
|
|
55
|
+
shutdown: (): void => {},
|
|
56
|
+
getContextUsage: (): undefined => undefined,
|
|
57
|
+
compact: (): void => {},
|
|
58
|
+
getSystemPrompt: (): string => '',
|
|
59
|
+
setCompactionThresholdOverride: (): void => {},
|
|
60
|
+
} as unknown as ExtensionContext;
|
|
61
|
+
|
|
62
|
+
const commandCtx = ctx as unknown as ExtensionCommandContext;
|
|
63
|
+
|
|
64
|
+
const api = {
|
|
65
|
+
on(event: string, handler: (event: unknown, ctx: ExtensionContext) => Promise<unknown> | unknown): void {
|
|
66
|
+
if (!handlers.has(event)) handlers.set(event, []);
|
|
67
|
+
handlers.get(event)!.push(handler);
|
|
68
|
+
},
|
|
69
|
+
registerCommand(name: string, options: Omit<RegisteredCommand, 'name'>): void {
|
|
70
|
+
commands.set(name, { name, ...options });
|
|
71
|
+
},
|
|
72
|
+
registerTool(tool: ToolDefinition): void {
|
|
73
|
+
tools.set(tool.name, tool);
|
|
74
|
+
},
|
|
75
|
+
sendMessage(): void {},
|
|
76
|
+
} as unknown as ExtensionAPI;
|
|
77
|
+
|
|
78
|
+
return { api, commands, tools, handlers, notifyCalls, ctx, commandCtx };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function fireSessionStart(fake: FakeApi, opts: { cwd: string }): Promise<void> {
|
|
82
|
+
const evt: SessionStartEvent = { type: 'session_start' };
|
|
83
|
+
// Mutate the fake.ctx so handlers see the requested cwd.
|
|
84
|
+
(fake.ctx as { cwd: string }).cwd = opts.cwd;
|
|
85
|
+
const list = fake.handlers.get('session_start') ?? [];
|
|
86
|
+
for (const h of list) await h(evt, fake.ctx);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function fireSessionShutdown(fake: FakeApi): Promise<void> {
|
|
90
|
+
const evt: SessionShutdownEvent = { type: 'session_shutdown' };
|
|
91
|
+
const list = fake.handlers.get('session_shutdown') ?? [];
|
|
92
|
+
for (const h of list) await h(evt, fake.ctx);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function fireBeforeAgentStart(
|
|
96
|
+
fake: FakeApi,
|
|
97
|
+
prompt: string,
|
|
98
|
+
systemPrompt: string,
|
|
99
|
+
): Promise<{ systemPrompt?: string } | undefined> {
|
|
100
|
+
const evt: BeforeAgentStartEvent = { type: 'before_agent_start', prompt, systemPrompt };
|
|
101
|
+
const list = fake.handlers.get('before_agent_start') ?? [];
|
|
102
|
+
let result: { systemPrompt?: string } | undefined;
|
|
103
|
+
for (const h of list) {
|
|
104
|
+
const r = await h(evt, fake.ctx);
|
|
105
|
+
if (r && typeof r === 'object' && 'systemPrompt' in r) {
|
|
106
|
+
result = r as { systemPrompt?: string };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function fireAgentStart(
|
|
113
|
+
fake: FakeApi,
|
|
114
|
+
sessionId: string,
|
|
115
|
+
turnId: string,
|
|
116
|
+
): Promise<void> {
|
|
117
|
+
const evt: AgentStartEvent = { type: 'agent_start', sessionId, turnId };
|
|
118
|
+
const list = fake.handlers.get('agent_start') ?? [];
|
|
119
|
+
for (const h of list) await h(evt, fake.ctx);
|
|
120
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// src/resources/extensions/coworker-vault/vault-singleton.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 { createVaultBundle } from './vault-singleton.js';
|
|
8
|
+
|
|
9
|
+
describe('vault singleton bundle', () => {
|
|
10
|
+
it('constructs vault, audit, and registry for given roots', async () => {
|
|
11
|
+
const root = mkdtempSync(join(tmpdir(), 'vault-bundle-'));
|
|
12
|
+
const b = await createVaultBundle({ globalDir: join(root, 'global'), workspaceDir: undefined });
|
|
13
|
+
assert.ok(b.vault);
|
|
14
|
+
assert.ok(b.audit);
|
|
15
|
+
assert.ok(b.registry);
|
|
16
|
+
assert.ok(b.registry.get('jira'));
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('honors workspace dir when provided', async () => {
|
|
20
|
+
const root = mkdtempSync(join(tmpdir(), 'vault-bundle-ws-'));
|
|
21
|
+
const b = await createVaultBundle({
|
|
22
|
+
globalDir: join(root, 'global'),
|
|
23
|
+
workspaceDir: join(root, 'workspace'),
|
|
24
|
+
});
|
|
25
|
+
assert.deepEqual(await b.vault.list(), []);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// src/resources/extensions/coworker-vault/vault-singleton.ts
|
|
2
|
+
//
|
|
3
|
+
// Factory that constructs the three vault collaborators (LocalDataVault, AuditLog,
|
|
4
|
+
// EngineRegistry) for given roots. Pure: side-effects are limited to whatever the
|
|
5
|
+
// underlying constructors already perform (dir creation, orphan sweeps, etc.).
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { homedir } from 'node:os';
|
|
8
|
+
import { AuditLog } from '@otto/coworker-utils';
|
|
9
|
+
import { LocalDataVault, EngineRegistry } from '@otto/coworker-vault';
|
|
10
|
+
|
|
11
|
+
export interface VaultBundleOptions {
|
|
12
|
+
/** Defaults to ~/.otto */
|
|
13
|
+
globalDir?: string;
|
|
14
|
+
/** Optional workspace root (NOT including .otto suffix). */
|
|
15
|
+
workspaceDir?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface VaultBundle {
|
|
19
|
+
vault: LocalDataVault;
|
|
20
|
+
audit: AuditLog;
|
|
21
|
+
registry: EngineRegistry;
|
|
22
|
+
globalDir: string;
|
|
23
|
+
workspaceDir?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function createVaultBundle(opts: VaultBundleOptions = {}): Promise<VaultBundle> {
|
|
27
|
+
const globalDir = opts.globalDir ?? join(homedir(), '.otto');
|
|
28
|
+
const auditPath = join(globalDir, 'audit.jsonl');
|
|
29
|
+
const audit = new AuditLog({ path: auditPath });
|
|
30
|
+
const vault = new LocalDataVault({
|
|
31
|
+
globalDir,
|
|
32
|
+
workspaceDir: opts.workspaceDir,
|
|
33
|
+
audit,
|
|
34
|
+
});
|
|
35
|
+
const registry = await EngineRegistry.load({
|
|
36
|
+
userDir: join(globalDir, 'engines'),
|
|
37
|
+
workspaceDir: opts.workspaceDir ? join(opts.workspaceDir, 'engines') : undefined,
|
|
38
|
+
});
|
|
39
|
+
return { vault, audit, registry, globalDir, workspaceDir: opts.workspaceDir };
|
|
40
|
+
}
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
//
|
|
4
4
|
// To add or correct release notes, edit CHANGELOG.md and rebuild. Editing this
|
|
5
5
|
// file directly will be clobbered on the next build.
|
|
6
|
+
//
|
|
7
|
+
// Bundled releases are capped (default 20, override via OTTO_RELEASE_NOTES_CAP
|
|
8
|
+
// at build time). Older releases stay in CHANGELOG.md and on GitHub; the
|
|
9
|
+
// runtime command surfaces a link when a requested version isn't bundled.
|
|
6
10
|
|
|
7
11
|
export interface ReleaseNote {
|
|
8
12
|
version: string;
|
|
@@ -14,7 +18,97 @@ export interface ReleaseNote {
|
|
|
14
18
|
notes?: string[];
|
|
15
19
|
}
|
|
16
20
|
|
|
21
|
+
export interface ReleaseNotesManifest {
|
|
22
|
+
/** True when older releases exist in CHANGELOG.md but weren't bundled. */
|
|
23
|
+
truncated: boolean;
|
|
24
|
+
/** Total releases in CHANGELOG.md at build time (incl. truncated ones). */
|
|
25
|
+
total: number;
|
|
26
|
+
/** Oldest version included in the bundle. */
|
|
27
|
+
oldestBundled: string;
|
|
28
|
+
/** Newest version included in the bundle. */
|
|
29
|
+
newestBundled: string;
|
|
30
|
+
/** Where to read full history when the bundle has been truncated. */
|
|
31
|
+
historyUrl: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const RELEASE_NOTES_MANIFEST: ReleaseNotesManifest = {
|
|
35
|
+
truncated: false,
|
|
36
|
+
total: 14,
|
|
37
|
+
oldestBundled: '1.0.0',
|
|
38
|
+
newestBundled: '1.2.5',
|
|
39
|
+
historyUrl: 'https://github.com/cmetech/otto-cli/blob/main/CHANGELOG.md',
|
|
40
|
+
};
|
|
41
|
+
|
|
17
42
|
export const RELEASE_NOTES: ReleaseNote[] = [
|
|
43
|
+
{
|
|
44
|
+
version: '1.2.5',
|
|
45
|
+
date: '2026-06-03',
|
|
46
|
+
headline: 'Windows-install hotfix: silences three startup errors that fired the first time `otto` launched after a global npm install, and stops the otto extension from racing the TUI welcome banner with raw stderr writes.',
|
|
47
|
+
fixed: [
|
|
48
|
+
'**`_coworker-paths.js` "Extension does not export a valid factory function".** The shared helper module sat at the top of `src/resources/extensions/` so the pi-coding-agent extension discovery (`discoverExtensionsInDir`) walked it as an extension candidate. Moved to `src/resources/extensions/shared/coworker-paths.{ts,test.ts}` (subdir is skipped by `resolveExtensionEntries` because it has no `index.ts`/`package.json`). The three coworker-{memory,scratchpad,vault}/index.ts importers were updated in lockstep.',
|
|
49
|
+
'**`Cannot find module \'better-sqlite3\'` on global install.** `better-sqlite3` was declared only in `packages/coworker-memory/package.json`. Because npm only resolves the root manifest for end users of a published tarball, the native module was never installed and `@otto/coworker-memory`\'s eager re-export of `local-sqlite-backend.js` blew up at first import. Hoisted `better-sqlite3@^11.7.0` into root `dependencies`.',
|
|
50
|
+
'**`/audit` slash-command conflict between coworker-vault and slash-commands.** Both extensions registered the same id; the vault command (the real Phase 2 reader) supersedes, but the conflict warning fired every launch. Renamed the slash-commands command to `/audit-codebase` (file + manifest + dispatch updated).',
|
|
51
|
+
'**Otto session_start bled raw stderr into the TUI welcome banner.** The gateway + langflow probes wrote ANSI-colored status lines directly to `process.stderr` while the pi TUI was still painting; on Windows the rendered output collided with the yellow `─` rule and overwrote the prompt area. Now routes status through `ctx.ui.setStatus("otto-gateway"|"otto-langflow", ...)` when `ctx.hasUI` is true; falls back to stderr only in headless/RPC modes.',
|
|
52
|
+
'**Duplicate "OTTO" brand on the no-project landing.** The `gsd-health` widget\'s no-project line began with `" OTTO No project loaded — …"`, which appeared alongside the ASCII OTTO logo in the welcome header — looking like the brand was loading twice. Dropped the `OTTO ` prefix; the header already brands the session.',
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
version: '1.2.4',
|
|
57
|
+
date: '2026-06-02',
|
|
58
|
+
headline: 'Co-worker pillars go live: persistent memory (Layer A rules/lessons + Layer B verbatim drawers with FTS5 recall), workspace-scoped artifact store with `artifact://` URIs and two-turn provenance, vault\'s `/connect` `/datasource` `/audit` slash surface, and per-subagent dedicated scratchpads. Day-2 milestone — paste an incident note Monday → recall the exact words Tuesday — works end-to-end.',
|
|
59
|
+
added: [
|
|
60
|
+
'**`@otto/coworker-memory` Layer A + Layer B (Phase 3).** Workspace-scoped SQLite store at `<workspace>/.otto/memory/layer-b.db` with FTS5 BM25 ranking + WAL journaling; markdown Layer A files at `<workspace>/.otto/memory/{profile,rules,lessons}.md` with YAML frontmatter and atomic writes. Three scoping modes (global, per-project, per-project-tagged). SecretScanner split policy: Layer A blocks writes containing secrets (`LayerAWriteBlocked`); Layer B redacts (`[REDACTED:<kind>]`) and sets `redacted:true`. Persona-seed helper for one-shot bootstrap from `<persona>/memory-seed/`.',
|
|
61
|
+
'**Production activators wire memory + vault + scratchpad to Otto\'s `ExtensionAPI` (Phase 3.1).** Vault\'s `/connect <engine> <name>` interactive wizard, `/datasource list|remove|test`, and `/audit [--producer|--action|--limit|--since|--engine|--severity]` now register at extension activation — first time these are reachable in a live chat. Memory activator captures `event.prompt` in `before_agent_start`, calls `recorder.recordTurn(...)` in `agent_start` (one-time warning on failure, then silent). `/memory note <text>` appends a workspace lesson; `/memory status` reports `workspace_wing`, `drawer_count`, db path; `/memory clear --wing <w> --confirm` deletes drawers. `memorize` and `recall` LLM tools are model-callable. Layer A injects into the system prompt on `session_start` (3000-token cap; sections sorted profile → rules → lessons → truncate lowest-priority on overflow).',
|
|
62
|
+
'**`/memory show` + `/memory recall` slash surface (closes #73).** `/memory show [profile|rules|lessons] [--scope workspace|global]` dumps Layer A markdown inline; defaults to all three for per-project-tagged scope (workspace first, then global). `/memory recall <query> [--kind|--room|--wing|--limit|--days_back]` searches Layer B drawers directly from chat without going through the model\'s tool call. Memory command tab-completion lists all 8 subcommands.',
|
|
63
|
+
'**`@otto/coworker-artifacts` workspace-scoped store (Phase 4).** `report` artifact kind (markdown v1; workbook + dataset deferred). `ArtifactStore` with slug derivation (`deriveSlug` lowercase ASCII kebab, max 64 chars) + collision suffixing (`-2`, `-3`, … up to 100). Atomic writes (tmp → chmod → rename), mode 0o700 dir / 0o600 files. `DirSnapshot` (nanosecond mtime + size_bytes) drives per-turn `files_touched` via before/after diff. Append-only `provenance.json` records `{ts, action: create|update, turn_id, agent_turn_id?, user_prompt, scratchpad_name?, files_touched}` per turn. Deterministic `README.md` re-renders on every save. `resolveArtifactUri(uri, workspaceDir)` validates `^artifact://[a-z0-9][a-z0-9-]*[a-z0-9]$|^artifact://[a-z0-9]$`, rejects `..` traversal + length > 64 + non-ASCII. Production activator registers `/artifacts list|show|remove --confirm` (flag-order-agnostic) + `list_artifacts` + `open_artifact` LLM tools. Kernel-side `otto.artifact.create(kind, name)` + `otto.artifact.spillIfLarge(value, opts?)` (10 KB default threshold) via bidirectional NDJSON RPC over stdio. `kind:\'artifact\'` drawer in memory (migration 002 rebuilds the `drawers.kind` CHECK constraint) tagged with the scratchpad name as `room`, so `/memory recall <q> --kind artifact` finds them.',
|
|
64
|
+
'**Subagent-scratchpad scoping (Phase 4.5).** Subagent dispatcher mints `subagent-<sanitized-agent>-<6-hex>` per child process; the spawned `pi` reads `OTTO_SUBAGENT_SCRATCHPAD` env var at `session_start` and force-attaches before sidecar/pointer restore (validated against `^subagent-[a-z0-9-]+$`, max 80 chars). Scratchpads persist after subagent exit — parent can `/sp attach subagent-<id>` post-hoc to inspect kernel state, cells, namespace globals. Failure (invalid name, disk error) emits a warning and falls through to normal restore; never crashes `session_start`.',
|
|
65
|
+
],
|
|
66
|
+
fixed: [
|
|
67
|
+
'**Strict-tsc compatibility for activator tool registrations.** `api.registerTool<TParams, TDetails>(...)` generics are explicit union types so success-vs-error result shapes both satisfy `AgentToolResult<TDetails>`. Caught at Phase 3.1 build-gate; not at test-compile (which uses esbuild). All Phase 3.1 + Phase 4 + Phase 4.5 activators carry the pattern.',
|
|
68
|
+
'**Strict `pi` kernel-entry main loop no longer deadlocks on artifact RPC.** `await runCell(req.code)` inside the `for await (const raw of readNdjson(stdin))` loop blocked the iterator from receiving its own RPC responses. Now fire-and-forget; manager already serializes cell submissions.',
|
|
69
|
+
'**Memory migration 002 rebuilds FTS5 triggers + virtual-table rowids** after the `drawers` table rename. Without this, `recall()` on existing v1 databases would return zero results post-upgrade.',
|
|
70
|
+
'**Memory recorder `byte_count` audit field** now uses `Buffer.byteLength(content, \'utf8\')` instead of character count.',
|
|
71
|
+
'**`LayerAStore` write-then-read round-trip preserves the section heading** on subsequent appends. Title-dedup regex tolerates leading newline left by frontmatter split.',
|
|
72
|
+
'**`optionalDependencies.@cmetech/otto-engine-*` pins synced to root version.** `version-sync.test.ts` asserts equality on every build; root publish would fail npm resolution otherwise.',
|
|
73
|
+
],
|
|
74
|
+
changed: [
|
|
75
|
+
'**Memory backend `local-sqlite-backend` runs migrations conditionally.** `open()` checks `PRAGMA user_version` and applies `001-init.sql` (if version < 1) then `002-artifact-kind.sql` (if version < 2). Migration 002 rebuilds the `drawers` table with `\'artifact\'` added to the kind CHECK and re-runs FTS5 contentless-rowid alignment.',
|
|
76
|
+
'**Memory drawer kinds vocabulary** now includes `\'artifact\'`. Existing kinds (`turn`, `paste`, `file_load`, `ticket`, `email`, `rca`, `note`) unchanged.',
|
|
77
|
+
'**`/memory` command** description is a sentence; `getArgumentCompletions` lists all 8 subcommands with one-line descriptions of each.',
|
|
78
|
+
'**Audit log gains three new producers**: `memory` (write-layer-a, write-drawer, redact, block, recall, seed-applied), `vault` (set, remove, test, inject, inject-skipped — Phase 2 already had these but now reachable via slash), and `artifacts` (write, remove). All land in the shared `~/.otto/audit.jsonl`.',
|
|
79
|
+
],
|
|
80
|
+
notes: [
|
|
81
|
+
'Phase 5 (Layer C entity graph + ACC + Cerebellum + Consolidator + daily digest + `memory://` URI resolver) is the next phase — depends on Phase 3 + Phase 4 (both shipped here). Phase 6 (NOC persona bundle, integration testing, time-to-first-artifact < 5 min) follows.',
|
|
82
|
+
'Smoke checklists for the four shipped phases live at `docs/superpowers/notes/2026-06-02-{phase-2-vault,phase-3-memory,phase-4-artifacts,phase-4.5-subagent-scratchpad}-smoke.md`. Each carries a PENDING verified-live placeholder — fill in after end-to-end TUI walkthrough.',
|
|
83
|
+
'Spec + plan artifacts at `docs/superpowers/specs/2026-06-02-*` and `docs/superpowers/plans/2026-06-02-*` for every phase shipped here.',
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
version: '1.2.0',
|
|
88
|
+
date: '2026-06-01',
|
|
89
|
+
headline: 'Co-worker scratchpad Phase 1.5 polish wave: typing `otto` in a workspace where you previously attached a scratchpad now auto-restores it without `--resume`; `/sp evict` and idle-age display land; `otto.duckdb.registerDf` drops polars→DuckDB from ~8 LLM cells to 2.',
|
|
90
|
+
added: [
|
|
91
|
+
'**Workspace-pointer auto-restore (Issue #6).** New `~/.otto/scratchpads/_workspaces/<sha256-16>.json` keyed on git toplevel (else cwd). On `session_start`, when no per-session sidecar matches, falls back to the workspace pointer. The spec\'s canonical day-2 RCA scenario now works without `--resume` — typing `otto` in the same workspace surfaces `attached to <name> (from workspace, last used <relative>)`. 7-day staleness threshold; cross-workspace isolation; restored scratchpads cold-restart from disk via Phase 1\'s existing snapshot path.',
|
|
92
|
+
'**`/sp list` idle-age column (Issue #4).** Warm entries now show `active` (cell running) or `idle Xm` / `idle Xh` / `idle Xd` (floored minutes/hours/days since last use). Cold entries unchanged.',
|
|
93
|
+
'**`/sp evict <name> [--force]` slash command (Issue #4).** Releases a warm kernel without deleting the scratchpad — on-disk state (kernel.db, namespace.json, cells.jsonl) remains for cold-restart on next attach. Refuses on active cell unless `--force`. `--force` reuses the existing `runtime.cancel()` SIGINT→SIGTERM→SIGKILL escalation; skips snapshot since the kernel is dead post-cancel.',
|
|
94
|
+
'**`otto.duckdb.registerDf(name, input, opts?)` (Issue #1).** Duck-typed input detection for polars DataFrames, Arrow Tables, and arrays of records. First-10-rows null-walk schema inference covers sparse leading rows. Optional `opts.schema` override (accepts `Record<string, type>` or `Array<[col, type]>`). All-or-nothing semantics: partial-row failure drops the partial table so retries don\'t hit "Table already exists." Per-column failure attribution in error messages. `cw_scratchpad` `promptGuidelines` updated so the LLM reaches for the helper instead of API discovery — polars→DuckDB drops from ~8 cells to 2.',
|
|
95
|
+
'**Stale sidecar GC on init (Issue #67).** `sweepStaleSidecars` runs once at `session_start`. Deletes foreign-session sidecars where the referenced scratchpad is gone OR sidecar mtime is > 7 days old. Per-file try/catch isolation; current session\'s sidecar always protected.',
|
|
96
|
+
],
|
|
97
|
+
fixed: [
|
|
98
|
+
'**`meta.json` on fresh `/sp new` reflects post-spawn disk state (Issue #2).** Previously showed `kernel_db.present: false` and `size_bytes: 0` because the initial `writeMeta` fired before `kernel.db` existed on disk. Added a second `writeMeta` call after `spawnRuntime` succeeds; the first call still preserves the lock-acquire side-effect.',
|
|
99
|
+
],
|
|
100
|
+
changed: [
|
|
101
|
+
'**`/sp attach <name>` errors on typo (Issue #5).** Previously, `/sp attach <typo>` silently spawned a phantom scratchpad. Now errors with `scratchpad not found: <name>. Use /sp new <name> to create it.` Library `manager.getOrAttach` and the LLM tool path (`cw_scratchpad` action=exec) remain permissive — the asymmetry is intentional (LLM-passed names should auto-create; user-typed names should fail loud).',
|
|
102
|
+
'**Session sidecar filename format (Issue #66).** Renamed from `<sessionId>.json` to `sidecar_<sessionId>.json` for visual scan and `sidecar_*` glob. Old-format files become inert orphans (the sweep ignores anything without the `sidecar_` prefix). Users upgrading can manually `rm ~/.otto/scratchpads/_sessions/*.json` once if they want a clean slate; otherwise old files are harmless and persistent.',
|
|
103
|
+
'**`ScratchpadInfo` interface gains `hasActiveCell: boolean`.** Workspace-private package change with two internal consumers; no out-of-tree breakage.',
|
|
104
|
+
],
|
|
105
|
+
notes: [
|
|
106
|
+
'Phase 1.5 closes the open known-issues backlog from `docs/superpowers/notes/2026-06-01-coworker-phase-1-known-issues.md` (Issues #1, #2, #4, #5, #6) and GH #66, #67.',
|
|
107
|
+
'Five follow-up items filed as #68–#72 for post-merge polish (workspace-pointer GC, `/sp evict <current>` sidecar question, `formatRelativeAge`/`hasActiveCell` overlap, `detectWorkspaceRoot` memoization, and dual-`writeMeta` doc comment).',
|
|
108
|
+
'Issue #3 (LLM "ask if unsure" reliability) remains accepted as inherent LLM behavior; revisited in Phase 6 (NOC persona polish).',
|
|
109
|
+
'See the Phase 1.5 re-verify section in `docs/superpowers/notes/2026-06-01-coworker-phase-1-human-tests.md` for the 11-scenario sign-off checklist.',
|
|
110
|
+
],
|
|
111
|
+
},
|
|
18
112
|
{
|
|
19
113
|
version: '1.1.1',
|
|
20
114
|
date: '2026-05-29',
|
|
@@ -25,6 +119,9 @@ export const RELEASE_NOTES: ReleaseNote[] = [
|
|
|
25
119
|
fixed: [
|
|
26
120
|
'`otto update` on Windows would log `EPERM: operation not permitted, unlink` warnings against `duckdb.dll`, `sharp.dll`, and `otto_engine.win32-x64.node` because the running otto.exe still held DLL handles. The install still succeeded but left orphan `.otto-RouGtD54`-style staging directories under `%APPDATA%\\npm\\node_modules\\@cmetech\\`. `otto update` now spawns a detached PowerShell bootstrap in a visible "OTTO Updater" console window that waits for the parent otto.exe to exit (max 30s), runs the install with locks released, sweeps the orphan staging dirs, and writes a completion status to `~/.otto/agent/update-status.json` that the next `otto update` surfaces. macOS / Linux behavior unchanged — POSIX inode semantics handle live-process replacement without the lock issue.',
|
|
27
121
|
],
|
|
122
|
+
changed: [
|
|
123
|
+
'`/release-notes` data is now capped at the last 20 releases per npm tarball (override at build time via `OTTO_RELEASE_NOTES_CAP=<n>`; set `0` for unlimited). `CHANGELOG.md` stays the canonical full history; the runtime command shows a footer like "Bundled here: v1.0.5 → v1.2.0 (20 of 35 total). Older releases: github.com/cmetech/otto-cli/blob/main/CHANGELOG.md" when truncation is active. Keeps the shipped data file from growing unbounded as the project ages.',
|
|
124
|
+
],
|
|
28
125
|
},
|
|
29
126
|
{
|
|
30
127
|
version: '1.1.0',
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import type { ExtensionAPI, ExtensionCommandContext } from "@otto/pi-coding-agent";
|
|
16
16
|
import {
|
|
17
17
|
RELEASE_NOTES,
|
|
18
|
+
RELEASE_NOTES_MANIFEST,
|
|
18
19
|
countItems,
|
|
19
20
|
findReleaseByVersion,
|
|
20
21
|
getLatestRelease,
|
|
@@ -40,6 +41,14 @@ function renderSection(title: string, items: string[] | undefined): string {
|
|
|
40
41
|
return `\n### ${title}\n${bulleted}\n`;
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
function renderCapFooter(): string {
|
|
45
|
+
const m = RELEASE_NOTES_MANIFEST;
|
|
46
|
+
if (!m.truncated) {
|
|
47
|
+
return `${RELEASE_NOTES.length} releases tracked.`;
|
|
48
|
+
}
|
|
49
|
+
return `Bundled here: v${m.oldestBundled} → v${m.newestBundled} (${RELEASE_NOTES.length} of ${m.total} total).\nOlder releases: ${m.historyUrl}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
43
52
|
function renderRelease(release: ReleaseNote): string {
|
|
44
53
|
const header = `# OTTO v${release.version} — ${release.date}`;
|
|
45
54
|
const headline = release.headline ? `\n_${release.headline}_\n` : "";
|
|
@@ -49,7 +58,7 @@ function renderRelease(release: ReleaseNote): string {
|
|
|
49
58
|
renderSection("Changed", release.changed),
|
|
50
59
|
renderSection("Notes", release.notes),
|
|
51
60
|
].join("");
|
|
52
|
-
const tail = `\n---\n${
|
|
61
|
+
const tail = `\n---\n${renderCapFooter()}\nUse \`/release-notes\` to browse, \`/release-notes <version>\` for any other release.`;
|
|
53
62
|
return `${header}${headline}${body}${tail}\n`;
|
|
54
63
|
}
|
|
55
64
|
|
|
@@ -60,7 +69,7 @@ function renderIndex(): string {
|
|
|
60
69
|
const headline = r.headline ? ` — ${r.headline}` : "";
|
|
61
70
|
return `- v${r.version} (${r.date}, ${badge})${headline}`;
|
|
62
71
|
}).join("\n");
|
|
63
|
-
return `# OTTO release index\n\n${rows}\n\nView any release with \`/release-notes <version>\`.\n`;
|
|
72
|
+
return `# OTTO release index\n\n${rows}\n\n${renderCapFooter()}\nView any release with \`/release-notes <version>\`.\n`;
|
|
64
73
|
}
|
|
65
74
|
|
|
66
75
|
function findByVersionToken(token: string): ReleaseNote | undefined {
|
|
@@ -87,9 +96,13 @@ export function registerReleaseNotesCommand(pi: ExtensionAPI): void {
|
|
|
87
96
|
if (trimmed && trimmed !== "list") {
|
|
88
97
|
const match = findByVersionToken(trimmed);
|
|
89
98
|
if (!match) {
|
|
99
|
+
const m = RELEASE_NOTES_MANIFEST;
|
|
100
|
+
const truncationHint = m.truncated
|
|
101
|
+
? `\n\nThis OTTO build bundles ${RELEASE_NOTES.length} of ${m.total} total releases (v${m.oldestBundled} → v${m.newestBundled}). For older releases, see ${m.historyUrl}`
|
|
102
|
+
: "";
|
|
90
103
|
postToChat(
|
|
91
104
|
pi,
|
|
92
|
-
`**No release found for \`${trimmed}\`**\n\nKnown versions: ${RELEASE_NOTES.map((r) => `v${r.version}`).join(", ")}`,
|
|
105
|
+
`**No release found for \`${trimmed}\`**\n\nKnown versions: ${RELEASE_NOTES.map((r) => `v${r.version}`).join(", ")}${truncationHint}`,
|
|
93
106
|
);
|
|
94
107
|
return;
|
|
95
108
|
}
|