@cleocode/core 2026.6.12 → 2026.6.13
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/conduit/local-transport.d.ts +12 -0
- package/dist/conduit/local-transport.d.ts.map +1 -1
- package/dist/conduit/local-transport.js +53 -23
- package/dist/conduit/local-transport.js.map +1 -1
- package/dist/crypto/credentials.d.ts +37 -0
- package/dist/crypto/credentials.d.ts.map +1 -1
- package/dist/crypto/credentials.js +110 -0
- package/dist/crypto/credentials.js.map +1 -1
- package/dist/dispatch/contracts/derive-output-contract.d.ts +86 -0
- package/dist/dispatch/contracts/derive-output-contract.d.ts.map +1 -0
- package/dist/dispatch/contracts/derive-output-contract.js +347 -0
- package/dist/dispatch/contracts/derive-output-contract.js.map +1 -0
- package/dist/dispatch/contracts/ensures-schema.d.ts +77 -0
- package/dist/dispatch/contracts/ensures-schema.d.ts.map +1 -0
- package/dist/dispatch/contracts/ensures-schema.js +91 -0
- package/dist/dispatch/contracts/ensures-schema.js.map +1 -0
- package/dist/dispatch/contracts/output-contracts.d.ts +27 -5
- package/dist/dispatch/contracts/output-contracts.d.ts.map +1 -1
- package/dist/dispatch/contracts/output-contracts.js +29 -6
- package/dist/dispatch/contracts/output-contracts.js.map +1 -1
- package/dist/docs/export-document.js +2294 -1250
- package/dist/docs/export-document.js.map +3 -3
- package/dist/go/driver.d.ts +132 -7
- package/dist/go/driver.d.ts.map +1 -1
- package/dist/go/driver.js +135 -10
- package/dist/go/driver.js.map +1 -1
- package/dist/go/index.d.ts +1 -1
- package/dist/go/index.d.ts.map +1 -1
- package/dist/go/index.js +1 -1
- package/dist/go/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +13 -10
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +11 -11
- package/dist/internal.js.map +1 -1
- package/dist/lifecycle/engine-ops.d.ts.map +1 -1
- package/dist/lifecycle/engine-ops.js +7 -4
- package/dist/lifecycle/engine-ops.js.map +1 -1
- package/dist/lifecycle/ivtr-loop.d.ts +108 -183
- package/dist/lifecycle/ivtr-loop.d.ts.map +1 -1
- package/dist/lifecycle/ivtr-loop.js +207 -522
- package/dist/lifecycle/ivtr-loop.js.map +1 -1
- package/dist/llm/auxiliary-fallback.js +1 -1
- package/dist/llm/auxiliary-fallback.js.map +1 -1
- package/dist/llm/cli-ops.d.ts +14 -1
- package/dist/llm/cli-ops.d.ts.map +1 -1
- package/dist/llm/cli-ops.js +41 -12
- package/dist/llm/cli-ops.js.map +1 -1
- package/dist/llm/concrete-session.d.ts.map +1 -1
- package/dist/llm/concrete-session.js +4 -0
- package/dist/llm/concrete-session.js.map +1 -1
- package/dist/llm/credentials.d.ts +35 -0
- package/dist/llm/credentials.d.ts.map +1 -1
- package/dist/llm/credentials.js +49 -0
- package/dist/llm/credentials.js.map +1 -1
- package/dist/llm/index.d.ts +4 -0
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +4 -0
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/pi/gondolin-loader.d.ts +241 -0
- package/dist/llm/pi/gondolin-loader.d.ts.map +1 -0
- package/dist/llm/pi/gondolin-loader.js +195 -0
- package/dist/llm/pi/gondolin-loader.js.map +1 -0
- package/dist/llm/pi/index.d.ts +28 -0
- package/dist/llm/pi/index.d.ts.map +1 -0
- package/dist/llm/pi/index.js +27 -0
- package/dist/llm/pi/index.js.map +1 -0
- package/dist/llm/pi/pi-agent-adapter.d.ts +135 -0
- package/dist/llm/pi/pi-agent-adapter.d.ts.map +1 -0
- package/dist/llm/pi/pi-agent-adapter.js +329 -0
- package/dist/llm/pi/pi-agent-adapter.js.map +1 -0
- package/dist/llm/pi/pi-errors.d.ts +161 -0
- package/dist/llm/pi/pi-errors.d.ts.map +1 -0
- package/dist/llm/pi/pi-errors.js +296 -0
- package/dist/llm/pi/pi-errors.js.map +1 -0
- package/dist/llm/pi/pi-execution-env.d.ts +286 -0
- package/dist/llm/pi/pi-execution-env.d.ts.map +1 -0
- package/dist/llm/pi/pi-execution-env.js +406 -0
- package/dist/llm/pi/pi-execution-env.js.map +1 -0
- package/dist/llm/pi/pi-gondolin-env.d.ts +227 -0
- package/dist/llm/pi/pi-gondolin-env.d.ts.map +1 -0
- package/dist/llm/pi/pi-gondolin-env.js +541 -0
- package/dist/llm/pi/pi-gondolin-env.js.map +1 -0
- package/dist/llm/pi/pi-session-storage.d.ts +154 -0
- package/dist/llm/pi/pi-session-storage.d.ts.map +1 -0
- package/dist/llm/pi/pi-session-storage.js +314 -0
- package/dist/llm/pi/pi-session-storage.js.map +1 -0
- package/dist/llm/pi/pi-stream-fn.d.ts +124 -0
- package/dist/llm/pi/pi-stream-fn.d.ts.map +1 -0
- package/dist/llm/pi/pi-stream-fn.js +504 -0
- package/dist/llm/pi/pi-stream-fn.js.map +1 -0
- package/dist/llm/pi/pi-tool-bridge.d.ts +99 -0
- package/dist/llm/pi/pi-tool-bridge.d.ts.map +1 -0
- package/dist/llm/pi/pi-tool-bridge.js +117 -0
- package/dist/llm/pi/pi-tool-bridge.js.map +1 -0
- package/dist/llm/pi/pi-types.d.ts +56 -0
- package/dist/llm/pi/pi-types.d.ts.map +1 -0
- package/dist/llm/pi/pi-types.js +16 -0
- package/dist/llm/pi/pi-types.js.map +1 -0
- package/dist/llm/pi/resolve-execution-env.d.ts +194 -0
- package/dist/llm/pi/resolve-execution-env.d.ts.map +1 -0
- package/dist/llm/pi/resolve-execution-env.js +132 -0
- package/dist/llm/pi/resolve-execution-env.js.map +1 -0
- package/dist/llm/plugin-facade.js +2253 -1151
- package/dist/llm/plugin-facade.js.map +3 -3
- package/dist/llm/role-executor.d.ts.map +1 -1
- package/dist/llm/role-executor.js +8 -5
- package/dist/llm/role-executor.js.map +1 -1
- package/dist/llm/role-resolver.d.ts +46 -14
- package/dist/llm/role-resolver.d.ts.map +1 -1
- package/dist/llm/role-resolver.js +178 -20
- package/dist/llm/role-resolver.js.map +1 -1
- package/dist/llm/sealed-credential.d.ts +125 -0
- package/dist/llm/sealed-credential.d.ts.map +1 -0
- package/dist/llm/sealed-credential.js +133 -0
- package/dist/llm/sealed-credential.js.map +1 -0
- package/dist/llm/session-factory.d.ts.map +1 -1
- package/dist/llm/session-factory.js +6 -2
- package/dist/llm/session-factory.js.map +1 -1
- package/dist/llm/system-key.d.ts +67 -0
- package/dist/llm/system-key.d.ts.map +1 -0
- package/dist/llm/system-key.js +87 -0
- package/dist/llm/system-key.js.map +1 -0
- package/dist/llm/system-of-use-registry.d.ts +119 -0
- package/dist/llm/system-of-use-registry.d.ts.map +1 -0
- package/dist/llm/system-of-use-registry.js +200 -0
- package/dist/llm/system-of-use-registry.js.map +1 -0
- package/dist/llm/system-resolver.d.ts +32 -10
- package/dist/llm/system-resolver.d.ts.map +1 -1
- package/dist/llm/system-resolver.js +70 -18
- package/dist/llm/system-resolver.js.map +1 -1
- package/dist/memory/brain-writer-thread.d.ts.map +1 -1
- package/dist/memory/brain-writer-thread.js +116 -11
- package/dist/memory/brain-writer-thread.js.map +1 -1
- package/dist/memory/claude-mem-migration.d.ts.map +1 -1
- package/dist/memory/claude-mem-migration.js +21 -0
- package/dist/memory/claude-mem-migration.js.map +1 -1
- package/dist/memory/graph-memory-bridge.d.ts.map +1 -1
- package/dist/memory/graph-memory-bridge.js +32 -25
- package/dist/memory/graph-memory-bridge.js.map +1 -1
- package/dist/memory/llm-backend-resolver.js +23 -5
- package/dist/memory/llm-backend-resolver.js.map +1 -1
- package/dist/nexus/migrations/M-split-graph.d.ts.map +1 -1
- package/dist/nexus/migrations/M-split-graph.js +60 -53
- package/dist/nexus/migrations/M-split-graph.js.map +1 -1
- package/dist/orchestrate/pivot.d.ts.map +1 -1
- package/dist/orchestrate/pivot.js +20 -2
- package/dist/orchestrate/pivot.js.map +1 -1
- package/dist/release/engine-ops.js +2 -2
- package/dist/release/engine-ops.js.map +1 -1
- package/dist/routing/capability-matrix.d.ts.map +1 -1
- package/dist/routing/capability-matrix.js +0 -57
- package/dist/routing/capability-matrix.js.map +1 -1
- package/dist/selfimprove/budget.d.ts +206 -0
- package/dist/selfimprove/budget.d.ts.map +1 -0
- package/dist/selfimprove/budget.js +203 -0
- package/dist/selfimprove/budget.js.map +1 -0
- package/dist/selfimprove/dhq-adapter.d.ts +105 -0
- package/dist/selfimprove/dhq-adapter.d.ts.map +1 -0
- package/dist/selfimprove/dhq-adapter.js +95 -0
- package/dist/selfimprove/dhq-adapter.js.map +1 -0
- package/dist/selfimprove/draft-pr.d.ts +129 -0
- package/dist/selfimprove/draft-pr.d.ts.map +1 -0
- package/dist/selfimprove/draft-pr.js +158 -0
- package/dist/selfimprove/draft-pr.js.map +1 -0
- package/dist/selfimprove/envelope-diff.d.ts +162 -0
- package/dist/selfimprove/envelope-diff.d.ts.map +1 -0
- package/dist/selfimprove/envelope-diff.js +321 -0
- package/dist/selfimprove/envelope-diff.js.map +1 -0
- package/dist/selfimprove/replay.d.ts +121 -0
- package/dist/selfimprove/replay.d.ts.map +1 -0
- package/dist/selfimprove/replay.js +109 -0
- package/dist/selfimprove/replay.js.map +1 -0
- package/dist/selfimprove/run-loop.d.ts +148 -0
- package/dist/selfimprove/run-loop.d.ts.map +1 -0
- package/dist/selfimprove/run-loop.js +240 -0
- package/dist/selfimprove/run-loop.js.map +1 -0
- package/dist/selfimprove/scenario.d.ts +145 -0
- package/dist/selfimprove/scenario.d.ts.map +1 -0
- package/dist/selfimprove/scenario.js +243 -0
- package/dist/selfimprove/scenario.js.map +1 -0
- package/dist/sentient/daemon-entry.js +5 -0
- package/dist/sentient/daemon-entry.js.map +1 -1
- package/dist/sentient/daemon.d.ts.map +1 -1
- package/dist/sentient/daemon.js +19 -0
- package/dist/sentient/daemon.js.map +1 -1
- package/dist/sentient/hygiene-scan.d.ts.map +1 -1
- package/dist/sentient/hygiene-scan.js +14 -6
- package/dist/sentient/hygiene-scan.js.map +1 -1
- package/dist/sessions/briefing.d.ts.map +1 -1
- package/dist/sessions/briefing.js +13 -9
- package/dist/sessions/briefing.js.map +1 -1
- package/dist/sessions/connection-session-handle.d.ts +161 -0
- package/dist/sessions/connection-session-handle.d.ts.map +1 -0
- package/dist/sessions/connection-session-handle.js +168 -0
- package/dist/sessions/connection-session-handle.js.map +1 -0
- package/dist/sessions/handoff.d.ts.map +1 -1
- package/dist/sessions/handoff.js +9 -0
- package/dist/sessions/handoff.js.map +1 -1
- package/dist/sessions/index.d.ts +4 -1
- package/dist/sessions/index.d.ts.map +1 -1
- package/dist/sessions/index.js +31 -1
- package/dist/sessions/index.js.map +1 -1
- package/dist/sessions/session-id.d.ts +34 -0
- package/dist/sessions/session-id.d.ts.map +1 -1
- package/dist/sessions/session-id.js +37 -0
- package/dist/sessions/session-id.js.map +1 -1
- package/dist/sessions/session-manifest-mirror.d.ts +115 -0
- package/dist/sessions/session-manifest-mirror.d.ts.map +1 -0
- package/dist/sessions/session-manifest-mirror.js +250 -0
- package/dist/sessions/session-manifest-mirror.js.map +1 -0
- package/dist/sessions/session-switch.d.ts.map +1 -1
- package/dist/sessions/session-switch.js +5 -2
- package/dist/sessions/session-switch.js.map +1 -1
- 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/db-helpers.d.ts.map +1 -1
- package/dist/store/db-helpers.js +2 -0
- package/dist/store/db-helpers.js.map +1 -1
- package/dist/store/dual-scope-db.d.ts.map +1 -1
- package/dist/store/dual-scope-db.js +87 -47
- package/dist/store/dual-scope-db.js.map +1 -1
- package/dist/store/pi-session-store.d.ts +151 -0
- package/dist/store/pi-session-store.d.ts.map +1 -0
- package/dist/store/pi-session-store.js +166 -0
- package/dist/store/pi-session-store.js.map +1 -0
- package/dist/store/safety-data-accessor.d.ts +1 -0
- package/dist/store/safety-data-accessor.d.ts.map +1 -1
- package/dist/store/safety-data-accessor.js +5 -0
- package/dist/store/safety-data-accessor.js.map +1 -1
- package/dist/store/schema/cleo-global/accounts.d.ts +357 -0
- package/dist/store/schema/cleo-global/accounts.d.ts.map +1 -0
- package/dist/store/schema/cleo-global/accounts.js +136 -0
- package/dist/store/schema/cleo-global/accounts.js.map +1 -0
- package/dist/store/schema/cleo-global/index.d.ts +2 -0
- package/dist/store/schema/cleo-global/index.d.ts.map +1 -1
- package/dist/store/schema/cleo-global/index.js +2 -0
- package/dist/store/schema/cleo-global/index.js.map +1 -1
- package/dist/store/schema/cleo-global/session-manifest.d.ts +217 -0
- package/dist/store/schema/cleo-global/session-manifest.d.ts.map +1 -0
- package/dist/store/schema/cleo-global/session-manifest.js +99 -0
- package/dist/store/schema/cleo-global/session-manifest.js.map +1 -0
- package/dist/store/schema/cleo-project/tasks-core.d.ts +16 -0
- package/dist/store/schema/cleo-project/tasks-core.d.ts.map +1 -1
- package/dist/store/schema/cleo-project/tasks-core.js +14 -0
- package/dist/store/schema/cleo-project/tasks-core.js.map +1 -1
- package/dist/store/schema/tasks.d.ts +16 -0
- package/dist/store/schema/tasks.d.ts.map +1 -1
- package/dist/store/schema/tasks.js +10 -0
- package/dist/store/schema/tasks.js.map +1 -1
- package/dist/store/selfimprove-dhq-schema.d.ts +284 -0
- package/dist/store/selfimprove-dhq-schema.d.ts.map +1 -0
- package/dist/store/selfimprove-dhq-schema.js +128 -0
- package/dist/store/selfimprove-dhq-schema.js.map +1 -0
- package/dist/store/selfimprove-dhq-store.d.ts +175 -0
- package/dist/store/selfimprove-dhq-store.d.ts.map +1 -0
- package/dist/store/selfimprove-dhq-store.js +182 -0
- package/dist/store/selfimprove-dhq-store.js.map +1 -0
- package/dist/store/session-store.d.ts +52 -13
- package/dist/store/session-store.d.ts.map +1 -1
- package/dist/store/session-store.js +71 -13
- package/dist/store/session-store.js.map +1 -1
- package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
- package/dist/store/sqlite-data-accessor.js +7 -0
- package/dist/store/sqlite-data-accessor.js.map +1 -1
- package/dist/store/umbrella-data-accessor.d.ts +1 -0
- package/dist/store/umbrella-data-accessor.d.ts.map +1 -1
- package/dist/store/umbrella-data-accessor.js +5 -0
- package/dist/store/umbrella-data-accessor.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/store/writer-lease-schema.d.ts +392 -0
- package/dist/store/writer-lease-schema.d.ts.map +1 -0
- package/dist/store/writer-lease-schema.js +142 -0
- package/dist/store/writer-lease-schema.js.map +1 -0
- package/dist/store/writer-lease.d.ts +368 -0
- package/dist/store/writer-lease.d.ts.map +1 -0
- package/dist/store/writer-lease.js +1002 -0
- package/dist/store/writer-lease.js.map +1 -0
- package/dist/tasks/backfill-child-projections.d.ts.map +1 -1
- package/dist/tasks/backfill-child-projections.js +14 -9
- package/dist/tasks/backfill-child-projections.js.map +1 -1
- package/dist/tasks/complete.d.ts.map +1 -1
- package/dist/tasks/complete.js +7 -10
- package/dist/tasks/complete.js.map +1 -1
- package/dist/tasks/duplicate-detector.d.ts.map +1 -1
- package/dist/tasks/duplicate-detector.js +14 -7
- package/dist/tasks/duplicate-detector.js.map +1 -1
- package/dist/telemetry/index.d.ts.map +1 -1
- package/dist/telemetry/index.js +10 -4
- package/dist/telemetry/index.js.map +1 -1
- package/dist/tools/agent-registry.d.ts +250 -0
- package/dist/tools/agent-registry.d.ts.map +1 -0
- package/dist/tools/agent-registry.js +292 -0
- package/dist/tools/agent-registry.js.map +1 -0
- package/dist/tools/agent-tool-families.d.ts +107 -0
- package/dist/tools/agent-tool-families.d.ts.map +1 -0
- package/dist/tools/agent-tool-families.js +527 -0
- package/dist/tools/agent-tool-families.js.map +1 -0
- package/dist/tools/browser-driver.d.ts +183 -0
- package/dist/tools/browser-driver.d.ts.map +1 -0
- package/dist/tools/browser-driver.js +242 -0
- package/dist/tools/browser-driver.js.map +1 -0
- package/dist/tools/builtin-agent-tools.d.ts +36 -0
- package/dist/tools/builtin-agent-tools.d.ts.map +1 -0
- package/dist/tools/builtin-agent-tools.js +150 -0
- package/dist/tools/builtin-agent-tools.js.map +1 -0
- package/dist/tools/dispatch.d.ts +370 -0
- package/dist/tools/dispatch.d.ts.map +1 -0
- package/dist/tools/dispatch.js +505 -0
- package/dist/tools/dispatch.js.map +1 -0
- package/dist/tools/env-scrub.d.ts +93 -0
- package/dist/tools/env-scrub.d.ts.map +1 -0
- package/dist/tools/env-scrub.js +176 -0
- package/dist/tools/env-scrub.js.map +1 -0
- package/dist/tools/fs.d.ts +30 -0
- package/dist/tools/fs.d.ts.map +1 -1
- package/dist/tools/fs.js +55 -2
- package/dist/tools/fs.js.map +1 -1
- package/dist/tools/guard.d.ts +15 -0
- package/dist/tools/guard.d.ts.map +1 -1
- package/dist/tools/guard.js +49 -12
- package/dist/tools/guard.js.map +1 -1
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +37 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/pty.d.ts +45 -0
- package/dist/tools/pty.d.ts.map +1 -0
- package/dist/tools/pty.js +166 -0
- package/dist/tools/pty.js.map +1 -0
- package/dist/tools/schema-gen.d.ts +57 -0
- package/dist/tools/schema-gen.d.ts.map +1 -0
- package/dist/tools/schema-gen.js +52 -0
- package/dist/tools/schema-gen.js.map +1 -0
- package/dist/tools/shell.d.ts +12 -0
- package/dist/tools/shell.d.ts.map +1 -1
- package/dist/tools/shell.js +14 -1
- package/dist/tools/shell.js.map +1 -1
- package/dist/tools/web-agent-tools.d.ts +72 -0
- package/dist/tools/web-agent-tools.d.ts.map +1 -0
- package/dist/tools/web-agent-tools.js +393 -0
- package/dist/tools/web-agent-tools.js.map +1 -0
- package/dist/tools/web-search-backends.d.ts +156 -0
- package/dist/tools/web-search-backends.d.ts.map +1 -0
- package/dist/tools/web-search-backends.js +290 -0
- package/dist/tools/web-search-backends.js.map +1 -0
- package/dist/validation/ops.d.ts +1 -15
- package/dist/validation/ops.d.ts.map +1 -1
- package/dist/validation/ops.js +0 -20
- package/dist/validation/ops.js.map +1 -1
- package/migrations/drizzle-cleo-global/20260607000000_t11891-writer-leases/migration.sql +55 -0
- package/migrations/drizzle-cleo-global/20260607010000_t11639-session-manifest/migration.sql +43 -0
- package/migrations/drizzle-cleo-global/20260608000000_t11899-pi-session-tree/migration.sql +52 -0
- package/migrations/drizzle-cleo-global/20260608010000_t11709-accounts-credential-pool/migration.sql +72 -0
- package/migrations/drizzle-cleo-global/20260608020000_t11889-selfimprove-dhq/migration.sql +64 -0
- package/migrations/drizzle-cleo-project/20260607000000_t11891-writer-leases/migration.sql +55 -0
- package/migrations/drizzle-cleo-project/20260607010000_t11639-session-parent/migration.sql +22 -0
- package/migrations/drizzle-cleo-project/20260608000000_t11899-pi-session-tree/migration.sql +52 -0
- package/migrations/drizzle-cleo-project/20260608020000_t11889-selfimprove-dhq/migration.sql +64 -0
- package/package.json +15 -13
- package/dist/lifecycle/chain-composition.d.ts +0 -30
- package/dist/lifecycle/chain-composition.d.ts.map +0 -1
- package/dist/lifecycle/chain-composition.js +0 -152
- package/dist/lifecycle/chain-composition.js.map +0 -1
- package/dist/lifecycle/chain-store.d.ts +0 -70
- package/dist/lifecycle/chain-store.d.ts.map +0 -1
- package/dist/lifecycle/chain-store.js +0 -246
- package/dist/lifecycle/chain-store.js.map +0 -1
- package/dist/lifecycle/default-chain.d.ts +0 -39
- package/dist/lifecycle/default-chain.d.ts.map +0 -1
- package/dist/lifecycle/default-chain.js +0 -176
- package/dist/lifecycle/default-chain.js.map +0 -1
- package/dist/lifecycle/tessera-engine.d.ts +0 -48
- package/dist/lifecycle/tessera-engine.d.ts.map +0 -1
- package/dist/lifecycle/tessera-engine.js +0 -249
- package/dist/lifecycle/tessera-engine.js.map +0 -1
- package/dist/validation/chain-validation.d.ts +0 -43
- package/dist/validation/chain-validation.d.ts.map +0 -1
- package/dist/validation/chain-validation.js +0 -146
- package/dist/validation/chain-validation.js.map +0 -1
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Draft-PR egress for the self-improvement loop (T11889 · T11889-C).
|
|
3
|
+
*
|
|
4
|
+
* On a detected regression the loop's ONLY outbound action is to open ONE DRAFT
|
|
5
|
+
* PR against the cleocode repo (ADR-065 — PRs target `main` via the Merge Queue;
|
|
6
|
+
* NEVER a direct push to `main`, NEVER an auto-merge, NEVER a publish). This module
|
|
7
|
+
* is the egress primitive, modeled on `propose-patch.ts` (the closest "agent
|
|
8
|
+
* proposes a fix PR" exemplar) with three self-dogfooding hardenings:
|
|
9
|
+
*
|
|
10
|
+
* 1. **`--draft` is ALWAYS appended** to `gh pr create` — the PR is never
|
|
11
|
+
* ready-for-merge; a human reviews and undrafts it.
|
|
12
|
+
* 2. **Dry-run is the DEFAULT.** `openDraftPr` returns the planned `steps[]`
|
|
13
|
+
* WITHOUT invoking git/gh unless `execute: true` is passed. So the loop is
|
|
14
|
+
* side-effect-free at egress by default.
|
|
15
|
+
* 3. **Branch `feat/T11889-selfimprove-<scenario>-<ts>`** — a feature branch
|
|
16
|
+
* (ADR-065); no main mutation path exists in this module.
|
|
17
|
+
*
|
|
18
|
+
* The live path wraps the git/gh shell-out in
|
|
19
|
+
* {@link "../sentient/skill-provenance.js".withProvenance}`('pr-generator', …)`
|
|
20
|
+
* (the established legal PR-cutting origin — the `SkillWriteOrigin` union is
|
|
21
|
+
* closed) and returns the `gh`-emitted PR URL so the caller can record it back
|
|
22
|
+
* into `selfimprove_dhq.pr_url` via the leased adapter.
|
|
23
|
+
*
|
|
24
|
+
* This module is import-time side-effect-free.
|
|
25
|
+
*
|
|
26
|
+
* @module @cleocode/core/selfimprove/draft-pr
|
|
27
|
+
* @epic T11889
|
|
28
|
+
* @task T11913
|
|
29
|
+
*/
|
|
30
|
+
import { execFileSync } from 'node:child_process';
|
|
31
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
32
|
+
import { resolve as resolvePath } from 'node:path';
|
|
33
|
+
import { cwd as processCwd } from 'node:process';
|
|
34
|
+
import { withProvenance } from '../sentient/skill-provenance.js';
|
|
35
|
+
/**
|
|
36
|
+
* The required Claude Code trailer every self-improvement PR body ends with.
|
|
37
|
+
* Matches the project PR-body convention.
|
|
38
|
+
*/
|
|
39
|
+
const PR_BODY_TRAILER = '\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)';
|
|
40
|
+
/** Default command runner — wraps `execFileSync` with stdout-piped semantics. */
|
|
41
|
+
const defaultRun = (file, args) => execFileSync(file, [...args], { stdio: 'pipe' }).toString('utf8');
|
|
42
|
+
/**
|
|
43
|
+
* Compute the feature branch name for a scenario's draft PR (ADR-065 feature
|
|
44
|
+
* branch — `feat/T11889-selfimprove-<scenario>-<ts>`). The timestamp is
|
|
45
|
+
* colon/dot-sanitized so it is a valid git ref.
|
|
46
|
+
*
|
|
47
|
+
* @param scenario - The scenario name.
|
|
48
|
+
* @param ts - The ISO timestamp string.
|
|
49
|
+
* @returns The branch name.
|
|
50
|
+
*/
|
|
51
|
+
export function draftPrBranchName(scenario, ts) {
|
|
52
|
+
const safeScenario = scenario.replace(/[^a-z0-9-]/gi, '-');
|
|
53
|
+
const safeTs = ts.replace(/[:.]/g, '-');
|
|
54
|
+
return `feat/T11889-selfimprove-${safeScenario}-${safeTs}`;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Open ONE DRAFT PR carrying a proposed self-improvement fix.
|
|
58
|
+
*
|
|
59
|
+
* DRY-RUN by default ({@link OpenDraftPrArgs.execute} falsey): returns the planned
|
|
60
|
+
* `steps[]` (the `gh pr create` step ALWAYS carries `--draft`) WITHOUT touching
|
|
61
|
+
* git/gh — so the loop is egress-side-effect-free unless `--execute` is threaded
|
|
62
|
+
* through. The live path (`execute: true`) cuts the feature branch, applies the
|
|
63
|
+
* diff, commits, pushes the FEATURE branch (NEVER `main`), and runs
|
|
64
|
+
* `gh pr create --base main --head <branch> --draft`, returning the PR URL.
|
|
65
|
+
*
|
|
66
|
+
* NEVER pushes `main`, NEVER auto-merges, NEVER publishes — the only egress is a
|
|
67
|
+
* draft PR against a feature branch.
|
|
68
|
+
*
|
|
69
|
+
* @param args - See {@link OpenDraftPrArgs}.
|
|
70
|
+
* @returns A {@link DraftPrResult} describing the outcome.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* // dry-run (default): no side effects, steps include `--draft`
|
|
75
|
+
* const plan = await openDraftPr({ scenario: 'x', diffPath: 'fix.patch', title, body });
|
|
76
|
+
* // live: cuts branch + opens draft PR
|
|
77
|
+
* const res = await openDraftPr({ scenario: 'x', diffPath: 'fix.patch', title, body, execute: true });
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export async function openDraftPr(args) {
|
|
81
|
+
const cwd = args.cwd ?? processCwd();
|
|
82
|
+
const run = args.run ?? defaultRun;
|
|
83
|
+
const execute = args.execute === true;
|
|
84
|
+
const isoNow = (args.timestamp ?? (() => new Date().toISOString()))();
|
|
85
|
+
const base = 'main';
|
|
86
|
+
const body = `${args.body}${PR_BODY_TRAILER}`;
|
|
87
|
+
const resolvedDiff = resolvePath(cwd, args.diffPath);
|
|
88
|
+
if (!existsSync(resolvedDiff)) {
|
|
89
|
+
return {
|
|
90
|
+
kind: 'error',
|
|
91
|
+
code: 'E_NOT_FOUND',
|
|
92
|
+
message: `Patch file not found at '${resolvedDiff}'`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const diffBytes = readFileSync(resolvedDiff, 'utf8');
|
|
96
|
+
if (diffBytes.length === 0) {
|
|
97
|
+
return {
|
|
98
|
+
kind: 'error',
|
|
99
|
+
code: 'E_PATCH_EMPTY',
|
|
100
|
+
message: `Patch file '${resolvedDiff}' is empty`,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (execute) {
|
|
104
|
+
try {
|
|
105
|
+
run('gh', ['--version']);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return {
|
|
109
|
+
kind: 'error',
|
|
110
|
+
code: 'E_GH_UNAVAILABLE',
|
|
111
|
+
message: 'gh CLI not found or not authenticated — install gh and run `gh auth login`',
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const branchName = draftPrBranchName(args.scenario, isoNow);
|
|
116
|
+
const steps = [
|
|
117
|
+
`git checkout -b ${branchName}`,
|
|
118
|
+
`git apply ${resolvedDiff}`,
|
|
119
|
+
'git add -A',
|
|
120
|
+
`git commit -m "fix(selfimprove): ${args.scenario} regression"`,
|
|
121
|
+
`git push -u origin ${branchName}`,
|
|
122
|
+
`gh pr create --base ${base} --head ${branchName} --draft --title "${args.title}" --body <stdin>`,
|
|
123
|
+
];
|
|
124
|
+
if (!execute) {
|
|
125
|
+
return { kind: 'dry-run', scenario: args.scenario, branchName, steps };
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
const prUrl = await withProvenance('pr-generator', () => {
|
|
129
|
+
run('git', ['checkout', '-b', branchName]);
|
|
130
|
+
run('git', ['apply', resolvedDiff]);
|
|
131
|
+
run('git', ['add', '-A']);
|
|
132
|
+
run('git', ['commit', '-m', `fix(selfimprove): ${args.scenario} regression`]);
|
|
133
|
+
run('git', ['push', '-u', 'origin', branchName]);
|
|
134
|
+
return run('gh', [
|
|
135
|
+
'pr',
|
|
136
|
+
'create',
|
|
137
|
+
'--base',
|
|
138
|
+
base,
|
|
139
|
+
'--head',
|
|
140
|
+
branchName,
|
|
141
|
+
'--draft',
|
|
142
|
+
'--title',
|
|
143
|
+
args.title,
|
|
144
|
+
'--body',
|
|
145
|
+
body,
|
|
146
|
+
]).trim();
|
|
147
|
+
});
|
|
148
|
+
return { kind: 'ok', scenario: args.scenario, branchName, prUrl };
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
return {
|
|
152
|
+
kind: 'error',
|
|
153
|
+
code: 'E_DRAFT_PR_FAILED',
|
|
154
|
+
message: `draft-pr failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=draft-pr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draft-pr.js","sourceRoot":"","sources":["../../src/selfimprove/draft-pr.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Envelope normalization + structural diff vs a golden (T11889-B).
|
|
3
|
+
*
|
|
4
|
+
* The self-improvement loop replays a scenario (see {@link "./replay.js"}) and
|
|
5
|
+
* diffs each captured envelope against the golden expected envelope. This module:
|
|
6
|
+
*
|
|
7
|
+
* 1. {@link normalizeEnvelope} — strips the VOLATILE `meta` fields so only
|
|
8
|
+
* deterministic structure is compared. Two classes are stripped:
|
|
9
|
+
* timing/trace fields (`timestamp`, `requestId`, `duration_ms` /
|
|
10
|
+
* `durationMs`) AND the per-run session-lineage UUIDs (`sessionId`,
|
|
11
|
+
* `originSessionId`, `executionSessionId`). The latter are non-deterministic:
|
|
12
|
+
* the live `Dispatcher` defaults `executionSessionId`/`originSessionId` to a
|
|
13
|
+
* fresh `randomUUID()` per request
|
|
14
|
+
* (`ensureRequestSessionLineage`), then stamps all three onto `response.meta`
|
|
15
|
+
* — so a real envelope carries UUIDs the hand-authored golden cannot know.
|
|
16
|
+
* Stripping them prevents a permanent phantom DHQ that self-fires on every
|
|
17
|
+
* clean run. (The runtime field is `meta` with snake_case `duration_ms`; the
|
|
18
|
+
* spec wording uses `_meta`/`durationMs`, so both spellings and both
|
|
19
|
+
* container keys are stripped defensively.)
|
|
20
|
+
* 2. {@link diffEnvelopes} — structurally compares the normalized envelope set
|
|
21
|
+
* against the golden, producing `{ regressions: DiffEntry[] }`. Zero
|
|
22
|
+
* regressions on a golden match; N on injected divergence. The `meta`
|
|
23
|
+
* container is compared as a SUBSET (only golden-declared meta keys are
|
|
24
|
+
* asserted) so deterministic-but-golden-absent infra keys
|
|
25
|
+
* (`specVersion`, `schemaVersion`, `transport`, `mvi`, `contextVersion`,
|
|
26
|
+
* `strict`, `x-cleo-transport`, …) stamped by the runtime are NOT phantom
|
|
27
|
+
* regressions. The semantic payload (`data`/`error`) is still compared by
|
|
28
|
+
* full key-union — a missing or extra payload key IS a regression.
|
|
29
|
+
* 3. {@link computeQuestionHash} — sha256 of the normalized regression
|
|
30
|
+
* signature (op coordinates + diff path set) so the SAME regression maps to
|
|
31
|
+
* the SAME open DHQ row (the idempotency partial-UNIQUE index in T11889-A).
|
|
32
|
+
*
|
|
33
|
+
* `extractFieldFromResult` from `@cleocode/lafs` is reused for targeted-field
|
|
34
|
+
* asserts where a full structural compare would be too brittle.
|
|
35
|
+
*
|
|
36
|
+
* This module is PURE — no DB, no native handle, no `cleo` mutation.
|
|
37
|
+
* Import-time side-effect-free.
|
|
38
|
+
*
|
|
39
|
+
* @module @cleocode/core/selfimprove/envelope-diff
|
|
40
|
+
* @epic T11889
|
|
41
|
+
* @task T11912
|
|
42
|
+
*/
|
|
43
|
+
import type { ReplayEnvelope } from './replay.js';
|
|
44
|
+
import type { GoldenEntry, ScenarioOp } from './scenario.js';
|
|
45
|
+
/**
|
|
46
|
+
* A normalized envelope: the replayed envelope with volatile `meta` fields removed.
|
|
47
|
+
*
|
|
48
|
+
* Structurally identical to {@link ReplayEnvelope} except every volatile
|
|
49
|
+
* meta field is absent. Stored as a plain JSON-serializable record so it can be
|
|
50
|
+
* compared deeply and hashed deterministically.
|
|
51
|
+
*/
|
|
52
|
+
export type NormalizedEnvelope = Record<string, unknown>;
|
|
53
|
+
/**
|
|
54
|
+
* A single structural difference between a normalized envelope and its golden.
|
|
55
|
+
*/
|
|
56
|
+
export interface DiffEntry {
|
|
57
|
+
/** Zero-based index of the op in the scenario whose envelope diverged. */
|
|
58
|
+
opIndex: number;
|
|
59
|
+
/** The op coordinate (`domain.operation`) for the diverging op. */
|
|
60
|
+
opCoord: string;
|
|
61
|
+
/** JSON-pointer-ish path to the diverging value (e.g. `data/tasks/0/id`). */
|
|
62
|
+
path: string;
|
|
63
|
+
/** The value found in the replayed (actual) normalized envelope. */
|
|
64
|
+
actual: unknown;
|
|
65
|
+
/** The value found in the golden (expected) envelope. */
|
|
66
|
+
expected: unknown;
|
|
67
|
+
}
|
|
68
|
+
/** The result of diffing a replayed envelope set against a golden. */
|
|
69
|
+
export interface EnvelopeDiffResult {
|
|
70
|
+
/** All structural regressions; empty ⇒ the replay matched the golden. */
|
|
71
|
+
regressions: DiffEntry[];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Strip the volatile `meta`/`_meta` fields from an envelope.
|
|
75
|
+
*
|
|
76
|
+
* Returns a NEW object; the input is not mutated. The volatile timing/trace fields
|
|
77
|
+
* AND the per-run session-lineage UUIDs (`sessionId`, `originSessionId`,
|
|
78
|
+
* `executionSessionId`) are removed — see {@link VOLATILE_META_FIELDS}. The stable
|
|
79
|
+
* meta remnant (`gateway`, `domain`, `operation`, `source`, …) is preserved so
|
|
80
|
+
* structural identity (which operation produced the envelope) still compares.
|
|
81
|
+
*
|
|
82
|
+
* Note: deterministic-but-golden-absent infra meta keys (`specVersion`,
|
|
83
|
+
* `transport`, …) are NOT stripped here — they are tolerated by the SUBSET compare
|
|
84
|
+
* in {@link diffEnvelopes} instead, so the strip set stays exactly the volatile
|
|
85
|
+
* fields and nothing semantic.
|
|
86
|
+
*
|
|
87
|
+
* @param envelope - The replayed envelope to normalize.
|
|
88
|
+
* @returns The normalized envelope with volatile meta fields removed.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* const norm = normalizeEnvelope(envelope);
|
|
93
|
+
* // norm.meta has no timestamp / requestId / duration_ms / *SessionId
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export declare function normalizeEnvelope(envelope: ReplayEnvelope): NormalizedEnvelope;
|
|
97
|
+
/**
|
|
98
|
+
* Diff a set of replayed envelopes against the golden envelope set.
|
|
99
|
+
*
|
|
100
|
+
* Normalizes each replayed envelope (volatile meta stripped) and structurally
|
|
101
|
+
* compares it against the positionally-aligned golden entry. The op coordinate
|
|
102
|
+
* for each `DiffEntry` is taken from `ops[opIndex]`. Callers MUST pass aligned
|
|
103
|
+
* arrays (`ops.length === replayed.length === golden.length`); the function
|
|
104
|
+
* diffs up to the shortest length and reports an extra regression for any length
|
|
105
|
+
* mismatch.
|
|
106
|
+
*
|
|
107
|
+
* The `meta` container is compared as a SUBSET (only golden-declared meta keys are
|
|
108
|
+
* asserted) so deterministic-but-golden-absent infra keys the runtime stamps
|
|
109
|
+
* (`specVersion`, `transport`, …) are not phantom regressions; the semantic
|
|
110
|
+
* payload (`data`/`error`) keeps a full key-union compare where an extra or
|
|
111
|
+
* missing key IS a regression. Together with the volatile-lineage strip in
|
|
112
|
+
* {@link normalizeEnvelope}, a real live envelope that matches the golden body
|
|
113
|
+
* yields ZERO regressions.
|
|
114
|
+
*
|
|
115
|
+
* @param ops - The scenario ops (for op-coordinate labelling).
|
|
116
|
+
* @param replayed - The captured envelopes, one per op (replay order).
|
|
117
|
+
* @param golden - The golden expected entries, positionally aligned with `ops`.
|
|
118
|
+
* @returns `{ regressions }` — empty when the replay matches the golden.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* const { regressions } = diffEnvelopes(scenario.ops, replayed, golden.envelopes);
|
|
123
|
+
* if (regressions.length === 0) { /* happy path — no DHQ, no PR *\/ }
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
export declare function diffEnvelopes(ops: ScenarioOp[], replayed: ReplayEnvelope[], golden: GoldenEntry[]): EnvelopeDiffResult;
|
|
127
|
+
/**
|
|
128
|
+
* Extract a targeted field from a normalized envelope's `data` payload.
|
|
129
|
+
*
|
|
130
|
+
* Reuses the LAFS `--field` extractor (`extractFieldFromResult`) for cases where
|
|
131
|
+
* a full structural diff is too brittle and only a specific field's value matters
|
|
132
|
+
* (e.g. assert `data.tasks[0].id` without comparing the whole envelope).
|
|
133
|
+
*
|
|
134
|
+
* @param normalized - A normalized envelope (from {@link normalizeEnvelope}).
|
|
135
|
+
* @param field - The field name to extract from the envelope's `data` payload.
|
|
136
|
+
* @returns The extracted value, or `undefined` when absent.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* const id = extractTargetedField(normalizeEnvelope(env), 'id');
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export declare function extractTargetedField(normalized: NormalizedEnvelope, field: string): unknown;
|
|
144
|
+
/**
|
|
145
|
+
* Compute the sha256 `question_hash` of a diff result's normalized signature.
|
|
146
|
+
*
|
|
147
|
+
* The hash is over the {@link regressionSignature} (op coordinates + diff path
|
|
148
|
+
* set), so it is stable across runs of the SAME regression and feeds the
|
|
149
|
+
* idempotency partial-UNIQUE index (`ux_selfimprove_dhq_open`, T11889-A): one
|
|
150
|
+
* open DHQ row per `question_hash`. An empty regression set hashes the empty
|
|
151
|
+
* signature deterministically (callers should not persist on the happy path).
|
|
152
|
+
*
|
|
153
|
+
* @param result - The diff result whose regressions are hashed.
|
|
154
|
+
* @returns The lowercase hex sha256 of the regression signature.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```ts
|
|
158
|
+
* const hash = computeQuestionHash(diffEnvelopes(ops, replayed, golden));
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
export declare function computeQuestionHash(result: EnvelopeDiffResult): string;
|
|
162
|
+
//# sourceMappingURL=envelope-diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope-diff.d.ts","sourceRoot":"","sources":["../../src/selfimprove/envelope-diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAmD7D;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEzD;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,MAAM,EAAE,OAAO,CAAC;IAChB,yDAAyD;IACzD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,sEAAsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,WAAW,EAAE,SAAS,EAAE,CAAC;CAC1B;AAeD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,cAAc,GAAG,kBAAkB,CAY9E;AAkED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,UAAU,EAAE,EACjB,QAAQ,EAAE,cAAc,EAAE,EAC1B,MAAM,EAAE,WAAW,EAAE,GACpB,kBAAkB,CAqCpB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAO3F;AAqBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAGtE"}
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Envelope normalization + structural diff vs a golden (T11889-B).
|
|
3
|
+
*
|
|
4
|
+
* The self-improvement loop replays a scenario (see {@link "./replay.js"}) and
|
|
5
|
+
* diffs each captured envelope against the golden expected envelope. This module:
|
|
6
|
+
*
|
|
7
|
+
* 1. {@link normalizeEnvelope} — strips the VOLATILE `meta` fields so only
|
|
8
|
+
* deterministic structure is compared. Two classes are stripped:
|
|
9
|
+
* timing/trace fields (`timestamp`, `requestId`, `duration_ms` /
|
|
10
|
+
* `durationMs`) AND the per-run session-lineage UUIDs (`sessionId`,
|
|
11
|
+
* `originSessionId`, `executionSessionId`). The latter are non-deterministic:
|
|
12
|
+
* the live `Dispatcher` defaults `executionSessionId`/`originSessionId` to a
|
|
13
|
+
* fresh `randomUUID()` per request
|
|
14
|
+
* (`ensureRequestSessionLineage`), then stamps all three onto `response.meta`
|
|
15
|
+
* — so a real envelope carries UUIDs the hand-authored golden cannot know.
|
|
16
|
+
* Stripping them prevents a permanent phantom DHQ that self-fires on every
|
|
17
|
+
* clean run. (The runtime field is `meta` with snake_case `duration_ms`; the
|
|
18
|
+
* spec wording uses `_meta`/`durationMs`, so both spellings and both
|
|
19
|
+
* container keys are stripped defensively.)
|
|
20
|
+
* 2. {@link diffEnvelopes} — structurally compares the normalized envelope set
|
|
21
|
+
* against the golden, producing `{ regressions: DiffEntry[] }`. Zero
|
|
22
|
+
* regressions on a golden match; N on injected divergence. The `meta`
|
|
23
|
+
* container is compared as a SUBSET (only golden-declared meta keys are
|
|
24
|
+
* asserted) so deterministic-but-golden-absent infra keys
|
|
25
|
+
* (`specVersion`, `schemaVersion`, `transport`, `mvi`, `contextVersion`,
|
|
26
|
+
* `strict`, `x-cleo-transport`, …) stamped by the runtime are NOT phantom
|
|
27
|
+
* regressions. The semantic payload (`data`/`error`) is still compared by
|
|
28
|
+
* full key-union — a missing or extra payload key IS a regression.
|
|
29
|
+
* 3. {@link computeQuestionHash} — sha256 of the normalized regression
|
|
30
|
+
* signature (op coordinates + diff path set) so the SAME regression maps to
|
|
31
|
+
* the SAME open DHQ row (the idempotency partial-UNIQUE index in T11889-A).
|
|
32
|
+
*
|
|
33
|
+
* `extractFieldFromResult` from `@cleocode/lafs` is reused for targeted-field
|
|
34
|
+
* asserts where a full structural compare would be too brittle.
|
|
35
|
+
*
|
|
36
|
+
* This module is PURE — no DB, no native handle, no `cleo` mutation.
|
|
37
|
+
* Import-time side-effect-free.
|
|
38
|
+
*
|
|
39
|
+
* @module @cleocode/core/selfimprove/envelope-diff
|
|
40
|
+
* @epic T11889
|
|
41
|
+
* @task T11912
|
|
42
|
+
*/
|
|
43
|
+
import { createHash } from 'node:crypto';
|
|
44
|
+
import { extractFieldFromResult } from '@cleocode/lafs';
|
|
45
|
+
/**
|
|
46
|
+
* Volatile envelope-meta field names stripped before diffing.
|
|
47
|
+
*
|
|
48
|
+
* Two classes, both non-deterministic per run:
|
|
49
|
+
*
|
|
50
|
+
* - Timing / trace: `timestamp`, `requestId`, `duration_ms` (the runtime
|
|
51
|
+
* canonical, snake_case) and `durationMs` (the spec wording — stripped so a
|
|
52
|
+
* golden authored either way normalizes identically).
|
|
53
|
+
* - Session lineage: `sessionId`, `originSessionId`, `executionSessionId`. The
|
|
54
|
+
* live `Dispatcher` stamps these onto `response.meta`
|
|
55
|
+
* after defaulting `executionSessionId`/`originSessionId` to a fresh
|
|
56
|
+
* `randomUUID()` per request (`ensureRequestSessionLineage`). They are
|
|
57
|
+
* per-run UUIDs the hand-authored golden cannot encode; leaving them in would
|
|
58
|
+
* make every clean replay emit a phantom regression at
|
|
59
|
+
* `meta/executionSessionId` and `meta/originSessionId`, and because the
|
|
60
|
+
* regression signature hashes only `opCoord@path` (constant), the resulting
|
|
61
|
+
* `question_hash` would be STABLE — a permanent self-firing DHQ. Stripping
|
|
62
|
+
* them keeps an identical-but-for-lineage envelope a non-regression.
|
|
63
|
+
*/
|
|
64
|
+
const VOLATILE_META_FIELDS = [
|
|
65
|
+
'timestamp',
|
|
66
|
+
'requestId',
|
|
67
|
+
'duration_ms',
|
|
68
|
+
'durationMs',
|
|
69
|
+
'sessionId',
|
|
70
|
+
'originSessionId',
|
|
71
|
+
'executionSessionId',
|
|
72
|
+
];
|
|
73
|
+
/**
|
|
74
|
+
* Meta container keys whose volatile fields are stripped, and whose remaining
|
|
75
|
+
* keys are compared as a SUBSET (see {@link diffEnvelopes}).
|
|
76
|
+
*
|
|
77
|
+
* Runtime envelopes use `meta`; `_meta` is stripped too for defensive parity with
|
|
78
|
+
* the gateway envelope shape.
|
|
79
|
+
*/
|
|
80
|
+
const META_CONTAINER_KEYS = ['meta', '_meta'];
|
|
81
|
+
/**
|
|
82
|
+
* Top-level envelope keys whose value is a meta container.
|
|
83
|
+
*
|
|
84
|
+
* The structural diff treats these containers as a SUBSET compare (golden-declared
|
|
85
|
+
* keys only) so deterministic-but-golden-absent infra keys the runtime stamps
|
|
86
|
+
* (`specVersion`, `schemaVersion`, `transport`, `mvi`, `contextVersion`, `strict`,
|
|
87
|
+
* `x-cleo-transport`, …) are not phantom regressions. Everything else (`data`,
|
|
88
|
+
* `error`, …) keeps a full key-union compare.
|
|
89
|
+
*/
|
|
90
|
+
const SUBSET_COMPARE_KEYS = new Set(META_CONTAINER_KEYS);
|
|
91
|
+
/**
|
|
92
|
+
* Deep-clone a JSON-serializable value via structured round-trip.
|
|
93
|
+
*
|
|
94
|
+
* Replay envelopes are JSON-shaped (they round-trip through transports), so a
|
|
95
|
+
* JSON clone is safe and avoids mutating the caller's object during normalization.
|
|
96
|
+
*
|
|
97
|
+
* @param value - The value to clone.
|
|
98
|
+
* @returns A deep copy.
|
|
99
|
+
*/
|
|
100
|
+
function jsonClone(value) {
|
|
101
|
+
return JSON.parse(JSON.stringify(value));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Strip the volatile `meta`/`_meta` fields from an envelope.
|
|
105
|
+
*
|
|
106
|
+
* Returns a NEW object; the input is not mutated. The volatile timing/trace fields
|
|
107
|
+
* AND the per-run session-lineage UUIDs (`sessionId`, `originSessionId`,
|
|
108
|
+
* `executionSessionId`) are removed — see {@link VOLATILE_META_FIELDS}. The stable
|
|
109
|
+
* meta remnant (`gateway`, `domain`, `operation`, `source`, …) is preserved so
|
|
110
|
+
* structural identity (which operation produced the envelope) still compares.
|
|
111
|
+
*
|
|
112
|
+
* Note: deterministic-but-golden-absent infra meta keys (`specVersion`,
|
|
113
|
+
* `transport`, …) are NOT stripped here — they are tolerated by the SUBSET compare
|
|
114
|
+
* in {@link diffEnvelopes} instead, so the strip set stays exactly the volatile
|
|
115
|
+
* fields and nothing semantic.
|
|
116
|
+
*
|
|
117
|
+
* @param envelope - The replayed envelope to normalize.
|
|
118
|
+
* @returns The normalized envelope with volatile meta fields removed.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* const norm = normalizeEnvelope(envelope);
|
|
123
|
+
* // norm.meta has no timestamp / requestId / duration_ms / *SessionId
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
export function normalizeEnvelope(envelope) {
|
|
127
|
+
const cloned = jsonClone(envelope);
|
|
128
|
+
for (const containerKey of META_CONTAINER_KEYS) {
|
|
129
|
+
const container = cloned[containerKey];
|
|
130
|
+
if (container !== null && typeof container === 'object' && !Array.isArray(container)) {
|
|
131
|
+
const meta = container;
|
|
132
|
+
for (const field of VOLATILE_META_FIELDS) {
|
|
133
|
+
delete meta[field];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return cloned;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Recursively collect structural differences between two JSON values.
|
|
141
|
+
*
|
|
142
|
+
* Walks both trees in parallel, recording a `{ path, actual, expected }` for each
|
|
143
|
+
* leaf-level divergence. Object key sets and array lengths are compared; the path
|
|
144
|
+
* uses `/`-delimited segments.
|
|
145
|
+
*
|
|
146
|
+
* Key-set semantics depend on the container:
|
|
147
|
+
* - **Subset** (`subset === true`): only the EXPECTED (golden) object's keys are
|
|
148
|
+
* compared. Extra keys present in `actual` but absent from the golden are
|
|
149
|
+
* ignored. Applied to the `meta` container (see {@link SUBSET_COMPARE_KEYS}) so
|
|
150
|
+
* deterministic infra meta keys the runtime stamps but the golden omits are not
|
|
151
|
+
* phantom regressions.
|
|
152
|
+
* - **Full union** (`subset === false`, the default): keys from BOTH objects are
|
|
153
|
+
* compared, so an extra OR missing key is a regression. Applied to the semantic
|
|
154
|
+
* payload (`data`/`error`).
|
|
155
|
+
*
|
|
156
|
+
* A child whose top-level key is in {@link SUBSET_COMPARE_KEYS} (i.e. `meta`)
|
|
157
|
+
* switches its subtree into subset mode.
|
|
158
|
+
*
|
|
159
|
+
* @param actual - The actual (normalized replay) value.
|
|
160
|
+
* @param expected - The expected (golden) value.
|
|
161
|
+
* @param path - Accumulated path prefix.
|
|
162
|
+
* @param out - Mutable accumulator of `{ path, actual, expected }` tuples.
|
|
163
|
+
* @param subset - When `true`, compare only the golden-declared keys at this node.
|
|
164
|
+
*/
|
|
165
|
+
function collectStructuralDiffs(actual, expected, path, out, subset = false) {
|
|
166
|
+
if (actual === expected)
|
|
167
|
+
return;
|
|
168
|
+
const bothObjects = actual !== null &&
|
|
169
|
+
expected !== null &&
|
|
170
|
+
typeof actual === 'object' &&
|
|
171
|
+
typeof expected === 'object' &&
|
|
172
|
+
Array.isArray(actual) === Array.isArray(expected);
|
|
173
|
+
if (!bothObjects) {
|
|
174
|
+
out.push({ path, actual, expected });
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const actualRec = actual;
|
|
178
|
+
const expectedRec = expected;
|
|
179
|
+
// Subset mode (meta container): compare ONLY the golden's keys, so extra
|
|
180
|
+
// deterministic runtime meta keys are not regressions. Full mode: union both
|
|
181
|
+
// key sets, so an extra OR missing payload key IS a regression.
|
|
182
|
+
const keys = subset
|
|
183
|
+
? new Set(Object.keys(expectedRec))
|
|
184
|
+
: new Set([...Object.keys(actualRec), ...Object.keys(expectedRec)]);
|
|
185
|
+
for (const key of keys) {
|
|
186
|
+
const childPath = path === '' ? key : `${path}/${key}`;
|
|
187
|
+
// A `meta`/`_meta` container's subtree is compared as a subset; never widen
|
|
188
|
+
// back to full-union once inside a subset container.
|
|
189
|
+
const childSubset = subset || SUBSET_COMPARE_KEYS.has(key);
|
|
190
|
+
collectStructuralDiffs(actualRec[key], expectedRec[key], childPath, out, childSubset);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Diff a set of replayed envelopes against the golden envelope set.
|
|
195
|
+
*
|
|
196
|
+
* Normalizes each replayed envelope (volatile meta stripped) and structurally
|
|
197
|
+
* compares it against the positionally-aligned golden entry. The op coordinate
|
|
198
|
+
* for each `DiffEntry` is taken from `ops[opIndex]`. Callers MUST pass aligned
|
|
199
|
+
* arrays (`ops.length === replayed.length === golden.length`); the function
|
|
200
|
+
* diffs up to the shortest length and reports an extra regression for any length
|
|
201
|
+
* mismatch.
|
|
202
|
+
*
|
|
203
|
+
* The `meta` container is compared as a SUBSET (only golden-declared meta keys are
|
|
204
|
+
* asserted) so deterministic-but-golden-absent infra keys the runtime stamps
|
|
205
|
+
* (`specVersion`, `transport`, …) are not phantom regressions; the semantic
|
|
206
|
+
* payload (`data`/`error`) keeps a full key-union compare where an extra or
|
|
207
|
+
* missing key IS a regression. Together with the volatile-lineage strip in
|
|
208
|
+
* {@link normalizeEnvelope}, a real live envelope that matches the golden body
|
|
209
|
+
* yields ZERO regressions.
|
|
210
|
+
*
|
|
211
|
+
* @param ops - The scenario ops (for op-coordinate labelling).
|
|
212
|
+
* @param replayed - The captured envelopes, one per op (replay order).
|
|
213
|
+
* @param golden - The golden expected entries, positionally aligned with `ops`.
|
|
214
|
+
* @returns `{ regressions }` — empty when the replay matches the golden.
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```ts
|
|
218
|
+
* const { regressions } = diffEnvelopes(scenario.ops, replayed, golden.envelopes);
|
|
219
|
+
* if (regressions.length === 0) { /* happy path — no DHQ, no PR *\/ }
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
export function diffEnvelopes(ops, replayed, golden) {
|
|
223
|
+
const regressions = [];
|
|
224
|
+
const n = Math.min(ops.length, replayed.length, golden.length);
|
|
225
|
+
for (let i = 0; i < n; i++) {
|
|
226
|
+
const op = ops[i];
|
|
227
|
+
const replayEnvelope = replayed[i];
|
|
228
|
+
const goldenEntry = golden[i];
|
|
229
|
+
if (op === undefined || replayEnvelope === undefined || goldenEntry === undefined)
|
|
230
|
+
continue;
|
|
231
|
+
const opCoord = `${op.domain}.${op.operation}`;
|
|
232
|
+
const normalized = normalizeEnvelope(replayEnvelope);
|
|
233
|
+
const leafDiffs = [];
|
|
234
|
+
collectStructuralDiffs(normalized, goldenEntry, '', leafDiffs);
|
|
235
|
+
for (const d of leafDiffs) {
|
|
236
|
+
regressions.push({
|
|
237
|
+
opIndex: i,
|
|
238
|
+
opCoord,
|
|
239
|
+
path: d.path,
|
|
240
|
+
actual: d.actual,
|
|
241
|
+
expected: d.expected,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (replayed.length !== golden.length) {
|
|
246
|
+
regressions.push({
|
|
247
|
+
opIndex: -1,
|
|
248
|
+
opCoord: '<count>',
|
|
249
|
+
path: 'envelopes/length',
|
|
250
|
+
actual: replayed.length,
|
|
251
|
+
expected: golden.length,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
return { regressions };
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Extract a targeted field from a normalized envelope's `data` payload.
|
|
258
|
+
*
|
|
259
|
+
* Reuses the LAFS `--field` extractor (`extractFieldFromResult`) for cases where
|
|
260
|
+
* a full structural diff is too brittle and only a specific field's value matters
|
|
261
|
+
* (e.g. assert `data.tasks[0].id` without comparing the whole envelope).
|
|
262
|
+
*
|
|
263
|
+
* @param normalized - A normalized envelope (from {@link normalizeEnvelope}).
|
|
264
|
+
* @param field - The field name to extract from the envelope's `data` payload.
|
|
265
|
+
* @returns The extracted value, or `undefined` when absent.
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* ```ts
|
|
269
|
+
* const id = extractTargetedField(normalizeEnvelope(env), 'id');
|
|
270
|
+
* ```
|
|
271
|
+
*/
|
|
272
|
+
export function extractTargetedField(normalized, field) {
|
|
273
|
+
const data = normalized.data;
|
|
274
|
+
if (data === undefined || data === null)
|
|
275
|
+
return undefined;
|
|
276
|
+
// The LAFS extractor operates on a result value (object | array | null); the
|
|
277
|
+
// dispatch envelope's `data` is the equivalent payload.
|
|
278
|
+
if (typeof data !== 'object')
|
|
279
|
+
return undefined;
|
|
280
|
+
return extractFieldFromResult(data, field);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Build the deterministic regression signature for a diff result.
|
|
284
|
+
*
|
|
285
|
+
* The signature is the SORTED set of `{ opCoord, path }` pairs from the
|
|
286
|
+
* regressions — NOT the values. Two runs that diverge at the same op coordinates
|
|
287
|
+
* on the same paths produce the same signature (and thus the same
|
|
288
|
+
* {@link computeQuestionHash}), so a repeated regression maps to ONE open DHQ row
|
|
289
|
+
* rather than spamming duplicates. Actual/expected values are intentionally
|
|
290
|
+
* excluded so transient value noise does not fork the hash.
|
|
291
|
+
*
|
|
292
|
+
* @param regressions - The diff regressions.
|
|
293
|
+
* @returns A stable, sorted signature string.
|
|
294
|
+
*/
|
|
295
|
+
function regressionSignature(regressions) {
|
|
296
|
+
const pairs = regressions.map((r) => `${r.opCoord}@${r.path}`);
|
|
297
|
+
pairs.sort();
|
|
298
|
+
return pairs.join('\n');
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Compute the sha256 `question_hash` of a diff result's normalized signature.
|
|
302
|
+
*
|
|
303
|
+
* The hash is over the {@link regressionSignature} (op coordinates + diff path
|
|
304
|
+
* set), so it is stable across runs of the SAME regression and feeds the
|
|
305
|
+
* idempotency partial-UNIQUE index (`ux_selfimprove_dhq_open`, T11889-A): one
|
|
306
|
+
* open DHQ row per `question_hash`. An empty regression set hashes the empty
|
|
307
|
+
* signature deterministically (callers should not persist on the happy path).
|
|
308
|
+
*
|
|
309
|
+
* @param result - The diff result whose regressions are hashed.
|
|
310
|
+
* @returns The lowercase hex sha256 of the regression signature.
|
|
311
|
+
*
|
|
312
|
+
* @example
|
|
313
|
+
* ```ts
|
|
314
|
+
* const hash = computeQuestionHash(diffEnvelopes(ops, replayed, golden));
|
|
315
|
+
* ```
|
|
316
|
+
*/
|
|
317
|
+
export function computeQuestionHash(result) {
|
|
318
|
+
const signature = regressionSignature(result.regressions);
|
|
319
|
+
return createHash('sha256').update(signature, 'utf-8').digest('hex');
|
|
320
|
+
}
|
|
321
|
+
//# sourceMappingURL=envelope-diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope-diff.js","sourceRoot":"","sources":["../../src/selfimprove/envelope-diff.ts"],"names":[],"mappings":"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"}
|