Skip to content

fix(logging): initialize logger from config in the standalone bridge#1955

Merged
syzsunshine219 merged 2 commits into
MemTensor:dev-v2.0.25from
chiefmojo:fix/daemon-init-logger
Jul 23, 2026
Merged

fix(logging): initialize logger from config in the standalone bridge#1955
syzsunshine219 merged 2 commits into
MemTensor:dev-v2.0.25from
chiefmojo:fix/daemon-init-logger

Conversation

@chiefmojo

Copy link
Copy Markdown
Contributor

Problem

The standalone bridge (bridge.cts) calls bootstrapMemoryCoreFull without passing initLogging: true. The logger stays on its bootstrap-console default, ignoring config.logging.* entirely. In practice this means logging.timezone has no effect when running as a daemon — logs are always stamped UTC regardless of config.

Fix

Add initLogging: true to the bootstrapMemoryCoreFull call in bridge.cts. The non-daemon stdio path (bridge/stdio.ts) already passes this flag correctly; this aligns the daemon path.

const { core, config, home } = await bootstrapMemoryCoreFull({
  // ...
  home: resolvedHome,
  initLogging: true,  // initialize logger from config.logging (timezone, level, channels)
});

The MemOS daemon (`bridge.cts`) resolved config inside
`bootstrapMemoryCoreFull` but never called `initLogger`, so the active
logger stayed on the `bootstrapConsoleOnly()` default with `tz` pinned to
"UTC". This made `logging.timezone` (PR MemTensor#44) inert in the daemon — and the
rest of `logging.*` (level, channels, file/audit/llm/perf/events sinks)
dead too. Pretty/compact timestamps kept printing UTC regardless of config.

Add an opt-in `initLogging` flag to `BootstrapOptions`. When set,
`bootstrapMemoryCoreFull` calls `initLogger(config, home)` right after
config resolution, before anything logs. The standalone bridge passes it;
embedded plugin hosts (`adapters/openclaw/index.ts`) leave it off so the
host keeps control of its own logging.

Test: bootstrap with `logging.timezone` set + `initLogging: true` stamps
records with the configured tz; without the flag the logger is untouched.
@Memtensor-AI
Memtensor-AI changed the base branch from dev-20260604-v2.0.19 to dev-v2.0.22 July 1, 2026 13:15
@Memtensor-AI

Copy link
Copy Markdown
Collaborator

Automated Test Results: FAILED

Cloud test-engine rerun against dev-v2.0.22 reached the PR code successfully after switching this run to the GitHub PR ref (refs/pull/1955/head), so the earlier fork-clone failure is resolved.

  • Run: tr-3a63216e-cc0 on cloud test-engine 10012
  • Scope: memos_local_plugin/unit
  • Result: 62 passed, 2 failed
  • Focused tests: bridge + pipeline logging/memory-core tests

Failure:

  • tests/unit/pipeline/bootstrap-logging.test.ts: expected the memory log buffer tail entry timezone to be UTC, received undefined when initLogging is not requested.

Do not merge until the logging initialization behavior or the regression test expectation is fixed and cloud tests pass.

@CarltonXiang
CarltonXiang deleted the branch MemTensor:dev-v2.0.25 July 3, 2026 07:25
@syzsunshine219 syzsunshine219 reopened this Jul 3, 2026
@syzsunshine219
syzsunshine219 changed the base branch from dev-v2.0.22 to main July 3, 2026 08:21
@Memtensor-AI Memtensor-AI added the area:plugin OpenClaw & Hermes label Jul 8, 2026
@Memtensor-AI
Memtensor-AI requested a review from bittergreen July 8, 2026 11:44
@syzsunshine219
syzsunshine219 changed the base branch from main to dev-v2.0.25 July 23, 2026 09:33
@syzsunshine219

Copy link
Copy Markdown
Collaborator

Automated Test Results: PASSED

Manual cloud test-engine rerun against the latest dev-v2.0.25 completed successfully.

  • Run: tr-f6d7f8a4-39d on cloud test-engine 10012
  • Ref: refs/pull/1955/head merged with latest dev-v2.0.25 before execution
  • Scope: memos_local_plugin/unit
  • Result: 71 passed, 0 failed, 0 skipped
  • Duration: 16s

The prior failure was caused by the older base not yet enriching log records with the tz field: the no-initLogging test expected UTC but received undefined. With #1956 now present in dev-v2.0.25, the same regression path passes. Manual code review is still required before merge.

@syzsunshine219
syzsunshine219 merged commit e067cf8 into MemTensor:dev-v2.0.25 Jul 23, 2026
CarltonXiang added a commit that referenced this pull request Jul 24, 2026
* ci: automate OpenClaw local plugin release notes (#2129)

Generate evidence-backed OpenClaw local plugin release notes through the configured draft service.

Add dry-run and publish workflow safeguards, retry/repair validation, prerelease handling, failure reporting hooks, redacted inspection artifacts, and focused local plugin test coverage.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix: Adjust feedback update review flow & using independent

* refactor: unify LLM provider environment config

# Conflicts:
#	examples/basic_modules/llm.py
#	src/memos/mem_reader/multi_modal_struct.py

* fix: fix log for embedding models

* feat(api):update SDK version number

* fix: fix tests

* fix: preserve text when downgrading feedback updates

* feat(api):update SDK version number

* fix: type parser factory config as base parser config

* fix: Open Code Review problems fix

* feat: OpenRouter provider routing and reasoning control (#1958)

* feat(memos): add OpenRouter provider routing

* fix(memos): route OpenRouter prefs through all slots

* Fix OpenRouter reasoning config types

* fix(openrouter): harden routing detection

* fix(openrouter): harden review follow-ups

* fix(openrouter): preserve provider defaults

---------

Co-authored-by: jiachengzhen <jiacz@memtensor.cn>
Co-authored-by: shinetata <149466187+shinetata@users.noreply.github.com>

* refactor: summarize search and embedding logs

* perf: cache and coalesce embedding requests

* fix: summarize cosine reranker logs

* perf: optimize PolarDB retrieval round trips

* perf: prune MMR candidates by memory bucket

* Fix #1611: classifyTopic fails when summarizer uses Anthropic endpoint (Kimi Code) (#2133)

* fix(openclaw): route classifyTopic + arbitrateTopicSplit per provider (#1611)

`Summarizer.classifyTopic` and `Summarizer.arbitrateTopicSplit` always
dispatched to the OpenAI implementation regardless of the summarizer's
configured provider. When configured against an Anthropic-only endpoint
(e.g. Kimi Code's `/coding/v1/messages`), every call returned 404 because
the OpenAI helper appended `/chat/completions` to a URL that does not
exist — wasting tokens, polluting logs, adding event-loop latency.

Fix (Option A from the issue):

- Add native classifyTopic{Anthropic,Gemini,Bedrock} and
  arbitrateTopicSplit{Anthropic,Gemini,Bedrock} in the three provider
  files, mirroring the shape of the existing judgeNewTopic* helpers.
- Re-export TOPIC_CLASSIFIER_PROMPT / TOPIC_ARBITRATION_PROMPT from
  openai.ts so all providers share prompt strings and parseTopicClassifyResult
  remains the single JSON parser — no behavioral drift across providers.
- Rewire callTopicClassifier / callTopicArbitration switch statements
  so the anthropic / gemini / bedrock arms route to their native
  implementations instead of falling through to the OpenAI helpers.
- Bedrock helpers require cfg.endpoint (throws
  `Bedrock topic-classifier|arbitration requires 'endpoint'`) matching
  every other Bedrock helper in the file.
- Mirror every edit into packages/memos-core/src/ingest/providers/ so
  the byte-similar sibling tree cannot silently regress the bug after
  the next sync.

Tests: new tests/topic-classifier-dispatch.test.ts (8 cases) stubs
globalThis.fetch and asserts each provider hits the correct URL + body
shape; includes a regression case where an Anthropic 404 surfaces an
`Anthropic topic-classifier failed` error rather than the old
`OpenAI topic-classifier failed` — the exact string from issue #1611.
TDD baseline: 7/8 fail against buggy code. Post-fix: 8/8 pass. Existing
tests/topic-judge-minimax-1315.test.ts (5/5) continues to pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(openclaw): harden topic-classifier/arbitrator providers per OCR review

Address code-review findings on the topic-classifier + arbitrator dispatch
helpers added in #2133 (issue #1611). Applied to both
apps/memos-local-openclaw and packages/memos-core (byte-similar mirrors).

Correctness / security fixes:
- anthropic: validate cfg.apiKey up front instead of silently sending "";
  move cfg.headers spread BEFORE x-api-key + anthropic-version so a
  misconfigured header cannot silently overwrite the credential
- anthropic: guard content parsing — treat missing/non-array json.content
  as [] to avoid TypeError on unexpected API payloads
- anthropic: reduce arbitrateTopicSplit max_tokens from 60 to 10 to match
  arbitrateTopicSplitOpenAI (single-word NEW/SAME reply)
- gemini: validate cfg.apiKey up front; construct URL via new URL() +
  URLSearchParams so a caller-supplied endpoint that already contains a
  query string ("?version=v1beta") does not produce malformed
  "…?version=v1beta?key=…"
- bedrock: emit warn log when arbitrateTopicSplit sees empty or
  unexpected text so silent bias toward SAME becomes visible at normal
  log levels (production usually suppresses debug)

Intentionally not applied (out-of-scope refactors that would touch
pre-existing helpers not in this PR): DRY-extract shared low-level
callers for anthropic/gemini/bedrock (findings #4/#7/#10/#14) and the
DEFAULT_BEDROCK_MODEL constant (finding #11).

Existing tests unchanged: tests/topic-classifier-dispatch.test.ts (8/8)
and tests/topic-judge-minimax-1315.test.ts (5/5) still pass. The new
guards use apiKey values already provided by every test case, so no
test edits were required.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(openclaw): address remaining OCR round-2 findings on topic-classifier providers

Round 1 (commit 646c28a) did not fully converge OCR review. Round 2 targets
the 2 repeated findings and 10 newly detected ones without expanding scope
beyond the classifier/arbitration functions added by PR #2133.

Bedrock (packages/memos-core + apps/memos-local-openclaw):
- Fix TRUE MISS from Round 1: `arbitrateTopicSplitBedrock` was still using
  `maxTokens: 60` while Anthropic's arbitrator was already lowered to 10.
  Aligned to `maxTokens: 10`.
- Introduce `DEFAULT_BEDROCK_TOPIC_MODEL` constant scoped to the two new
  functions only (`classifyTopicBedrock`, `arbitrateTopicSplitBedrock`).
  Pre-existing helpers (`judgeDedupBedrock`, `summarizeBedrock`, etc.) keep
  their inline default to stay out of scope.
- Extract `bedrockConverseTopic()` shared transport used by the two new
  functions to eliminate the DRY duplication OCR flagged.

Gemini (packages/memos-core + apps/memos-local-openclaw):
- Add empty/unexpected-response `log.warn` branches to
  `arbitrateTopicSplitGemini`, mirroring the Bedrock arbitrator's
  defensive handling (Round 1 covered Bedrock only).
- Extract `callGeminiTopic()` shared transport used by the two new
  functions to eliminate the DRY duplication OCR flagged.
- Rejected escalating the empty-response case to `throw`: throwing would
  cascade through `Summarizer.tryChain` and burn tokens on every fallback;
  warn+default-to-SAME is the safer choice for a boundary detector.

Anthropic (packages/memos-core + apps/memos-local-openclaw):
- Extract `callAnthropicMessagesTopic()` shared transport used by the two
  new functions to eliminate the DRY duplication OCR flagged. Header spread
  order is preserved verbatim: `...cfg.headers` comes BEFORE the credential
  and `anthropic-version` so user-supplied headers cannot override them
  (this was the Round 1 security fix — do not undo).

Pre-existing helpers in each provider are intentionally untouched to keep
the diff minimum-scoped to PR #2133's classifier/arbitrator additions.

Verification (executed in apps/memos-local-openclaw):
- `./node_modules/.bin/vitest run tests/topic-classifier-dispatch.test.ts \\
  tests/topic-judge-minimax-1315.test.ts` → 13/13 passed
- `./node_modules/.bin/tsc --noEmit` on the 3 modified provider files → 0 errors
- All 6 files (3 apps + 3 packages/memos-core mirrors) are byte-identical

---------

Co-authored-by: MemOS AutoDev <autodev@memtensor.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Fix #2131: [Bug] memos-local-plugin : chunks table never created — causes viewer dashboard  (#2132)

* fix: viewer dashboard drifts to zero after a namespace flip (#2131)

The viewer's Overview/metrics/traces/session routes filtered reads
through the core's mutable activeNamespace, which every turn/session
rewrites from caller context hints (openclaw: profileId = ctx.agentId).
When the gateway session or a sub-agent turn flipped the profile, all
historical rows failed the visibility clauses and dashboard counts
collapsed to zero.

The reported root cause (missing chunks table) is a misdiagnosis: the
only 2.0 reference to chunks is the legacy 1.0-import reader in
server/routes/migrate.ts; the live pipeline queries traces/episodes/
sessions with vectors stored in BLOB columns, so no schema change is
needed.

Fix: complete the existing includeAllNamespaces viewer convention
(already used by diag.ts/session.ts/memory.ts) — add the option to
core.metrics() and core.listEpisodes(), and pass it from overview.ts,
metrics.ts, trace.ts and session.ts. Scoped reads and turn-time
retrieval isolation are unchanged; explicit owner filters still narrow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: align listApiLogs namespace scoping with viewer metrics feeds

Follow-up to #2131 (OCR round 1/2):

- listApiLogs: accept includeAllNamespaces in the contract and core
  facade (contract symmetry with listTraces/listSkills/listPolicies);
  metrics/tools now passes it explicitly so both feeds of the bump()
  aggregation are scoped identically
- metrics(): document that the traces fetch feeding sessions/
  writesToday/embeddings/dailyWrites is intentionally cross-namespace;
  only totalTurns respects includeAllNamespaces
- listEpisodes: apply limit/offset after the visibility filter on the
  namespace-scoped path so pages are not silently under-filled by
  other namespaces' rows

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: memos-autodev[bot] <autodev-bot@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* fix(plugin): paginate dirty-closed reward recovery (#2120)

* fix(plugin): page dirty-closed reward recovery

* fix(plugin): share dirty scan cursor type

---------

Co-authored-by: HarveyXiang <harvey_xiang@163.com>

* fix(adapter): fire session.close in daemon thread to unblock event loop (#1953)

* fix(adapter): fire session.close in daemon thread to unblock event loop

on_session_end() called bridge.request("session.close") inline with a
30 s blocking urlopen(). gateway/run.py calls this synchronously from
_handle_reset_command (an async fn), so the blocking I/O ran on the
asyncio event loop thread, preventing Discord heartbeats from firing and
causing forced reconnection after 10–30 s.

The session.close response is unused and errors are already suppressed,
so the call is semantically fire-and-forget. Moving it to a daemon thread
is the correct fix: the event loop is never blocked, the request still
goes out, and a 5 s timeout keeps it bounded if the bridge is dead.

Reproducer: Violet 2026-06-12 09:54 (agent.log lines 3629–3791).
Spec: ~/specs/memos-bridge-blocking-shutdown-spec.md

* Fix #1897: bug(memos-local-plugin): Hermes background model status checks can trigger paid  (#1899)

* Fix #1897: fix(memos-local-plugin): add LLM circuit breaker for terminal provider errors

Issue #1897 reported ~12,900 paid LLM requests in 24 h on Hermes
against a DeepSeek key with insufficient balance. The local
`system_model_status` row count (12,900) closely tracked the
provider-side `request_count` (11,344) for the same billing window.
The naming is misleading: `system_model_status` is not a health probe;
it is the audit row written once per LLM call (ok / fallback / error)
inside `core/llm/client.ts`. With no circuit breaker, every pipeline
subscriber (capture / session-relation / reward / L2 / L3 / skill /
retrieval LLM filter / world-model) kept firing on every turn / closed
episode / induction, generating one paid request each.

Add a per-`LlmClient` circuit breaker:

- Trips on terminal errors: HTTP 401/402/403 or messages containing
  `insufficient balance` / `invalid api key` / `unauthorized` /
  `account suspended` / `billing`.
- Open: short-circuits subsequent calls inside the facade without
  contacting the provider. Throws `MemosError(LLM_UNAVAILABLE)` with
  `details.circuitOpen=true` so existing catch blocks still work.
- Half-open after cool-down (default 5 min, configurable, min 30 s):
  next call probes the provider; success closes the breaker, terminal
  failure re-opens it for another cool-down.
- Host fallback rescues a call without tripping the breaker —
  fallback exists precisely to keep going when the primary is down.
- Coalesces `system_model_status="circuit_open"` audit rows to at
  most one per ~25 s while the breaker stays open, so we don't
  replace paid spam with audit-row spam.
- Exposes `circuitOpen` / `circuitOpenUntil` / `circuitOpenedReason`
  via `LlmClientStats` for the Overview viewer card.
- Enabled by default; legacy behaviour available via
  `circuitBreaker.enabled = false`.

Tests: 9 new vitest cases under `tests/unit/llm/client.test.ts`
covering trip on 402, trip on "insufficient balance" message, no
trip on generic transient, coalescing, half-open close on success,
host-fallback rescues without trip, disabled mode, stats fields,
and re-open on terminal probe failure. All 59 LLM and 28 pipeline
tests pass; `tsc --noEmit` clean.

Out of scope (tracked separately): 429 `Retry-After` handling
(issue #1620), per-tool rate limits, daily budget caps.

* Fix LLM breaker with host fallback

---------

Co-authored-by: autodev-bot <autodev@memtensor.local>
Co-authored-by: Jiang <33757498+hijzy@users.noreply.github.com>
Co-authored-by: GU TIANCHUN <96930846+TianchunGu@users.noreply.github.com>
Co-authored-by: Dubberman <48425266+whipser030@users.noreply.github.com>

---------

Co-authored-by: Memtensor-AI <project@memtensor.cn>
Co-authored-by: autodev-bot <autodev@memtensor.local>
Co-authored-by: Jiang <33757498+hijzy@users.noreply.github.com>
Co-authored-by: GU TIANCHUN <96930846+TianchunGu@users.noreply.github.com>
Co-authored-by: Dubberman <48425266+whipser030@users.noreply.github.com>

* feat: configurable log timezone (#1956)

feat(2c): add log timezone support

Co-authored-by: Fayenix <fayenix@users.noreply.github.com>
Co-authored-by: jiachengzhen <jiacz@memtensor.cn>

* feat: chunk batch reflection scoring (#2146)

Co-authored-by: Erick Wipprecht <53452309+chiefmojo@users.noreply.github.com>

* Fix #1897 recovery replay request storm (#1939)

* Fix #1897: fix(memos-local-plugin): add LLM circuit breaker for terminal provider errors

Issue #1897 reported ~12,900 paid LLM requests in 24 h on Hermes
against a DeepSeek key with insufficient balance. The local
`system_model_status` row count (12,900) closely tracked the
provider-side `request_count` (11,344) for the same billing window.
The naming is misleading: `system_model_status` is not a health probe;
it is the audit row written once per LLM call (ok / fallback / error)
inside `core/llm/client.ts`. With no circuit breaker, every pipeline
subscriber (capture / session-relation / reward / L2 / L3 / skill /
retrieval LLM filter / world-model) kept firing on every turn / closed
episode / induction, generating one paid request each.

Add a per-`LlmClient` circuit breaker:

- Trips on terminal errors: HTTP 401/402/403 or messages containing
  `insufficient balance` / `invalid api key` / `unauthorized` /
  `account suspended` / `billing`.
- Open: short-circuits subsequent calls inside the facade without
  contacting the provider. Throws `MemosError(LLM_UNAVAILABLE)` with
  `details.circuitOpen=true` so existing catch blocks still work.
- Half-open after cool-down (default 5 min, configurable, min 30 s):
  next call probes the provider; success closes the breaker, terminal
  failure re-opens it for another cool-down.
- Host fallback rescues a call without tripping the breaker —
  fallback exists precisely to keep going when the primary is down.
- Coalesces `system_model_status="circuit_open"` audit rows to at
  most one per ~25 s while the breaker stays open, so we don't
  replace paid spam with audit-row spam.
- Exposes `circuitOpen` / `circuitOpenUntil` / `circuitOpenedReason`
  via `LlmClientStats` for the Overview viewer card.
- Enabled by default; legacy behaviour available via
  `circuitBreaker.enabled = false`.

Tests: 9 new vitest cases under `tests/unit/llm/client.test.ts`
covering trip on 402, trip on "insufficient balance" message, no
trip on generic transient, coalescing, half-open close on success,
host-fallback rescues without trip, disabled mode, stats fields,
and re-open on terminal probe failure. All 59 LLM and 28 pipeline
tests pass; `tsc --noEmit` clean.

Out of scope (tracked separately): 429 `Retry-After` handling
(issue #1620), per-tool rate limits, daily budget caps.

* Fix LLM breaker with host fallback

* fix(plugin): bound recovery reflect replay LLM calls

---------

Co-authored-by: autodev-bot <autodev@memtensor.local>
Co-authored-by: Jiang <33757498+hijzy@users.noreply.github.com>
Co-authored-by: jiachengzhen <jiacz@memtensor.cn>

* fix: guard against episode storm stalling foreground sessions (#1844)

fix: guard against episode storm stalling foreground sessions (#1755)

Co-authored-by: jiachengzhen <jiacz@memtensor.cn>

* fix(logging): initialize logger from config in the standalone bridge (#1955)

The MemOS daemon (`bridge.cts`) resolved config inside
`bootstrapMemoryCoreFull` but never called `initLogger`, so the active
logger stayed on the `bootstrapConsoleOnly()` default with `tz` pinned to
"UTC". This made `logging.timezone` (PR #44) inert in the daemon — and the
rest of `logging.*` (level, channels, file/audit/llm/perf/events sinks)
dead too. Pretty/compact timestamps kept printing UTC regardless of config.

Add an opt-in `initLogging` flag to `BootstrapOptions`. When set,
`bootstrapMemoryCoreFull` calls `initLogger(config, home)` right after
config resolution, before anything logs. The standalone bridge passes it;
embedded plugin hosts (`adapters/openclaw/index.ts`) leave it off so the
host keeps control of its own logging.

Test: bootstrap with `logging.timezone` set + `initLogging: true` stamps
records with the configured tz; without the flag the logger is untouched.

* fix(bridge): add 20s timeout guard to core.shutdown() to prevent orphaned processes (#1799)

* fix(bridge): add shutdown timeout to prevent orphan bridge processes

core.shutdown() drains the L2/L3/skill flush pipeline which can block
on hanging LLM calls. Without a deadline the bridge never exits after
stdin EOF when the Python parent is already gone, re-creating the
process-leak condition. Race all three shutdown sites (daemon SIGTERM,
non-daemon SIGTERM, headless stdin-EOF) against a 20s timeout so the
process always terminates within a bounded time.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(bridge): wrap remaining core.shutdown() calls with 20s timeout

Three sites missed by the original patch (56ebe7a3) were calling
core.shutdown() without withShutdownTimeout, leaving the bridge process
able to hang indefinitely if L2/L3/skill LLM calls stalled at shutdown:

  • bridge.cts: EADDRINUSE exit (×2) — daemon can't bind viewer port
  • bridge.cts: viewer-running keepalive path — stdin closes but viewer
    is still serving; core.shutdown fires from the interval callback

All six core.shutdown() call sites now go through withShutdownTimeout,
guaranteeing the bridge exits within 20s regardless of which path is
taken. Adds bridge-shutdown-audit.md documenting the full call chain and
confirming no blocking sync calls prevent the timeout from firing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(docs): correct stale line references + move audit doc to apps/memos-local-plugin/docs/

- Removed companion-stable branch reference from header
- Updated bridge.cts line numbers to match main (per shinetata review)
- Removed companion-stable commit hash 56ebe7a3
- Moved from repo root to apps/memos-local-plugin/docs/ per review request

---------

Co-authored-by: Fayenix <fayenix@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(build): rebuild bridge.cjs when any TypeScript source is newer (#1850)

* fix(migrations): remove duplicate ALTER TABLE stmts from 008 now in 001

All columns added by 008-feedback-experience-metadata were backported into
001-initial.sql; the ALTER TABLE statements now fail on fresh DBs. Keeps
only the idempotent CREATE INDEX lines.

Also fix scripts/replay.py bridge_client import path (adapters/ is under
apps/memos-local-plugin/, not the repo root) and add
scripts/export-state-sessions.py for exporting state.db sessions to JSON
files compatible with replay.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(daemon): rebuild when any .ts source is newer than dist/bridge.cjs

_rebuild_if_stale() only checked bridge.cts mtime, so changes to
core/**/*.ts (e.g. memory-core.ts) didn't trigger an auto-rebuild on
gateway restart. Walk all .ts files under plugin_root and compare the
newest mtime against the compiled binary instead.

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: remove 500-row cap that truncated viewer count displays (#1954)

* fix: remove 500-row cap that truncated viewer count displays

clampLimit() in _helpers.ts capped all repo list() calls at 500,
silently truncating skill/policy/world-model counts everywhere they
were derived from array length rather than a COUNT(*) query.

- Raise clampLimit cap 500 → 100,000 so metrics() and other internal
  analytics can read full datasets (fixes Analytics tab)
- Rewrite /api/v1/overview to use countSkills/countPolicies/
  countWorldModels/countEpisodes instead of list+length, making
  Overview counts correct regardless of scale (fixes Overview tab)
- Align countSkills to use limit:100_000 consistent with other
  count methods

* feat: chunk batch reflection scoring (#1957)

* Fix #1540: fix: (#1824)

docs(memos-local-plugin): clarify install path and stale dir names (#1540)

The README's 'Quick start' section told users to use install.sh instead
of npm install, but the warning was buried and users still tried
'npm install -g @memtensor/memos-local-plugin' first. The reporter in
#1540 encountered this on a Hermes deployment.

This change:

- Promotes the 'do not run npm install -g' notice to a prominent
  IMPORTANT callout explaining why global install is wrong (no
  agent-home deploy, no config.yaml, no bridge/viewer) and that the
  tarball intentionally ships built artifacts only.
- Adds a Troubleshooting subsection covering the two specific symptoms
  in the bug report: the 'package not found' misread, and the stale
  web/ and site/ directory names (web/ is now viewer/, site/ was
  removed by commit 26e7e3d).
- Mentions install.ps1 for Windows alongside install.sh.
- CHANGELOG: record the docs fix and reference #1540.

Documentation-only change; no code or runtime behavior touched.

Co-authored-by: MemOS AutoDev <autodev@memtensor.ai>
Co-authored-by: Matthew <heimixiaozhuang@zju.edu.cn>

* Fix #1888: [Bug] test_system_parser.py: SystemParser.__init__() got an unexpected keyword a (#1889)

fix: remove invalid chunker parameter from SystemParser test instantiation

- SystemParser.__init__() signature changed to (embedder, llm=None)
- Test was still passing chunker=None causing TypeError
- Fixes all 5 failing tests in test_system_parser.py

Fixes #1888

Co-authored-by: MemOS AutoDev <autodev@memos.ai>
Co-authored-by: Matthew <heimixiaozhuang@zju.edu.cn>

* Fix #1525: [Bug] clean_json_response crashes with cryptic AttributeError when given None (#1884)

* test: add comprehensive tests for clean_json_response (issue #1525)

- Add test suite in tests/mem_os/test_format_utils.py
- Cover None input ValueError with diagnostic message
- Cover markdown removal, whitespace stripping, edge cases
- Verify fix for AttributeError when LLM returns None

* style: format clean_json_response tests

---------

Co-authored-by: MemOS AutoDev <autodev@memos.ai>
Co-authored-by: Matthew <heimixiaozhuang@zju.edu.cn>

* Fix #1901: share_cube_with_user passes swapped args to _validate_cube_access — fails for ev (#1903)

fix: validate current user not target in share_cube_with_user (#1901)

share_cube_with_user(cube_id, target_user_id) called
_validate_cube_access(cube_id, target_user_id), but the validator
signature is (user_id, cube_id). The cube_id therefore landed in the
user_id slot and _validate_user_exists raised
"User '<cube_id>' does not exist or is inactive" for every well-formed
call, making the API unusable.

The in-code comment "Validate current user has access to this cube"
already documented the correct intent: the sharing user (self.user_id)
must have access to the cube being shared, not the target. Switch the
call to self._validate_cube_access(self.user_id, cube_id). The target
user's existence is independently checked on the next line via
validate_user(target_user_id), so that path is unchanged.

Add regression tests in tests/mem_os/test_memos_core.py that pin down:
- validate_user_cube_access is consulted with (self.user_id, cube_id),
- add_user_to_cube is called with (target_user_id, cube_id) on success,
- a missing target raises "Target user '<id>' does not exist".

Closes #1901

Co-authored-by: MemOS AutoDev Bot <autodev@memtensor.local>
Co-authored-by: Matthew <heimixiaozhuang@zju.edu.cn>

* feat(memos): chunk batch reflection scoring

* Fix #1897: bug(memos-local-plugin): Hermes background model status checks can trigger paid  (#1899)

* Fix #1897: fix(memos-local-plugin): add LLM circuit breaker for terminal provider errors

Issue #1897 reported ~12,900 paid LLM requests in 24 h on Hermes
against a DeepSeek key with insufficient balance. The local
`system_model_status` row count (12,900) closely tracked the
provider-side `request_count` (11,344) for the same billing window.
The naming is misleading: `system_model_status` is not a health probe;
it is the audit row written once per LLM call (ok / fallback / error)
inside `core/llm/client.ts`. With no circuit breaker, every pipeline
subscriber (capture / session-relation / reward / L2 / L3 / skill /
retrieval LLM filter / world-model) kept firing on every turn / closed
episode / induction, generating one paid request each.

Add a per-`LlmClient` circuit breaker:

- Trips on terminal errors: HTTP 401/402/403 or messages containing
  `insufficient balance` / `invalid api key` / `unauthorized` /
  `account suspended` / `billing`.
- Open: short-circuits subsequent calls inside the facade without
  contacting the provider. Throws `MemosError(LLM_UNAVAILABLE)` with
  `details.circuitOpen=true` so existing catch blocks still work.
- Half-open after cool-down (default 5 min, configurable, min 30 s):
  next call probes the provider; success closes the breaker, terminal
  failure re-opens it for another cool-down.
- Host fallback rescues a call without tripping the breaker —
  fallback exists precisely to keep going when the primary is down.
- Coalesces `system_model_status="circuit_open"` audit rows to at
  most one per ~25 s while the breaker stays open, so we don't
  replace paid spam with audit-row spam.
- Exposes `circuitOpen` / `circuitOpenUntil` / `circuitOpenedReason`
  via `LlmClientStats` for the Overview viewer card.
- Enabled by default; legacy behaviour available via
  `circuitBreaker.enabled = false`.

Tests: 9 new vitest cases under `tests/unit/llm/client.test.ts`
covering trip on 402, trip on "insufficient balance" message, no
trip on generic transient, coalescing, half-open close on success,
host-fallback rescues without trip, disabled mode, stats fields,
and re-open on terminal probe failure. All 59 LLM and 28 pipeline
tests pass; `tsc --noEmit` clean.

Out of scope (tracked separately): 429 `Retry-After` handling
(issue #1620), per-tool rate limits, daily budget caps.

* Fix LLM breaker with host fallback

---------

Co-authored-by: autodev-bot <autodev@memtensor.local>
Co-authored-by: Jiang <33757498+hijzy@users.noreply.github.com>
Co-authored-by: GU TIANCHUN <96930846+TianchunGu@users.noreply.github.com>
Co-authored-by: Dubberman <48425266+whipser030@users.noreply.github.com>

---------

Co-authored-by: Memtensor-AI <project@memtensor.cn>
Co-authored-by: MemOS AutoDev <autodev@memtensor.ai>
Co-authored-by: Matthew <heimixiaozhuang@zju.edu.cn>
Co-authored-by: MemOS AutoDev <autodev@memos.ai>
Co-authored-by: MemOS AutoDev Bot <autodev@memtensor.local>
Co-authored-by: Jiang <33757498+hijzy@users.noreply.github.com>
Co-authored-by: GU TIANCHUN <96930846+TianchunGu@users.noreply.github.com>
Co-authored-by: Dubberman <48425266+whipser030@users.noreply.github.com>

* Revert "feat: chunk batch reflection scoring (#1957)" (#2145)

This reverts commit 21a27ed.

Co-authored-by: jiachengzhen <jiacz@memtensor.cn>

* test: stub overview count methods

---------

Co-authored-by: jiachengzhen <jiacz@memtensor.cn>
Co-authored-by: Memtensor-AI <project@memtensor.cn>
Co-authored-by: MemOS AutoDev <autodev@memtensor.ai>
Co-authored-by: Matthew <heimixiaozhuang@zju.edu.cn>
Co-authored-by: MemOS AutoDev <autodev@memos.ai>
Co-authored-by: MemOS AutoDev Bot <autodev@memtensor.local>
Co-authored-by: Jiang <33757498+hijzy@users.noreply.github.com>
Co-authored-by: GU TIANCHUN <96930846+TianchunGu@users.noreply.github.com>
Co-authored-by: Dubberman <48425266+whipser030@users.noreply.github.com>
Co-authored-by: zhaxi <syzsunshine219@gmail.com>

* fix(memos-local-openclaw): declare contracts.tools in plugin manifest (#1757)

OpenClaw runtime requires every plugin to declare contracts.tools
before registering agent tools. Without it, the gateway logs the
following warning on every registration:

    [plugins] plugin must declare contracts.tools before registering
    agent tools (plugin=memos-local-openclaw-plugin, source=index.js)

In a production OpenClaw deployment with active memos plugin, this
warning flooded gateway.err.log at ~1300 lines/day, masking real
errors and bloating logs by ~33MB/day.

This commit declares all 20 tools the plugin actually registers
(grep'd via `api.registerTool(...)` calls in index.ts):

  - memory_*: get, search, share, timeline, unshare, viewer, write_public
  - network_*: memory_detail, skill_pull, team_info
  - skill_*: file_get, files, get, install, publish, search, unpublish
  - task_*: share, summary, unshare

Verified: post-deploy gateway.err.log warning count = 0.

Refs: contracts.tools field per OpenClaw plugin spec (see
openclaw-lark and similar plugins as reference).

Co-authored-by: Richard-JC-Yan <150349207+Richard-JC-Yan@users.noreply.github.com>
Co-authored-by: jiachengzhen <jiacz@memtensor.cn>

* Fix #2148: [Bug] captureRunner uses reflectLlm (skill-evolver) for batch reflection — shoul (#2151)

* fix(plugin): wire capture reflection to main llm (#2148)

Capture batch reflection is a JSON-output task. When operators configure
skillEvolver.enableThinking=true alongside llm.enableThinking=false
(the typical Qwen3 setup), passing `deps.reflectLlm` into the capture
runner made the batch reflection run on the thinking-enabled skill-evolver
model, which emits <think> blocks that break JSON parsing and produce
`malformed JSON` errors during capture summarisation. Route the capture
runner's reflectLlm slot to `deps.llm` so JSON tasks stay on the
non-thinking main model, matching the l3Llm pattern from #1959.

- deps.ts: captureRunner.reflectLlm = deps.llm
- tests: regression guard in tests/unit/pipeline/capture-reflect-llm-wiring.test.ts

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(plugin): clarify reflectLlm exposure comment (#2148 OCR follow-up)

Enumerate the two read-only consumers that still reach `deps.reflectLlm`
(reward-runner evaluator metadata + Overview health card via
`resolveSkillEvolver`) and note that no other subscriber is wired to
it. Prevents future readers from misreading the previous prose as
"skill / capture also use reflectLlm".

---------

Co-authored-by: autodev-bot <autodev@memtensor.local>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: MemTensor Bot <bot@memtensor.local>
Co-authored-by: jiachengzhen <jiacz@memtensor.cn>

* test(logging): verify standalone bootstrap writes memos.log (#2147) (#2150)

* fix(local-plugin): call initLogger in bootstrapMemoryCoreFull

`bootstrapMemoryCoreFull` in `apps/memos-local-plugin/core/pipeline/memory-core.ts`
never invoked `initLogger(config, home)` after `loadConfig(home)`. The logger
stayed in the `bootstrapConsoleOnly()` fallback (console + memory buffer +
SSE broadcast only), so the `FileRotatingTransport` / `JsonlEventsTransport`
sinks were never wired and `MEMOS_HOME/logs/` (memos.log, error.log,
audit.log, llm.jsonl, perf.jsonl, events.jsonl) was left empty even though
`config.logging.file.enabled` defaults to true.

The fix adds `initLogger` to the logger import and calls it after the config
is resolved and before the first `rootLogger.child(...)` call. `initLogger`
is idempotent and swaps the active root in place, so existing child handles
keep working.

A new regression test `tests/unit/pipeline/bootstrap-init-logger.test.ts`
boots the pipeline against a tmp home, emits a marker line, and asserts
that `memos.log` is created on disk and contains the marker. It fails on
the previous code (ENOENT for memos.log) and passes with this fix.

Fixes #2147

* style(hermes): format daemon manager

---------

Co-authored-by: MemOS AutoDev <autodev@memtensor.local>
Co-authored-by: jiachengzhen <jiacz@memtensor.cn>

* fix: align generated skill guidance (#2100)

* feat(api): align OpenMem v1 SDK with cloud API

* fix: align generated skill guidance

* feat(api): align OpenMem v1 SDK with cloud API

* fix(memos-local): include json hint in user messages (#1756)

Co-authored-by: shinetata <149466187+shinetata@users.noreply.github.com>

---------

Co-authored-by: dingliang <2650876010@qq.com>
Co-authored-by: Qi Weng <bittergreen.wengqi@hotmail.com>
Co-authored-by: de1ty <7804799+de1tydev@users.noreply.github.com>
Co-authored-by: shinetata <149466187+shinetata@users.noreply.github.com>
Co-authored-by: jiachengzhen <jiacz@memtensor.cn>

* fix: Add dedicated document LLM, improve Markdown detection and context-aware structured extraction

---------

Co-authored-by: Memtensor-AI <project@memtensor.cn>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: bittergreen <bittergreen.wengqi@hotmail.com>
Co-authored-by: dingliang <2650876010@qq.com>
Co-authored-by: Erick Wipprecht <53452309+chiefmojo@users.noreply.github.com>
Co-authored-by: jiachengzhen <jiacz@memtensor.cn>
Co-authored-by: shinetata <149466187+shinetata@users.noreply.github.com>
Co-authored-by: Wenqiang Wei <46308778+endxxxx@users.noreply.github.com>
Co-authored-by: MemOS AutoDev <autodev@memtensor.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: memos-autodev[bot] <autodev-bot@users.noreply.github.com>
Co-authored-by: HarveyXiang <harvey_xiang@163.com>
Co-authored-by: autodev-bot <autodev@memtensor.local>
Co-authored-by: Jiang <33757498+hijzy@users.noreply.github.com>
Co-authored-by: GU TIANCHUN <96930846+TianchunGu@users.noreply.github.com>
Co-authored-by: Dubberman <48425266+whipser030@users.noreply.github.com>
Co-authored-by: Fayenix <fayenix@users.noreply.github.com>
Co-authored-by: de1ty <7804799+de1tydev@users.noreply.github.com>
Co-authored-by: MemOS AutoDev <autodev@memtensor.ai>
Co-authored-by: Matthew <heimixiaozhuang@zju.edu.cn>
Co-authored-by: MemOS AutoDev <autodev@memos.ai>
Co-authored-by: Richard-JC-Yan <128337844+Richard-JC-Yan@users.noreply.github.com>
Co-authored-by: Richard-JC-Yan <150349207+Richard-JC-Yan@users.noreply.github.com>
Co-authored-by: MemTensor Bot <bot@memtensor.local>
Co-authored-by: Ziyang Guo <121015044+RerankerGuo@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:plugin OpenClaw & Hermes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants