Group-scoped agents could read sessions from other groups via session
tools (sessions_list, sessions_history, session_status, sessions_send)
because they only checked agent_key, not group context. This caused
cron jobs to leak data from unrelated groups into reports.
Add isSessionInScope() guard to all 4 session tools with colon-bounded
chatID matching. New share_sessions setting (default false) controls
cross-group visibility, following the same pattern as share_memory and
share_knowledge_graph. Web UI toggle and i18n strings included.
63 test cases covering guild/DM/group users, realistic Zalo IDs,
boundary exactness, multi-colon chatIDs, and the exact bug scenario.
Closes#532
- Replace prefix truncation with SHA-256 hash-based shortening for oversized tool call IDs (40-char OpenAI/Azure limit)
- Normalize provider-prefixed model IDs (e.g. openai/o3-mini) before capability checks for temperature and max_completion_tokens
- Add regression tests for ID collision, correlation, and prefixed model routing
Root cause: pool.go applied busy_timeout PRAGMA via db.Exec() which only
affects one connection in the pool. Other connections had no busy_timeout,
causing immediate SQLITE_BUSY errors during concurrent startup operations
(agent creation, WebSocket connect, health checks). This silently aborted
context file seeding — BOOTSTRAP.md, USER.md, AGENTS.md all missing from
system prompt on first interaction.
Fix (3 layers):
1. pragmaConnector: wraps sql.Driver to apply PRAGMAs (busy_timeout, WAL,
etc.) on every new connection, not just one. All SQLite queries benefit.
2. CacheInvalidateFunc: clears ContextFileInterceptor cache after seeding
so LoadContextFiles sees newly seeded files on the first turn.
3. fallbackBootstrap: if DB seed still fails, injects embedded templates
in-memory so the first turn still gets onboarding. Clears after use.
- Split EnsureUserFilesFunc into EnsureUserProfileFunc (profile + workspace)
and SeedUserFilesFunc (context file seeding) for single-responsibility
- Merge userWorkspaces + userFilesSeeded sync.Maps into unified userSetups
struct to prevent desync between workspace and seeding state
- Add skipIfAnyExist param to SeedUserFiles to encapsulate the
"seed only for brand-new users" logic within the bootstrap package
- Extract getOrCreateUserSetup helper for clean per-user initialization
- Add bootstrap state tests covering all 4 system prompt branches
- Keep legacy EnsureUserFilesFunc as fallback for backward compatibility
Replace inline workspace path computation in loop_context.go with composable
WorkspaceLayer pipeline (tenant → team → project → user/chat). Each layer is
a pure function that appends a path segment or is a no-op.
- New workspace_resolver.go: ResolveWorkspace, TenantLayer, TeamLayer,
ProjectLayer (future), UserChatLayer, SanitizePathSegment
- 16 unit tests covering all layer combinations
- Migrate loop_context.go, loop_history.go, team_tasks_mutations.go
- Move sanitizePathSegment from agent to tools package (exported)
- Zero behavior change — identical paths for all scenarios
- Add embedding column to kg_entities with HNSW index
- Generate entity embeddings during IngestExtraction
- Hybrid search in SearchEntities (ILIKE 0.3 + vector 0.7)
- Add team_id scope for shared KG across team members
- Add IsSharedKG/WithSharedKG context helpers
Closes#327
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Simplified approach to fix duplicate tool_call_ids that cause HTTP 400 on OpenAI-compatible APIs (OpenRouter, vLLM, DeepSeek).
- uniquifyToolCallIDs: appends runID+iteration+index to all IDs at response time, guaranteeing cross-turn uniqueness via UUID
- sanitizeHistory: simple seen-set dedup for legacy sessions with pre-existing duplicate IDs
- Anthropic guard: skips ID rewriting when RawAssistantContent is present
- Unit tests for both paths + edge cases
Closes#283
Major refactoring of the team system with multiple improvements:
## Removed legacy delegation tools
- Delete `delegate.go`, `delegate_async.go`, `delegate_sync.go`, `delegate_events.go`,
`delegate_policy.go`, `delegate_prep.go`, `delegate_state.go`, `delegate_search_tool.go`
- Delete `evaluate_loop_tool.go`, `handoff_tool.go`
- Remove all references and registrations from tool manager and policy
- Clean up TEAM_PLAYBOOK_IDEAS.md and TEAM_SYSTEM.md (moved to docs)
## Rename await_reply → ask_user
- Rename action `await_reply` → `ask_user`, `clear_followup` → `clear_ask_user`
- Rename functions `executeAwaitReply` → `executeAskUser`, `executeClearFollowup` → `executeClearAskUser`
- Update system prompt with stronger wording to prevent model misuse
- Model was confusing "await_reply" with general waiting; "ask_user" is unambiguous
## Fix auto-followup false positives
- Add `HasActiveMemberTasks(ctx, teamID, excludeAgentID)` store method
- Guard `autoSetFollowup()` in consumer: skip when lead has active member tasks
- Prevents auto-followup when lead is orchestrating teammates (not waiting for user)
## Task identifier zero-padding
- Change format from `T-1-xxxx` → `T-001-xxxx` (3-digit minimum)
## Refactor workspace WS handlers to filesystem-only
- Rewrite `teams.workspace.list/read/delete` to use pure filesystem (os.ReadDir/ReadFile/Remove)
- Remove DB dependency from workspace WS handlers
- Consistent with storage handler and workspace tools
- Simplify TeamWorkspaceFile type and frontend hook
## Add team events listing API
- New WS method `teams.events.list` with team_id, limit, offset params
- New HTTP endpoint `GET /v1/teams/{id}/events` with bearer auth
- New `ListTeamEvents(ctx, teamID, limit, offset)` store method
- JOIN with team_tasks for team-wide event filtering
## Extract team access policy
- New `team_access_policy.go` — centralized team tool access control
## Migration 000019: team_id columns
- Add team_id foreign key columns to relevant tables
## Other improvements
- Add team_id propagation through agent loop, tracing, sessions
- Update i18n locale files (en/vi/zh) for new tool labels
- Update frontend builtin-tools page and require-setup component
- Bump RequiredSchemaVersion for migration 000019
Add independent `share_memory` config flag to control memory and
knowledge graph sharing separately from workspace folder isolation.
- Add ShareMemory field to WorkspaceSharingConfig
- Decouple WithSharedMemory(ctx) from shouldShareWorkspace() in loop.go
- Add shouldShareMemory() helper independent of workspace sharing
- Fix KG Traverse CTE to scope user_id in recursive step (pre-existing bug)
- Add memory toggle UI with violet styling in workspace sharing section
- Add i18n translations (en/vi/zh) for new memory sharing controls
- Add unit tests for shouldShareMemory() independence
Add workspace_sharing config in other_config JSONB to control per-user
workspace isolation. When enabled, users share the base workspace directory
instead of isolated subfolders — configurable separately for DMs and groups,
with a per-user allowlist override.
Backend: WorkspaceSharingConfig struct, ParseWorkspaceSharing(), conditional
isolation in loop.go/loop_history.go, 7 unit tests.
Frontend: prominent always-visible config section with contact search
combobox, sticky save bar layout fix, i18n (en/vi/zh).
- Add tool status display on channels during tool execution (streaming preview + reactions)
- Emit agent.activity events at phase transitions (thinking, tool_exec, compacting)
- Enrich delegation progress with per-member activity and tool info
- Add LLM-based intent classifier for DM status queries when agent is busy
- Keyword fast-path for cancel/status patterns (no LLM cost)
- Falls back to LLM classification with 5s timeout
- Supports status_query (immediate reply) and cancel (abort run) intents
- Register/unregister runs in makeSchedulerRunFunc for channel inbound tracking
- Add sessionRuns secondary index in Router for O(1) IsSessionBusy lookups
- Add intent_classify config toggle (global default + per-agent override)
- Add tool_status config toggle for channel tool status display
- Add i18n keys and translations (en/vi/zh) for status messages
- Add web UI config toggles for intent_classify and tool_status