* Manage Codex app-server binary
* Use plugin deps for Codex app-server binary
* Stabilize media model registry test
* Exclude checkpoint transcripts from memory ingestion
* fix(memory-core): add runtime cron service fallback for dreaming reconciliation
When the cron service is unavailable during gateway_start (e.g., due to
a startup timing race or deferred initialization), the startupCronSource
is captured as null and never refreshed. All subsequent runtime
reconciliation attempts fail with 'cron service unavailable', even when
the cron service is fully operational.
This adds a fallback path in the runtime reconciliation that attempts to
obtain the cron service from the plugin API runtime when the startup
capture was null. This handles the case where the cron service becomes
available after the initial startup event.
Fixes#67362
* fix(memory-core): hold gateway context for runtime cron resolution
The previous attempt tried to access api.runtime.cron which doesn't exist
on the PluginRuntime type. The cron service is only accessible through
PluginHookGatewayContext.getCron().
This fix stores the gateway context from the gateway_start event and uses
it to retry cron resolution at runtime when the initial capture was null.
This handles the race condition where the cron service isn't available
during gateway_start (250ms deferred init) but is ready later.
Also refreshes the startupCron capture when the runtime retry succeeds,
so subsequent reconciliation calls resolve immediately.
Addresses review feedback on #71694
Expose raw `vectorScore` and `textScore` alongside the combined hybrid memory search `score`.
- Preserve vector/text component scores from `mergeHybridResults` output.
- Add optional component-score fields to both memory host SDK type surfaces.
- Extend hybrid merge tests for vector-only, text-only, and overlapping result cases.
- Document that component scores remain raw retrieval diagnostics while temporal decay/MMR only adjust or reorder the combined ranking `score`.
Closes#68166.
Maintainer verification:
- `pnpm test extensions/memory-core/src/memory/hybrid.test.ts`
- `pnpm check:changed`
- Fresh GitHub checks passed.
Co-authored-by: Alex Fries <alex@engramlabs.io>
* [EV-001] memory-core: filter memory_search session hits by visibility
- Move session visibility + listSpawnedSessionKeys to plugin-sdk; sync test
hook with sessions-resolution __testing.setDepsForTest
- Extract loadCombinedSessionStoreForGateway to config/sessions; re-export
from gateway session-utils
- Add session-transcript-hit stem resolver for builtin + QMD paths
- Post-filter memory_search results before citations/recall; fail closed when
requester session key missing; optional corpus=sessions
- Tests: stem extraction, visibility filter smoke, existing suites green
* chore: sync plugin-sdk exports for session-transcript-hit and session-visibility
Run pnpm plugin-sdk:sync-exports so package.json exports match
scripts/lib/plugin-sdk-entrypoints.json. Fixes contract tests and
lint:plugins:plugin-sdk-subpaths-exported for memory-core imports.
* fix(EV-001): cross-agent session memory hits + hoist combined store load
- resolveTranscriptStemToSessionKeys: stop filtering by requester agentId so
keys from other agents reach createSessionVisibilityGuard (a2a + visibility=all).
- Re-export loadCombinedSessionStoreForGateway from session-transcript-hit;
filterMemorySearchHitsBySessionVisibility loads the combined store once per pass.
- Drop unused agentId from filter params; extend tests (Greptile/Codex review).
* fix(memory_search): honor corpus=sessions before maxResults cap
Pass sources into MemoryIndexManager.search so FTS/vector queries add
source IN (...) before ranking and top-N slice (Codex: non-session hits
could fill the window).
QMD path: oversample fetch limit for single-source recall, filter by
source, then diversify/clamp to the requested maxResults.
Wire corpus=sessions from tools; extend MemorySearchManager opts and
wrappers.
* fix(memory_search): apply corpus=memory source filter like sessions
Pass sources: ["memory"] into manager.search so maxResults applies only
within the memory index; post-filter for defense in depth. Document
corpus=memory in the tool description.
* fix: scope qmd session memory search
* fix: enforce memory search session visibility (#70761) (thanks @nefainl)
---------
Co-authored-by: NefAI <info@nefai.nl>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* Revert "fix(memory/dreaming): surface blocked status when heartbeat is disabled for main (#69875)"
This reverts commit 529577e045.
Making way for the dreaming-vs-heartbeat decoupling from Josh's
josh/dreaming-isolated-cron-fix branch, which moves the managed dreaming
cron to isolated agent turns (sessionTarget: "isolated") so dreaming no
longer requires heartbeat to fire. Once the cron no longer rides the
heartbeat path, the blocked-reason observability has nothing left to
report — removing it cleanly here before the cherry-picks land.
* openclaw-3ba.1: move managed dreaming cron to isolated agent turns
* openclaw-46d: claim cron runs before embedded attempts
* openclaw-575: disable managed dreaming cron delivery
* openclaw-575: accept wrapped dreaming cron tokens
* openclaw-ccd: filter cron and wrapper transcript noise from dreaming corpus
* openclaw-cd9: filter archived, cron, and heartbeat transcript noise from dreaming corpus
* openclaw-cd9: suppress role-label reflection tags in rem dreaming
* openclaw-b49: stop narrative timeouts from blocking dreaming cron
* openclaw-b49: keep managed dreaming cron out of diary subagents
* openclaw-ff9: restore cron dream diary generation without serial waits
* openclaw-ff9: run dreaming narratives with lightweight isolated subagent lanes
* openclaw-ff9: detach cron dream diary generation from run completion
* openclaw-ff9: defer cron diary task startup until after cron completion
* doctor/cron: migrate stale managed dreaming jobs to isolated agent turns
After the dreaming cron moved off the heartbeat path to sessionTarget:
"isolated" + payload.kind: "agentTurn" (see the preceding memory-core
changes), users with existing ~/.openclaw/cron/jobs.json entries in the
old sessionTarget: "main" + payload.kind: "systemEvent" shape still
carry stale jobs until the gateway restart reconcile rewrites them.
Add a dreaming-specific cron migration to the existing
maybeRepairLegacyCronStore doctor path so "openclaw doctor" (and
"openclaw doctor --fix") rewrites those jobs without needing a gateway
restart. Match lives in a new doctor-cron-dreaming-payload-migration
helper alongside the existing legacy-delivery and store-migration files.
The matching uses the memory-core managed-job name and description tag
plus the short-term-promotion payload token. Constants are mirrored
from extensions/memory-core/src/dreaming.ts and commented so a future
rename in memory-core is a visible drift point here too.
* memory/dreaming: tighten cron-token match to known wrapper, not substring
The previous match relaxed the line check from 'trimmed line equals token'
to 'line contains token anywhere as a substring' to accept the
`[cron:<id>] <token>` wrapper that isolated-cron turns add. Substring
matching also let any user message embedding the token mid-sentence
trigger the dream-promotion hook, and was flagged by both Greptile and
Aisle on PR #70737.
Replace it with strip-the-known-prefix-then-exact-match: keep the
`[cron:<id>]` wrapper case working, reject every other variant. Add
focused unit coverage that the bare token, the wrapped token, and bare
multiline cases match while embedded / code-fenced / arbitrarily-wrapped
variants do not.
* memory/dreaming: drop assistant followup only on assistant-side signals
Per PR #70737 review (aisle-research-bot, Medium): the previous logic
suppressed the next assistant message whenever the prior user message
matched a 'generated prompt' pattern (`[cron:...]`,
`System (untrusted): ...`, heartbeat prompts, exec-completion events).
Real users can type those same patterns, which let a user exfiltrate
real assistant replies from the dreaming corpus by prefixing their own
prompt — the assistant's reply would be silently dropped.
Remove the cross-message coupling. Assistant-side machinery (silent
replies, system wrappers) is already dropped by sanitizeSessionText,
which is the right layer for that filter. Add an explicit assistant-side
HEARTBEAT_TOKEN check to keep the legitimate `HEARTBEAT_OK` ack drop
working without depending on the prior user message. Add a regression
test exercising the spoofing scenario.
* doctor/cron: assert mirrored dreaming constants stay in sync
Per PR #70737 review (greptile-apps): the doctor migration mirrors three
constants (MANAGED_DREAMING_CRON_NAME, MANAGED_DREAMING_CRON_TAG,
DREAMING_SYSTEM_EVENT_TEXT) from extensions/memory-core/src/dreaming.ts.
A future rename in either file would silently break the migration.
Add a vitest unit that reads both files and asserts the literals match.
Manually verified the assertion fires with a clear error when one side
diverges. Adds no runtime cost; sits in the regular test pipeline.
* fix(memory): stabilize dreaming CI checks
* memory/dreaming: skip eager narrative session cleanup when detached
Per PR #70737 review (chatgpt-codex-connector, P2): runDreamingSweepPhases
called deleteNarrativeSessionBestEffort synchronously right after each
phase. Once narrative generation moved to detached mode (queued via
queueMicrotask), the eager cleanup races the writer: the session is
deleted before the queued subagent run reads it, silently dropping cron
diary entries.
Skip the eager cleanup branch when params.detachNarratives is true.
generateAndAppendDreamNarrative still runs its own deleteSession in the
finally{} block, so the cleanup intent is preserved without the race.
Heartbeat-driven (non-detached) runs keep the original eager-cleanup
behavior.
* fix(plugin-sdk): restore heartbeat-summary re-export
Per PR #70737 review (chatgpt-codex-connector, P1): the revert of
PR #69875 dropped the `heartbeat-summary` re-export from
`openclaw/plugin-sdk/infra-runtime`. That subpath shipped publicly two
days earlier, so removing it is technically a breaking change to a
public SDK surface — third-party plugins importing
`isHeartbeatEnabledForAgent` / `resolveHeartbeatIntervalMs` from this
path would fail with no replacement contract introduced.
Restore the re-export. Costs nothing to keep; the helpers are already
public via `../infra/heartbeat-summary.ts`. SDK additions are by
default backwards-compatible (CLAUDE.md), so removing within days of
introduction violates that intent.
* changelog: note dreaming decoupling from heartbeat
Refs PR #70737.
---------
Co-authored-by: Josh Lehman <josh@martian.engineering>
Replace full-table scan via vec_distance_cosine() + ORDER BY LIMIT with
sqlite-vec's native MATCH + k = ? KNN operator. Keep vec_distance_cosine()
in the SELECT so score = 1 - dist preserves the existing cosine [0,1]
semantics the downstream merge pipeline depends on.
Fixes#69666.
Benchmark on 10,827 chunks, 4096-dim embeddings:
- Before (full scan): ~8490 ms/query
- After (KNN + join): ~50 ms/query
No behavioral changes: returned ids and ordering are identical to the
previous query on all tested queries. The LIMIT ? binding is replaced by
k = ? which caps sqlite-vec's candidate set to the same count.
memory-core registers a gateway:startup hook that runs reconcileManagedDreamingCron() before deps.cron is attached to the startup event (the startup hook is deferred via a 250ms setTimeout in server.impl).
Downgrade the first startup-time "cron service unavailable" warning to a debug log, and rely on the existing runtime reconciliation path to warn if the cron service truly stays unavailable after boot. The managed dreaming cron job itself runs correctly — this was a log-noise regression, not a functional failure.
Signed-off-by: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com>
* fix(memory/dreaming): surface blocked status in memory status when heartbeat disabled for main
Replace the hand-rolled heartbeat-rules logic in resolveDreamingBlockedReason
with the shared resolveHeartbeatSummaryForAgent helper, promoted from core to
the plugin-sdk via infra-runtime. Collapses the two disabled-reason branches
into a single message that points at a new Troubleshooting section in the
dreaming docs, so the silent-failure mode described in openclaw/openclaw#69843
becomes legible without the extension re-encoding heartbeat-enablement rules.
Refs openclaw/openclaw#69843, openclaw/openclaw#46046.
* refactor(memory/dreaming): share resolveDreamingBlockedReason across cli and /dreaming surfaces
- Move resolveDreamingBlockedReason from cli.runtime.ts into dreaming.ts as an exported helper and pin its heartbeat check to DEFAULT_AGENT_ID (now exported from plugin-sdk/routing) so the status-line check agrees with the cron's hardcoded sessionTarget even when the configured default agent is not main.
- Render the blocked reason from formatStatus in dreaming-command.ts directly under the enabled line, so /dreaming status, /dreaming on, /dreaming off, and bare /dreaming all flag that the cron is blocked instead of implying dreaming is healthy.
- Tighten the blocked-reason text to lead with user impact ('dreaming is enabled but will not run because heartbeat is disabled for main'), so operators immediately understand the config is toggled on but nothing is actually running.
- Tighten the dreaming Troubleshooting copy to name main explicitly and mention both surfaces.
- Add tests locking the new behavior across cli.test.ts (default-agent=ops still reports blocked for main) and dreaming-command.test.ts (/dreaming status ordering, /dreaming on surfacing, healthy-heartbeat omission).
Refs openclaw/openclaw#69843, openclaw/openclaw#46046.
* fix(memory/dreaming): check heartbeat for the resolved default agent, not the literal 'main'
sessionTarget: 'main' is a cron session-type enum variant meaning 'the default agent's main session', not an agent id (see src/cron/service/jobs.ts). buildManagedDreamingCronJob does not set agentId, and cron runtime resolves the missing agentId through resolveDefaultAgentId(cfg) before enqueuing or waking. The previous pin to DEFAULT_AGENT_ID could produce a false 'blocked' reading when a configured default agent is not 'main' and its heartbeat is fine, and could miss a real block when the default agent is not 'main' and that agent's heartbeat is actually off.
Switch resolveDreamingBlockedReason to resolveDefaultAgentId(cfg) and interpolate the resolved agent id into the message so the blocked line names the agent whose heartbeat is the blocker. Introduce a narrow local CRON_SESSION_TARGET_MAIN constant for the cron session-type enum variant (used by the sessionTarget type and value) so the remaining 'main' literal is semantically distinct from any agent id. Revert the DEFAULT_AGENT_ID export addition on plugin-sdk/routing; memory-core no longer needs it. Update the Troubleshooting doc wording and the cli test that was locking the wrong behaviour.
Refs openclaw/openclaw#69843, openclaw/openclaw#46046.
* fix(memory/dreaming): align blocked check with server-cron wake's defaults-only heartbeat
resolveDreamingBlockedReason was using resolveHeartbeatSummaryForAgent, which merges agents.defaults.heartbeat with agents.list[].heartbeat. The managed dreaming cron leaves job.agentId and job.sessionKey unset, so server-cron's wake wrapper cannot look up a per-agent entry and calls runHeartbeatOnce with agents.defaults.heartbeat only. Using the summary helper would disagree with the actual wake when the default agent overrides heartbeat.every differently from the defaults (either direction — false blocked when the override would run, or false healthy when defaults block).
Mirror the wake path explicitly: rule-1 enablement via isHeartbeatEnabledForAgent against the default agent, rule-3 interval via resolveHeartbeatIntervalMs with defaults-only heartbeat config. Comment points at server-cron so a future cleanup of that latent override-propagation gap sees the coupling.
Refs openclaw/openclaw#69843.
Fixes concurrent manager creation races that caused SafeOpenErrors during session export.
Deduplicates in-flight manager creation so only one full QMD manager arms per agent/config at a time, eliminating the concurrent exportSessions() collisions that triggered path changed during write errors
Resolves and snapshots runtime inputs before cache reuse, replacing stale managers atomically when workspace/config changes, and aborting queued export work promptly on close()
* Feat: LM Studio Integration
* Format
* Support usage in streaming true
Fix token count
* Add custom window check
* Drop max tokens fallback
* tweak docs
Update generated
* Avoid error if stale header does not resolve
* Fix test
* Fix test
* Fix rebase issues
Trim code
* Fix tests
Drop keyless
Fixes
* Fix linter issues in tests
* Update generated artifacts
* Do not have fatal header resoltuion for discovery
* Do the same for API key as well
* fix: honor lmstudio preload runtime auth
* fix: clear stale lmstudio header auth
* fix: lazy-load lmstudio runtime facade
* fix: preserve lmstudio shared synthetic auth
* fix: clear stale lmstudio header auth in discovery
* fix: prefer lmstudio header auth for discovery
* fix: honor lmstudio header auth in warmup paths
* fix: clear stale lmstudio profile auth
* fix: ignore lmstudio env auth on header migration
* fix: use local lmstudio setup seam
* fix: resolve lmstudio rebase fallout
---------
Co-authored-by: Frank Yang <frank.ekn@gmail.com>