@cleocode/core 2026.3.74 → 2026.4.0
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/README.md +1 -1
- package/dist/agents/agent-schema.d.ts.map +1 -1
- package/dist/agents/retry.js +26 -21
- package/dist/agents/retry.js.map +1 -1
- package/dist/cant/approval.d.ts +110 -0
- package/dist/cant/approval.d.ts.map +1 -0
- package/dist/cant/approval.js +185 -0
- package/dist/cant/approval.js.map +1 -0
- package/dist/cant/context-builder.d.ts +79 -0
- package/dist/cant/context-builder.d.ts.map +1 -0
- package/dist/cant/context-builder.js +117 -0
- package/dist/cant/context-builder.js.map +1 -0
- package/dist/cant/discretion.d.ts +95 -0
- package/dist/cant/discretion.d.ts.map +1 -0
- package/dist/cant/discretion.js +116 -0
- package/dist/cant/discretion.js.map +1 -0
- package/dist/cant/index.d.ts +25 -0
- package/dist/cant/index.d.ts.map +1 -0
- package/dist/cant/index.js +23 -0
- package/dist/cant/index.js.map +1 -0
- package/dist/cant/parallel-runner.d.ts +38 -0
- package/dist/cant/parallel-runner.d.ts.map +1 -0
- package/dist/cant/parallel-runner.js +173 -0
- package/dist/cant/parallel-runner.js.map +1 -0
- package/dist/cant/types.d.ts +127 -0
- package/dist/cant/types.d.ts.map +1 -0
- package/dist/cant/types.js +11 -0
- package/dist/cant/types.js.map +1 -0
- package/dist/cant/workflow-executor.d.ts +105 -0
- package/dist/cant/workflow-executor.d.ts.map +1 -0
- package/dist/cant/workflow-executor.js +440 -0
- package/dist/cant/workflow-executor.js.map +1 -0
- package/dist/cleo.js +21 -1
- package/dist/cleo.js.map +1 -1
- package/dist/code/index.d.ts +10 -0
- package/dist/code/index.d.ts.map +1 -0
- package/dist/code/outline.d.ts +51 -0
- package/dist/code/outline.d.ts.map +1 -0
- package/dist/code/parser.d.ts +30 -0
- package/dist/code/parser.d.ts.map +1 -0
- package/dist/code/search.d.ts +42 -0
- package/dist/code/search.d.ts.map +1 -0
- package/dist/code/unfold.d.ts +44 -0
- package/dist/code/unfold.d.ts.map +1 -0
- package/dist/conduit/conduit-client.d.ts +35 -0
- package/dist/conduit/conduit-client.d.ts.map +1 -0
- package/dist/conduit/conduit-client.js +94 -0
- package/dist/conduit/conduit-client.js.map +1 -0
- package/dist/conduit/factory.d.ts +15 -0
- package/dist/conduit/factory.d.ts.map +1 -0
- package/dist/conduit/factory.js +35 -0
- package/dist/conduit/factory.js.map +1 -0
- package/dist/conduit/http-transport.d.ts +44 -0
- package/dist/conduit/http-transport.d.ts.map +1 -0
- package/dist/conduit/http-transport.js +165 -0
- package/dist/conduit/http-transport.js.map +1 -0
- package/dist/conduit/index.d.ts +15 -0
- package/dist/conduit/index.d.ts.map +1 -0
- package/dist/conduit/index.js +12 -0
- package/dist/conduit/index.js.map +1 -0
- package/dist/conduit/local-transport.d.ts +91 -0
- package/dist/conduit/local-transport.d.ts.map +1 -0
- package/dist/conduit/sse-transport.d.ts +68 -0
- package/dist/conduit/sse-transport.d.ts.map +1 -0
- package/dist/config.js +4 -3
- package/dist/config.js.map +1 -1
- package/dist/crypto/credentials.d.ts +40 -0
- package/dist/crypto/credentials.d.ts.map +1 -0
- package/dist/crypto/credentials.js +144 -0
- package/dist/crypto/credentials.js.map +1 -0
- package/dist/engine-result.d.ts +1 -1
- package/dist/engine-result.d.ts.map +1 -1
- package/dist/error-catalog.d.ts +1 -1
- package/dist/error-catalog.d.ts.map +1 -1
- package/dist/error-registry.d.ts +1 -1
- package/dist/error-registry.d.ts.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/hooks/handlers/agent-hooks.d.ts.map +1 -1
- package/dist/hooks/handlers/agent-hooks.js +106 -0
- package/dist/hooks/handlers/agent-hooks.js.map +1 -0
- package/dist/hooks/handlers/context-hooks.d.ts.map +1 -1
- package/dist/hooks/handlers/context-hooks.js +111 -0
- package/dist/hooks/handlers/context-hooks.js.map +1 -0
- package/dist/hooks/handlers/error-hooks.d.ts +14 -5
- package/dist/hooks/handlers/error-hooks.d.ts.map +1 -1
- package/dist/hooks/handlers/error-hooks.js +15 -6
- package/dist/hooks/handlers/error-hooks.js.map +1 -1
- package/dist/hooks/handlers/file-hooks.d.ts.map +1 -1
- package/dist/hooks/handlers/file-hooks.js +35 -11
- package/dist/hooks/handlers/file-hooks.js.map +1 -1
- package/dist/hooks/handlers/handler-helpers.d.ts +41 -0
- package/dist/hooks/handlers/handler-helpers.d.ts.map +1 -0
- package/dist/hooks/handlers/handler-helpers.js +61 -0
- package/dist/hooks/handlers/handler-helpers.js.map +1 -0
- package/dist/hooks/handlers/index.js +10 -1
- package/dist/hooks/handlers/index.js.map +1 -1
- package/dist/hooks/handlers/mcp-hooks.d.ts.map +1 -1
- package/dist/hooks/handlers/mcp-hooks.js +88 -21
- package/dist/hooks/handlers/mcp-hooks.js.map +1 -1
- package/dist/hooks/handlers/session-hooks.d.ts.map +1 -1
- package/dist/hooks/handlers/session-hooks.js +5 -10
- package/dist/hooks/handlers/session-hooks.js.map +1 -1
- package/dist/hooks/handlers/task-hooks.d.ts.map +1 -1
- package/dist/hooks/handlers/task-hooks.js +5 -10
- package/dist/hooks/handlers/task-hooks.js.map +1 -1
- package/dist/hooks/handlers/work-capture-hooks.d.ts.map +1 -1
- package/dist/hooks/handlers/work-capture-hooks.js +165 -0
- package/dist/hooks/handlers/work-capture-hooks.js.map +1 -0
- package/dist/hooks/payload-schemas.js +83 -26
- package/dist/hooks/payload-schemas.js.map +1 -1
- package/dist/hooks/provider-hooks.js +37 -5
- package/dist/hooks/provider-hooks.js.map +1 -1
- package/dist/hooks/registry.js +76 -23
- package/dist/hooks/registry.js.map +1 -1
- package/dist/hooks/types.js +17 -13
- package/dist/hooks/types.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6452 -3371
- package/dist/index.js.map +4 -4
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +12 -0
- package/dist/init.js.map +1 -1
- package/dist/internal.d.ts +11 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +10 -0
- package/dist/internal.js.map +1 -1
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/tree-sitter-languages.d.ts +29 -0
- package/dist/lib/tree-sitter-languages.d.ts.map +1 -0
- package/dist/memory/brain-links.d.ts.map +1 -1
- package/dist/memory/brain-maintenance.d.ts +13 -0
- package/dist/memory/brain-maintenance.d.ts.map +1 -1
- package/dist/memory/brain-retrieval.d.ts +3 -0
- package/dist/memory/brain-retrieval.d.ts.map +1 -1
- package/dist/memory/brain-retrieval.js +5 -0
- package/dist/memory/brain-retrieval.js.map +1 -1
- package/dist/memory/decisions.d.ts.map +1 -1
- package/dist/mvi-helpers.d.ts +52 -0
- package/dist/mvi-helpers.d.ts.map +1 -0
- package/dist/mvi-helpers.js +74 -0
- package/dist/mvi-helpers.js.map +1 -0
- package/dist/nexus/index.js +2 -0
- package/dist/nexus/index.js.map +1 -1
- package/dist/nexus/workspace.d.ts.map +1 -1
- package/dist/nexus/workspace.js +355 -0
- package/dist/nexus/workspace.js.map +1 -0
- package/dist/orchestration/hierarchy.d.ts +32 -0
- package/dist/orchestration/hierarchy.d.ts.map +1 -0
- package/dist/orchestration/index.d.ts +1 -0
- package/dist/orchestration/index.d.ts.map +1 -1
- package/dist/output.d.ts +2 -2
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +40 -8
- package/dist/output.js.map +1 -1
- package/dist/pagination.d.ts +1 -1
- package/dist/pagination.d.ts.map +1 -1
- package/dist/sessions/find.d.ts +3 -0
- package/dist/sessions/find.d.ts.map +1 -1
- package/dist/sessions/find.js +3 -1
- package/dist/sessions/find.js.map +1 -1
- package/dist/sessions/index.d.ts.map +1 -1
- package/dist/sessions/index.js +11 -4
- package/dist/sessions/index.js.map +1 -1
- package/dist/sessions/snapshot.js +213 -0
- package/dist/sessions/snapshot.js.map +1 -0
- package/dist/store/agent-registry-accessor.d.ts +31 -0
- package/dist/store/agent-registry-accessor.d.ts.map +1 -0
- package/dist/store/agent-registry-accessor.js +169 -0
- package/dist/store/agent-registry-accessor.js.map +1 -0
- package/dist/store/converters.d.ts.map +1 -1
- package/dist/store/converters.js +2 -0
- package/dist/store/converters.js.map +1 -1
- package/dist/store/cross-db-cleanup.d.ts +34 -0
- package/dist/store/cross-db-cleanup.d.ts.map +1 -1
- package/dist/store/db-helpers.d.ts.map +1 -1
- package/dist/store/db-helpers.js +1 -0
- package/dist/store/db-helpers.js.map +1 -1
- package/dist/store/json.js +2 -2
- package/dist/store/safety-data-accessor.d.ts +7 -0
- package/dist/store/safety-data-accessor.d.ts.map +1 -1
- package/dist/store/safety-data-accessor.js +14 -0
- package/dist/store/safety-data-accessor.js.map +1 -1
- package/dist/store/signaldock-sqlite.d.ts +48 -0
- package/dist/store/signaldock-sqlite.d.ts.map +1 -0
- package/dist/store/signaldock-sqlite.js +178 -0
- package/dist/store/signaldock-sqlite.js.map +1 -0
- package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
- package/dist/store/sqlite-data-accessor.js +50 -0
- package/dist/store/sqlite-data-accessor.js.map +1 -1
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.js +30 -1
- package/dist/store/sqlite.js.map +1 -1
- package/dist/store/task-store.d.ts.map +1 -1
- package/dist/store/task-store.js +2 -0
- package/dist/store/task-store.js.map +1 -1
- package/dist/store/tasks-schema.d.ts +16 -0
- package/dist/store/tasks-schema.d.ts.map +1 -1
- package/dist/store/tasks-schema.js +33 -0
- package/dist/store/tasks-schema.js.map +1 -1
- package/dist/store/validation-schemas.d.ts +32 -0
- package/dist/store/validation-schemas.d.ts.map +1 -1
- package/dist/system/health.d.ts +1 -1
- package/dist/system/health.d.ts.map +1 -1
- package/dist/system/health.js +35 -0
- package/dist/system/health.js.map +1 -1
- package/dist/task-work/index.d.ts.map +1 -1
- package/dist/task-work/index.js +8 -4
- package/dist/task-work/index.js.map +1 -1
- package/dist/tasks/complete.js +5 -2
- package/dist/tasks/complete.js.map +1 -1
- package/dist/tasks/find.d.ts +3 -0
- package/dist/tasks/find.d.ts.map +1 -1
- package/dist/tasks/find.js +7 -1
- package/dist/tasks/find.js.map +1 -1
- package/dist/tasks/list.d.ts +5 -2
- package/dist/tasks/list.d.ts.map +1 -1
- package/dist/tasks/list.js +9 -2
- package/dist/tasks/list.js.map +1 -1
- package/dist/tasks/show.d.ts +3 -0
- package/dist/tasks/show.d.ts.map +1 -1
- package/dist/tasks/show.js +2 -0
- package/dist/tasks/show.js.map +1 -1
- package/dist/upgrade.d.ts.map +1 -1
- package/dist/upgrade.js +15 -0
- package/dist/upgrade.js.map +1 -1
- package/migrations/drizzle-tasks/20260324000000_assignee-column/migration.sql +6 -0
- package/migrations/drizzle-tasks/20260324000000_assignee-column/snapshot.json +9 -0
- package/migrations/drizzle-tasks/20260327000000_agent-credentials/migration.sql +23 -0
- package/package.json +17 -7
- package/src/__tests__/cli-parity.test.js +11 -1
- package/src/__tests__/cli-parity.test.js.map +1 -1
- package/src/__tests__/cli-parity.test.ts +17 -1
- package/src/__tests__/human-output.test.js +11 -1
- package/src/__tests__/human-output.test.js.map +1 -1
- package/src/__tests__/human-output.test.ts +18 -1
- package/src/__tests__/injection-chain.test.js +3 -2
- package/src/__tests__/injection-chain.test.js.map +1 -1
- package/src/__tests__/injection-mvi-tiers.test.d.ts +2 -2
- package/src/__tests__/injection-mvi-tiers.test.js +15 -15
- package/src/__tests__/injection-mvi-tiers.test.js.map +1 -1
- package/src/__tests__/lafs-conformance.test.d.ts +1 -1
- package/src/__tests__/lafs-conformance.test.js +2 -2
- package/src/__tests__/sharing.test.js +19 -0
- package/src/__tests__/sharing.test.js.map +1 -1
- package/src/agents/__tests__/agent-registry.test.d.ts +12 -0
- package/src/agents/__tests__/agent-registry.test.d.ts.map +1 -0
- package/src/agents/__tests__/agent-registry.test.js +262 -0
- package/src/agents/__tests__/agent-registry.test.js.map +1 -0
- package/src/agents/__tests__/execution-learning.test.d.ts +14 -0
- package/src/agents/__tests__/execution-learning.test.d.ts.map +1 -0
- package/src/agents/__tests__/execution-learning.test.js +533 -0
- package/src/agents/__tests__/execution-learning.test.js.map +1 -0
- package/src/agents/__tests__/health-monitor.test.d.ts +10 -0
- package/src/agents/__tests__/health-monitor.test.d.ts.map +1 -0
- package/src/agents/__tests__/health-monitor.test.js +259 -0
- package/src/agents/__tests__/health-monitor.test.js.map +1 -0
- package/src/agents/__tests__/registry.test.js +27 -2
- package/src/agents/__tests__/registry.test.js.map +1 -1
- package/src/agents/agent-schema.ts +2 -5
- package/src/cant/__tests__/cant-agent-parse.test.ts +94 -0
- package/src/cant/approval.ts +218 -0
- package/src/cant/context-builder.ts +135 -0
- package/src/cant/discretion.ts +149 -0
- package/src/cant/index.ts +58 -0
- package/src/cant/parallel-runner.ts +205 -0
- package/src/cant/types.ts +158 -0
- package/src/cant/workflow-executor.ts +618 -0
- package/src/code/index.ts +10 -0
- package/src/code/outline.ts +214 -0
- package/src/code/parser.ts +299 -0
- package/src/code/search.ts +173 -0
- package/src/code/unfold.ts +204 -0
- package/src/conduit/__tests__/dual-api-e2e.test.ts +212 -0
- package/src/conduit/__tests__/local-credential-flow.test.ts +230 -0
- package/src/conduit/__tests__/local-transport.test.ts +320 -0
- package/src/conduit/__tests__/sse-transport.test.ts +344 -0
- package/src/conduit/conduit-client.ts +123 -0
- package/src/conduit/factory.ts +49 -0
- package/src/conduit/http-transport.ts +201 -0
- package/src/conduit/index.ts +15 -0
- package/src/conduit/local-transport.ts +309 -0
- package/src/conduit/sse-transport.ts +382 -0
- package/src/crypto/credentials.ts +166 -0
- package/src/engine-result.ts +1 -1
- package/src/error-catalog.ts +1 -1
- package/src/error-registry.ts +1 -1
- package/src/errors.ts +1 -1
- package/src/hooks/handlers/__tests__/hook-automation-e2e.test.d.ts +13 -0
- package/src/hooks/handlers/__tests__/hook-automation-e2e.test.d.ts.map +1 -0
- package/src/hooks/handlers/__tests__/hook-automation-e2e.test.js +501 -0
- package/src/hooks/handlers/__tests__/hook-automation-e2e.test.js.map +1 -0
- package/src/hooks/handlers/agent-hooks.ts +1 -30
- package/src/hooks/handlers/context-hooks.ts +1 -30
- package/src/hooks/handlers/error-hooks.ts +14 -5
- package/src/hooks/handlers/file-hooks.ts +1 -6
- package/src/hooks/handlers/handler-helpers.ts +62 -0
- package/src/hooks/handlers/mcp-hooks.ts +2 -14
- package/src/hooks/handlers/session-hooks.ts +1 -6
- package/src/hooks/handlers/task-hooks.ts +1 -6
- package/src/hooks/handlers/work-capture-hooks.ts +1 -10
- package/src/index.ts +12 -1
- package/src/init.ts +12 -0
- package/src/intelligence/__tests__/adaptive-validation.test.d.ts +11 -0
- package/src/intelligence/__tests__/adaptive-validation.test.d.ts.map +1 -0
- package/src/intelligence/__tests__/adaptive-validation.test.js +517 -0
- package/src/intelligence/__tests__/adaptive-validation.test.js.map +1 -0
- package/src/intelligence/__tests__/impact.test.d.ts +1 -0
- package/src/intelligence/__tests__/impact.test.d.ts.map +1 -1
- package/src/intelligence/__tests__/impact.test.js +132 -1
- package/src/intelligence/__tests__/impact.test.js.map +1 -1
- package/src/internal.ts +22 -0
- package/src/lib/__tests__/retry.test.d.ts +7 -0
- package/src/lib/__tests__/retry.test.d.ts.map +1 -0
- package/src/lib/__tests__/retry.test.js +225 -0
- package/src/lib/__tests__/retry.test.js.map +1 -0
- package/src/lib/index.ts +8 -0
- package/src/lib/tree-sitter-languages.ts +88 -0
- package/src/lifecycle/__tests__/chain-store.test.js +6 -0
- package/src/lifecycle/__tests__/chain-store.test.js.map +1 -1
- package/src/lifecycle/__tests__/tessera-engine.test.js +52 -0
- package/src/lifecycle/__tests__/tessera-engine.test.js.map +1 -1
- package/src/memory/__tests__/brain-automation.test.d.ts +11 -0
- package/src/memory/__tests__/brain-automation.test.d.ts.map +1 -0
- package/src/memory/__tests__/brain-automation.test.js +730 -0
- package/src/memory/__tests__/brain-automation.test.js.map +1 -0
- package/src/memory/__tests__/brain-links.test.ts +14 -0
- package/src/memory/__tests__/brain-retrieval.test.ts +10 -0
- package/src/memory/__tests__/session-memory.test.ts +17 -0
- package/src/memory/brain-links.ts +17 -0
- package/src/memory/brain-maintenance.ts +33 -1
- package/src/memory/brain-retrieval.ts +27 -2
- package/src/memory/decisions.ts +18 -2
- package/src/mvi-helpers.ts +81 -0
- package/src/nexus/workspace.ts +19 -7
- package/src/orchestration/hierarchy.ts +202 -0
- package/src/orchestration/index.ts +1 -0
- package/src/output.ts +43 -10
- package/src/pagination.ts +1 -1
- package/src/sessions/__tests__/session-edge-cases.test.js +20 -1
- package/src/sessions/__tests__/session-edge-cases.test.js.map +1 -1
- package/src/sessions/__tests__/session-find.test.js +1 -1
- package/src/sessions/__tests__/session-find.test.js.map +1 -1
- package/src/sessions/__tests__/session-find.test.ts +1 -1
- package/src/sessions/find.ts +6 -1
- package/src/sessions/index.ts +9 -0
- package/src/store/__tests__/migration-safety.test.js +3 -0
- package/src/store/__tests__/migration-safety.test.js.map +1 -1
- package/src/store/__tests__/session-store.test.js +128 -1
- package/src/store/__tests__/session-store.test.js.map +1 -1
- package/src/store/__tests__/task-store.test.js +18 -1
- package/src/store/__tests__/task-store.test.js.map +1 -1
- package/src/store/__tests__/test-db-helper.d.ts.map +1 -1
- package/src/store/__tests__/test-db-helper.js +12 -0
- package/src/store/__tests__/test-db-helper.js.map +1 -1
- package/src/store/agent-registry-accessor.ts +375 -0
- package/src/store/converters.ts +2 -0
- package/src/store/cross-db-cleanup.ts +175 -1
- package/src/store/db-helpers.ts +1 -0
- package/src/store/safety-data-accessor.ts +23 -0
- package/src/store/signaldock-sqlite.ts +429 -0
- package/src/store/sqlite-data-accessor.ts +72 -0
- package/src/store/sqlite.ts +4 -1
- package/src/store/task-store.ts +9 -1
- package/src/store/tasks-schema.ts +7 -0
- package/src/system/__tests__/health.test.ts +2 -2
- package/src/system/health.ts +54 -2
- package/src/task-work/index.ts +5 -0
- package/src/tasks/__tests__/add.test.js +19 -1
- package/src/tasks/__tests__/add.test.js.map +1 -1
- package/src/tasks/__tests__/assignee.test.d.ts +14 -0
- package/src/tasks/__tests__/assignee.test.d.ts.map +1 -0
- package/src/tasks/__tests__/assignee.test.js +125 -0
- package/src/tasks/__tests__/assignee.test.js.map +1 -0
- package/src/tasks/__tests__/assignee.test.ts +162 -0
- package/src/tasks/__tests__/complete-unblocks.test.js +13 -1
- package/src/tasks/__tests__/complete-unblocks.test.js.map +1 -1
- package/src/tasks/__tests__/complete.test.js +28 -7
- package/src/tasks/__tests__/complete.test.js.map +1 -1
- package/src/tasks/__tests__/epic-enforcement.test.d.ts +15 -0
- package/src/tasks/__tests__/epic-enforcement.test.d.ts.map +1 -0
- package/src/tasks/__tests__/epic-enforcement.test.js +669 -0
- package/src/tasks/__tests__/epic-enforcement.test.js.map +1 -0
- package/src/tasks/__tests__/hierarchy-policy.test.js +5 -0
- package/src/tasks/__tests__/hierarchy-policy.test.js.map +1 -1
- package/src/tasks/__tests__/minimal-test.test.d.ts +2 -0
- package/src/tasks/__tests__/minimal-test.test.d.ts.map +1 -0
- package/src/tasks/__tests__/minimal-test.test.js +25 -0
- package/src/tasks/__tests__/minimal-test.test.js.map +1 -0
- package/src/tasks/__tests__/pipeline-stage.test.d.ts +14 -0
- package/src/tasks/__tests__/pipeline-stage.test.d.ts.map +1 -0
- package/src/tasks/__tests__/pipeline-stage.test.js +277 -0
- package/src/tasks/__tests__/pipeline-stage.test.js.map +1 -0
- package/src/tasks/__tests__/update.test.js +43 -6
- package/src/tasks/__tests__/update.test.js.map +1 -1
- package/src/tasks/find.ts +11 -1
- package/src/tasks/list.ts +14 -3
- package/src/tasks/show.ts +6 -0
- package/src/upgrade.ts +16 -0
- package/dist/tasks/reparent.d.ts +0 -38
- package/dist/tasks/reparent.d.ts.map +0 -1
- package/dist/ui/injection-legacy.d.ts +0 -26
- package/dist/ui/injection-legacy.d.ts.map +0 -1
- package/dist/ui/injection-legacy.js +0 -42
- package/dist/ui/injection-legacy.js.map +0 -1
- package/src/signaldock/__tests__/claude-code-transport.test.d.ts +0 -7
- package/src/signaldock/__tests__/claude-code-transport.test.d.ts.map +0 -1
- package/src/signaldock/__tests__/claude-code-transport.test.js +0 -147
- package/src/signaldock/__tests__/claude-code-transport.test.js.map +0 -1
- package/src/signaldock/__tests__/claude-code-transport.test.ts +0 -180
- package/src/signaldock/__tests__/factory.test.d.ts +0 -7
- package/src/signaldock/__tests__/factory.test.d.ts.map +0 -1
- package/src/signaldock/__tests__/factory.test.js +0 -55
- package/src/signaldock/__tests__/factory.test.js.map +0 -1
- package/src/signaldock/__tests__/factory.test.ts +0 -61
- package/src/signaldock/__tests__/signaldock-transport.test.d.ts +0 -9
- package/src/signaldock/__tests__/signaldock-transport.test.d.ts.map +0 -1
- package/src/signaldock/__tests__/signaldock-transport.test.js +0 -321
- package/src/signaldock/__tests__/signaldock-transport.test.js.map +0 -1
- package/src/signaldock/__tests__/signaldock-transport.test.ts +0 -421
- package/src/signaldock/claude-code-transport.ts +0 -137
- package/src/signaldock/factory.ts +0 -39
- package/src/signaldock/index.ts +0 -28
- package/src/signaldock/signaldock-transport.ts +0 -194
- package/src/signaldock/transport.ts +0 -78
- package/src/signaldock/types.ts +0 -100
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* smart_unfold — single symbol extraction from source files.
|
|
3
|
+
*
|
|
4
|
+
* Takes a file path + symbol name, parses the file via tree-sitter,
|
|
5
|
+
* finds the matching symbol node, and extracts complete source including
|
|
6
|
+
* JSDoc/docstring, decorators, and full body. AST node boundaries
|
|
7
|
+
* guarantee no truncation.
|
|
8
|
+
*
|
|
9
|
+
* @task T153
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { readFileSync } from 'node:fs';
|
|
13
|
+
import { relative } from 'node:path';
|
|
14
|
+
import type { CodeSymbol } from '@cleocode/contracts';
|
|
15
|
+
import { detectLanguage } from '../lib/tree-sitter-languages.js';
|
|
16
|
+
import { parseFile } from './parser.js';
|
|
17
|
+
|
|
18
|
+
/** Result of unfolding a single symbol. */
|
|
19
|
+
export interface SmartUnfoldResult {
|
|
20
|
+
/** Whether the symbol was found. */
|
|
21
|
+
found: boolean;
|
|
22
|
+
/** The matched symbol metadata. */
|
|
23
|
+
symbol?: CodeSymbol;
|
|
24
|
+
/** Complete source text including JSDoc, decorators, and body. */
|
|
25
|
+
source: string;
|
|
26
|
+
/** Start line (1-based, inclusive of leading doc comment). */
|
|
27
|
+
startLine: number;
|
|
28
|
+
/** End line (1-based). */
|
|
29
|
+
endLine: number;
|
|
30
|
+
/** Estimated token count for this extraction. */
|
|
31
|
+
estimatedTokens: number;
|
|
32
|
+
/** File path (relative). */
|
|
33
|
+
filePath: string;
|
|
34
|
+
/** Errors encountered. */
|
|
35
|
+
errors: string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Find leading documentation comment (JSDoc, docstring, Rust doc) above a symbol.
|
|
40
|
+
*
|
|
41
|
+
* Scans backwards from the symbol's start line to find attached doc comments.
|
|
42
|
+
*/
|
|
43
|
+
function findLeadingDocStart(lines: string[], symbolStartLine: number, language: string): number {
|
|
44
|
+
const idx = symbolStartLine - 1; // 1-based to 0-based
|
|
45
|
+
if (idx <= 0) return symbolStartLine;
|
|
46
|
+
|
|
47
|
+
let docStart = idx;
|
|
48
|
+
|
|
49
|
+
if (language === 'python') {
|
|
50
|
+
// Python: look for decorator lines above
|
|
51
|
+
let i = idx - 1;
|
|
52
|
+
while (i >= 0 && lines[i]!.trimStart().startsWith('@')) {
|
|
53
|
+
docStart = i + 1; // back to 1-based
|
|
54
|
+
i--;
|
|
55
|
+
}
|
|
56
|
+
return docStart;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// C-family / Rust / JS/TS: look for block comments (/** ... */) or /// lines
|
|
60
|
+
let i = idx - 1;
|
|
61
|
+
|
|
62
|
+
// Skip blank lines immediately above
|
|
63
|
+
while (i >= 0 && lines[i]!.trim() === '') i--;
|
|
64
|
+
|
|
65
|
+
if (i < 0) return symbolStartLine;
|
|
66
|
+
|
|
67
|
+
const line = lines[i]!.trimStart();
|
|
68
|
+
|
|
69
|
+
// Block comment ending: */
|
|
70
|
+
if (line.endsWith('*/')) {
|
|
71
|
+
// Walk backwards to find opening /**
|
|
72
|
+
while (i >= 0) {
|
|
73
|
+
if (lines[i]!.trimStart().startsWith('/**') || lines[i]!.trimStart().startsWith('/*')) {
|
|
74
|
+
docStart = i + 1; // 1-based
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
i--;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Line comments: /// (Rust) or // (TS/JS decorators less common)
|
|
81
|
+
else if (line.startsWith('///') || line.startsWith('//!')) {
|
|
82
|
+
while (
|
|
83
|
+
i >= 0 &&
|
|
84
|
+
(lines[i]!.trimStart().startsWith('///') || lines[i]!.trimStart().startsWith('//!'))
|
|
85
|
+
) {
|
|
86
|
+
docStart = i + 1;
|
|
87
|
+
i--;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Also capture decorators (TS/Java) above the doc comment
|
|
92
|
+
i = docStart - 2; // 0-based, line above current docStart
|
|
93
|
+
while (i >= 0 && lines[i]!.trimStart().startsWith('@')) {
|
|
94
|
+
docStart = i + 1;
|
|
95
|
+
i--;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return docStart;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Extract a symbol's complete source from a file.
|
|
103
|
+
*
|
|
104
|
+
* Finds the symbol by name (supports "Class.method" dot notation),
|
|
105
|
+
* determines its full range including leading documentation, and
|
|
106
|
+
* returns the exact source text.
|
|
107
|
+
*
|
|
108
|
+
* @param filePath - Absolute path to source file
|
|
109
|
+
* @param symbolName - Symbol to extract (e.g. "parseFile" or "HttpTransport.connect")
|
|
110
|
+
* @param projectRoot - Project root for relative path computation
|
|
111
|
+
* @returns Unfold result with complete source text
|
|
112
|
+
*/
|
|
113
|
+
export function smartUnfold(
|
|
114
|
+
filePath: string,
|
|
115
|
+
symbolName: string,
|
|
116
|
+
projectRoot?: string,
|
|
117
|
+
): SmartUnfoldResult {
|
|
118
|
+
const root = projectRoot ?? process.cwd();
|
|
119
|
+
const relPath = relative(root, filePath);
|
|
120
|
+
const language = detectLanguage(filePath);
|
|
121
|
+
|
|
122
|
+
const empty: SmartUnfoldResult = {
|
|
123
|
+
found: false,
|
|
124
|
+
source: '',
|
|
125
|
+
startLine: 0,
|
|
126
|
+
endLine: 0,
|
|
127
|
+
estimatedTokens: 0,
|
|
128
|
+
filePath: relPath,
|
|
129
|
+
errors: [],
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
if (!language) {
|
|
133
|
+
return { ...empty, errors: ['Unsupported language'] };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Parse the file
|
|
137
|
+
const parseResult = parseFile(filePath, root);
|
|
138
|
+
if (parseResult.symbols.length === 0) {
|
|
139
|
+
return {
|
|
140
|
+
...empty,
|
|
141
|
+
errors: parseResult.errors.length > 0 ? parseResult.errors : ['No symbols found'],
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Find the matching symbol
|
|
146
|
+
const parts = symbolName.split('.');
|
|
147
|
+
let match: CodeSymbol | undefined;
|
|
148
|
+
|
|
149
|
+
if (parts.length === 2) {
|
|
150
|
+
// Nested: "Class.method" — find method with matching parent range
|
|
151
|
+
const [parentName, childName] = parts;
|
|
152
|
+
const parent = parseResult.symbols.find(
|
|
153
|
+
(s) =>
|
|
154
|
+
s.name === parentName && (s.kind === 'class' || s.kind === 'struct' || s.kind === 'impl'),
|
|
155
|
+
);
|
|
156
|
+
if (parent) {
|
|
157
|
+
match = parseResult.symbols.find(
|
|
158
|
+
(s) =>
|
|
159
|
+
s.name === childName && s.startLine >= parent.startLine && s.endLine <= parent.endLine,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
// Top-level: exact name match, prefer non-method over method
|
|
164
|
+
match = parseResult.symbols.find((s) => s.name === symbolName && s.kind !== 'method');
|
|
165
|
+
if (!match) {
|
|
166
|
+
match = parseResult.symbols.find((s) => s.name === symbolName);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (!match) {
|
|
171
|
+
return { ...empty, errors: [`Symbol "${symbolName}" not found in ${relPath}`] };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Read source lines
|
|
175
|
+
let lines: string[];
|
|
176
|
+
try {
|
|
177
|
+
lines = readFileSync(filePath, 'utf-8').split('\n');
|
|
178
|
+
} catch (err) {
|
|
179
|
+
return {
|
|
180
|
+
...empty,
|
|
181
|
+
errors: [`Failed to read file: ${err instanceof Error ? err.message : String(err)}`],
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Determine full range including doc comment
|
|
186
|
+
const docStart = findLeadingDocStart(lines, match.startLine, language);
|
|
187
|
+
const startLine = Math.min(docStart, match.startLine);
|
|
188
|
+
const endLine = match.endLine;
|
|
189
|
+
|
|
190
|
+
// Extract source text
|
|
191
|
+
const source = lines.slice(startLine - 1, endLine).join('\n');
|
|
192
|
+
const estimatedTokens = Math.ceil(source.length / 4);
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
found: true,
|
|
196
|
+
symbol: match,
|
|
197
|
+
source,
|
|
198
|
+
startLine,
|
|
199
|
+
endLine,
|
|
200
|
+
estimatedTokens,
|
|
201
|
+
filePath: relPath,
|
|
202
|
+
errors: [],
|
|
203
|
+
};
|
|
204
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dual-API E2E test — verify messaging across both SignalDock endpoints.
|
|
3
|
+
*
|
|
4
|
+
* Tests that agents can exchange messages on:
|
|
5
|
+
* 1. api.signaldock.io (canonical)
|
|
6
|
+
* 2. api.clawmsgr.com (legacy)
|
|
7
|
+
* 3. Local signaldock.db (offline)
|
|
8
|
+
*
|
|
9
|
+
* These tests hit real APIs and require valid credentials.
|
|
10
|
+
* Set SKIP_E2E=1 to skip when running in CI without network.
|
|
11
|
+
*
|
|
12
|
+
* @task T226
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { describe, expect, it } from 'vitest';
|
|
18
|
+
import { HttpTransport } from '../http-transport.js';
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Config helpers
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
const CLEO_DIR = join(process.cwd(), '.cleo');
|
|
25
|
+
const SKIP_E2E = process.env['SKIP_E2E'] === '1';
|
|
26
|
+
|
|
27
|
+
/** Load a ClawMsgr/SignalDock config file. */
|
|
28
|
+
function loadConfig(
|
|
29
|
+
filename: string,
|
|
30
|
+
): { agentId: string; apiKey: string; apiBaseUrl: string } | null {
|
|
31
|
+
const path = join(CLEO_DIR, filename);
|
|
32
|
+
if (!existsSync(path)) return null;
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(readFileSync(path, 'utf-8')) as {
|
|
35
|
+
agentId: string;
|
|
36
|
+
apiKey: string;
|
|
37
|
+
apiBaseUrl: string;
|
|
38
|
+
};
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Send a test message and verify it arrives. */
|
|
45
|
+
async function testMessageExchange(
|
|
46
|
+
apiBaseUrl: string,
|
|
47
|
+
senderConfig: { agentId: string; apiKey: string },
|
|
48
|
+
receiverAgentId: string,
|
|
49
|
+
): Promise<{ sent: boolean; messageId: string | null; error: string | null }> {
|
|
50
|
+
const transport = new HttpTransport();
|
|
51
|
+
try {
|
|
52
|
+
await transport.connect({
|
|
53
|
+
agentId: senderConfig.agentId,
|
|
54
|
+
apiKey: senderConfig.apiKey,
|
|
55
|
+
apiBaseUrl,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const testContent = `/info @${receiverAgentId} #e2e-test T226 verification at ${new Date().toISOString()}`;
|
|
59
|
+
const result = await transport.push(receiverAgentId, testContent);
|
|
60
|
+
|
|
61
|
+
await transport.disconnect();
|
|
62
|
+
return { sent: true, messageId: result.messageId, error: null };
|
|
63
|
+
} catch (err) {
|
|
64
|
+
await transport.disconnect();
|
|
65
|
+
return { sent: false, messageId: null, error: String(err) };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ============================================================================
|
|
70
|
+
// Test suite
|
|
71
|
+
// ============================================================================
|
|
72
|
+
|
|
73
|
+
describe('Dual-API E2E', () => {
|
|
74
|
+
// --------------------------------------------------------------------------
|
|
75
|
+
// Config verification
|
|
76
|
+
// --------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
describe('config files exist', () => {
|
|
79
|
+
it('has clawmsgr configs (legacy)', () => {
|
|
80
|
+
const config = loadConfig('clawmsgr-cleo-rust-lead.json');
|
|
81
|
+
if (!config) {
|
|
82
|
+
console.log('SKIP: clawmsgr config not found');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
expect(config.agentId).toBe('cleo-rust-lead');
|
|
86
|
+
expect(config.apiBaseUrl).toContain('clawmsgr.com');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('has signaldock configs (canonical)', () => {
|
|
90
|
+
const config = loadConfig('signaldock-cleo-rust-lead.json');
|
|
91
|
+
if (!config) {
|
|
92
|
+
console.log('SKIP: signaldock config not found');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
expect(config.agentId).toBe('cleo-rust-lead');
|
|
96
|
+
expect(config.apiBaseUrl).toContain('signaldock.io');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// --------------------------------------------------------------------------
|
|
101
|
+
// API health checks
|
|
102
|
+
// --------------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
describe('API health', () => {
|
|
105
|
+
it('api.signaldock.io is healthy', async () => {
|
|
106
|
+
if (SKIP_E2E) return;
|
|
107
|
+
const response = await fetch('https://api.signaldock.io/health', {
|
|
108
|
+
signal: AbortSignal.timeout(10_000),
|
|
109
|
+
});
|
|
110
|
+
expect(response.ok).toBe(true);
|
|
111
|
+
const data = (await response.json()) as { data?: { status?: string } };
|
|
112
|
+
expect(data.data?.status).toBe('ok');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('api.clawmsgr.com is healthy', async () => {
|
|
116
|
+
if (SKIP_E2E) return;
|
|
117
|
+
const response = await fetch('https://api.clawmsgr.com/health', {
|
|
118
|
+
signal: AbortSignal.timeout(10_000),
|
|
119
|
+
});
|
|
120
|
+
expect(response.ok).toBe(true);
|
|
121
|
+
const data = (await response.json()) as { data?: { status?: string } };
|
|
122
|
+
expect(data.data?.status).toBe('ok');
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// --------------------------------------------------------------------------
|
|
127
|
+
// Message exchange on api.signaldock.io
|
|
128
|
+
// --------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
describe('api.signaldock.io messaging', () => {
|
|
131
|
+
it('can send a message via signaldock.io', async () => {
|
|
132
|
+
if (SKIP_E2E) return;
|
|
133
|
+
const config = loadConfig('signaldock-cleo-rust-lead.json');
|
|
134
|
+
if (!config) {
|
|
135
|
+
console.log('SKIP: no signaldock config');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const result = await testMessageExchange(
|
|
139
|
+
'https://api.signaldock.io',
|
|
140
|
+
{ agentId: config.agentId, apiKey: config.apiKey },
|
|
141
|
+
'cleo-dev',
|
|
142
|
+
);
|
|
143
|
+
expect(result.sent).toBe(true);
|
|
144
|
+
expect(result.messageId).toBeDefined();
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// --------------------------------------------------------------------------
|
|
149
|
+
// Message exchange on api.clawmsgr.com
|
|
150
|
+
// --------------------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
describe('api.clawmsgr.com messaging', () => {
|
|
153
|
+
it('can send a message via clawmsgr.com', async () => {
|
|
154
|
+
if (SKIP_E2E) return;
|
|
155
|
+
const config = loadConfig('clawmsgr-cleo-rust-lead.json');
|
|
156
|
+
if (!config) {
|
|
157
|
+
console.log('SKIP: no clawmsgr config');
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const result = await testMessageExchange(
|
|
161
|
+
'https://api.clawmsgr.com',
|
|
162
|
+
{ agentId: config.agentId, apiKey: config.apiKey },
|
|
163
|
+
'cleo-dev',
|
|
164
|
+
);
|
|
165
|
+
expect(result.sent).toBe(true);
|
|
166
|
+
expect(result.messageId).toBeDefined();
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// --------------------------------------------------------------------------
|
|
171
|
+
// Cross-API metadata consistency
|
|
172
|
+
// --------------------------------------------------------------------------
|
|
173
|
+
|
|
174
|
+
describe('cross-API consistency', () => {
|
|
175
|
+
it('agent profile exists on both APIs', async () => {
|
|
176
|
+
if (SKIP_E2E) return;
|
|
177
|
+
const clawConfig = loadConfig('clawmsgr-cleo-rust-lead.json');
|
|
178
|
+
const sdConfig = loadConfig('signaldock-cleo-rust-lead.json');
|
|
179
|
+
if (!clawConfig || !sdConfig) {
|
|
180
|
+
console.log('SKIP: missing configs');
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Check agent exists on clawmsgr
|
|
185
|
+
const clawResp = await fetch('https://api.clawmsgr.com/agents/cleo-rust-lead', {
|
|
186
|
+
headers: {
|
|
187
|
+
Authorization: `Bearer ${clawConfig.apiKey}`,
|
|
188
|
+
'X-Agent-Id': 'cleo-rust-lead',
|
|
189
|
+
},
|
|
190
|
+
signal: AbortSignal.timeout(10_000),
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Check agent exists on signaldock
|
|
194
|
+
const sdResp = await fetch('https://api.signaldock.io/agents/cleo-rust-lead', {
|
|
195
|
+
headers: {
|
|
196
|
+
Authorization: `Bearer ${sdConfig.apiKey}`,
|
|
197
|
+
'X-Agent-Id': 'cleo-rust-lead',
|
|
198
|
+
},
|
|
199
|
+
signal: AbortSignal.timeout(10_000),
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
expect(clawResp.ok).toBe(true);
|
|
203
|
+
expect(sdResp.ok).toBe(true);
|
|
204
|
+
|
|
205
|
+
const clawData = (await clawResp.json()) as { data?: { agent?: { agentId?: string } } };
|
|
206
|
+
const sdData = (await sdResp.json()) as { data?: { agent?: { agentId?: string } } };
|
|
207
|
+
|
|
208
|
+
expect(clawData.data?.agent?.agentId).toBe('cleo-rust-lead');
|
|
209
|
+
expect(sdData.data?.agent?.agentId).toBe('cleo-rust-lead');
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
});
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local credential flow — end-to-end validation.
|
|
3
|
+
*
|
|
4
|
+
* Tests the full lifecycle: signaldock.db creation → agent register →
|
|
5
|
+
* credential encryption/decryption → LocalTransport connect →
|
|
6
|
+
* push/poll/ack messaging.
|
|
7
|
+
*
|
|
8
|
+
* @task T227
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import { tmpdir } from 'node:os';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
15
|
+
import { ensureSignaldockDb, getSignaldockDbPath } from '../../store/signaldock-sqlite.js';
|
|
16
|
+
import { LocalTransport } from '../local-transport.js';
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Test helpers
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
let testDir: string;
|
|
23
|
+
let originalCwd: string;
|
|
24
|
+
|
|
25
|
+
async function setupTestEnvironment(): Promise<string> {
|
|
26
|
+
const dir = join(
|
|
27
|
+
tmpdir(),
|
|
28
|
+
`credential-flow-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
29
|
+
);
|
|
30
|
+
mkdirSync(join(dir, '.cleo'), { recursive: true });
|
|
31
|
+
// Create a minimal .cleo/project-context.json so paths resolve
|
|
32
|
+
writeFileSync(
|
|
33
|
+
join(dir, '.cleo', 'project-context.json'),
|
|
34
|
+
JSON.stringify({ schemaVersion: '1.0.0', projectTypes: ['node'], primaryType: 'node' }),
|
|
35
|
+
);
|
|
36
|
+
return dir;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ============================================================================
|
|
40
|
+
// Test suite
|
|
41
|
+
// ============================================================================
|
|
42
|
+
|
|
43
|
+
describe('Local Credential Flow E2E', () => {
|
|
44
|
+
beforeEach(async () => {
|
|
45
|
+
originalCwd = process.cwd();
|
|
46
|
+
testDir = await setupTestEnvironment();
|
|
47
|
+
process.chdir(testDir);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
afterEach(() => {
|
|
51
|
+
process.chdir(originalCwd);
|
|
52
|
+
if (testDir && existsSync(testDir)) {
|
|
53
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// --------------------------------------------------------------------------
|
|
58
|
+
// Step 1: cleo init creates signaldock.db
|
|
59
|
+
// --------------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
describe('Step 1: signaldock.db creation', () => {
|
|
62
|
+
it('ensureSignaldockDb creates the database file', async () => {
|
|
63
|
+
const result = await ensureSignaldockDb(testDir);
|
|
64
|
+
expect(result.action).toBe('created');
|
|
65
|
+
expect(existsSync(result.path)).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('ensureSignaldockDb is idempotent', async () => {
|
|
69
|
+
const first = await ensureSignaldockDb(testDir);
|
|
70
|
+
const second = await ensureSignaldockDb(testDir);
|
|
71
|
+
expect(first.action).toBe('created');
|
|
72
|
+
expect(second.action).toBe('exists');
|
|
73
|
+
expect(first.path).toBe(second.path);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('signaldock.db has the messages table', async () => {
|
|
77
|
+
await ensureSignaldockDb(testDir);
|
|
78
|
+
const dbPath = getSignaldockDbPath(testDir);
|
|
79
|
+
expect(existsSync(dbPath)).toBe(true);
|
|
80
|
+
|
|
81
|
+
// Verify by connecting LocalTransport (which checks for messages table)
|
|
82
|
+
const transport = new LocalTransport();
|
|
83
|
+
await transport.connect({
|
|
84
|
+
agentId: 'test',
|
|
85
|
+
apiKey: 'sk_test',
|
|
86
|
+
apiBaseUrl: 'http://localhost',
|
|
87
|
+
});
|
|
88
|
+
await transport.disconnect();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// --------------------------------------------------------------------------
|
|
93
|
+
// Step 2: LocalTransport availability
|
|
94
|
+
// --------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
describe('Step 2: LocalTransport availability', () => {
|
|
97
|
+
it('isAvailable returns false before init', () => {
|
|
98
|
+
const emptyDir = join(tmpdir(), `no-init-${Date.now()}`);
|
|
99
|
+
mkdirSync(join(emptyDir, '.cleo'), { recursive: true });
|
|
100
|
+
expect(LocalTransport.isAvailable(emptyDir)).toBe(false);
|
|
101
|
+
rmSync(emptyDir, { recursive: true, force: true });
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('isAvailable returns true after init', async () => {
|
|
105
|
+
await ensureSignaldockDb(testDir);
|
|
106
|
+
expect(LocalTransport.isAvailable(testDir)).toBe(true);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// --------------------------------------------------------------------------
|
|
111
|
+
// Step 3: Full messaging lifecycle
|
|
112
|
+
// --------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
describe('Step 3: messaging lifecycle', () => {
|
|
115
|
+
it('complete flow: init → connect → push → poll → ack', async () => {
|
|
116
|
+
// 1. Init signaldock.db
|
|
117
|
+
await ensureSignaldockDb(testDir);
|
|
118
|
+
|
|
119
|
+
// 2. Connect two agents
|
|
120
|
+
const agent1 = new LocalTransport();
|
|
121
|
+
const agent2 = new LocalTransport();
|
|
122
|
+
|
|
123
|
+
await agent1.connect({
|
|
124
|
+
agentId: 'cleo-rust-lead',
|
|
125
|
+
apiKey: 'sk_live_fake1',
|
|
126
|
+
apiBaseUrl: 'http://localhost',
|
|
127
|
+
});
|
|
128
|
+
await agent2.connect({
|
|
129
|
+
agentId: 'cleo-dev',
|
|
130
|
+
apiKey: 'sk_live_fake2',
|
|
131
|
+
apiBaseUrl: 'http://localhost',
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// 3. Agent 1 sends a message to Agent 2
|
|
135
|
+
const sendResult = await agent1.push(
|
|
136
|
+
'cleo-dev',
|
|
137
|
+
'/action @cleo-dev #test Hello from rust-lead',
|
|
138
|
+
);
|
|
139
|
+
expect(sendResult.messageId).toBeDefined();
|
|
140
|
+
|
|
141
|
+
// 4. Agent 2 polls and receives the message
|
|
142
|
+
const messages = await agent2.poll();
|
|
143
|
+
expect(messages).toHaveLength(1);
|
|
144
|
+
expect(messages[0].from).toBe('cleo-rust-lead');
|
|
145
|
+
expect(messages[0].content).toContain('Hello from rust-lead');
|
|
146
|
+
|
|
147
|
+
// 5. Agent 2 acknowledges the message
|
|
148
|
+
await agent2.ack([messages[0].id]);
|
|
149
|
+
|
|
150
|
+
// 6. Subsequent poll returns empty (message was acked)
|
|
151
|
+
const afterAck = await agent2.poll();
|
|
152
|
+
expect(afterAck).toHaveLength(0);
|
|
153
|
+
|
|
154
|
+
// 7. Agent 2 replies
|
|
155
|
+
const replyResult = await agent2.push('cleo-rust-lead', '/ack Received.');
|
|
156
|
+
expect(replyResult.messageId).toBeDefined();
|
|
157
|
+
|
|
158
|
+
// 8. Agent 1 receives the reply
|
|
159
|
+
const replies = await agent1.poll();
|
|
160
|
+
expect(replies).toHaveLength(1);
|
|
161
|
+
expect(replies[0].from).toBe('cleo-dev');
|
|
162
|
+
expect(replies[0].content).toBe('/ack Received.');
|
|
163
|
+
|
|
164
|
+
// Cleanup
|
|
165
|
+
await agent1.disconnect();
|
|
166
|
+
await agent2.disconnect();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('messages persist across transport reconnections', async () => {
|
|
170
|
+
await ensureSignaldockDb(testDir);
|
|
171
|
+
|
|
172
|
+
// Send a message
|
|
173
|
+
const sender = new LocalTransport();
|
|
174
|
+
await sender.connect({
|
|
175
|
+
agentId: 'sender',
|
|
176
|
+
apiKey: 'sk_test',
|
|
177
|
+
apiBaseUrl: 'http://localhost',
|
|
178
|
+
});
|
|
179
|
+
await sender.push('receiver', 'persistent message');
|
|
180
|
+
await sender.disconnect();
|
|
181
|
+
|
|
182
|
+
// New transport instance reads the same DB
|
|
183
|
+
const receiver = new LocalTransport();
|
|
184
|
+
await receiver.connect({
|
|
185
|
+
agentId: 'receiver',
|
|
186
|
+
apiKey: 'sk_test',
|
|
187
|
+
apiBaseUrl: 'http://localhost',
|
|
188
|
+
});
|
|
189
|
+
const messages = await receiver.poll();
|
|
190
|
+
expect(messages).toHaveLength(1);
|
|
191
|
+
expect(messages[0].content).toBe('persistent message');
|
|
192
|
+
|
|
193
|
+
await receiver.disconnect();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('multiple agents can communicate in the same conversation', async () => {
|
|
197
|
+
await ensureSignaldockDb(testDir);
|
|
198
|
+
|
|
199
|
+
const prime = new LocalTransport();
|
|
200
|
+
const rustLead = new LocalTransport();
|
|
201
|
+
const dbLead = new LocalTransport();
|
|
202
|
+
|
|
203
|
+
await prime.connect({ agentId: 'prime', apiKey: 'sk1', apiBaseUrl: 'http://localhost' });
|
|
204
|
+
await rustLead.connect({
|
|
205
|
+
agentId: 'rust-lead',
|
|
206
|
+
apiKey: 'sk2',
|
|
207
|
+
apiBaseUrl: 'http://localhost',
|
|
208
|
+
});
|
|
209
|
+
await dbLead.connect({ agentId: 'db-lead', apiKey: 'sk3', apiBaseUrl: 'http://localhost' });
|
|
210
|
+
|
|
211
|
+
// Prime sends to rust-lead
|
|
212
|
+
await prime.push('rust-lead', 'Fix cant-lsp');
|
|
213
|
+
// Prime sends to db-lead
|
|
214
|
+
await prime.push('db-lead', 'Review schemas');
|
|
215
|
+
|
|
216
|
+
// Each agent only sees their own messages
|
|
217
|
+
const rustMessages = await rustLead.poll();
|
|
218
|
+
const dbMessages = await dbLead.poll();
|
|
219
|
+
|
|
220
|
+
expect(rustMessages).toHaveLength(1);
|
|
221
|
+
expect(rustMessages[0].content).toBe('Fix cant-lsp');
|
|
222
|
+
expect(dbMessages).toHaveLength(1);
|
|
223
|
+
expect(dbMessages[0].content).toBe('Review schemas');
|
|
224
|
+
|
|
225
|
+
await prime.disconnect();
|
|
226
|
+
await rustLead.disconnect();
|
|
227
|
+
await dbLead.disconnect();
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
});
|