@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
|
@@ -1,21 +1,53 @@
|
|
|
1
1
|
// OTTO + Subagent launch contract and child process safety helpers.
|
|
2
|
+
import * as crypto from "node:crypto";
|
|
2
3
|
import * as fs from "node:fs";
|
|
3
4
|
import * as path from "node:path";
|
|
4
5
|
import { SessionManager } from "@otto/pi-coding-agent";
|
|
5
6
|
export const SUBAGENT_CHILD_ENV_VAR = "OTTO_SUBAGENT_CHILD";
|
|
6
7
|
export const SUBAGENT_CHILD_ENV_VALUE = "1";
|
|
8
|
+
export const SUBAGENT_SCRATCHPAD_ENV_VAR = "OTTO_SUBAGENT_SCRATCHPAD";
|
|
9
|
+
const MAX_SCRATCHPAD_AGENT_PART = 32;
|
|
10
|
+
export function mintSubagentScratchpadName(agentName) {
|
|
11
|
+
const hex = crypto.randomBytes(3).toString("hex");
|
|
12
|
+
// Sanitize: NFKD + strip combining marks + lowercase + non-[a-z0-9] → '-' + collapse + trim.
|
|
13
|
+
let sanitized = agentName
|
|
14
|
+
.normalize("NFKD")
|
|
15
|
+
.replace(/[̀-ͯ]/g, "")
|
|
16
|
+
.toLowerCase()
|
|
17
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
18
|
+
.replace(/-+/g, "-")
|
|
19
|
+
.replace(/^-+|-+$/g, "");
|
|
20
|
+
if (sanitized.length > MAX_SCRATCHPAD_AGENT_PART) {
|
|
21
|
+
sanitized = sanitized.slice(0, MAX_SCRATCHPAD_AGENT_PART).replace(/-+$/, "");
|
|
22
|
+
}
|
|
23
|
+
if (!sanitized)
|
|
24
|
+
return `subagent-${hex}`;
|
|
25
|
+
return `subagent-${sanitized}-${hex}`;
|
|
26
|
+
}
|
|
7
27
|
export function isSubagentChildProcess(env = process.env) {
|
|
8
28
|
return env[SUBAGENT_CHILD_ENV_VAR] === SUBAGENT_CHILD_ENV_VALUE;
|
|
9
29
|
}
|
|
10
|
-
export function buildSubagentProcessEnv(env = process.env) {
|
|
11
|
-
|
|
30
|
+
export function buildSubagentProcessEnv(env = process.env, scratchpadName) {
|
|
31
|
+
const next = {
|
|
12
32
|
...env,
|
|
13
33
|
[SUBAGENT_CHILD_ENV_VAR]: SUBAGENT_CHILD_ENV_VALUE,
|
|
14
34
|
};
|
|
35
|
+
if (scratchpadName) {
|
|
36
|
+
next[SUBAGENT_SCRATCHPAD_ENV_VAR] = scratchpadName;
|
|
37
|
+
}
|
|
38
|
+
return next;
|
|
15
39
|
}
|
|
16
40
|
export function buildShellEnvAssignments(env = process.env) {
|
|
17
|
-
const
|
|
18
|
-
|
|
41
|
+
const out = [];
|
|
42
|
+
const childValue = env[SUBAGENT_CHILD_ENV_VAR];
|
|
43
|
+
if (childValue) {
|
|
44
|
+
out.push(`${SUBAGENT_CHILD_ENV_VAR}=${JSON.stringify(childValue)}`);
|
|
45
|
+
}
|
|
46
|
+
const scratchpadValue = env[SUBAGENT_SCRATCHPAD_ENV_VAR];
|
|
47
|
+
if (scratchpadValue) {
|
|
48
|
+
out.push(`${SUBAGENT_SCRATCHPAD_ENV_VAR}=${JSON.stringify(scratchpadValue)}`);
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
19
51
|
}
|
|
20
52
|
export function buildSubagentProcessArgs(agent, task, tmpPromptPath, modelOverride, session = { mode: "fresh" }) {
|
|
21
53
|
const args = ["--mode", "json", "-p"];
|
|
@@ -70,7 +102,7 @@ export function createSubagentLaunchPlan(input) {
|
|
|
70
102
|
const session = input.session ?? resolveSubagentSessionArgs(input.contextMode ?? "fresh", input.parentSessionManager);
|
|
71
103
|
return {
|
|
72
104
|
args: buildSubagentProcessArgs(input.agent, input.task, input.tmpPromptPath, input.modelOverride, session),
|
|
73
|
-
env: buildSubagentProcessEnv(),
|
|
105
|
+
env: buildSubagentProcessEnv(process.env, input.scratchpadName),
|
|
74
106
|
cwd: input.cwd ?? input.defaultCwd,
|
|
75
107
|
session,
|
|
76
108
|
};
|
|
@@ -19,6 +19,7 @@ import { logWarning } from "../workflow-logger.js";
|
|
|
19
19
|
// session hook calling startAuto) would be silently dropped because Node's
|
|
20
20
|
// EventEmitter does not buffer events for late subscribers.
|
|
21
21
|
import { initCmuxEventListeners } from "../../cmux/index.js";
|
|
22
|
+
import { registerPersonaCommands } from "../persona-status.js";
|
|
22
23
|
import { BRAND } from "../strings.js";
|
|
23
24
|
export { writeCrashLog } from "./crash-log.js";
|
|
24
25
|
export function handleRecoverableExtensionProcessError(err) {
|
|
@@ -110,6 +111,7 @@ export function registerWorkflowExtension(pi) {
|
|
|
110
111
|
["memory-tools", () => registerMemoryTools(pi)],
|
|
111
112
|
["exec-tools", () => registerExecTools(pi)],
|
|
112
113
|
["schedule-wakeup-tool", () => registerScheduleWakeupTool(pi)],
|
|
114
|
+
["persona-commands", () => registerPersonaCommands(pi)],
|
|
113
115
|
["shortcuts", () => registerShortcuts(pi)],
|
|
114
116
|
// cmux is a library (no pi), so gsd sets up the event listeners on its
|
|
115
117
|
// behalf using the shared event channel contract. Registration is
|
|
@@ -299,6 +299,15 @@ async function resetAskUserQuestionsTurnCache() {
|
|
|
299
299
|
const { resetAskUserQuestionsCache } = await import("../../ask-user-questions.js");
|
|
300
300
|
resetAskUserQuestionsCache();
|
|
301
301
|
}
|
|
302
|
+
async function initPersonaStatusChip(ctx) {
|
|
303
|
+
try {
|
|
304
|
+
const { initPersonaWidget } = await import("../persona-status.js");
|
|
305
|
+
await initPersonaWidget(ctx);
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
// Non-fatal: persona chip simply won't render if the registry is unavailable.
|
|
309
|
+
}
|
|
310
|
+
}
|
|
302
311
|
async function syncServiceTierStatus(ctx) {
|
|
303
312
|
const { getEffectiveServiceTier, formatServiceTierFooterStatus } = await import("../service-tier.js");
|
|
304
313
|
ctx.ui.setStatus("gsd-fast", formatServiceTierFooterStatus(getEffectiveServiceTier(), ctx.model?.id));
|
|
@@ -424,6 +433,7 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
424
433
|
approvalQuestionAbortInFlight = false;
|
|
425
434
|
clearDeferredApprovalGate();
|
|
426
435
|
await resetAskUserQuestionsTurnCache();
|
|
436
|
+
await initPersonaStatusChip(ctx);
|
|
427
437
|
await syncServiceTierStatus(ctx);
|
|
428
438
|
await applyDisabledModelProviderPolicy(ctx);
|
|
429
439
|
await applyCompactionThresholdOverride(ctx);
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Wires /persona slash commands + the persona status-bar chip into Otto.
|
|
2
|
+
// Spec §2.5 + §5.2. Lives inside the src/resources extension boundary, so it
|
|
3
|
+
// imports only the @otto/coworker-persona package (never src/ modules).
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { PersonaRegistry, handleList, handleCurrent, handleSwitch, handleReset, handleInstall, handleUninstall, } from "@otto/coworker-persona";
|
|
7
|
+
// Sorts ahead of alphabetic extension status keys so the chip lands leftmost
|
|
8
|
+
// in the footer's extension-status block.
|
|
9
|
+
const STATUS_KEY = "00-persona";
|
|
10
|
+
const SUBCOMMANDS = ["list", "current", "switch", "install", "uninstall", "reset"];
|
|
11
|
+
function ottoHome() {
|
|
12
|
+
return process.env.OTTO_HOME || join(homedir(), ".otto");
|
|
13
|
+
}
|
|
14
|
+
function getRegistry() {
|
|
15
|
+
return new PersonaRegistry({ ottoHome: ottoHome() });
|
|
16
|
+
}
|
|
17
|
+
function formatPersonaChip(persona) {
|
|
18
|
+
const { icon, label } = persona.status_line;
|
|
19
|
+
return `${icon} ${label}`.trim();
|
|
20
|
+
}
|
|
21
|
+
async function refreshPersonaChip(ctx, registry) {
|
|
22
|
+
if (!ctx.hasUI)
|
|
23
|
+
return;
|
|
24
|
+
try {
|
|
25
|
+
const active = await registry.activeInWorkspace(ctx.cwd);
|
|
26
|
+
ctx.ui.setStatus(STATUS_KEY, formatPersonaChip(active));
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
ctx.ui.setStatus(STATUS_KEY, undefined);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Called on session_start to seed the default persona and render the chip.
|
|
33
|
+
export async function initPersonaWidget(ctx) {
|
|
34
|
+
const registry = getRegistry();
|
|
35
|
+
try {
|
|
36
|
+
await registry.ensureDefaultInstalled();
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Non-fatal: chip simply won't render if the default can't be installed.
|
|
40
|
+
}
|
|
41
|
+
await refreshPersonaChip(ctx, registry);
|
|
42
|
+
}
|
|
43
|
+
export function registerPersonaCommands(pi) {
|
|
44
|
+
pi.registerCommand("persona", {
|
|
45
|
+
description: "Manage Otto co-worker personas (list, current, switch, install, uninstall, reset)",
|
|
46
|
+
getArgumentCompletions: (prefix) => SUBCOMMANDS.filter((s) => s.startsWith(prefix)).map((s) => ({ value: s, label: s })),
|
|
47
|
+
handler: async (args, ctx) => {
|
|
48
|
+
const registry = getRegistry();
|
|
49
|
+
await registry.ensureDefaultInstalled();
|
|
50
|
+
const [sub, ...rest] = args.trim().split(/\s+/).filter(Boolean);
|
|
51
|
+
const ws = ctx.cwd;
|
|
52
|
+
try {
|
|
53
|
+
let lines;
|
|
54
|
+
switch (sub ?? "current") {
|
|
55
|
+
case "list":
|
|
56
|
+
lines = await handleList(registry, ws);
|
|
57
|
+
break;
|
|
58
|
+
case "current":
|
|
59
|
+
lines = await handleCurrent(registry, ws);
|
|
60
|
+
break;
|
|
61
|
+
case "switch":
|
|
62
|
+
lines = await handleSwitch(registry, ws, rest[0]);
|
|
63
|
+
await refreshPersonaChip(ctx, registry);
|
|
64
|
+
break;
|
|
65
|
+
case "reset":
|
|
66
|
+
lines = await handleReset(registry, ws);
|
|
67
|
+
await refreshPersonaChip(ctx, registry);
|
|
68
|
+
break;
|
|
69
|
+
case "install":
|
|
70
|
+
lines = await handleInstall(registry, rest[0]);
|
|
71
|
+
break;
|
|
72
|
+
case "uninstall":
|
|
73
|
+
lines = await handleUninstall(registry, rest[0], [ws]);
|
|
74
|
+
await refreshPersonaChip(ctx, registry);
|
|
75
|
+
break;
|
|
76
|
+
default:
|
|
77
|
+
ctx.ui.notify(`Unknown subcommand: ${sub}. Try: ${SUBCOMMANDS.join(", ")}`, "warning");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
ctx.ui.notify(err instanceof Error ? err.message : String(err), "error");
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmetech/otto",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "Terminal-based developer chat assistant. Permanent hard fork of gsd-pi with LangFlow flow triggers, a flow builder, and optional gateway routing for compliance environments.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -65,10 +65,18 @@
|
|
|
65
65
|
"build:pi-coding-agent": "npm run build:contracts && npm run build -w @otto/pi-coding-agent",
|
|
66
66
|
"build:native-pkg": "npm run build -w @otto/native",
|
|
67
67
|
"build:contracts": "npm run build -w @otto-build/contracts",
|
|
68
|
+
"build:coworker-types": "npm run build -w @otto/coworker-types",
|
|
69
|
+
"build:coworker-utils": "npm run build -w @otto/coworker-utils",
|
|
70
|
+
"build:coworker-memory": "npm run build -w @otto/coworker-memory",
|
|
71
|
+
"build:coworker-vault": "npm run build -w @otto/coworker-vault",
|
|
72
|
+
"build:coworker-artifacts": "npm run build -w @otto/coworker-artifacts",
|
|
73
|
+
"build:coworker-scratchpad": "npm run build -w @otto/coworker-scratchpad",
|
|
74
|
+
"build:coworker-persona": "npm run build -w @otto/coworker-persona",
|
|
75
|
+
"build:coworker": "npm run build:coworker-types && npm run build:coworker-utils && npm run build:coworker-memory && npm run build:coworker-vault && npm run build:coworker-artifacts && npm run build:coworker-scratchpad && npm run build:coworker-persona",
|
|
68
76
|
"build:rpc-client": "npm run build -w @otto-build/rpc-client",
|
|
69
77
|
"build:pi": "npm run build:contracts && npm run build:native-pkg && npm run build:pi-tui && npm run build:pi-ai && npm run build:pi-agent-core && npm run build:pi-coding-agent",
|
|
70
78
|
"build:mcp-server": "npm run build -w @otto-build/mcp-server",
|
|
71
|
-
"build:core": "npm run build:contracts && npm run build:pi && npm run build:rpc-client && npm run build:mcp-server && tsc && npm run copy-resources && npm run copy-themes && npm run copy-export-html",
|
|
79
|
+
"build:core": "npm run build:contracts && npm run build:coworker && npm run build:pi && npm run build:rpc-client && npm run build:mcp-server && tsc && npm run copy-resources && npm run copy-themes && npm run copy-export-html",
|
|
72
80
|
"prebuild": "node scripts/sync-brand-colors.mjs && node scripts/sync-piconfig.mjs && node scripts/sync-release-notes.mjs && node scripts/generate-theme-const.mjs",
|
|
73
81
|
"build": "npm run build:core",
|
|
74
82
|
"sync-brand-colors": "node scripts/sync-brand-colors.mjs",
|
|
@@ -86,7 +94,7 @@
|
|
|
86
94
|
"baseline:refactor:phase0": "npm run baseline:refactor:gate && node --import ./src/resources/extensions/workflow/tests/resolve-ts.mjs --experimental-strip-types --test src/resources/extensions/workflow/tests/derive-state-db.test.ts src/resources/extensions/workflow/tests/single-writer-invariant.test.ts src/resources/extensions/workflow/tests/auto-recovery.test.ts src/resources/extensions/workflow/tests/auto-worktree-registry.test.ts",
|
|
87
95
|
"legacy:cleanup:gate": "node scripts/legacy-cleanup-gate.mjs",
|
|
88
96
|
"legacy:cleanup:evidence": "node scripts/legacy-cleanup-evidence.mjs",
|
|
89
|
-
"test:unit:compiled": "node --import ./scripts/dist-test-resolve.mjs --experimental-test-isolation=process --test-reporter=./scripts/test-reporter-compact.mjs --test \"dist-test/src/tests/*.test.js\" \"dist-test/src/resources/extensions/workflow/tests/*.test.js\" \"dist-test/src/resources/extensions/workflow/tests/*.test.mjs\" \"dist-test/src/resources/extensions/shared/tests/*.test.js\" \"dist-test/src/resources/extensions/subagent/tests/*.test.js\" \"dist-test/src/resources/extensions/claude-code-cli/tests/*.test.js\" \"dist-test/src/resources/extensions/github-sync/tests/*.test.js\" \"dist-test/src/resources/extensions/universal-config/tests/*.test.js\" \"dist-test/src/resources/extensions/visual-brief/tests/*.test.js\" \"dist-test/src/resources/extensions/voice/tests/*.test.js\" \"dist-test/src/resources/extensions/mcp-client/tests/*.test.js\" \"dist-test/src/resources/extensions/remote-questions/tests/*.test.js\"",
|
|
97
|
+
"test:unit:compiled": "node --import ./scripts/dist-test-resolve.mjs --experimental-test-isolation=process --test-reporter=./scripts/test-reporter-compact.mjs --test \"dist-test/src/tests/*.test.js\" \"dist-test/src/resources/extensions/workflow/tests/*.test.js\" \"dist-test/src/resources/extensions/workflow/tests/*.test.mjs\" \"dist-test/src/resources/extensions/shared/tests/*.test.js\" \"dist-test/src/resources/extensions/subagent/tests/*.test.js\" \"dist-test/src/resources/extensions/claude-code-cli/tests/*.test.js\" \"dist-test/src/resources/extensions/github-sync/tests/*.test.js\" \"dist-test/src/resources/extensions/universal-config/tests/*.test.js\" \"dist-test/src/resources/extensions/visual-brief/tests/*.test.js\" \"dist-test/src/resources/extensions/voice/tests/*.test.js\" \"dist-test/src/resources/extensions/mcp-client/tests/*.test.js\" \"dist-test/src/resources/extensions/remote-questions/tests/*.test.js\" \"dist-test/src/resources/extensions/coworker-memory/*.test.js\" \"dist-test/src/resources/extensions/coworker-vault/*.test.js\" \"dist-test/src/resources/extensions/coworker-scratchpad/*.test.js\" \"dist-test/src/resources/extensions/coworker-artifacts/*.test.js\"",
|
|
90
98
|
"test:unit": "npm run test:compile && npm run test:unit:compiled",
|
|
91
99
|
"test:changed:src": "node scripts/verify-changed-src-tests.mjs",
|
|
92
100
|
"test:packages": "npm run test:compile && npm run test:packages:compiled",
|
|
@@ -147,17 +155,22 @@
|
|
|
147
155
|
"@sinclair/typebox": "^0.34.41",
|
|
148
156
|
"ajv": "^8.17.1",
|
|
149
157
|
"ajv-formats": "^3.0.1",
|
|
158
|
+
"axios": "^1.16.1",
|
|
150
159
|
"chalk": "^5.6.2",
|
|
151
160
|
"chokidar": "^5.0.0",
|
|
161
|
+
"date-fns": "^4.4.0",
|
|
152
162
|
"diff": "^8.0.2",
|
|
153
163
|
"echarts": "^5.6.0",
|
|
164
|
+
"exceljs": "^4.4.0",
|
|
154
165
|
"extract-zip": "^2.0.1",
|
|
155
166
|
"file-type": "^21.1.1",
|
|
156
167
|
"glob": "^13.0.1",
|
|
157
168
|
"hosted-git-info": "^9.0.2",
|
|
158
169
|
"ignore": "^7.0.5",
|
|
170
|
+
"lodash": "^4.18.1",
|
|
159
171
|
"marked": "^15.0.12",
|
|
160
172
|
"minimatch": "^10.2.3",
|
|
173
|
+
"nodejs-polars": "^0.24.1",
|
|
161
174
|
"openai": "^6.26.0",
|
|
162
175
|
"picomatch": "^4.0.3",
|
|
163
176
|
"playwright": "^1.58.2",
|
|
@@ -167,9 +180,11 @@
|
|
|
167
180
|
"sql.js": "^1.14.1",
|
|
168
181
|
"strip-ansi": "^7.1.0",
|
|
169
182
|
"undici": "^7.24.2",
|
|
170
|
-
"yaml": "^2.8.2"
|
|
183
|
+
"yaml": "^2.8.2",
|
|
184
|
+
"zod": "^4.4.3"
|
|
171
185
|
},
|
|
172
186
|
"devDependencies": {
|
|
187
|
+
"@types/lodash": "^4.17.24",
|
|
173
188
|
"@types/node": "^24.12.0",
|
|
174
189
|
"@types/picomatch": "^4.0.2",
|
|
175
190
|
"@types/proper-lockfile": "^4.1.4",
|
|
@@ -181,13 +196,13 @@
|
|
|
181
196
|
},
|
|
182
197
|
"optionalDependencies": {
|
|
183
198
|
"@anthropic-ai/claude-agent-sdk": "0.2.83",
|
|
199
|
+
"@cmetech/otto-engine-darwin-arm64": "1.2.4",
|
|
200
|
+
"@cmetech/otto-engine-darwin-x64": "1.2.4",
|
|
201
|
+
"@cmetech/otto-engine-linux-arm64-gnu": "1.2.4",
|
|
202
|
+
"@cmetech/otto-engine-linux-x64-gnu": "1.2.4",
|
|
203
|
+
"@cmetech/otto-engine-win32-x64-msvc": "1.2.4",
|
|
184
204
|
"fsevents": "~2.3.3",
|
|
185
|
-
"koffi": "^2.9.0"
|
|
186
|
-
"@cmetech/otto-engine-darwin-arm64": "1.1.1",
|
|
187
|
-
"@cmetech/otto-engine-darwin-x64": "1.1.1",
|
|
188
|
-
"@cmetech/otto-engine-linux-arm64-gnu": "1.1.1",
|
|
189
|
-
"@cmetech/otto-engine-linux-x64-gnu": "1.1.1",
|
|
190
|
-
"@cmetech/otto-engine-win32-x64-msvc": "1.1.1"
|
|
205
|
+
"koffi": "^2.9.0"
|
|
191
206
|
},
|
|
192
207
|
"overrides": {
|
|
193
208
|
"gaxios": "7.1.4",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ArtifactHandle, ArtifactKind, FileWrite, TurnEntry } from './types.js';
|
|
2
|
+
export interface ArtifactStoreOptions {
|
|
3
|
+
workspaceDir: string;
|
|
4
|
+
now?: () => string;
|
|
5
|
+
}
|
|
6
|
+
export declare class ArtifactStore {
|
|
7
|
+
private readonly workspaceDir;
|
|
8
|
+
private readonly now;
|
|
9
|
+
constructor(opts: ArtifactStoreOptions);
|
|
10
|
+
private rootDir;
|
|
11
|
+
private existingSlugs;
|
|
12
|
+
private handleFor;
|
|
13
|
+
private atomicWrite;
|
|
14
|
+
private readMetadata;
|
|
15
|
+
private readProvenance;
|
|
16
|
+
private fileStats;
|
|
17
|
+
create(kind: ArtifactKind, name: string): Promise<ArtifactHandle>;
|
|
18
|
+
update(handle: ArtifactHandle, files: FileWrite[]): Promise<{
|
|
19
|
+
files_touched: string[];
|
|
20
|
+
}>;
|
|
21
|
+
recordTurn(handle: ArtifactHandle, entry: Omit<TurnEntry, '_schema' | 'ts'> & Partial<Pick<TurnEntry, 'ts'>>): Promise<void>;
|
|
22
|
+
list(): Promise<ArtifactHandle[]>;
|
|
23
|
+
get(slug: string): Promise<ArtifactHandle | null>;
|
|
24
|
+
remove(slug: string, confirm: true): Promise<void>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// packages/coworker-artifacts/src/artifact-store.ts
|
|
2
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync, } from 'node:fs';
|
|
3
|
+
import { join, normalize } from 'node:path';
|
|
4
|
+
import { ARTIFACT_KINDS } from './types.js';
|
|
5
|
+
import { ArtifactKindRejected, ArtifactNotFound, } from './errors.js';
|
|
6
|
+
import { deriveSlug, nextCollisionSlug } from './slug.js';
|
|
7
|
+
import { takeSnapshot, diffSnapshots } from './dir-snapshot.js';
|
|
8
|
+
import { renderReadme } from './readme-renderer.js';
|
|
9
|
+
const ARTIFACTS_DIR_NAME = '.otto/artifacts';
|
|
10
|
+
const PRIMARY_FILE = 'report.md';
|
|
11
|
+
export class ArtifactStore {
|
|
12
|
+
workspaceDir;
|
|
13
|
+
now;
|
|
14
|
+
constructor(opts) {
|
|
15
|
+
this.workspaceDir = opts.workspaceDir;
|
|
16
|
+
this.now = opts.now ?? (() => new Date().toISOString());
|
|
17
|
+
}
|
|
18
|
+
rootDir() {
|
|
19
|
+
return join(this.workspaceDir, ARTIFACTS_DIR_NAME);
|
|
20
|
+
}
|
|
21
|
+
existingSlugs() {
|
|
22
|
+
const root = this.rootDir();
|
|
23
|
+
if (!existsSync(root))
|
|
24
|
+
return new Set();
|
|
25
|
+
return new Set(readdirSync(root, { withFileTypes: true })
|
|
26
|
+
.filter(e => e.isDirectory())
|
|
27
|
+
.map(e => e.name));
|
|
28
|
+
}
|
|
29
|
+
handleFor(slug, kind, name) {
|
|
30
|
+
const dir = join(this.rootDir(), slug);
|
|
31
|
+
return {
|
|
32
|
+
slug, kind, name, dir,
|
|
33
|
+
uri: `artifact://${slug}`,
|
|
34
|
+
primaryPath: join(dir, PRIMARY_FILE),
|
|
35
|
+
metadataPath: join(dir, 'metadata.json'),
|
|
36
|
+
provenancePath: join(dir, 'provenance.json'),
|
|
37
|
+
readmePath: join(dir, 'README.md'),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
atomicWrite(path, content, mode = 0o600) {
|
|
41
|
+
const tmp = `${path}.tmp`;
|
|
42
|
+
writeFileSync(tmp, content, { mode });
|
|
43
|
+
chmodSync(tmp, mode);
|
|
44
|
+
renameSync(tmp, path);
|
|
45
|
+
}
|
|
46
|
+
readMetadata(path) {
|
|
47
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
48
|
+
}
|
|
49
|
+
readProvenance(path) {
|
|
50
|
+
if (!existsSync(path))
|
|
51
|
+
return [];
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
fileStats(dir) {
|
|
60
|
+
const snap = takeSnapshot(dir);
|
|
61
|
+
return [...snap.entries()]
|
|
62
|
+
.filter(([p]) => p !== 'metadata.json' && p !== 'provenance.json' && p !== 'README.md')
|
|
63
|
+
.map(([path, { sizeBytes }]) => ({ path, sizeBytes }));
|
|
64
|
+
}
|
|
65
|
+
async create(kind, name) {
|
|
66
|
+
if (!ARTIFACT_KINDS.includes(kind))
|
|
67
|
+
throw new ArtifactKindRejected(kind);
|
|
68
|
+
mkdirSync(this.rootDir(), { recursive: true, mode: 0o700 });
|
|
69
|
+
const base = deriveSlug(name);
|
|
70
|
+
const slug = nextCollisionSlug(base, this.existingSlugs());
|
|
71
|
+
const handle = this.handleFor(slug, kind, name);
|
|
72
|
+
// Use mkdirSync (non-recursive) on the artifact dir for race detection,
|
|
73
|
+
// but the parent is created above; if this throws EEXIST, retry with bumped slug.
|
|
74
|
+
try {
|
|
75
|
+
mkdirSync(handle.dir, { mode: 0o700 });
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
if (err.code === 'EEXIST') {
|
|
79
|
+
// Race — recurse with refreshed slug set
|
|
80
|
+
return this.create(kind, name);
|
|
81
|
+
}
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
const ts = this.now();
|
|
85
|
+
const meta = {
|
|
86
|
+
_schema: 1, slug, kind, name,
|
|
87
|
+
created_at: ts, last_updated_at: ts,
|
|
88
|
+
turn_count: 0, primary_file: PRIMARY_FILE,
|
|
89
|
+
uri: handle.uri,
|
|
90
|
+
};
|
|
91
|
+
this.atomicWrite(handle.metadataPath, JSON.stringify(meta, null, 2));
|
|
92
|
+
this.atomicWrite(handle.primaryPath, '');
|
|
93
|
+
this.atomicWrite(handle.provenancePath, '[]');
|
|
94
|
+
this.atomicWrite(handle.readmePath, renderReadme(meta, [], this.fileStats(handle.dir)));
|
|
95
|
+
return handle;
|
|
96
|
+
}
|
|
97
|
+
async update(handle, files) {
|
|
98
|
+
if (!existsSync(handle.dir))
|
|
99
|
+
throw new ArtifactNotFound(handle.slug);
|
|
100
|
+
for (const f of files) {
|
|
101
|
+
const normalized = normalize(f.path);
|
|
102
|
+
if (!f.path || !normalized || normalized === '.' ||
|
|
103
|
+
normalized.startsWith('..') || normalized.startsWith('/') ||
|
|
104
|
+
normalized.includes('\0')) {
|
|
105
|
+
throw new Error(`Bad FileWrite path: ${f.path}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const before = takeSnapshot(handle.dir);
|
|
109
|
+
for (const f of files) {
|
|
110
|
+
const abs = join(handle.dir, f.path);
|
|
111
|
+
mkdirSync(join(abs, '..'), { recursive: true, mode: 0o700 });
|
|
112
|
+
this.atomicWrite(abs, f.content);
|
|
113
|
+
}
|
|
114
|
+
const after = takeSnapshot(handle.dir);
|
|
115
|
+
const diff = diffSnapshots(before, after);
|
|
116
|
+
const filesTouched = [...new Set([...diff.added, ...diff.modified])]
|
|
117
|
+
.filter(p => p !== 'metadata.json' && p !== 'provenance.json' && p !== 'README.md')
|
|
118
|
+
.sort();
|
|
119
|
+
// Bump metadata (last_updated_at; turn_count incremented by recordTurn)
|
|
120
|
+
const meta = this.readMetadata(handle.metadataPath);
|
|
121
|
+
meta.last_updated_at = this.now();
|
|
122
|
+
this.atomicWrite(handle.metadataPath, JSON.stringify(meta, null, 2));
|
|
123
|
+
const prov = this.readProvenance(handle.provenancePath);
|
|
124
|
+
this.atomicWrite(handle.readmePath, renderReadme(meta, prov, this.fileStats(handle.dir)));
|
|
125
|
+
return { files_touched: filesTouched };
|
|
126
|
+
}
|
|
127
|
+
async recordTurn(handle, entry) {
|
|
128
|
+
if (!existsSync(handle.dir))
|
|
129
|
+
throw new ArtifactNotFound(handle.slug);
|
|
130
|
+
const prov = this.readProvenance(handle.provenancePath);
|
|
131
|
+
const ts = entry.ts ?? this.now();
|
|
132
|
+
const fullEntry = {
|
|
133
|
+
_schema: 1, ts,
|
|
134
|
+
action: entry.action,
|
|
135
|
+
turn_id: entry.turn_id,
|
|
136
|
+
user_prompt: entry.user_prompt,
|
|
137
|
+
files_touched: entry.files_touched,
|
|
138
|
+
...(entry.agent_turn_id !== undefined ? { agent_turn_id: entry.agent_turn_id } : {}),
|
|
139
|
+
...(entry.scratchpad_name !== undefined ? { scratchpad_name: entry.scratchpad_name } : {}),
|
|
140
|
+
};
|
|
141
|
+
prov.push(fullEntry);
|
|
142
|
+
this.atomicWrite(handle.provenancePath, JSON.stringify(prov, null, 2));
|
|
143
|
+
// Bump metadata
|
|
144
|
+
const meta = this.readMetadata(handle.metadataPath);
|
|
145
|
+
meta.turn_count = prov.length;
|
|
146
|
+
meta.last_updated_at = ts;
|
|
147
|
+
this.atomicWrite(handle.metadataPath, JSON.stringify(meta, null, 2));
|
|
148
|
+
this.atomicWrite(handle.readmePath, renderReadme(meta, prov, this.fileStats(handle.dir)));
|
|
149
|
+
}
|
|
150
|
+
async list() {
|
|
151
|
+
const root = this.rootDir();
|
|
152
|
+
if (!existsSync(root))
|
|
153
|
+
return [];
|
|
154
|
+
const handles = [];
|
|
155
|
+
for (const slug of this.existingSlugs()) {
|
|
156
|
+
const metaPath = join(root, slug, 'metadata.json');
|
|
157
|
+
if (!existsSync(metaPath))
|
|
158
|
+
continue;
|
|
159
|
+
try {
|
|
160
|
+
const meta = this.readMetadata(metaPath);
|
|
161
|
+
const h = this.handleFor(meta.slug, meta.kind, meta.name);
|
|
162
|
+
handles.push({ handle: h, created_at: meta.created_at });
|
|
163
|
+
}
|
|
164
|
+
catch { /* skip malformed */ }
|
|
165
|
+
}
|
|
166
|
+
handles.sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
167
|
+
return handles.map(x => x.handle);
|
|
168
|
+
}
|
|
169
|
+
async get(slug) {
|
|
170
|
+
const metaPath = join(this.rootDir(), slug, 'metadata.json');
|
|
171
|
+
if (!existsSync(metaPath))
|
|
172
|
+
return null;
|
|
173
|
+
const meta = this.readMetadata(metaPath);
|
|
174
|
+
return this.handleFor(meta.slug, meta.kind, meta.name);
|
|
175
|
+
}
|
|
176
|
+
async remove(slug, confirm) {
|
|
177
|
+
if (confirm !== true)
|
|
178
|
+
throw new Error(`/artifacts remove requires --confirm`);
|
|
179
|
+
if (!slug || slug.includes('/') || slug.includes('..') || slug === '.') {
|
|
180
|
+
throw new ArtifactNotFound(slug);
|
|
181
|
+
}
|
|
182
|
+
const dir = join(this.rootDir(), slug);
|
|
183
|
+
if (!existsSync(dir))
|
|
184
|
+
throw new ArtifactNotFound(slug);
|
|
185
|
+
rmSync(dir, { recursive: true, force: true });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// packages/coworker-artifacts/src/dir-snapshot.ts
|
|
2
|
+
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
3
|
+
import { join, relative, sep } from 'node:path';
|
|
4
|
+
export function takeSnapshot(dir) {
|
|
5
|
+
const out = new Map();
|
|
6
|
+
if (!existsSync(dir))
|
|
7
|
+
return out;
|
|
8
|
+
walk(dir, dir, out);
|
|
9
|
+
return out;
|
|
10
|
+
}
|
|
11
|
+
function walk(root, current, out) {
|
|
12
|
+
let entries;
|
|
13
|
+
try {
|
|
14
|
+
entries = readdirSync(current, { withFileTypes: true });
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
for (const entry of entries) {
|
|
20
|
+
const abs = join(current, entry.name);
|
|
21
|
+
if (entry.isDirectory()) {
|
|
22
|
+
walk(root, abs, out);
|
|
23
|
+
}
|
|
24
|
+
else if (entry.isFile()) {
|
|
25
|
+
const stat = statSync(abs, { bigint: true });
|
|
26
|
+
const rel = relative(root, abs).split(sep).join('/');
|
|
27
|
+
out.set(rel, {
|
|
28
|
+
mtimeNs: stat.mtimeNs,
|
|
29
|
+
sizeBytes: Number(stat.size),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function diffSnapshots(before, after) {
|
|
35
|
+
const added = [];
|
|
36
|
+
const modified = [];
|
|
37
|
+
const removed = [];
|
|
38
|
+
for (const [path, a] of after) {
|
|
39
|
+
const b = before.get(path);
|
|
40
|
+
if (!b)
|
|
41
|
+
added.push(path);
|
|
42
|
+
else if (b.mtimeNs !== a.mtimeNs || b.sizeBytes !== a.sizeBytes)
|
|
43
|
+
modified.push(path);
|
|
44
|
+
}
|
|
45
|
+
for (const path of before.keys()) {
|
|
46
|
+
if (!after.has(path))
|
|
47
|
+
removed.push(path);
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
added: added.sort(),
|
|
51
|
+
modified: modified.sort(),
|
|
52
|
+
removed: removed.sort(),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare class ArtifactNotFound extends Error {
|
|
2
|
+
readonly slug: string;
|
|
3
|
+
constructor(slug: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class ArtifactKindRejected extends Error {
|
|
6
|
+
readonly kind: string;
|
|
7
|
+
constructor(kind: string);
|
|
8
|
+
}
|
|
9
|
+
export declare class ArtifactUriMalformed extends Error {
|
|
10
|
+
readonly uri: string;
|
|
11
|
+
readonly reason: string;
|
|
12
|
+
constructor(uri: string, reason: string);
|
|
13
|
+
}
|
|
14
|
+
export declare class ArtifactSlugCollision extends Error {
|
|
15
|
+
readonly base: string;
|
|
16
|
+
readonly attempts: number;
|
|
17
|
+
constructor(base: string, attempts: number);
|
|
18
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// packages/coworker-artifacts/src/errors.ts
|
|
2
|
+
export class ArtifactNotFound extends Error {
|
|
3
|
+
slug;
|
|
4
|
+
constructor(slug) {
|
|
5
|
+
super(`Artifact not found: ${slug}. /artifacts list to see available.`);
|
|
6
|
+
this.slug = slug;
|
|
7
|
+
this.name = 'ArtifactNotFound';
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export class ArtifactKindRejected extends Error {
|
|
11
|
+
kind;
|
|
12
|
+
constructor(kind) {
|
|
13
|
+
super(`Artifact kind '${kind}' is not supported. v1 ships only 'report'.`);
|
|
14
|
+
this.kind = kind;
|
|
15
|
+
this.name = 'ArtifactKindRejected';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class ArtifactUriMalformed extends Error {
|
|
19
|
+
uri;
|
|
20
|
+
reason;
|
|
21
|
+
constructor(uri, reason) {
|
|
22
|
+
super(`Bad artifact URI ${uri}: ${reason}.`);
|
|
23
|
+
this.uri = uri;
|
|
24
|
+
this.reason = reason;
|
|
25
|
+
this.name = 'ArtifactUriMalformed';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export class ArtifactSlugCollision extends Error {
|
|
29
|
+
base;
|
|
30
|
+
attempts;
|
|
31
|
+
constructor(base, attempts) {
|
|
32
|
+
super(`Slug collision: '${base}' has ${attempts} colliding suffixes (-2…-${attempts + 1}). Pick a different name.`);
|
|
33
|
+
this.base = base;
|
|
34
|
+
this.attempts = attempts;
|
|
35
|
+
this.name = 'ArtifactSlugCollision';
|
|
36
|
+
}
|
|
37
|
+
}
|