Commit Graph
510 Commits
Author SHA1 Message Date
viettranx 4678065887 refactor: remove dead quality gates / hook engine code
The delegation system this depended on was previously removed,
leaving internal/hooks/ as dead code with zero imports. Remove
the entire hook engine, UI config section, protocol types, i18n
keys, and all documentation references.
2026-03-17 18:00:09 +07:00
viettranx 6106d3d04f fix(teams): show task_number in create result and allow search to satisfy gate 2026-03-17 17:11:22 +07:00
viettranx de2eca9acb fix(i18n): keep "workspace" untranslated in Vietnamese locale 2026-03-17 16:10:22 +07:00
viettranx 946cbb9eb1 fix(teams): allow search action to satisfy list-before-create gate
Search now acquires the team create lock and marks listed, so leaders
can use search instead of list before creating tasks — reduces token
consumption by returning only matching tasks instead of the full board.
2026-03-17 16:00:59 +07:00
viettranx 70f3e1f5d5 feat(tools): auto-deliver write_file results as channel attachments
Flip write_file deliver param default from false to true so result files
(reports, articles, generated content) are automatically sent as document
attachments to chat channels without requiring explicit LLM opt-in.
Add .md MIME type to mimeFromExt for proper markdown file delivery.
2026-03-17 15:43:24 +07:00
viettranx 66de3504d4 feat(teams): add client-side pagination to task list view
Use existing usePagination hook (default 20/page) with Pagination
component. Select-all checkbox applies to current page only; selections
persist across pages.
2026-03-17 15:25:59 +07:00
viettranx ae3e5cebcf feat(teams): add multi-select checkboxes and bulk delete to task list
Add checkbox column to task list view for selecting terminal-status
tasks (completed/failed/cancelled). Header checkbox supports select-all
with indeterminate state. Bulk action bar appears on selection with
delete button that opens ConfirmDeleteDialog requiring user to type
"delete" to confirm.

Backend: new teams.tasks.delete-bulk RPC method with DeleteTasks batch
SQL (DELETE ... WHERE id = ANY($1) RETURNING id). Broadcasts delete
event per task for real-time UI sync.

i18n: added bulk action keys for en/vi/zh.
2026-03-17 15:10:43 +07:00
viettranx aeadb20ba7 fix(teams): deduplicate task notifications and batch with debounce
Remove premature EventTeamTaskAssigned broadcast in executeCreate() that
caused duplicate "assigned to" Telegram notifications. Assignment
notification now only fires at actual dispatch (post-turn, fallback, or
unblocked).

Add TeamNotifyQueue (2s debounce, cap 20) to batch rapid-fire task
notifications per chat — reduces N messages to 1 when leader dispatches
multiple tasks at once. In leader mode this also reduces agent turns
from N to 1.

Also fix: ResetTaskStatus now clears progress_percent/progress_step on
retry, and retry broadcast includes TaskNumber/Subject for correct
notification formatting.
2026-03-17 14:29:52 +07:00
viettranx 8c662e7af4 fix(teams): hide progress bar on completed tasks
Clear progress_percent in DB on all terminal transitions (complete,
cancel, fail, approve, reject). Also hide progress bar in UI for
terminal statuses as a safety net (kanban, list, detail dialog).
2026-03-17 14:19:29 +07:00
99dd363b13 feat(mcp): lazy-activate deferred tools on direct call in search mode (#235)
* feat: Implement MCP manager for server connections, tool registration, and deferred tool loading for agents.

* feat: Add tests for deferred tool activation logic within the tool registry and agent loop.

* fix(mcp): prevent deny list bypass via lazy activation + fix idempotency race

- Add PolicyEngine.IsDenied() to check deny patterns (incl. group: expansion)
  before allowing lazily-activated deferred tools to execute
- Check IsDenied() in both single-tool and parallel execution paths in loop.go
- Make ActivateToolIfDeferred idempotent by checking activatedTools before
  returning false, preventing concurrent goroutines from being blocked
- Add tests for deny-on-first-call, group deny patterns, and idempotent
  concurrent activation

---------

Co-authored-by: viettranx <viettranx@gmail.com>
2026-03-17 13:18:27 +07:00
1c4dce0ccf feat(telegram): implement robust message splitting and dynamic HTML retry logic (#236)
* feat(telegram): implement robust message splitting and dynamic HTML retry logic

* fix(telegram): fix sendHTML error chain regression and add split depth limit

- Re-check err.Error() in thread-not-found handler instead of stale errStr,
  restoring the original chained fallback behavior
- Add maxSplitDepth (5) to prevent unbounded recursion when Telegram
  repeatedly rejects split chunks
- Rename misleading test case to reflect actual monolithic fallback behavior

---------

Co-authored-by: viettranx <viettranx@gmail.com>
2026-03-17 13:01:20 +07:00
GoonandGitHub 9429a7c844 ci: publish Docker images to GHCR and Docker Hub (#237)
* feat(ci): add node/python/full runtime variants to Docker publish

Add runtime image variants alongside existing build-tag variants:
- :node (pre-installed Node.js)
- :python (pre-installed Python)
- :full (Node.js + Python + all skill deps)

* feat(ci): add install scripts, release binaries, and Discord notifications

- scripts/install.sh: one-liner binary installer from GitHub Releases
- scripts/setup-docker.sh: interactive Docker setup with variant selection
  (alpine/node/python/full), auto-generates .env + docker-compose.yaml
  with persistent volumes for data, skills, workspace, storage
- release.yaml: build cross-platform binaries (linux/darwin × amd64/arm64)
  and attach to GitHub Release, notify Discord on new releases
- .gitignore: ignore Apple Double (._*) files from external volumes

* docs(docker): add pre-built image references and update docker-compose workflows

- Add `image:` directives to docker-compose.yml, docker-compose.selfservice.yml, and docker-compose.upgrade.yml pointing to ghcr.io/nextlevelbuilder/goclaw pre-built images
- Add Docker Hub mirror references (digitop/goclaw) for public access
- Document available image tags (latest, node, python, full, otel, tsnet, redis) with descriptions
- Update README with pre-built image pull instructions and semver tag examples
- Clarify deployment workflows: use pre-built images by default (no --build), add --build only when building from source
- Update upgrade workflow to pull pre-built images instead of rebuild
- Add note about build args requirement for otel/tsnet/redis overlays
- Update .dockerignore to exclude macOS temp files (._*)

* fix(ci): use claude_code_oauth_token instead of anthropic_api_key
2026-03-17 12:44:18 +07:00
viettranx 97cacfe68b feat(teams): member task progress reminder + fix broken progress notifications
- Fix progress event payload missing TaskNumber, Subject, OwnerAgentKey,
  ProgressPercent, ProgressStep — notifications were rendering empty
- Fix progress notification format to include task name (consistent with
  dispatched/failed) and guard empty ProgressStep
- Change percent tool schema from number to integer for clarity
- Add pre-run member task reminder injecting task context before LLM loop
- Add mid-loop progress nudge every 10 iterations with suggested percent
  based on iteration ratio (handles maxIter=0 unlimited case)
- Enhance leader cross-session reminder to show progress % when available
- Strengthen TEAM.md member guidance: focus, result quality, progress rules
- Add progress bar to task list table view (matches kanban card pattern)
2026-03-17 12:43:09 +07:00
viettranx 9a4a5ef2c1 docs: add Mermaid diagrams to agent evolution docs
Add 5 Mermaid charts: system overview, skill learning lifecycle,
tool operations flow, visibility state machine, security layers.
2026-03-17 12:29:09 +07:00
viettranx 8d1c270427 docs: add agent evolution & skill management documentation
Cover the 3 undocumented subsystems: self-evolution (SOUL.md),
skill learning loop (skill_evolve), and skill management
(skill_manage tool, HTTP/WS API, grants, security model).
2026-03-17 12:25:20 +07:00
viettranx 32f84f8f1c fix(ui): prevent white screen on navigation with ErrorBoundary + chunk retry
- Add ErrorBoundary wrapping routes (outer) and Outlet (inner with key reset)
- Replace lazy() with lazyWithRetry() to retry failed chunk loads + auto-reload
- Invalidate all queries on WS reconnect to prevent stale/empty data renders
2026-03-17 12:20:14 +07:00
viettranx d205691a13 fix(skills): hide skill_manage from LLM when skill_evolve is off
- skill_manage builtin tool default Enabled: true (available in registry)
- When skill_evolve=false: filter skill_manage from both tool definitions
  (API params) and system prompt tooling section — agent has zero awareness
- When skill_evolve=true: tool visible + system prompt guidance + nudges
- Update UI hints to reflect tool is available by default
2026-03-17 12:05:48 +07:00
viettranx 0dea765b74 fix(ui): reorder agent config sections + hide managed keys from Other Config
- Move Self-Evolution + Skill Learning sections before LLM Config (right after Identity)
- Strip managed keys (emoji, self_evolve, description, skill_evolve, skill_nudge_interval)
  from Other Config raw JSON editor so they don't leak as editable JSON
- Fix info text: remove hardcoded "5+ tool calls" — nudge threshold is configurable
2026-03-17 11:57:39 +07:00
viettranx b2a74ba487 feat(skills): skill_manage tool + skill_evolve learning loop (#218)
Adds skill_manage — a first-class agent tool for creating, updating, and
deleting skills from within a conversation — paired with per-agent
skill_evolve that nudges predefined agents to capture reusable workflows.

Tool (skill_manage):
- create: write skill from SKILL.md content string (auto-grant, dep scan)
- patch: find/replace producing new immutable version (advisory-locked)
- delete: soft-delete (archive in DB, move to .trash/)
- Security guard: 25 regex patterns block shell injection, credential exfil,
  path traversal, SQL injection, privilege escalation
- Ownership enforced: only skill owner can patch/delete (admin bypass)
- Content size limit: 100KB; companion file copy: 20MB, symlink-safe (WalkDir)
- Enabled: false by default — admin opt-in per agent

Learning loop (skill_evolve, predefined agents only):
- System prompt: SHOULD/SHOULD NOT guidance for skill creation
- Budget nudges: [System] prefix at 70%/90% iteration budget (ephemeral, i18n)
- Postscript: once-per-run suggestion with explicit user consent
- Config: other_config.skill_evolve + skill_nudge_interval (default 15)

Security hardening (pre-existing + new):
- CreateSkillManaged: RETURNING id + pg_advisory_xact_lock (atomic upsert)
- GetNextVersionLocked: advisory lock for race-safe patch versioning
- Ownership checks on HTTP update/delete, gateway update, 4 grant/revoke handlers
- copyOtherFiles: filepath.WalkDir for real symlink detection

UI: Skill Learning toggle + nudge interval in Agent General Tab
i18n: backend (en/vi/zh catalogs) + frontend (en/vi/zh locale files)
2026-03-17 11:38:35 +07:00
viettranx 0a78902f9b fix(ui): add loading spinners and toast feedback to create/update modals
- TeamMembersDialog: Loader2 spinner on add/remove, toast on success/error
- TeamCreateDialog: Loader2 spinner on create button
- TeamSettingsTab: Loader2 spinner on save button (was text-only)
- ProviderFormDialog: Loader2 spinner on save/create button
- Add i18n keys for member add/remove toast (en/vi/zh)
2026-03-17 10:53:07 +07:00
viettranx 1b876231a1 fix(telegram): default draft_transport to false to prevent reply-deleted artifacts
sendMessageDraft causes "reply to deleted message" artifacts on some
Telegram clients (tdesktop#10315). Disable by default so content
streaming uses message transport (same as reasoning stream).
2026-03-17 09:26:34 +07:00
viettranx baa4bb6d45 refactor(teams): batch teammate announce via queue with dedup
Extract announce logic from handleTeammateMessage into a dedicated
announce queue that batches concurrent task completions. Handles
failure reporting and deduplicates rapid-fire announces.
2026-03-17 09:26:00 +07:00
viettranx fe68bd86bc perf(tools): cache LoadContextFiles + fix task detail dialog UX
- Cache agent/user context files via existing agentCache/userCache (TTL 5min)
- Extract cachedAgentFiles/cachedUserFiles helpers, DRY readAgentFile/readUserFile
- Move delete button away from close in task detail modal
- Replace native window.confirm with ConfirmDialog component
2026-03-17 09:25:37 +07:00
viettranx ca44b7279f feat(bootstrap): predefined agents keep full system prompt during onboarding
Predefined agents now retain all tools and system prompt sections when
BOOTSTRAP.md is present, instead of entering slim mode with only write_file.
Open agents keep the existing slim bootstrap mode.

- Gate tool filtering and IsBootstrap on agentType != "predefined"
- Add FIRST RUN reminder for predefined agents (without tool restriction)
- Skip bootstrap/user seeding for team-dispatched sessions (IsTeamSession)
- Group chats skip BOOTSTRAP.md entirely
- Track bootstrapWriteDetected + inject nudge after 2 turns without write_file
- Update templates: never reveal process, no capability listing, no "locked"
- Cache LoadContextFiles via existing agentCache/userCache (TTL 5min)
2026-03-17 09:25:23 +07:00
viettranx 1ca1e41844 refactor(teams): slim down task list/detail JSON for model
Remove fields the model doesn't need from tool response DTOs:
- owner_agent_id → model uses owner_agent_key
- created_by_agent_id → model uses created_by_agent_key
- task_number → model uses identifier (e.g. "T-018-5e65")

Reduces context token consumption. UI (WS API) unaffected —
uses separate TeamTaskData struct with all fields.
2026-03-17 07:15:18 +07:00
badgerbeesandGitHub 365f41f81c fix: pass custom name to DashScopeProvider for correct registry lookup (#228) 2026-03-16 22:54:10 +07:00
badgerbeesandGitHub 5c0612a07f fix(providers): prevent Gemini thought_signature from leaking to other providers and breaking validation (#230)
* fix: prevent gemini thought_signature from leaking to other providers

* test: refine gemini model detection for robust provider multiplexing
2026-03-16 22:53:21 +07:00
Viet TranandGitHub 037d18f711 docs: comprehensive audit and update of all documentation (#231)
* feat(ui): improve kanban UX, fix dialog scroll, remove delegation page

- Kanban: reorder columns (blocked after pending), show blocked-by info
  on cards, clickable blocker links in task detail, framer-motion card
  animation between columns
- Dialogs: standardize scroll pattern across all modals — header fixed,
  scrollbar flush with outer edge via negative margin trick
- Remove delegation page, types, events, i18n, routes, and all references
- Fix activity_logs NULL jsonb scan error (COALESCE)
- Board header: show text labels on action buttons (desktop)

* docs: comprehensive audit and update of all documentation

- Update Go 1.25 → 1.26, PostgreSQL 15+ → 18 across all docs
- Add 10 missing internal modules to CLAUDE.md project structure
- Expand provider docs from 2 to 6 packages (Anthropic, OpenAI, DashScope, Claude CLI, ACP, Codex)
- Add 8 missing store interfaces to data model docs (22 total)
- Update bootstrap files from 7 to 13 templates
- Expand tool inventory from ~35 to 60+ tools with media/KG/credential categories
- Fix Team Task Board: add blocked status, 3 missing actions, V2 versioning, delegate restrictions
- Remove all references to removed features: handoff, delegate_search, evaluate_loop, agent_links
- Fix lane defaults (2/4/1 → 30/50/100/30), ghost file references, models.list → providers.models
- Add SecureCLI, snapshot worker, cost calculation, pairing security docs
- Comprehensive changelog catch-up
- Trim docs/03-tools-system.md to 800-line limit
2026-03-16 22:51:57 +07:00
viettranx 446feda3cd fix(ui): workspace dialog scope filter + remove dead tab files
- Fix scope dropdown: cache scopes from initial "all" load so dropdown
  stays stable when filtering by chatID
- Derive scopes from file chat_id when task scopes are empty
- Remove dead files: team-workspace-tab.tsx, team-tasks-tab.tsx
  (replaced by board view and workspace dialog)
2026-03-16 22:47:04 +07:00
viettranx eee79d111e feat(teams): granular progress notifications with direct/leader mode
- Replace progress_notifications toggle with granular config:
  dispatched (on), progress (on), failed (on) + delivery mode
- Direct mode: outbound to channel, no AI processing
- Leader mode: inject into leader session with NO-ACTION instructions
- Add consumer.team-notify subscriber for event forwarding
- Enrich TeamTaskEventPayload with TaskNumber, ProgressPercent/Step
- Add auto-status system prompt section
- UI: card-select for delivery mode (Zap/Bot icons), 3 toggles
2026-03-16 22:46:51 +07:00
viettranx 2c5ae04c01 feat(teams): implement workspace scope setting (shared vs isolated)
- IsSharedWorkspace() reads team.settings.workspace_scope
- Shared: workspace at teams/{teamID}/ (all chats share)
- Isolated (default): workspace at teams/{teamID}/{chatID}/
- Remove _default fallback; isolated mode requires chat_id
- Update loop.go, task creation, task listing, message dispatch,
  workspace API (list/read/delete), task board snapshot
- Update UI descriptions to reflect per-conversation scoping
2026-03-16 22:46:35 +07:00
viettranx 3f2b6e258e chore(teams): remove deprecated delegation tools
Remove delegate_search, evaluate_loop, handoff from:
- Seed data, system prompt, i18n keys/catalogs, channel events
- Consumer handler (handleHandoffAnnounce), handoff route lookup
- HandoffRouteData struct + PG implementation
- Protocol events, MCP bridge comment
- Web UI locale files (en/vi/zh)
2026-03-16 22:46:18 +07:00
viettranx 514c5e0bfc refactor(teams): batch TaskTicker queries + leader notifications
- Replace per-team loop with batch SQL (v2 filter in JOIN)
- RecoverAllStaleTasks/ForceRecoverAllTasks/MarkAllStaleTasks return
  RecoveredTaskInfo for notification routing
- Notify leaders per (teamID, channel, chatID) scope with actionable hints
- Fix notifyLeaderCycleError routing (was silently DROPPED)
- Stale threshold: 24h → 2h default
- Remove per-session RecoverStaleTasks from loop.go (ticker handles it)
- Add rows.Err() check to scanRecoveredTaskInfoRows
2026-03-16 22:46:00 +07:00
viettranx 6075d588e3 feat(ui): improve kanban UX, fix dialog scroll, remove delegation page
- Kanban: reorder columns (blocked after pending), show blocked-by info
  on cards, clickable blocker links in task detail, framer-motion card
  animation between columns
- Dialogs: standardize scroll pattern across all modals — header fixed,
  scrollbar flush with outer edge via negative margin trick
- Remove delegation page, types, events, i18n, routes, and all references
- Fix activity_logs NULL jsonb scan error (COALESCE)
- Board header: show text labels on action buttons (desktop)
2026-03-16 22:37:33 +07:00
viettranx b0bd4d6198 fix(pairing): fix browser approval stuck + security hardening
Squash-merge PR #225 with security fixes:

- Fix browser pairing stuck on "Waiting for approval" (stale closure:
  useState → useRef for senderID in pairing-form)
- Fix auto-kick after pairing (RequireAuth now accepts senderID,
  onAuthFailure skips logout for paired browser sessions)
- Allow browser-paired users to access HTTP APIs via X-GoClaw-Sender-Id
  header with fail-closed IsPaired check
- Remove ad-hoc IsInternalOrBrowser(), use channels.IsInternalChannel()
- Log failed HTTP pairing auth attempts for security monitoring
- Pass senderID to HttpClient for authenticated HTTP requests
2026-03-16 20:09:44 +07:00
viettranx 89303f5624 feat(ui): improve team board with task snapshots and board toolbar 2026-03-16 20:06:35 +07:00
viettranx 96898a3daa fix(teams): status filter default=all, reduce page size to 30, update WorkspaceDir callers
- status="" now returns all tasks (was active-only); add explicit "active" filter
- Reduce list/search page size from 50 to 30
- Update WorkspaceDir callers after signature change (remove unused channel param)
- Update team_tasks schema descriptions for status and page params
2026-03-16 20:06:15 +07:00
viettranx 50a42ad110 feat(agent): team workspace resolution for lead/member agents
- Lead agents: auto-resolve team workspace as default (relative paths)
- Dispatched members: team workspace as default via req.TeamWorkspace
- Direct-chat members: own workspace default, team workspace accessible
- Add dataDir field to Loop/LoopConfig for global workspace root
- System prompt shows team workspace absolute path for model guidance
- Remove orphan task detector (superseded by post-turn dispatch)
- Log warning on OpenAI tool call argument parse failures
2026-03-16 20:06:01 +07:00
viettranx 8d7259f637 feat(tools): add team workspace context + WorkspaceInterceptor + file tools access
- Add WithToolTeamWorkspace/ToolTeamWorkspaceFromCtx context key for
  team workspace path (accessible but not necessarily default)
- Create WorkspaceInterceptor for team-specific write validation
  (RBAC, quota, blocked extensions, event broadcasting)
- File tools (read_file, write_file, list_files, edit) allow access
  to team workspace via allowedWithTeamWorkspace() helper
- read_file/list_files hint team workspace path when file not found
- Registry detects empty tool call args and returns actionable hint
  (DashScope/Qwen large-output truncation workaround)
2026-03-16 20:05:42 +07:00
viettranx 27fb900510 refactor(tools): remove workspace_read/workspace_write, use file tools for team workspace
Remove dedicated workspace tools in favor of making existing file tools
(read_file, write_file, list_files, edit) team-workspace-aware.

- Delete workspace_tool_read.go and workspace_tool_write.go
- Clean up workspace_dir.go: export WorkspaceDir, remove dead code
  (workspaceRelPath, sanitizeFilePath, inferMimeType, templates, etc.)
- Remove workspace tool registration from gateway_managed.go
- Remove workspace tool references from policy, subagent, MCP bridge
- Add PathAllowable/PathDenyable to types.go for interface abstraction
2026-03-16 20:05:26 +07:00
viettranx 0b5124a8f1 fix(security): harden pairing auth — fail-closed, rate-limit, TTL
- Change WS pairing check from fail-open to fail-closed on DB error
  (router.go: previously granted RoleOperator on any IsPaired() error)
- Add "browser" to InternalChannels so it's properly excluded from
  outbound dispatch without ad-hoc helpers
- Rate-limit browser.pairing.status endpoint to prevent sender_id
  enumeration (reuses server RateLimiter via PairingMethods injection)
- Add expires_at column to paired_devices with 30-day TTL for
  defense-in-depth; IsPaired() now checks expiry, ListPaired() prunes
- Add confidence_score column to team_tasks, team_messages,
  team_task_comments
- Bump RequiredSchemaVersion to 21
2026-03-16 19:55:08 +07:00
viettranx 8d6729e959 feat(teams): improve task dispatch, concurrency, and tool ergonomics
- Move task dispatch from mid-turn to post-turn to prevent dependent
  tasks from completing before the current agent's run finishes
- Add team create lock to serialize list→create flows across concurrent
  group chat sessions, preventing duplicate task creation
- Require list-before-create gate: agents must call team_tasks(list)
  before creating tasks
- Make assignee required on task creation
- Add pagination (50 per page) to task list with offset support
- Slim task list/get/search responses with dedicated structs to reduce
  context token usage
- Add task board snapshot in announce messages to leader
- Workspace: allow subdirectory paths in read/delete, show directories
  in list output
- UI: reduce kanban card title font size for better visual balance
2026-03-16 15:26:25 +07:00
viettranx 0d9906d40e feat(traces): improve trace/span UI with timestamps, copy, syntax highlight
- Add created_at display to trace summary and span detail
- Add PreviewBlock component with copy button (top-right) and JSON
  syntax highlighting via highlight.js
- Show span start/end time inline with timezone support
- Use Badge for span duration and created_at on summary row
- StatusBadge: icon-only on mobile, text on desktop
- Double verbose trace preview limit from 100K to 200K chars
- Double preview content height on desktop (20vh → 40vh)
- formatDate() now accepts timezone param from useUiStore
- Show seconds in all timestamp displays
- Add i18n keys for all 3 locales (en, vi, zh)
2026-03-16 14:01:24 +07:00
viettranx 0dc3124607 fix(teams): propagate peer_kind and local_key through task dispatch chain
Team task announce was writing to wrong session (direct instead of group)
because origin_peer_kind was hardcoded as "direct" in dispatch metadata.
This caused leaders to miss completed task results in group conversations.

- Store peer_kind and local_key in task metadata at creation time
- Resolve peer_kind from context → metadata → "direct" fallback in all
  dispatch paths (tool, gateway, unblocked)
- Use actual origPeerKind in announce handler session key + request
- Add origin_local_key to gateway dispatch for forum topic routing
- Clarify ask_user guidance: bot must present question directly
- Guide members to use team_tasks progress instead of team_message
- Improve error message when non-owner calls progress action
2026-03-16 09:01:13 +07:00
viettranx 0857321a6b fix(providers): correct Anthropic prompt caching + add datetime tool
- Move cache_control from request root (ignored by API) to per-block
  placement on last system block and last tool definition
- Change system prompt time format to date-only for better cache stability
- Add builtin datetime tool for precise timestamps (cron, memory, etc.)
- Add atMs past-time validation in cron handleUpdate (was only in handleAdd)
- Update cron description to guide model to use datetime tool first
2026-03-16 08:14:03 +07:00
viettranx 9468aae422 refactor(providers): simplify DashScope per-model thinking guard
Remove ModelThinkingCapable interface and ChatRequest.ModelSupportsThinking
hint field — DashScope handles per-model checks internally via its own
whitelist. Fix double applyThinkingGuard on ChatStream→Chat tool fallback
by calling OpenAIProvider.Chat directly.
2026-03-16 07:55:25 +07:00
a44dbf2ba4 feat(providers/dashscope): add Qwen 3.5 series support with per-model thinking capability (#215)
* feat(providers/dashscope): guard enable_thinking injection by per-model capability check
Introduces ModelThinkingCapable interface and ModelSupportsThinking field
on ChatRequest so DashScope can skip thinking-param injection for models
that do not support it (e.g. qwen3-plus, qwen3-turbo), preventing
\"model not supported\" API errors.
- types.go: add ModelThinkingCapable interface + ModelSupportsThinking *bool on ChatRequest
- dashscope.go: add dashscopeThinkingModels whitelist + ModelSupportsThinking(); honour pre-computed hint
- loop.go: detect ModelThinkingCapable and set hint on ChatRequest before LLM call
- provider_models.go: add qwen3.5-plus / qwen3.5-turbo to DashScope model list
- dashscope_test.go: full test suite for whitelist, injection, hint override, budget mapping

* Fix review code.

---------

Co-authored-by: Nguyen Gia Hoang Vinh <vinhngh@runsystem.net>
2026-03-16 07:43:08 +07:00
viettranx a1495fc317 refactor(teams): move workspace button from header to board toolbar
Move workspace button to toolbar (left of Kanban/List toggle) with
FolderOpen icon + text label for better discoverability.
2026-03-16 07:25:15 +07:00
viettranx d5ede64595 fix(teams): workspace file view/download, scope filter, storage depth
- Workspace binary files: use HTTP /v1/files/ endpoint for blob fetch
  and download instead of WS text content (fixes corruption). Skip WS
  readFile for non-text files, set metadata only for ImageViewer.
- Workspace scope filter: prefix chatID as top-level folder in unscoped
  listing so scope switching shows correct tree structure.
- Workspace WS ops: strip chatID prefix from file_name before WS calls
  (backend already scopes by chat_id param, was causing doubled path).
- Storage depth: record dirs beyond maxDepth with hasChildren flag before
  SkipDir so they appear in tree with lazy-load indicator.
- Remove unused team_workspace_files UNION from ListTaskScopes query.
2026-03-16 07:16:46 +07:00
61832d34ec fix(teams): filter empty chat_id scopes to prevent Select crash (#216)
* fix(channels): start outbound dispatcher before channel check

StartAll() returned early when no channels existed at boot,
skipping the dispatchOutbound goroutine. Channels loaded later
via Reload() assumed the dispatcher was running, causing outbound
messages (agent responses) to never reach Telegram.

Move dispatcher startup before the empty-channel early return so
dynamically loaded channels always have a running consumer.

* feat(ui): add LLM provider warning on overview page and ignore plans dir

Show alert when no providers configured or all disabled, linking to provider settings. Add plans/ to .gitignore.

* feat(onboard): add provider connectivity verification and placeholder seeding

- Add onboard_verify.go: verify API keys via POST to chat/completions
  endpoint (401/403 = fatal, 400/422 = key valid, 5xx = warn)
- Verify all configured providers before seeding in auto-onboard
- Seed disabled placeholder providers (OpenRouter, Synthetic, AliCloud
  API/Sub) for UI discoverability after managed data seeding

* fix: use model ID as display name in OpenAI-compatible provider list

The `owned_by` field (e.g. "system") was incorrectly used as the model
display name, causing all models to show as "system" in the UI dropdown
for providers like AliCloud DashScope.

* fix(chat): show all active agents in chat dropdown

Chat agent selector showed "No agents available" because:
- WS agents.list only returned in-memory router cache (empty in managed mode)
- useEffect had stale [ws] dep that never re-fired after connect

Frontend: switch agent-selector from WS to HTTP /v1/agents API with
proper access control (ListAccessible). Backend: add store-backed
agents.list for WS consumers + Router.IsRunning() helper.

* feat: channel-isolated workspace, resolvePath fix, create_image workspace, summoner Expertise section, bus Topic constants

- Fix resolvePath for nested non-existent dirs (use resolveThroughExistingAncestors)
- Channel-isolated workspace: user_agent_profiles.workspace stores channel prefix,
  used as source of truth with backward compat for existing users
- Loop caches workspace per-user with CacheKindUserWorkspace invalidation via pubsub
- ContractHome/ExpandHome for portable ~-based paths in DB
- create_image saves to workspace/generated/YYYY-MM-DD/ instead of OS temp dir
- SOUL.md template: add ## Expertise section for domain knowledge
- Summoner buildEditPrompt: section guide, complete file output, frontmatter update
- Bus: Topic* constants for Subscribe/Broadcast keys, CacheKind* for payload kinds
- Teams, delegates, sessions, agent links: various enhancements

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

* feat(telegram): port forum topic features from TS — per-topic config, DM threads, thread fallback, createForumTopic tool, Web UI

Port 4 missing Telegram forum/topic features from TypeScript OpenClaw:

1. Thread-not-found fallback: retry sends without message_thread_id when
   a topic is deleted (sendHTML, sendPhoto, sendVideo, sendAudio,
   sendDocument, stream flush).

2. Per-topic config: hierarchical config resolution (global → wildcard
   group "*" → specific group → specific topic) for groupPolicy,
   requireMention, allowFrom, enabled, skills, systemPrompt.
   New TelegramGroupConfig/TelegramTopicConfig structs, resolveTopicConfig()
   with 10 unit tests.

3. DM topic support: preserve message_thread_id in private chats for
   session isolation. New BuildDMThreadSessionKey, parseRawChatID handles
   🧵 suffix.

4. createForumTopic agent tool: ForumTopicCreator interface decoupled
   from telego, lazy bot resolution via channel manager.

5. Web UI: structured group/topic config form with tri-state booleans
   (Inherit/Yes/No), nested collapsible group and topic entries.

Also fix: forum group pairing reply and approval notification now
correctly set MessageThreadID so messages land in the right topic.
Send() extracts threadID from localKey suffix as fallback for cases
where metadata is absent (e.g. pairing approval via SendToChannel).

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

* feat: Propagate local key for subagent, delegation, and team messages to enable topic/thread-specific routing and context.

* feat: add a hint to bot reply bodies indicating full content is in session history for LLM context.

* fix(store): handle NULL JSONB columns in MCP server scan (#40)

* fix(store): handle NULL JSONB columns in MCP server scan

Scan JSONB nullable columns (args, headers, env, settings) into *[]byte
instead of directly into json.RawMessage to prevent silent scan failures
when database values are SQL NULL. Also initialize result slices with
make() to return empty JSON arrays instead of null.

* fix(store): keep settings scanning direct since column is NOT NULL

* feat(security): enforce group file writer restrictions + harden exec against env/config leaks

Group writer enforcement (managed mode):
- GroupWriterCache with 5min TTL wrapping AgentStore.ListGroupFileWriters
- Tool-level blocking: write_file, edit, read_file (SOUL.md/AGENTS.md), cron mutations
- System prompt injection: non-writers get refusal instructions + filtered context files
- Cache invalidation via bus events on add/remove writer
- Wired through resolver, loop, gateway_managed, gateway_callbacks

Exec security hardening:
- Block /proc/PID/environ and /proc/self/environ reads (env var exfiltration)
- Block strings on /proc files (binary env dump)
- DenyPaths() on ExecTool: block data dir, .goclaw/, config file from exec commands
- Scrub VIRTUAL_* env vars from tool output

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

* feat: Refine tool policies with updated groups and aliases, and enhance credential scrubbing by dynamically detecting and redacting server IPs.

* feat: Add tool allow list configuration and enforcement for Telegram channels, allowing per-group/topic tool restrictions.

* feat: Add support for sending and receiving media attachments in the Feishu channel.

* feat: Add Feishu channel configuration options for topic session mode, message limits, and group allowlist, refine existing field descriptions, and create a staging tarball.

* feat(skills): per-agent skill filtering with grant-based access control (#45)

* fix(store): expand tilde in skills storage directory path

The default skillsDir (~/.goclaw/skills-store) was not expanded,
causing os.MkdirAll to fail when creating skill upload directories.

* feat(skills): per-agent skill filtering with grant-based access control (#42)

Wire skill_agent_grants into the agent resolver so each agent only sees
skills explicitly granted to it. Add Skills tab to the web UI for
managing per-agent skill grants with toggle switches.

- Add SkillAccessStore interface to avoid import cycles
- Filter skills in resolver via ListAccessible + filesystem union
- Add GET /v1/agents/:id/skills endpoint with grant status
- Invoke onGrantChange callback to invalidate agent caches on grant/revoke
- Add agent-skills-tab React component with Switch toggles
- Allow read_file access to managed skills-store directory
- Fix rows.Err() propagation in ListAccessible/ListWithGrantStatus

Closes #42

* feat: centralize agent skill access filtering within the skill search tool and implement optimistic UI updates for skill grants

* feat: Mount channel webhook handlers directly on the main gateway.

* feat: restrict /reset command to file writers in Telegram group chats.

* fix: improve spawn tool team_task_id validation and orphan detection

When LLMs call team_tasks create + spawn in parallel, the spawn
tool receives a hallucinated task_id that fails uuid.Parse, causing
a misleading error and bypassing orphan detection.

- Include pending task IDs in spawn error message so LLM can retry
  with the correct UUID
- Move spawn counting to post-execution so failed spawns don't
  increment teamTaskSpawns, allowing orphan detection to fire

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

* fix(feishu): group pairing uses group-level ID instead of per-user

Changed Lark group pairing to use "group:<chatID>" as sender_id
(matching Telegram pattern) so one approval covers the entire group.
Added approvedGroups in-memory cache to avoid DB queries per message.

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

* fix(feishu): mention detection fallback when botOpenID is empty

If probeBotInfo fails (missing bot:read permission), botOpenID is empty
and mention detection always returns false — causing all group messages
to be silently recorded to history instead of processed.

Now treats any mention as bot mention when botOpenID is unknown.

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

* feat(channels): introduce Zalo Personal channel integration (#32)

* feat(channels): implement Zalo Personal Chat (ZCA) protocol layer

Implement complete Zalo Personal Chat integration including:
- Message protocol layer (request/response/event types)
- Connection management with auth flow
- Message sending/receiving with text and media support
- User/group management and sync
- Telegram-style contact and conversation handling
- Comprehensive unit tests with 85%+ coverage

Architecture follows existing channel patterns (Telegram, Feishu) with
raw API calls for session management and message delivery. Includes
error handling, rate limiting awareness, and logging.

* feat(channels): add Zalo Personal channel integration layer

Wire protocol package to GoClaw's channel system:
- channel.go: Channel struct, Start/Stop/Send, listenLoop, message handlers
- auth.go: credential resolution (preloaded > file > QR), persistence
- policy.go: DM/group policy, @mention gating, pairing with debounce
- factory.go: managed mode factory (requires credentials, no QR)
- cmd/gateway.go: register standalone + managed factory

* feat(ui): add Zalo Personal channel type to web dashboard

Add zalo_personal to channel type dropdown, credential fields
(IMEI, cookie, userAgent), and config schema (DM/group policy,
require_mention, allow_from).

* feat(channels): add WebSocket QR login for Zalo Personal channel

Add real-time QR code login flow for zalo_personal channel instances
in managed mode. Users create an instance without credentials, then
trigger QR login from the web dashboard.

Backend:
- New RPC method zalo.personal.qr.start with per-instance mutex
- QR PNG pushed via client-scoped WS events (not broadcast)
- Credentials encrypted and saved to DB on successful scan
- Cache invalidation triggers automatic channel reload/start
- Factory returns nil,nil for missing credentials (skip, not error)
- Instance loader handles nil-channel gracefully

Frontend:
- ZaloPersonalQRDialog with auto-start, retry, and auto-close
- QR button in channel instances table for zalo_personal type
- Credential fields no longer required (auto-populated via QR)

* fix(channels): skip redundant LoginWithCredentials after QR login

QR flow already validates session via qrCheckSession + qrGetUserInfo.
Calling LoginWithCredentials again conflicts with the active QR session
state, causing "empty response" errors. Credentials are validated when
the channel starts instead. Also rename log prefix from "zca" to
"Zalo Personal".

* fix(channels): fix Zalo Personal cookie domain for login API

BuildCookieJar only set cookies for chat.zalo.me but the login API
uses wpa.chat.zalo.me. Cookies weren't sent to the subdomain, causing
"empty response" on channel startup. Now sets cookies for both hosts.

* fix(channels): move UTF-8 check after gzip decompression in Zalo listener

The UTF-8 validity check in decryptAESGCMPayload ran on raw decrypted
bytes before gzip decompression, causing all encType=2 (AES-GCM+gzip)
messages to fail with "decrypted payload is not valid UTF-8".

Move the check to decryptEventData so it runs after all processing
(decryption + decompression) is complete.

* feat(channels): add QR-only onboarding and contacts picker for Zalo Personal

- Remove credential text fields for zalo_personal, show QR auth info banner
- Add has_credentials boolean to HTTP and WS mask functions
- Implement FetchFriends/FetchGroups protocol (encrypted Zalo API)
- Add zalo.personal.contacts WS RPC method with parallel fetch
- Create ZaloContactsPicker component with search, selection, manual entry
- Integrate picker in channel instance edit dialog for allow_from config

* refactor(channels): rename zca error prefix to zalo_personal across protocol package

* fix(channels): unwrap inner response envelope in Zalo contacts decryption

The Zalo API returns double-wrapped responses: outer envelope contains
encrypted base64 data, which when decrypted yields another Response
envelope with error_code and data fields. The decryptDataField helper
was returning the raw decrypted bytes without unwrapping the inner
envelope, causing json unmarshal failures when parsing friends/groups.

* fix(channels): pass version 0 for group details to get full data

The Zalo group info endpoint uses a version-based caching mechanism.
Passing the actual version from step 1 causes the server to return
the group in "unchangedsGroup" with empty "gridInfoMap". By passing
version 0 for all groups, we force the server to return full group
info including name, avatar, and member count.

* fix(ui): auto-load contacts on modal reopen to resolve display names

When the edit modal is reopened with already-selected contact IDs,
contacts are now auto-fetched so badges show display names instead
of raw numeric IDs.

* fix(channels): handle gzip-compressed response in Zalo SendMessage

SendMessage used io.ReadAll + json.Unmarshal directly but the response
is gzip-compressed (Accept-Encoding: gzip header). Use readJSON() which
handles gzip decompression, fixing "invalid character '\x1f'" errors.

* fix(channels): decrypt encrypted send response in Zalo SendMessage

The Zalo send message API response is encrypted like all other endpoints.
Parse outer envelope, decrypt the data field, then extract msgId from
the decrypted inner response.

* feat(channels): improve Zalo listener reliability and UI channel wizard

- Migrate WebSocket client from gorilla to coder/websocket, eliminating
  unsafe/reflect hacks for RSV1 decompression and buffer inspection
- Add channel-level restart with exponential backoff (2s→60s cap, max 10)
  so channels auto-recover instead of stopping permanently
- Reset listener retry counters after 60s stable connection to prevent
  long-lived connections from exhausting retry budget
- Add code 3000 (duplicate session) recovery with 60s initial delay
- Detect silent disconnects via read deadline (2.5x ping interval)
- Fix Stop() to always cancel context, preventing reconnect timer leaks
- Refactor UI channel form into wizard-based flow with registry pattern
- Auto-refresh channel status after create/update dialog closes

* refactor(channels): move Zalo RPC methods to zalomethods package

Move Zalo personal channel RPC handlers from internal/gateway/methods to
internal/channels/zalo/personal/zalomethods, improving code organization
and removing prefix redundancy. Rename types: ZaloPersonalQRMethods →
QRMethods, ZaloPersonalContactsMethods → ContactsMethods.

- Move zalo_personal_qr.go → zalomethods/qr.go
- Move zalo_personal_contacts.go → zalomethods/contacts.go
- Update imports in cmd/gateway.go (2 call sites)
- Update internal/channels/zalo/personal imports

* feat(channels): add typing indicator to Zalo Personal channel

Show "typing..." in Zalo while the LLM processes messages, matching
the Telegram/Discord pattern. Uses the shared typing.Controller with
4s keepalive (Zalo typing expires ~5s) and 60s TTL safety net.

* feat(channels): handle image attachments in Zalo Personal channel

- Add Raw field to Content struct to preserve non-string JSON payloads
- Add Attachment struct with IsImage() detection (ext + Zalo CDN paths)
- Add AttachmentText() for human-readable placeholders (image/file/other)
- Download image attachments to temp files for agent vision pipeline
- Non-image files get text placeholder only (no download)
- Fix URL query param stripping in file extension detection

* fix(channels): switch Zalo WS client to gorilla/websocket with cookie jar fix

coder/websocket did not propagate session cookies for wss:// URLs,
causing Zalo backend to reject connections with "zpw_sek not found".
Switch to gorilla/websocket which handles wss→https scheme conversion
natively. Add wsJar safety wrapper and fix Close() mutex consistency.

Also update Makefile `up` target to use --no-cache builds.

* fix(channels): inject cookies manually for Zalo WS connection

Replace wsJar wrapper with direct cookie injection from chat.zalo.me
base domain. Fixes host-only cookies (zpw_sek) not matching WS
subdomains (ws*-msg.chat.zalo.me) due to Go cookiejar limitations.

* fix(channels): harden Zalo Personal channel security and concurrency

- Add SSRF protection to downloadFile using CheckSSRF (URL validation,
  private IP blocking, DNS pinning) with context and 30s timeout
- Protect c.sess/c.listener with sync.RWMutex to eliminate data races
  during restart; add thread-safe session()/getListener() accessors
- Add stopped flag + reconnTimer to Listener to prevent zombie reconnects
  after Stop(); timer cancelled on Stop(), checked before Start()
- Fix QR flow using context.Background() detached from WS client; now
  derives from parent ctx so flow cancels on client disconnect
- Set initial 30s read deadline for cipher key handshake to prevent
  indefinite blocking before ping loop starts
- Use defer in WSClient.Close() to prevent connection leak on panic
- Document ReadMessage ctx limitation and two-layer reconnect design

* chore: remove unused gobwas/ws dependency from go.mod

gobwas/ws was a leftover from the previous coder/websocket usage,
no longer imported by any Go source files.

* fix(channels): align Zalo Personal policy defaults across UI and backend

Policy defaults were inconsistent across three layers causing group/DM
allowlist enforcement to silently fail. New() applied "allowlist" default
to local vars but never wrote back to config; checkGroupPolicy() then
read empty string and defaulted to "open", bypassing the allowlist.
UI Select components displayed schema defaults visually without
persisting them to configValues, so DB config never stored the policy.

* feat: Resolve Feishu message mentions by stripping bot mentions and replacing user mentions with names.

* fix(zalo_personal): data races in policy, directory perms, Makefile --no-cache

- Fix 2 data races in policy.go: sendPairingReply and checkGroupPolicy
  accessed c.sess without the read lock — use c.session() accessor
- Fix credentials directory permissions: 0755 → 0700 to prevent other
  users from listing contents
- Revert Makefile --no-cache (debugging leftover that disables Docker
  layer caching)

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

* feat: mid-loop context compaction + team task user scoping

Add mid-loop compaction to prevent context overflow during long-running
delegated agent runs (e.g. 225K+ tokens causing DashScope timeouts).
Uses same threshold as maybeSummarize (contextWindow * historyShare)
with actual PromptTokens from LLM response. Only compacts the in-memory
messages slice; pendingMsgs preserves full history for session flush.

Add user_id/channel columns to team_tasks so end users only see their
own tasks. Delegate/system channels bypass the filter to see all tasks.
Group chats use the group-scoped UserID (group:channel:chatID) so all
members share visibility.

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

* fix: bump RequiredSchemaVersion to 8 for team_tasks user_id migration

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

* fix: Handle bot commands before enriching content to prevent parsing issues with reply/forward context.

* fix(web_fetch): replace regex HTML parsing with DOM-based extraction

Regex-based htmlToMarkdown/htmlToText leaked CSS, JS, and non-content
elements. Replaced with golang.org/x/net/html DOM parser that extracts
<body> only and skips 16 non-content element types (script, style,
noscript, svg, template, iframe, form, nav, footer, etc.).

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

* fix(delegation): add pending tasks hint when team_task_id not found

LLM models often hallucinate UUIDs when delegating, passing a wrong
team_task_id that doesn't exist. Previously the error was bare
("task not found") with no guidance, causing the model to get stuck.

Now the error includes a list of pending tasks so the model can
self-correct. Also refactored prepareDelegation to resolve team once
instead of 3 separate GetTeamForAgent calls, and extracted
pendingTasksHint() to deduplicate hint-building logic.

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

* fix(web_fetch): increase read limit and add empty content detection

- Read limit: maxChars*4 → max(maxChars*10, 512KB) to handle pages with
  large <head> sections (WordPress sites often have 30-50KB+ heads)
- Add warning message when HTML extraction returns empty despite non-empty
  response body (bot protection, JS-only pages)
- Enable HTTP/2 via ForceAttemptHTTP2 on custom Transport

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

* fix(delegation): notify user on failure before retry + pending tasks hint

Two improvements to delegation UX:

1. When a delegation fails, the announce now instructs the coordinator
   to send a brief friendly message to the user before retrying, so
   users aren't left waiting in silence for minutes.

2. When spawn is called with a wrong team_task_id (LLM hallucinated
   UUID), the error now includes a list of pending tasks so the model
   can self-correct. Also refactored prepareDelegation to resolve team
   once instead of 3 separate GetTeamForAgent calls.

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

* fix(delegation): clear senderID in sync delegation context

Sync delegations inherited the caller's senderID, causing the delegate
agent to check group writer permissions against its own (empty) writer
list instead of bypassing like async delegations do. This resulted in
"permission denied: only file writers can modify files" errors when
delegate agents tried to write files.

Fix: clear senderID from the sync delegation context so it behaves
consistently with async delegations (context.Background has no
senderID). All 4 downstream usages of SenderIDFromContext are
group-writer-related and correctly bypass when senderID is empty.

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

* docs: overhaul documentation for v0.2–v0.5 features

Add new docs for agent teams (11) and extended thinking (12).
Major rewrite of channels/messaging (05) with Telegram forum topics,
Feishu streaming cards, Zalo Personal. Update providers (02), tools (03),
bootstrap/skills (07), security (09), architecture (00), scheduling (08),
and tracing (10) with current implementation details.

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

* feat: Update README

* fix(teams): scope ListTasks by userID to prevent cross-group task leaking

Tasks from one Telegram group were being injected into another group's
session because the pending-task hint and /tasks command queried all
tasks team-wide without filtering by the group-scoped userID.

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

* feat(telegram): add draft streaming infrastructure + split dm/group stream config

- Add sendMessageDraft transport (disabled pending Telegram client fix for
  "reply to deleted message" artifact — tdesktop#10315, bugs.telegram.org/c/561)
- Split stream_mode into dm_stream/group_stream boolean flags (both default false)
- DM messages no longer set reply_to_message_id (cleaner UX, matching TS)
- Progressive placeholder editing for DMs: "Thinking..." → stream chunks → final
- Update web UI with separate DM/Group streaming toggles

fix(agent): prevent false MEDIA: detection in tool output

parseMediaResult() used strings.Index to find "MEDIA:" anywhere in tool output,
causing false positives when external content (e.g. GitHub releases page)
contained commit messages like "return MEDIA: path from screenshot".
Changed to strings.HasPrefix to only match at start of output.

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

* fix(delegation): inject dependency results + guard completed task reuse

- Add injectDependencyResults() to auto-inject blocked_by task results
  into delegation context, so delegatees receive prior results without
  needing to search for them (orchestrator-worker pattern)
- Guard against spawning with completed/cancelled team_task_id to
  enforce one-task-per-delegation rule
- Add cross-user scope guard in prepareDelegation() to prevent
  cross-group task leak (delegate/system channels bypass by design)
- Track CompletedTaskIDs in DelegateArtifacts and include them in
  announce messages so lead agent knows not to reuse completed IDs
- UI: reduce trace detail preview heights for better readability

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

* refactor: Prioritize timeout error handling and renumber error classification.

* fix(chat): show all active agents in chat dropdown (#48)

* fix(channels): start outbound dispatcher before channel check

StartAll() returned early when no channels existed at boot,
skipping the dispatchOutbound goroutine. Channels loaded later
via Reload() assumed the dispatcher was running, causing outbound
messages (agent responses) to never reach Telegram.

Move dispatcher startup before the empty-channel early return so
dynamically loaded channels always have a running consumer.

* feat(ui): add LLM provider warning on overview page and ignore plans dir

Show alert when no providers configured or all disabled, linking to provider settings. Add plans/ to .gitignore.

* feat(onboard): add provider connectivity verification and placeholder seeding

- Add onboard_verify.go: verify API keys via POST to chat/completions
  endpoint (401/403 = fatal, 400/422 = key valid, 5xx = warn)
- Verify all configured providers before seeding in auto-onboard
- Seed disabled placeholder providers (OpenRouter, Synthetic, AliCloud
  API/Sub) for UI discoverability after managed data seeding

* fix: use model ID as display name in OpenAI-compatible provider list

The `owned_by` field (e.g. "system") was incorrectly used as the model
display name, causing all models to show as "system" in the UI dropdown
for providers like AliCloud DashScope.

* fix(chat): show all active agents in chat dropdown

Chat agent selector showed "No agents available" because:
- WS agents.list only returned in-memory router cache (empty in managed mode)
- useEffect had stale [ws] dep that never re-fired after connect

Frontend: switch agent-selector from WS to HTTP /v1/agents API with
proper access control (ListAccessible). Backend: add store-backed
agents.list for WS consumers + Router.IsRunning() helper.

* fix(security): prevent agent list leaking on empty userID or store error

- Return error instead of falling through to unfiltered router cache
  when userID is empty or DB query fails in managed mode
- Add empty-string guard to isOwnerUser to prevent false owner match

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

---------

Co-authored-by: ntduc <ntduc@cpp.ai.vn>
Co-authored-by: viettranx <viettranx@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix: correct cron job delivery to Discord channels (#47)

- Override LLM-provided channel ID with context value to prevent
  misrouted deliveries (LLM was confusing guild ID with channel ID)
- Send cron reminder message directly instead of agent response
  so reminders appear as bot notifications in Discord

* fix(teams): filter empty chat_id scopes to prevent Select crash

Radix UI Select.Item requires non-empty value prop. Scope entries
with empty chat_id caused uncaught error on team detail page.

---------

Co-authored-by: ntduc <ntduc@cpp.ai.vn>
Co-authored-by: viettranx <viettranx@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Thieu Nguyen <79964592+thieung@users.noreply.github.com>
Co-authored-by: Duc Nguyen <me@vanducng.dev>
Co-authored-by: Winter279 <103654924+Winter279@users.noreply.github.com>
2026-03-16 07:16:32 +07:00