Finish M14 conversation engine ownership review fixes#46
Conversation
There was a problem hiding this comment.
💡 Codex Review
ConversationEngineConfig now accepts memoryDir, but turn execution still hardcodes stateRoot/memory, so any caller that sets a custom memory location will silently read/write memories in the wrong directory. This breaks isolation for multi-workspace embeddings and makes the new config field ineffective; the runtime should consume the normalized memoryDir instead of recomputing it.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| memoryDir, | ||
| traceDir, | ||
| workspaceId: config.workspaceId, | ||
| apiKeyPresent: config.apiKeyPresent ?? Boolean(config.apiKey), |
There was a problem hiding this comment.
Resolve apiKeyPresent from real credential availability
Defaulting apiKeyPresent to Boolean(config.apiKey) treats users with credential-store or environment credentials as unauthenticated unless they pass apiKeyPresent manually. New sessions created via createConversationEngine can therefore get the “No provider credential detected” bootstrap message despite valid credentials, which is misleading and alters session UX/state unless callers know to override this flag.
Useful? React with 👍 / 👎.
Summary
Details
Verification
$ node scripts/clean-dist.mjs
$ tsc -p src/web/tsconfig.json --noEmit && vite build --config src/web/vite.config.ts
vite v8.0.3 building client environment for production...
�[2K
transforming...✓ 1250 modules transformed.
rendering chunks...
computing gzip size...
dist/src/web/index.html 0.61 kB │ gzip: 0.36 kB
dist/src/web/assets/favicon-CT3fFV8g.jpeg 35.61 kB
dist/src/web/assets/codicon-ngg6Pgfi.ttf 121.97 kB
dist/src/web/assets/editor.worker-DWlYVeeX.js 279.97 kB
dist/src/web/assets/json.worker-CCzEOxDx.js 409.23 kB
dist/src/web/assets/index-CYd4sslC.css 54.78 kB │ gzip: 11.23 kB
dist/src/web/assets/editor-Br_kD0ds.css 145.98 kB │ gzip: 22.61 kB
dist/src/web/assets/chunk-DECur_0Z.js 0.68 kB │ gzip: 0.41 kB
dist/src/web/assets/javascript-6bpvthJD.js 0.68 kB │ gzip: 0.41 kB
dist/src/web/assets/go-CaScRb1i.js 2.35 kB │ gzip: 1.11 kB
dist/src/web/assets/shell-Dgo0Jcwc.js 2.81 kB │ gzip: 1.16 kB
dist/src/web/assets/java-_yhwA1y2.js 2.90 kB │ gzip: 1.34 kB
dist/src/web/assets/python-kY56oSAs.js 3.45 kB │ gzip: 1.42 kB
dist/src/web/assets/yaml-B3q_s4Yl.js 3.54 kB │ gzip: 1.29 kB
dist/src/web/assets/markdown-wO2PN3EC.js 3.63 kB │ gzip: 1.34 kB
dist/src/web/assets/rust-BOwv_5ZO.js 3.66 kB │ gzip: 1.80 kB
dist/src/web/assets/css-ejLFi3lO.js 4.36 kB │ gzip: 1.33 kB
dist/src/web/assets/html-Cft6PSNf.js 4.51 kB │ gzip: 1.15 kB
dist/src/web/assets/typescript-KZ9-zMZK.js 4.67 kB │ gzip: 1.94 kB
dist/src/web/assets/scss-Cdfe_FFe.js 6.26 kB │ gzip: 1.70 kB
dist/src/web/assets/MonacoDiffViewer-tvXEFlgh.js 8.81 kB │ gzip: 3.35 kB
dist/src/web/assets/jsonMode-yXXGoTnD.js 41.30 kB │ gzip: 11.60 kB
dist/src/web/assets/index-WQmlPSO9.js 517.00 kB │ gzip: 152.89 kB
dist/src/web/assets/editor.api2-DRhrSk9h.js 3,626.66 kB │ gzip: 926.50 kB
✓ built in 609ms
$ vitest run src/tests/unit
�[1m�[46m RUN �[49m�[22m �[36mv4.1.2 �[39m�[90m/Users/roackb2/Studio/projects/ProjectHeddle/heddle�[39m
�[32m✓�[39m src/tests/unit/core/import-boundaries.test.ts �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 16�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/tui/tui-frame-recorder.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 7�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/conversation-activity.test.ts �[2m(�[22m�[2m8 tests�[22m�[2m)�[22m�[32m 3�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/tools/run-shell.command.test.ts �[2m(�[22m�[2m10 tests�[22m�[2m)�[22m�[32m 6�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/project-approval-rules.test.ts �[2m(�[22m�[2m12 tests�[22m�[2m)�[22m�[32m 24�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/conversation-engine.test.ts �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[32m 48�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/openai-oauth.test.ts �[2m(�[22m�[2m10 tests�[22m�[2m)�[22m�[32m 23�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/chat-turn-host-bridge.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 4�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/tui/chat-drift.test.ts �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 8�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/slash-command-modules.test.ts �[2m(�[22m�[2m9 tests�[22m�[2m)�[22m�[32m 7�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/chat/chat-format.test.ts �[2m(�[22m�[2m8 tests�[22m�[2m)�[22m�[32m 22�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/chat/chat-session-lease.test.ts �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[32m 4�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/chat-turn-persistence.test.ts �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 8�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/tui/local-commands.test.ts �[2m(�[22m�[2m34 tests�[22m�[2m)�[22m�[32m 25�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/slash-commands.test.ts �[2m(�[22m�[2m13 tests�[22m�[2m)�[22m�[32m 11�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/tools/core-utils.test.ts �[2m(�[22m�[2m9 tests�[22m�[2m)�[22m�[32m 5�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/approval-policy-chain.test.ts �[2m(�[22m�[2m8 tests�[22m�[2m)�[22m�[32m 5�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/llm-factory.test.ts �[2m(�[22m�[2m7 tests�[22m�[2m)�[22m�[32m 4�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/eval-schema.test.ts �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 11�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/diff-domain.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 5�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/chat-turn-memory-maintenance.test.ts �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 5�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/control-plane/control-plane-host-surface.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 5�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/tui/eval-cli.test.ts �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 4�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/report-writer.test.ts �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 3�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/trace-format.test.ts �[2m(�[22m�[2m9 tests�[22m�[2m)�[22m�[32m 3�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/tui/chat-activity-format.test.ts �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[32m 6�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/git-artifacts.test.ts �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[33m 777�[2mms�[22m�[39m
�[33m�[2m✓�[22m�[39m includes untracked files in changed files and patch artifacts �[33m 773�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/chat-turn-runtime.test.ts �[2m(�[22m�[2m8 tests�[22m�[2m)�[22m�[32m 20�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/control-plane/control-plane-turn-review.test.ts �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 17�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/tui/file-mentions.test.ts �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 2�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/trace-analyzer.test.ts �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 3�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/control-plane/control-plane-routes.test.ts �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 8�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/trace-summarizers.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 2�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/agent-runner.test.ts �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 2�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/heartbeat-lucid.test.ts �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 3�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/core/heartbeat-views.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 3�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/tui/heartbeat-cli.test.ts �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[32m 5�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/tui/prompt-input.test.tsx �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[32m 2�[2mms�[22m�[39m
�[32m✓�[39m src/tests/unit/tui/daemon.test.ts �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[32m 2�[2mms�[22m�[39m
�[2m Test Files �[22m �[1m�[32m39 passed�[39m�[22m�[90m (39)�[39m
�[2m Tests �[22m �[1m�[32m236 passed�[39m�[22m�[90m (236)�[39m
�[2m Start at �[22m 16:18:53
�[2m Duration �[22m 1.48s�[2m (transform 5.19s, setup 0ms, import 9.72s, tests 1.12s, environment 2ms)�[22m
$ vitest run src/tests/integration
�[1m�[46m RUN �[49m�[22m �[36mv4.1.2 �[39m�[90m/Users/roackb2/Studio/projects/ProjectHeddle/heddle�[39m
�[32m✓�[39m src/tests/integration/server/runtime-hosts.test.ts �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 21�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/chat/chat-storage.test.ts �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 44�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/memory/memory-maintainer.test.ts �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[32m 270�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/core/run-agent.test.ts �[2m(�[22m�[2m30 tests�[22m�[2m)�[22m�[32m 67�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/memory/memory-integration.test.ts �[2m(�[22m�[2m8 tests�[22m�[2m)�[22m�[33m 521�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/core/cyberloop-observer.test.ts �[2m(�[22m�[2m11 tests�[22m�[2m)�[22m�[32m 28�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/control-plane/layout-snapshots.test.ts �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 24�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/control-plane/workspace-catalog.test.ts �[2m(�[22m�[2m7 tests�[22m�[2m)�[22m�[32m 70�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/control-plane/control-plane-heartbeat-mutations.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 15�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/memory/memory-catalog.test.ts �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[32m 36�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/memory/memory-visibility.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 218�[2mms�[22m�[39m
[08:18:57.272] �[32mINFO�[39m: �[36mHeddle�[39m
�[35mgoal�[39m: "what is this project"
�[35mmodel�[39m: "gpt-5.1-codex-mini"
�[35mprovider�[39m: "openai"
�[35mmaxSteps�[39m: 100
�[35mcwd�[39m: "/var/folders/pb/ktw30d7573s4bycdqq02n4w00000gn/T/heddle-ask-cli-CtI0aM"
[08:18:57.278] �[32mINFO�[39m: �[36mHeddle�[39m
�[35mgoal�[39m: "inspect the repository"
�[35mmodel�[39m: "gpt-5.1-codex-mini"
�[35mprovider�[39m: "openai"
�[35mmaxSteps�[39m: 100
�[35mcwd�[39m: "/var/folders/pb/ktw30d7573s4bycdqq02n4w00000gn/T/heddle-ask-cli-session-ZFwji0"
[08:18:57.287] �[32mINFO�[39m: �[36mHeddle�[39m
�[35mgoal�[39m: "follow up question"
�[35mmodel�[39m: "gpt-5.1-codex-mini"
�[35mprovider�[39m: "openai"
�[35mmaxSteps�[39m: 100
�[35mcwd�[39m: "/var/folders/pb/ktw30d7573s4bycdqq02n4w00000gn/T/heddle-ask-cli-continue-hgFKMb"
[08:18:57.291] �[32mINFO�[39m: �[36mHeddle�[39m
�[35mgoal�[39m: "follow up question"
�[35mmodel�[39m: "gpt-5.1-codex-mini"
�[35mprovider�[39m: "openai"
�[35mmaxSteps�[39m: 100
�[35mcwd�[39m: "/var/folders/pb/ktw30d7573s4bycdqq02n4w00000gn/T/heddle-ask-cli-preflight-Yb52mY"
�[35m [step 1]�[0m �[1mAssistant:�[0m
Follow-up answer.
Session: session-preflight
Outcome: done
Summary: Follow-up answer.
Trace: /var/folders/pb/ktw30d7573s4bycdqq02n4w00000gn/T/heddle-ask-cli-preflight-Yb52mY/.heddle/traces/trace-1777796337294.json
Latest archive: .heddle/chat-sessions/session-preflight/archives/archive-1.jsonl
�[32m✓�[39m src/tests/integration/server/daemon-registry.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 10�[2mms�[22m�[39m
[08:18:57.306] �[32mINFO�[39m: �[36mHeddle�[39m
�[35mgoal�[39m: "daemon stateless ask"
�[35mmodel�[39m: "gpt-5.1-codex-mini"
�[35mprovider�[39m: "openai"
�[35mmaxSteps�[39m: 100
�[35mcwd�[39m: "/var/folders/pb/ktw30d7573s4bycdqq02n4w00000gn/T/heddle-ask-cli-remote-stateless-A5IhoB"
[08:18:57.275] �[32mINFO�[39m: �[36mTrace saved�[39m
�[35mtraceFile�[39m: "/var/folders/pb/ktw30d7573s4bycdqq02n4w00000gn/T/heddle-ask-cli-CtI0aM/.heddle/traces/trace-1777796337275.json"
[08:18:57.330] �[32mINFO�[39m: �[36mHTTP request�[39m
�[35mmethod�[39m: "POST"
�[35mpath�[39m: "/trpc/controlPlane.agentAsk?batch=1"
�[35mstatusCode�[39m: 200
�[35mdurationMs�[39m: 7
[08:18:57.336] �[32mINFO�[39m: �[36mHeddle�[39m
�[35mgoal�[39m: "remote session ask"
�[35mmodel�[39m: "gpt-5.1-codex-mini"
�[35mprovider�[39m: "openai"
�[35mmaxSteps�[39m: 100
�[35mcwd�[39m: "/var/folders/pb/ktw30d7573s4bycdqq02n4w00000gn/T/heddle-ask-cli-remote-session-Trn14e"
[08:18:57.341] �[32mINFO�[39m: �[36mHTTP request�[39m
�[35mmethod�[39m: "POST"
�[35mpath�[39m: "/trpc/controlPlane.sessionCreate?batch=1"
�[35mstatusCode�[39m: 200
�[35mdurationMs�[39m: 2
[08:18:57.349] �[32mINFO�[39m: �[36mHTTP request�[39m
�[35mmethod�[39m: "POST"
�[35mpath�[39m: "/trpc/controlPlane.sessionSendPrompt?batch=1"
�[35mstatusCode�[39m: 200
�[35mdurationMs�[39m: 5
�[32m✓�[39m src/tests/integration/tui/ask-cli.test.ts �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[32m 83�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/core/cleanup.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 14�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/core/agent-loop.test.ts �[2m(�[22m�[2m17 tests�[22m�[2m)�[22m�[32m 21�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/tui/auth-cli.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 7�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/chat/chat-runtime.test.ts �[2m(�[22m�[2m17 tests�[22m�[2m)�[22m�[32m 44�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/core/progress.test.ts �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 12�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/core/provider-credentials.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 5�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/tui/session-cli.test.ts �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 5�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/core/heartbeat-scheduler.test.ts �[2m(�[22m�[2m7 tests�[22m�[2m)�[22m�[32m 11�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/core/workspace-fixture.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[33m 1967�[2mms�[22m�[39m
�[33m�[2m✓�[22m�[39m creates an inline repository even when setup has no files �[33m 340�[2mms�[22m�[39m
�[33m�[2m✓�[22m�[39m creates a pinned git worktree and commits setup changes as the baseline �[33m 1392�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/tools/tools.test.ts �[2m(�[22m�[2m65 tests�[22m�[2m)�[22m�[33m 1283�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/web/control-plane-sessions-state.test.tsx �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[33m 537�[2mms�[22m�[39m
�[33m�[2m✓�[22m�[39m keeps the optimistic user prompt visible across silent session refreshes �[33m 323�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/web/control-plane-screens.test.tsx �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[33m 340�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/web/control-plane-sessions-screen.test.tsx �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[33m 1004�[2mms�[22m�[39m
�[33m�[2m✓�[22m�[39m renders current workspace diff separately from trace-backed turn review �[33m 358�[2mms�[22m�[39m
�[33m�[2m✓�[22m�[39m preserves file mention lookup, keyboard selection, and insertion behavior �[33m 488�[2mms�[22m�[39m
�[32m✓�[39m src/tests/integration/control-plane/workspace-diff.test.ts �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[33m 2133�[2mms�[22m�[39m
�[33m�[2m✓�[22m�[39m reports a clean git workspace �[33m 530�[2mms�[22m�[39m
�[33m�[2m✓�[22m�[39m reports modified files with diff stats and patch text �[33m 484�[2mms�[22m�[39m
�[33m�[2m✓�[22m�[39m reports untracked files and can render a no-index patch for them �[33m 343�[2mms�[22m�[39m
�[33m�[2m✓�[22m�[39m reports deleted files and their patch text �[33m 327�[2mms�[22m�[39m
�[33m�[2m✓�[22m�[39m exposes workspace diff through the control-plane router �[33m 379�[2mms�[22m�[39m
�[2m Test Files �[22m �[1m�[32m27 passed�[39m�[22m�[90m (27)�[39m
�[2m Tests �[22m �[1m�[32m232 passed�[39m�[22m�[90m (232)�[39m
�[2m Start at �[22m 16:18:55
�[2m Duration �[22m 3.79s�[2m (transform 7.94s, setup 0ms, import 14.27s, tests 8.79s, environment 3.71s)�[22m