* fix(daemon): suppress git console windows on Windows
Apply the same HideConsoleWindow pattern used for agent processes
(PR #1474) to all git commands spawned by the daemon's repo-cache,
execenv, and GC packages. Each exec.Command now calls
util.HideConsoleWindow(cmd) which sets CREATE_NEW_CONSOLE + HideWindow
so grandchildren inherit a hidden console instead of flashing visible
console windows.
Closes#2357
Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
* refactor: use EnsureHiddenConsole at daemon startup
Replace per-site HideConsoleWindow(cmd) calls with a single
EnsureHiddenConsole() invoked once at daemon startup. The daemon
now owns a hidden console that every child process (git, cmd /c
mklink, etc.) inherits automatically, eliminating the need for
per-call SysProcAttr configuration.
This also covers the previously missed exec.Command in
codex_home_link_windows.go (cmd /c mklink) which never had a
HideConsoleWindow call.
Signed-off-by: kagura-agent <kagura.agent.ai@gmail.com>
---------
Signed-off-by: kagura-agent <kagura.agent.ai@gmail.com>
Co-authored-by: Claude Opus 4 (1M context) <noreply@anthropic.com>
* fix: handle square brackets in agent names for mention parsing (#1991)
The mention regex used [^\]]* to match labels, which broke when agent
names contained square brackets (e.g. David[TF]). The ] inside the name
caused the regex to stop matching prematurely, silently dropping the
mention.
Changes:
- Backend (mention.go): Switch to .+? (non-greedy) anchored on
](mention:// to correctly match labels with brackets
- Frontend (mention-extension.ts): Same regex fix in tokenizer, plus
escape [ and ] in renderMarkdown to prevent creating ambiguous
markdown syntax
- Add comprehensive tests for ParseMentions covering bracket names
Fixes#1991
* fix: add optional chaining for match group access
Fixes TS2532: Object is possibly 'undefined' on match[1] when calling
.replace() in the mention tokenizer.
* fix: tighten mention tokenizer to reject ordinary Markdown links
- Replace .+? with (?:\\.|[^\]])+ in start() and tokenize() regexes
so the label cannot cross a ]( Markdown link boundary
- Escaped brackets (\[ \]) from renderMarkdown() are still accepted
- Add frontend tokenizer/serializer round-trip tests:
- Plain mention
- Escaped brackets (David[TF]) round-trip
- Normal Markdown link + mention on same line (regression)
- Multiple links before mention
- Nested brackets (Bot[v2][beta])
- Issue mentions without @ prefix
Addresses review feedback on #1992.
* fix: add type assertions for tiptap MarkdownTokenizer interface in tests
The tiptap MarkdownTokenizer type allows start to be string | function
and tokenize to accept 3 arguments. Our extension always provides
single-arg functions, so cast them for TypeScript satisfaction.
Fixes CI typecheck failure in @multica/views package.
* fix: cast renderMarkdown to single-arg shape and reset file modes to 0644
A complete UX upgrade for chat sending → receiving → recovering.
* StatusPill replaces the orphan spinner — stage-aware copy
("Reading files · 12s", "Searching the web · 14s", "Typing · 24s"),
shimmer text, monotonic timer, derived effective status, > 60s
warning tone, > 5min cancel button.
* WS writethrough on task:queued / task:dispatch / task:cancelled so
pendingTask cache stays in sync with the daemon state machine without
invalidate-refetch latency. broadcastTaskDispatch now includes
chat_session_id when the task is for a chat session — the existing
payload only carried it on the generic task: events, leaving the pill
stuck at "Queued" until completion.
* Failure fallback — FailTask writes a chat_message tagged with
failure_reason (mirrors the issue path's system comment, gated on
retried==nil). Front-end renders an inline note ("Connection failed",
with a Show details collapsible) instead of the previous black hole.
* Elapsed timing — chat_message.elapsed_ms persists task.completed_at -
task.created_at on success/failure rows. UI shows "Replied in 38s" /
"Failed after 12s" beneath assistant bubbles. Format helper shared
between StatusPill and the persisted caption so the live timer and
final reading never disagree.
* Optimistic burst rebalanced — pendingTask seed + created_at moved
before the HTTP roundtrip so the pill appears the instant the user
hits send; handleStop is fire-and-forget so cancel feels immediate
(server confirmation arrives via task:cancelled WS).
* Presence integration — chat avatars use ActorAvatar (status dot +
hover card); OfflineBanner above the input on offline/unstable;
SessionDropdown shows per-row in-flight/unread pip plus a
cross-session aggregate pip on the closed trigger.
* Editor blur on send so the caret stops competing with the StatusPill
/ streaming reply for the user's attention.
* Chat panel isOpen now persists globally; defaults to OPEN for new
users (storage key absence) so the feature is discoverable. Existing
users' prior choice is respected.
* DB: migrations 062 (failure_reason) + 063 (elapsed_ms), both
ADD COLUMN NULL — fast, non-blocking, backwards compatible.
* WS: task:failed chat path now invalidates chatKeys.messages — fixes
a pre-existing bug where the failure bubble required a page refresh
to appear.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #1744 fixed literal `\n\n` rendering for the CLI surfaces (`issue
create / update --description`, `issue comment add --content`) but the
agent-completion path bypasses the CLI entirely: the daemon POSTs the
agent's stdout to `/api/daemon/tasks/:id/complete`, and `TaskService.
CompleteTask` writes `payload.Output` straight into `createAgentComment`
and `CreateChatMessage` without decoding. Models (e.g. Codex) routinely
emit Python/JSON-style `\n` literals in their final output, which then
land in the DB as the 4-char escape sequence and render as one wall of
text in the issue/chat panel — exactly the bug report in #1820.
- Move `unescapeFlagText` from `server/cmd/multica/cmd_issue.go` to
`server/internal/util/text.go` as `UnescapeBackslashEscapes` so the
CLI and the service layer share one implementation. The full
contract-boundary test suite moves with it.
- Apply `UnescapeBackslashEscapes` to `payload.Output` before it
reaches `createAgentComment` and `CreateChatMessage` in
`TaskService.CompleteTask`. Same `\n / \r / \t / \\` decoding as the
CLI; other escape sequences (`\d`, `\w`, `\u`, etc.) pass through
verbatim so regex/format strings in agent output survive.
Closes#1820
* refactor(server): make ParseUUID error-returning to prevent silent data loss (MUL-1410)
util.ParseUUID previously swallowed errors and returned a zero pgtype.UUID
on invalid input. When this zero UUID reached a write query (DELETE/UPDATE),
the SQL matched zero rows and the handler returned 2xx success — producing
silent data corruption. #1661 (DeleteIssue with identifier-style ID) was the
visible symptom; PR #1680 patched that one site, this commit closes the
class of bug.
Changes:
- util.ParseUUID now returns (pgtype.UUID, error). Add util.MustParseUUID
for trusted round-trips that should panic on invalid input.
- handler/handler.go: parseUUID wrapper now calls MustParseUUID — any
unguarded user-input string reaching it surfaces as a recovered panic
(chi middleware.Recoverer → 500) instead of silently corrupting data.
Add parseUUIDOrBadRequest(w, s, fieldName) for handler entry points.
- Convert every Queries.Delete*/Update* call site reachable from raw user
input (autopilot, comment, project, skill, skill_file, label, pin,
attachment, feedback, issue assignee, daemon runtime, workspace) to
validate UUIDs explicitly with parseUUIDOrBadRequest, returning 400 on
invalid input. Where a resolved entity.ID is already in scope, write
queries now use it directly instead of re-parsing the URL string.
- Update getWorkspaceMember + loadIssueForUser to handle invalid UUIDs
gracefully (404/400 instead of panic).
- Update util/middleware/cmd-level callers (subscriber_listeners,
notification_listeners, activity_listeners, scope_authorizer,
middleware/workspace) to use the error-returning API.
- Add server/internal/util/pgx_test.go covering valid/invalid input and
the MustParseUUID panic contract.
- Add TestDeleteIssueByIdentifier + TestDeleteIssueRejectsInvalidUUID
regression tests in handler_test.go (the original #1661 bug + the
invalid-input case).
- Document the handler UUID parsing convention in CLAUDE.md so the rule
is enforceable in future PR review.
* fix(server): address GPT-Boy review of #1748
P1 fixes from PR #1748 review:
1. Migrate remaining request-boundary UUIDs to parseUUIDOrBadRequest so
malformed input returns 400 instead of panic/500. Was missing on:
- issue.go: workspace_id in CreateIssue/ChildIssueProgress/ListIssues/
SearchIssues/BatchUpdateIssues/BatchDeleteIssues; project_id /
parent_issue_id / lead_id / assignee_id / assignee_ids / creator_id
filters; batch issue_ids and assignee/parent/project fields in
BatchUpdateIssues (skip on bad input via util.ParseUUID, matching
the existing per-row continue semantics).
- project.go: project id + workspace_id in GetProject/UpdateProject/
DeleteProject; lead_id in CreateProject/UpdateProject;
workspace_id in ListProjects + SearchProjects.
- handler.go: resolveActor now uses util.ParseUUID for X-Agent-ID /
X-Task-ID headers; invalid UUID falls back to "member" (matches
pre-existing semantics) instead of panicking.
- issue.go: validateAssigneePair returns 400 on invalid workspace_id
instead of panicking.
2. Fix issue:deleted WS event payloads to emit uuidToString(issue.ID)
instead of the raw URL string. After an identifier-path delete
("MUL-7"), the previous payload would have leaked the identifier to
subscribers, leaving stale entries in frontend caches that key by
UUID. Updated DeleteIssue (issue.go:1341) and BatchDeleteIssues
(issue.go:1641). The slog "issue deleted" log line also now records
the resolved UUID so logs match the WS payload.
3. Extend TestDeleteIssueByIdentifier to subscribe to the bus and
assert issue:deleted.payload.issue_id is the resolved UUID, not
the identifier.
* fix(server): validate remaining reviewed UUID inputs
* fix(server): validate remaining handler UUID inputs
* fix(server): finish request boundary UUID audit
* fix(server): validate remaining request body UUIDs
* fix(server): validate runtime path UUIDs
* fix(server): validate remaining audit UUID inputs
---------
Co-authored-by: Eve <eve@multica.ai>
* feat(mentions): support @mentioning issues in comments
- Extend MentionItem type to include "issue" alongside "member"/"agent"
- Add issue search (by identifier and title) to mention suggestion dropdown
- Render issue mentions with CircleDot icon in autocomplete popup
- Issue mentions serialize as [MUL-117 Title](mention://issue/id) (no @ prefix)
- Markdown renderer shows issue mentions as clickable links to /issues/:id
- Backend mentionRe regex updated to match issue mention type
* feat(mentions): auto-expand issue identifiers and add mention format to agent instructions
1. Path A — CLAUDE.md template (runtime_config.go):
Add a "## Mentions" section teaching agents the mention serialization
format for issues, members, and agents. All agents automatically
receive this via the auto-generated CLAUDE.md.
2. Approach 2 — Server-side auto-conversion (internal/mention/):
New ExpandIssueIdentifiers() utility that scans comment content for
bare issue identifiers (e.g. MUL-117) and replaces them with
[MUL-117](mention://issue/<uuid>) mention links. Skips code blocks,
inline code, and existing markdown links. Integrated into both:
- handler.CreateComment (HTTP API path)
- service.createAgentComment (agent task output path)
Add @all mention type that notifies all workspace members (excluding
agents). Includes backend parsing, notification expansion to all members,
and frontend UI with autocomplete suggestion, rendering, and hover card.
When a user @mentions an agent in any issue's comment, the system now
enqueues a task for that agent. The agent reads the issue context and
replies to the triggering comment thread.
Changes:
- Add shared util.ParseMentions for mention parsing (used by both
comment handler and notification listeners)
- Add EnqueueTaskForMention to TaskService for explicit agent targeting
- Add on_mention trigger type support in agent trigger config
- Add HasPendingTaskForIssueAndAgent SQL query for per-agent dedup
- Add enqueueMentionedAgentTasks in CreateComment handler
Safety: prevents self-trigger (agent mentioning itself), dedup with
assignee on_comment trigger, terminal issue status check, and per-agent
pending task dedup.