• - 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 »
  • - drop jQuery + page-in-page card; collapsible sidebar, single scroll area,
      light minimal style, markdown tables (markdown-it)
    - collectForm: grid layout, required flags, typed controls, enum dropdown,
      FK secondary picker modal (multi-column list / search / paging / select)
    - form submit posts structured fields to /api/agent/form/submit (no NL re-encoding);
      legacy FORM_SUBMIT_MARK route removed
    - askUser card: option chips + always-available free-text input
    - history replays from ledger endpoint; localStorage history dropped
    zichun authored
     
    Browse Code »
  • …r free-input flag, readFormData paging
    
    - POST /api/agent/form/submit: structured fields go straight to
      proposeWrite(action=create), no LLM re-encoding; ledger/state/memory recorded
    - GET /api/agent/form/options: {columns,rows,total,page,pageSize} with name search
    - askUser result carries allowFree=true (last option is always free text)
    - readFormData(page?) with page shown in output
    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 »

  • - delete queryData (QueryTool, sqlChatModel bean, llm.sql-model, jsqlparser dep)
    - delete kgSearch (findForms stays), delete loadSkill (SkillTool/SkillService)
    - single system prompt (domain map + 业务常识) replaces 3 ToolScope versions; ToolScope removed
    - intent gate reduced to 查询/新增/操作已有单据/其他 with class definitions only;
      write action derived from utterance in router
    - dedup: shared locateRecord() in ProposeWriteTool; name-field/label lookups
      consolidated into FormResolverService
    zichun authored
     
    Browse Code »
  • zichun authored
     
    Browse Code »
  • P4 retirement + code slim-down (~18.5k lines deleted), on top of the
    intent-gate WIP (docs §17/§18 + agent/tool refinements):
    
    - Delete old 8-scene multi-agent stack: XlyErpService, SceneSelector/
      Chati/ErpAi/DynamicTableNl2Sql agents, DynamicToolProvider (62 meta
      tools), Scene/ToolMeta/ParamRule entities+mappers+startup caches
    - Delete whole milvus/tts/ocr packages, /api/tts + /api/ocr endpoints,
      tts.html, python stream_server.py; strip dead TTS JS from chat.html
      (playByIndex/handleNormalResponse had no callers)
    - Three-pass orphan sweep: 17 dead utils, 16 dead entities, dead
      constants/exceptions, RedisService+RedisConfig, duplicate
      JacksonConfig, OperableChatMemoryProvider, old prompt generators;
      fold PageController into MvcConfig; trim OkHttpUtil 495→39 lines
    - pom: ~35→13 deps (drop mybatis/JPA/webflux/tika/pdfbox/poi/jieba/
      jsoup/gson/fastjson2/springdoc/mapstruct/pagehelper/pinyin4j/
      spring-retry/jnr-ffi/hutool/ocr/milvus/embeddings; add starter-jdbc);
      war 200M+→48M
    - application.yml: drop milvus/tts/ocr/tesseract/mybatis blocks;
      GlobalExceptionHandler returns plain {code,message} JSON
    - docs: mark P4 retirement done in agent-architecture §13/§18
    
    Verified: mvn clean package OK; boot smoke on :8199 (Started 0.9s,
    /chat 200, health db UP). Old /api/tts & /api/ocr now 404 by design.
    zichun authored
     
    Browse Code »

  • Add a deterministic §5 intent stage before ReAct: extract {intent, form,
    entities, missing slots} via constrained JSON, then expose only the 3-5
    tools relevant to that intent instead of all 12.
    
    New:
    - agent/Intent, agent/ToolScope: intent + visible-tool-set model
    - service/IntentService: constrained-JSON intent/entity extraction
    - service/OllamaJsonClient: JSON-mode Ollama calls
    - service/SlotFillService: slot extraction/prefill of known values
    
    Wire through AgentFactory, SystemPromptService, QueryTool,
    ProposeWriteTool, ErpClient, AgentChatController, OpController, chat.html.
    zichun authored
     
    Browse Code »

  • …ormCollect + Skills + KgSearch
    
    - AgentIdentity + AgentFactory: build agent per request with token+form-allowlist carried in tool instances (per-call context; robust vs LC4j callback threading)
    - ErpClient: token-aware read/write/examine; user-token expiry does NOT silently re-login as dev-admin (no privilege escalation)
    - AuthzService: devIdentity()/userIdentity() resolve granted-form set (sAuthsId)
    - proposeExamine tool + OpController examine dispatch + forward user Authorization on confirm
    - InteractionTool.askUser (structured options), FormCollectTool.collectForm (real gdsconfigformslave schema)
    - SkillService + SkillTool.loadSkill + ai_skill digest in system prompt
    - KgQueryTool.kgSearch: L2 neighbors/flow + L3 field->table
    - tools ErpReadTool/ProposeWriteTool/QueryTool/FormCollectTool now per-request (not @Component)
    zichun authored
     
    Browse File »
  • …i_op_queue staging cols + ai_skill registry
    
    - proposeCreate tool -> ai_op_queue draft(payload) -> confirm executes addBusinessData
    - ai_op_queue: add sPayload/sSourceRef/bAutoExecute/sResultBillId/sErrorMsg/tExecutedDate/tExpireAt (create/examine/auto-flow ready)
    - ai_skill registry table + seed playbooks (Skills §6)
    - QueryTool: self-repair retry (feed SQL error back to model, up to 3x)
    - TracingChatModelListener: per-call LLM latency/token/error trace (Langfuse hook point)
    zichun authored
     
    Browse Code »
  • - AuthzService: form-level allowlist ported from backend getsAuthsIdNew (sysjurisdiction sKey set); sysadmin=all; wired into Read/lookupRecord/proposeUpdate/proposeDelete (admin dev-login => all-access; enforces per-user once prod passes through user token)
    - proposeDelete + ErpClient.deleteForm (handleType=del) + OpController branches op type; HITL-gated like update
    - docs/security-findings.md: report of the 5 current-system vulns (backend form-perm off, read-API-can-write, NL2SQL no-tenant/over-privilege/partial-SQL-safety/cache-skips-validation) for the business
    - Verified end-to-end: read after authz = 93; propose-delete card -> confirm -> row actually deleted; audit rows written.
    zichun authored
     
    Browse File »

  • - ai_op_queue: staging table for AI write ops (draft|confirmed|executed|failed|cancelled)
    - ProposeWriteTool.proposeUpdate(entity, record, field, newValue): self-resolves the entity master table (excludes viw_* report views), resolves field via 字段字典, locates the unique record + old value, stages a DRAFT to ai_op_queue — DOES NOT execute; returns a proposal
    - ErpClient.updateForm: executes via ERP addUpdateDelBusinessData with the frontend-compatible payload {data:[{sTable,name:master,column:[{handleType:update,sId,field:value}]}]}
    - OpController: deterministic (non-LLM) POST /op/{id}/confirm (executes + writes back status) / cancel / GET pending
    - AgentChatController.onToolExecuted: on proposeUpdate, attach conversation + push write_proposal SSE
    - chat.html: confirm/cancel card + result echo
    - system prompt: proposeUpdate flow; never claim a write is done before confirm
    
    Verified end-to-end: '把必胜客简称改成必胜客中国' -> proposal card -> confirm -> ERP update -> DB changed; cancel -> DB unchanged. Nothing executes without explicit user confirm.
    zichun authored
     
    Browse File »
  • - 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 »
  • - ReActAgent: AiServices streaming tool-loop (replaces 8-scene SceneSelector path)
    - AgentConfig: single agent = streaming Ollama(qwen2.5:14b) + KgQueryTool + per-conversation memory + L1-domain system prompt
    - SystemPromptService: injects L1 domain map (viw_kg_domain) into system prompt; degrades gracefully if KG views missing
    - KgQueryTool.findForms: read-only form-catalog lookup over local viw_ai_useful_forms (global metadata, no tenant) — interim formId resolver
    - AgentChatController: POST /api/agent/chat SSE stream ({type:token|reset|done|error}); reset drops pre-tool-call narration
    - chat.html: consume the new SSE endpoint (streamed), per-page conversationId, drop TTS-borrow path
    - application-saaslocal.yml: add to worktree (was master-only untracked -> profile silently hit remote DB)
    
    Verified running (saaslocal, :8099): streaming chat, tool loop returns real forms, write requests correctly report 'under development'.
    zichun authored
     
    Browse File »