• - chat:ledger becomes the only conversation store: model messages land as
      user/tool_call(payload)/tool_result/ai events via EventLogChatMemory
      (per-event rightPush, no more whole-blob read-modify-write races)
    - EventProjectionService: one renderer for both frontend history and LLM
      context (system+flow card / deterministic digest of expired turns /
      ~6000-char verbatim recent turns, current turn payload-exact)
    - pinned active-flow card (form pending / proposal pending / skill_active
      full text) unpinned on executed/cancelled/skill_done
    - anti-fab retry nudges logged as internal user events (LLM-visible,
      hidden from history); grounded suffixes never duplicate user events
    - delete ProjectedChatMemory; RedisChatMemoryStore demoted to legacy
      read-only compat (old convs age out via 30d TTL); conv list lazy cleanup
    - tests: projection correctness + concurrent stream/confirm append safety
    zichun authored
     
    Browse Code »
  • …p+CAS, tenant enforcement
    
    Audit findings 1-5 confirmed real; root causes were (a) identity self-reported by
    the client, (b) every failure path defaulting open.
    
    - ERP /ai/whoami (saas-8s+ @c8e0057 follow-up) resolves token → real user/tenant/type;
      AuthzService.resolveIdentity is now the single identity entry point. Client-supplied
      userid/brandsid/usertype removed from all request bodies and from chat.html.
    - dev-login is a real switch (erp.dev-login.enabled, default false) and its ERP creds
      no longer have built-in admin/666666 defaults; blank token in production → 401,
      never a silent fall back to the dev (sysadmin) account.
    - conversations/op/form endpoints require login; conversation ids are namespaced by
      user id and ownership-checked (403 otherwise); /op/pending no longer returns sPayload.
    - op confirm/cancel check the proposer, and confirm claims the draft via CAS so
      concurrent/repeat confirms cannot execute twice; bill numbers are regenerated at
      execution time instead of replaying the propose-time snapshot.
    - FK options take the tenant from the introspected identity and return empty rather
      than dropping the sBrandsId predicate.
    - secrets moved to env vars (DB_URL/DB_USERNAME/DB_PASSWORD/REDIS_*/LLM_*/ERP_BASEURL);
      allowMultiQueries=false. NOTE: the previously committed credentials must be rotated.
    - ids interpolated into ERP URLs are validated (safeId) to stop query/path injection.
    - update path rejects system columns, resolves FK names, and coerces by column type;
      numeric coercion now rejects unparseable input instead of writing 0/truncating, and
      proposal summaries show the value that will actually be written.
    - ResponseStatusException keeps its 401/403 status instead of being wrapped as 200.
    - anti-fabrication guard stays on when the intent gate itself fails.
    zichun authored
     
    Browse Code »
  • …ction, anti-fabrication guards
    
    - LedgerService: append-only chat:ledger:{conv} events incl. deterministic-path
      form/clarify/proposal/confirm-outcome; history replays from ledger
    - StateService: chat:state:{conv} slots (上轮意图/最近实体/在办单据) written by code,
      fed to intent gate + write-slot extraction and appended to agent user text
    - ProjectedChatMemory replaces MessageWindowChatMemory: full store + char-budget
      projection, old tool results collapsed to one line, current turn kept intact
    - RedisChatMemoryStore.appendTurn patches memory holes on deterministic paths;
      op confirm/cancel outcomes recorded to ledger/state/memory
    - anti-fabrication (tool_choice=required NOT enforced by Ollama 0.32.3, retested):
      query turn zero-tools+digits → one forced retry then flag; write claim without
      proposeWrite → corrective notice
    zichun authored
     
    Browse Code »

  • - RedisChatMemoryStore (ChatMemoryStore): persist per-conversation messages to Redis (chat:mem:{convId}, 30d TTL) -> survives restart; agent memory now Redis-backed via MessageWindowChatMemory.builder().chatMemoryStore(...)
    - ConversationService: per-user conversation metadata (chat:convs:{userId} hash), title from first message, list/create/delete/history
    - ConversationController: GET/POST/DELETE /api/agent/conversations + GET /{id}/messages
    - AgentChatController: touch conversation on each turn
    - chat.html: conversation sidebar (list/switch/new/delete), load history on switch, refresh on send
    
    Verified: multi-turn memory recalls facts; after full restart, conversation list + history + memory all persist (agent still recalls prior-turn facts).
    zichun authored
     
    Browse File »