mirror of
https://github.com/multica-ai/multica.git
synced 2026-08-01 17:37:37 +02:00
agent/lambda/440c8314
70 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
da7b33561e |
fix: make quick-create output prefix agnostic (#2604)
* fix: make quick-create output prefix agnostic * fix: remove quick-create prefix assumption from runtime config |
||
|
|
464201ba0d |
feat(execenv): native OpenClaw skill discovery via per-task config (MUL-2219) (#2628)
* feat(execenv): native OpenClaw skill discovery via per-task config
MUL-2213 stopped lying about native discovery and routed openclaw skills
to .agent_context/skills/ — a path openclaw's scanner never reads.
Multica skills attached to openclaw-backed agents were still invisible to
the runtime; the AGENTS.md fallback was only a documentation patch.
OpenClaw's skill scanner walks <workspaceDir>/skills/ (plus a few other
roots), and workspaceDir is resolved from the openclaw config file —
specifically agents.list[id].workspace → agents.defaults.workspace →
~/.openclaw/workspace. There is no CLI flag or env var override on the
agent runtime; the only knob is the config file.
This change wires a per-task synthesized config:
1. execenv.prepareOpenclawConfig deep-copies the user's existing
openclaw.json (priority: $OPENCLAW_CONFIG_PATH, else
~/.openclaw/openclaw.json), rewrites agents.defaults.workspace AND
every agents.list[].workspace to the task workdir, and writes the
result to {envRoot}/openclaw-config.json. Provider sections,
registered agents, model providers, gateway settings — everything
openclaw needs to actually start — are preserved as-is.
2. resolveSkillsDir for "openclaw" now points at {workDir}/skills/,
which is the first path openclaw scans under workspaceDir. Skills
written here are picked up natively.
3. daemon.go exports OPENCLAW_CONFIG_PATH={env.OpenclawConfigPath} on
the openclaw subprocess and adds OPENCLAW_CONFIG_PATH to the
custom_env blocklist so users cannot accidentally override it.
4. buildMetaSkillContent now lists openclaw alongside the
"discovered automatically" providers; the .agent_context/skills/
fallback line stays for gemini/hermes.
The new regression test TestPrepareOpenclawSkillWriteMatchesScanPath is
the one MUL-2219's DoD calls out: it resolves the workspaceDir the way
openclaw does (reading agents.defaults.workspace out of the synthesized
config) and proves {workspaceDir}/skills/<name>/SKILL.md is what Multica
actually wrote. The pre-MUL-2219 fix asserted "we wrote a file" without
checking the scanner would ever see it — which is how the dead drop into
.openclaw/skills/ landed in #2621's first commit.
Verified locally: minimum-viable synthesized config validates via
`openclaw config validate`, and `OPENCLAW_CONFIG_PATH=<path> openclaw
config get agents.defaults.workspace` returns the task workdir as
expected. MUL-2219
Co-authored-by: multica-agent <github@multica.ai>
* fix(execenv): delegate openclaw config parsing to CLI and fail closed
Address Elon's must-fix on PR #2628: the previous implementation parsed
~/.openclaw/openclaw.json with encoding/json, which cannot read JSON5
or follow $include — the OpenClaw spec's actual format. When parsing
failed, prepareOpenclawConfig silently emitted a minimal config, which
could boot OpenClaw without the user's registered agents, model
providers, or API keys.
Two changes:
1. Delegate active-config-path resolution and config reading to the
openclaw CLI itself. `openclaw config file` locates the active
config (covering OPENCLAW_CONFIG_PATH / OPENCLAW_STATE_DIR /
OPENCLAW_HOME / default and the legacy chain), and the wrapper we
write uses $include to point at it so OpenClaw's own loader handles
JSON5, $include nesting, env-substitution, and secret refs. We read
only agents.list via `openclaw config get --json` to rewrite each
entry's workspace — secrets, comments, and includes in the user
config are never touched.
2. Remove the silent minimal-config fallback. Any CLI failure,
malformed output, or write error now surfaces as a hard error from
Prepare / Reuse. The only "synthesize minimal" path left is a fresh
install (CLI reports a path but the file doesn't exist), where
there is no user data to lose.
The per-task override still rewrites every agents.list[].workspace,
not just agents.defaults.workspace — this is intentional task
isolation, documented in prepareOpenclawConfig and the PR body. A
host-scope per-agent workspace would otherwise silently route the
scanner back to the user's shared workspace.
Cleanups Elon flagged in the same review:
- daemon.go inline-system-prompt comment no longer claims openclaw
ignores the task workdir; it does load it now, and the inline brief
is a belt-and-suspenders carryover for older releases.
- execenv.go openclaw block no longer references "skill file paths in
the inline brief" — the brief uses "discovered automatically".
Reuse() switches to a ReuseParams struct so the openclaw binary path
threads through alongside CodexVersion without a 6th positional arg.
MUL-2219
Co-authored-by: multica-agent <github@multica.ai>
* fix(execenv): grant OpenClaw $include cross-dir confinement for per-task wrapper
The per-task wrapper at envRoot/openclaw-config.json $includes the user's
active config (typically ~/.openclaw/openclaw.json), but OpenClaw confines
$include resolution to the wrapper file's directory unless the target's
parent is granted via OPENCLAW_INCLUDE_ROOTS. Without this, OpenClaw refuses
to follow the link at runtime and the wrapper boots with no user-registered
agents.
prepareOpenclawConfig now returns dirname(activePath) as IncludeRoot, and
the daemon prepends it to whatever the user already has in
OPENCLAW_INCLUDE_ROOTS via the new composeOpenclawIncludeRoots helper
(dedupes, drops empty segments, preserves user-configured roots). Fresh
install emits no $include and leaves the env var untouched.
Adds OPENCLAW_INCLUDE_ROOTS to the custom_env blocklist so a per-agent
override cannot strip the granted root.
Regression tests:
- TestPrepareOpenclawConfigWrapperLoadableUnderIncludeConfinement asserts
every $include target's dirname is covered by the IncludeRoot we surface.
- TestPrepareEnvironmentOpenclawWiresIncludeRoot covers the non-fresh-install
Environment wiring.
- TestComposeOpenclawIncludeRoots covers the daemon-side env composition
(preserve, dedupe, drop empties).
Co-authored-by: multica-agent <github@multica.ai>
---------
Co-authored-by: multica-agent <github@multica.ai>
|
||
|
|
f82a6adde9 |
fix(execenv): fall back OpenClaw skills to .agent_context/skills/ and stop claiming native auto-discovery (#2621)
* fix(execenv): write OpenClaw skills to .openclaw/skills/ for native discovery The OpenClaw provider was missing a case in resolveSkillsDir, so workspace skills attached to OpenClaw-backed agents fell through to .agent_context/ skills/ — a path the openclaw CLI never inspects. The result: agents created against the OpenClaw runtime saw zero of their loaded Skills in chat or task runs, even though the meta AGENTS.md content advertised them as auto-discovered. Mirrors the same per-provider mapping already in place for OpenCode, Copilot, Pi, Cursor, Kimi, Kiro. Also adds .openclaw to the repocache git-exclude list so the per-task skills directory does not pollute checked-out repos. MUL-2213 Co-authored-by: multica-agent <github@multica.ai> * fix(execenv): drop .openclaw/skills dead-drop write; flag openclaw as non-auto-discovery Reviewer (Elon) pointed out that {workDir}/.openclaw/skills/ is not in any OpenClaw skill discovery path. Confirmed by reading openclaw upstream (src/agents/skills/refresh.ts, src/agents/agent-scope-config.ts, src/cli/program/register.agent.ts): - OpenClaw scans <workspaceDir>/skills, <workspaceDir>/.agents/skills, ~/.openclaw/skills, ~/.agents/skills, bundled, and config skills.load.extraDirs. - workspaceDir is resolved from the openclaw config (per-agent workspace -> agents.defaults.workspace -> ~/.openclaw/workspace). It is NOT the cwd of the openclaw process. - There is no --workspace CLI flag on 'openclaw agent', and no OPENCLAW_WORKSPACE env var consumed at runtime. The only knob is the config file. So {workDir}/.openclaw/skills/ written by Multica is never seen by the openclaw runtime, and the meta AGENTS.md was lying to the agent by claiming auto-discovery. Reverts: - resolveSkillsDir: drop the openclaw case; falls back to .agent_context/skills/ (same path as hermes). - agentGitExcludePatterns: drop .openclaw; nothing is written there now. Also updates the openclaw branch in buildMetaSkillContent to point the agent at .agent_context/skills/ explicitly (alongside gemini/hermes), so loaded skills are at least referenced by path in the AGENTS.md context. The openclaw native loader still won't see them as installed skills. Native auto-discovery for openclaw needs per-task workspace integration (e.g. synthesized per-task config via OPENCLAW_CONFIG_PATH that overrides agents.defaults.workspace, or resolving the agent's actual configured workspace at exec time) — tracked as follow-up. MUL-2213 Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
f6ac53a967 |
fix: squad leader no_action must not post comment on comment-triggered path (#2573)
PR #2564 only added IsSquadLeader handling to the assignment-triggered workflow path and the Output section. When a squad leader is triggered by a comment (the common case for re-evaluation), the comment-triggered workflow path had NO squad leader special handling, so the model still posted comments announcing no_action/silence. Changes: - runtime_config.go: Add IsSquadLeader check to comment-triggered step 4 with explicit prohibition against posting no_action announcement comments - runtime_config.go: Strengthen Output section from 'may exit silently' to 'MUST exit without posting any comment' with explicit DO NOT examples - runtime_config.go: Strengthen assignment-triggered step 5 similarly - prompt.go: Add squad leader no_action rule to per-turn comment prompt when trigger author is an agent and agent instructions contain the Squad Operating Protocol marker - Add tests for both the per-turn prompt and CLAUDE.md generation Fixes MUL-2168 Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
7a1284128d |
fix: allow squad leader to exit silently on no_action without posting a comment (#2564)
The runtime prompt's Output section unconditionally required all tasks to post a comment via 'multica issue comment add', which conflicted with the squad leader protocol that says to 'exit silently' on no_action. Changes: - Add IsSquadLeader bool to TaskContextForEnv (detected via Squad Operating Protocol marker in agent instructions) - Relax the Output section and assignment-triggered workflow step 5 to allow squad leaders to exit with only a 'multica squad activity' call when the outcome is no_action Fixes MUL-2168 Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
21b49eb59b |
fix(cli): resolve squad assignees in issue create/update/assign (MUL-2165) (#2551)
* fix(cli): resolve squad assignees in issue create/update/assign (MUL-2165) The CLI assignee resolver only searched workspace members and agents, so a quick-create input like "assign to <SquadName>" silently fell through to "Unrecognized assignee: <SquadName>" in the issue description — even though squads are first-class assignees server-side and the prompt's whole point was to route the work for the user. Extend resolveAssignee / resolveAssigneeByID to also fetch /api/squads, teach the actor display lookup to render squad names in table output, update the quick-create prompt and runtime-config command listing to mention `multica squad list` alongside members and agents, and lock in the new behavior with tests. Co-authored-by: multica-agent <github@multica.ai> * fix(cli): gate squad assignee resolution behind an allowed-kinds set (MUL-2165) The earlier MUL-2165 fix taught resolveAssignee / resolveAssigneeByID to also return (squad, ...), but those helpers are shared. Project lead and issue subscriber callers were still using them, and their target schemas reject squads — project.lead_type has a DB CHECK constraint (server/migrations/034_projects.up.sql:10) and the subscriber handler's isWorkspaceEntity switch only knows member/agent (server/internal/handler/handler.go:414). So `multica project create --lead "<SquadName>"` and `multica issue subscriber add --user "<SquadName>"` would resolve to (squad, ...) and surface as a 500/403 server-side instead of a clean CLI-side resolution error. Thread an assigneeKinds set through the resolver and the pickAssigneeFromFlags helper. Issue create/update/assign/list pass `issueAssigneeKinds` (all three); project lead and subscriber pass `memberOrAgentKinds`. The squads fetch is skipped entirely when not allowed, and the not-found / no-match error wording adapts to the allowed kinds so it never mentions a type the caller cannot use. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
046e4b1efa |
fix(execenv): switch every provider's Windows reply template to --content-file (#2411)
Three user reports converge on the same Windows-shell encoding bug: - #2198 / #2236 — Chinese, Codex on Win11. Comments / descriptions generated by the agent arrive as `?`. - #2376 — Cyrillic, non-Codex agent ("Ops Lead") on Win11 Desktop. Title preserved (argv → CreateProcessW UTF-16), description / agent reply garbled (stdin → shell-codepage re-encoding). woodcoal's independent diagnosis on #2198 confirms the root cause: Windows PowerShell 5.1's `$OutputEncoding` defaults to ASCIIEncoding when piping to a native command, so non-ASCII bytes are silently replaced with `?` before they reach `multica.exe`. The CLI's stdin parsing is fine; the bytes are corrupted upstream, in the agent's shell layer. This PR ships the fix that supersedes the codex-only attempt in PR #2265 (which is closed in favour of this one): ## CLI Add `--content-file <path>` to `multica issue comment add` and `--description-file <path>` to `multica issue {create,update}`. The CLI reads bytes off disk via `os.ReadFile` and skips the shell entirely; UTF-8 survives end-to-end regardless of `$OutputEncoding` or `chcp`. The three input modes (`--content`, `--content-stdin`, `--content-file`) are mutually exclusive. ## Runtime config `buildMetaSkillContent`'s Available Commands section is rewritten as a neutral three-mode menu. The previous unconditional "MUST pipe via stdin" / `--description-stdin` mandate (over-spread from #1795 / #1851's Codex-multi-line fix) is gone for non-Codex providers; the strong directive now lives only in the Codex-Specific section, which branches on host: - Codex / Linux+macOS: `--content-stdin` + HEREDOC (preserves MUL-1467 fix against codex's literal `\n` habit). - Codex / Windows: `--content-file` (PowerShell ASCII pipe is the exact bug we're patching). ## Per-turn reply template `BuildCommentReplyInstructions` now takes a provider arg and branches provider × OS: - Windows + any provider → `--content-file` (the bug is shell-layer, not provider-layer; #2376 shows non-Codex agents on Windows also hit it). All providers write a UTF-8 file with their file-write tool and post via `--content-file ./reply.md`. - Linux/macOS + Codex → stdin/HEREDOC (MUL-1467 protection). - Linux/macOS + non-Codex → lightweight pre-#1795 inline `--content "..."`. The CLI server-side decodes `\n`, so escaped multi-line works; the agent retains stdin / file as escape hatches for richer formatting. `BuildPrompt` and `buildCommentPrompt` gain a `provider` arg; `daemon.runTask` already has it in scope. ## Tests - `TestResolveTextFlag` — file-source verbatim with non-ASCII (`标题 / Заголовок / 中文段落`), missing-file error, empty-file rejection, three-way mutual exclusion. - `TestInjectRuntimeConfigAvailableCommandsIsNeutral` — every non-Codex provider × {linux, darwin, windows} pins the three-mode menu present + over-spread "MUST stdin" substrings absent. - `TestInjectRuntimeConfigCodexLinuxEmphasizesStdin` + `TestInjectRuntimeConfigCodexWindowsUsesContentFile` — Codex section's per-OS branch. - `TestBuildCommentReplyInstructionsCodexLinux` + `TestBuildCommentReplyInstructionsNonCodexLinux` + `TestBuildCommentReplyInstructionsWindowsUsesContentFile` — the reply-template provider × OS matrix. - `TestInjectRuntimeConfigWindowsCommentTriggerHasNoStdin` — end-to-end AGENTS.md / CLAUDE.md on Windows has no prescriptive stdin directive, for claude / codex / opencode. `go test ./...` and `go vet ./...` clean. Closes #2198, #2236, #2376. Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
39e57b870f |
fix(cli): allow --mode run_only on autopilot create/update (#2360)
* fix(cli): allow --mode run_only on autopilot create/update The autopilot run_only dispatch path is wired end-to-end (handler accepts the mode, AutopilotService.dispatchRunOnly enqueues a task with AutopilotRunID, daemon resolves workspace via autopilot_run -> autopilot in ClaimTaskByRuntime and TaskService.ResolveTaskWorkspaceID). The CLI guard was added before those fixes landed and never removed. Drop the CLI rejection on both create and update so callers can pick the same modes the API and UI already support, and remove the stale "unstable" callout from the autopilots docs. Closes multica-ai/multica#2347 Co-authored-by: multica-agent <github@multica.ai> * fix(daemon): advertise autopilot run_only in agent runtime instructions The runtime config injected into AGENTS.md / CLAUDE.md only listed `--mode create_issue` for autopilot create and didn't expose `--mode` on update at all. So even after the CLI guard was lifted, agents reading their harness instructions would still believe create_issue was the only choice — undermining the "agents operate the same surface as humans" intent. Update both lines to advertise create_issue|run_only on create and on update, and add an InjectRuntimeConfig assertion so the runtime prompt can't drift away from the CLI surface again. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
a6968c7485 |
fix(daemon): inline runtime brief for providers that need system prompt (#2355)
InjectRuntimeConfig writes the full meta skill content (CLI catalog, workflow instructions, project context, skills) to workdir/AGENTS.md, but providers like OpenClaw, Hermes, Kiro, and Kimi read bootstrap files from their own agent workspace — not the task workdir. The inline system prompt path (providerNeedsInlineSystemPrompt) only passed the agent persona instructions, so these providers never received the runtime brief. Have InjectRuntimeConfig return the rendered content so the daemon can both write it to disk (for file-reading providers) and pass it inline (for workspace-isolated providers). This avoids double-rendering and keeps the file and inline payloads identical. Fixes #2353 |
||
|
|
3f20999597 |
refactor(timeline): drop server-side comment + timeline pagination (#2322)
* refactor(timeline): drop server-side comment + timeline pagination (MUL-1929) The cursor-paginated /timeline and /comments endpoints were sized for a problem the data shape doesn't have: prod p99 is ~30 comments per issue and the all-time max is ~1.1k. Time-based pagination also splits reply threads across page boundaries (orphan replies), which the frontend was papering over with an "orphan rescue" that promoted disconnected replies to top-level — confusing UX with no real benefit. Replace both endpoints with a single full-issue fetch, capped server-side at 2000 rows as a defensive safety net (never hit in practice). Server - /api/issues/:id/timeline now returns a flat ASC TimelineEntry[] (matches the legacy desktop contract — older Multica.app builds keep working because the wrapped TimelineResponse + cursors are gone, and the raw array shape was always what they consumed). - /api/issues/:id/comments drops limit/offset; only ?since is honoured for the CLI agent-polling flow. - Drop ListCommentsBefore/After/Latest, ListActivitiesBefore/After/Latest and the timelineCursor encoding. - Replace with ListCommentsForIssue / ListCommentsSinceForIssue / ListActivitiesForIssue (capped by argument). CLI - multica issue comment list drops --limit / --offset and the X-Total-Count reporting; --since is preserved for incremental polling. Frontend - Replace useInfiniteQuery with useQuery in useIssueTimeline; drop fetchOlder/Newer, jumpToLatest, isAtLatest, newEntriesBelowCount. - Remove timeline-cache helpers (mapAllEntries / filterAllEntries / prependToLatestPage) and the TimelinePage / TimelinePageParam types. - WS event handlers update the single flat-array cache directly. - Drop the orphan-reply rescue in issue-detail — every reply's parent is now guaranteed to be in the same array. - Strip the "show older / show newer / jump to latest" buttons and their i18n strings. Co-authored-by: multica-agent <github@multica.ai> * fix(timeline): address review feedback on pagination removal Three issues caught in PR #2322 review: 1. /timeline broke for stale clients between #2128 and this PR. They send ?limit/?before/?after/?around and parse with the wrapped TimelinePageSchema; the new flat-array response was failing schema validation and falling back to an empty timeline. Restore the wrapped shape on those query params (DESC entries, null cursors, has_more_*=false), keeping the flat ASC array for bare requests. Around-mode now also fills target_index from the merged slice so legacy clients can still scroll-to-anchor without a follow-up. 2. The agent prompts in runtime_config.go and prompt.go still told agents that `multica issue comment list` accepts --limit/--offset and to use `--limit 30` on truncated output. With those flags removed in this PR, new agent runs would hit "unknown flag" or skip context. Update the prompt copy to "returns all comments, capped at 2000; --since for incremental polling". 3. useCreateComment's onSuccess was a bare append to the timeline cache with no id-dedupe, so a fast comment:created WS event firing before onSuccess produced a transient duplicate. Restore the id guard the old prependToLatestPage helper used to provide. Adds two new boundary tests: - TestListTimeline_LegacyWrappedShape_OnPaginationParams - TestListTimeline_LegacyWrappedShape_AroundFillsTargetIndex Co-authored-by: multica-agent <github@multica.ai> * test(handler): fix timeline test assertions for handler-package isolation The TestListTimeline_* assertions assumed CreateIssue would seed an "issue_created" activity_log row, but the activity listener that publishes those rows is registered in cmd/server/main.go — handler-package tests don't wire it up. CI saw 5 entries (3 comments + 2 activities) where the test expected ≥6. Drop the auto-activity assumption: assert exactly 5 entries in TestListTimeline_MergesCommentsAndActivities, and tighten TestListTimeline_EmptyIssue to assert a fully-empty timeline. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
9a3a99cef8 |
fix: make CLI short IDs routable
Make CLI table IDs routable across issue, autopilot, project, label, and task-run workflows. Adds scoped UUID-prefix resolution, --full-id table options, issue KEY display, safer actor/name output, and updated CLI docs/runtime prompt. |
||
|
|
d964d37f97 |
Revert "fix(cli): add --content-file / --description-file for non-ASCII on Wi…" (#2252)
This reverts commit
|
||
|
|
9650788709 |
fix(cli): add --content-file / --description-file for non-ASCII on Windows (#2247)
* fix(cli): add --content-file / --description-file for non-ASCII on Windows Windows PowerShell 5.1 (the Win11 default) and cmd.exe re-encode HEREDOC content through the active console codepage before piping it to a child process. Characters the codepage cannot represent are silently replaced with `?`, so agents on Chinese Win11 hosts emitting `--content-stdin` / `--description-stdin` HEREDOCs land all of their Chinese as `?` in the issue body and comments. The daemon log shows the original Chinese correctly because slog writes to a file directly, so the regression hides until the user opens the issue page. Add a `--content-file <path>` / `--description-file <path>` source to `resolveTextFlag`: the CLI reads the file straight off disk, preserves UTF-8 bytes verbatim, and skips the shell entirely. The runtime config injected into AGENTS.md / CLAUDE.md now surfaces this as the canonical Windows fallback when the daemon host runs on Windows; non-Windows hosts keep the existing stdin/HEREDOC guidance untouched. Closes #2198, #2236. Co-authored-by: multica-agent <github@multica.ai> * fix(execenv): route every Windows-host stdin directive at --content-file GPT-Boy on PR #2247 caught that the previous patch only inserted a Windows fallback into the Available Commands section. Two later prompt surfaces still hard-coded `--content-stdin` and overrode it for the agent: - The Codex-specific paragraph in `buildMetaSkillContent`, which always said "always use `--content-stdin` with a HEREDOC". - `BuildCommentReplyInstructions`, which is re-emitted on every turn for comment-triggered tasks (both via the AGENTS.md/CLAUDE.md workflow and the daemon's per-turn prompt) and mandated the same HEREDOC pipe. On Windows hosts we now branch both surfaces to a file-based template: the agent writes the body to a UTF-8 file with its file-write tool and posts via `--content-file <path>`. Non-Windows hosts keep the existing stdin/HEREDOC guidance untouched. Tests: - `TestBuildCommentReplyInstructionsWindowsUsesContentFile` pins the Windows / non-Windows reply-instruction text directly. - `TestInjectRuntimeConfigWindowsCommentTriggerHasNoStdin` asserts that the end-to-end CLAUDE.md / AGENTS.md surface for a comment-triggered Windows task has no remaining `--content-stdin` directive that could override the Windows fallback (covers Claude + Codex providers). Co-authored-by: multica-agent <github@multica.ai> * fix(execenv): make Windows comment block file-first, pin tests by GOOS GPT-Boy's second review on PR #2247 flagged two follow-up blockers: 1. The Windows comment/description block in `buildMetaSkillContent` was "stdin first, file caveat appended" — agents on Windows still saw "Agent-authored comments should always pipe content via stdin" / "MUST pipe via stdin" / `--description-stdin` directives before reaching the Windows fallback, so the contradicting instruction was live in the same prompt. Rewrite the entire Available Commands bullet for Windows hosts as file-first: the headline line names `--content-file`, the bulleted rules name `--content-file` / `--description-file`, and stdin only appears in anti-prescriptive "do NOT pipe via …" prose. 2. The existing non-Windows tests (TestBuildCommentReplyInstructions IncludesTriggerID, TestInjectRuntimeConfigDirectsMultiLineWritesToStdin, TestInjectRuntimeConfigCodexEmphasizesStdinForFormattedComments, TestInjectRuntimeConfigCommentTriggerUsesHelper) all depended on `runtimeGOOS` defaulting to non-Windows; they would silently fail on a Windows test runner. Pin them to `runtimeGOOS = "linux"` via save+restore and drop t.Parallel so they don't race with the GOOS-mutating Windows tests. Test additions: - TestInjectRuntimeConfigWindowsRecommendsContentFile now asserts the Windows AGENTS.md does NOT contain prescriptive stdin phrasings (`MUST pipe via stdin`, `use --description-stdin and pipe a HEREDOC`, `<<'COMMENT'`, `Agent-authored comments should always pipe content via stdin`, `always use --content-stdin`) on top of the file-first positive assertions. The ban list pins prescriptive substrings, not bare flag names, so anti-prescriptive prose like "do NOT pipe via --content-stdin" doesn't trip the ban. - TestInjectRuntimeConfigWindowsCommentTriggerHasNoStdin gets the same expanded ban list across the Available Commands, Codex paragraph, and per-turn reply template surfaces. - The non-Windows side of TestInjectRuntimeConfigWindowsRecommendsContentFile pins that the Linux stdin/HEREDOC contract is still in place, so a future refactor can't accidentally move every host to file-first. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
a4fac51cf5 |
fix(projects): add resource_count breadcrumb instead of inlining resources (#2118)
* fix(projects): add resource_count breadcrumb to project responses Closes #2087 `multica project get` previously returned project metadata with no signal that resources existed. Agents that fetched a project this way had no way to discover its attached resources without already knowing about `/api/projects/{id}/resources` or the on-disk `.multica/project/resources.json`. Rather than inline the full resource list into the parent payload (which conflates parent metadata with a child sub-collection and locks the resource_ref shape into the project endpoint's contract), this adds a scalar `resource_count` breadcrumb to ProjectResponse. The actual list stays at the dedicated sub-collection endpoint. Changes: - GetProjectResourceCounts :many — new batched sqlc query - ProjectResponse.ResourceCount populated in GetProject, ListProjects, SearchProjects, and the with-resources CreateProject echo - multica project get prints a stderr hint pointing at multica project resource list <id> when count > 0; the JSON on stdout stays parseable - Meta-skill (runtime_config.go) lists multica project get and multica project resource list in Available Commands so agents that read CLAUDE.md / AGENTS.md know about both paths Co-authored-by: multica-agent <github@multica.ai> * fix(projects): wire ResourceCount through Update + Create event payload Review feedback on #2118. - UpdateProject now reloads ResourceCount before responding/publishing. Previously a title- or status-only PUT served (and broadcast over WS) resource_count: 0 even when resources existed. - The with-resources CreateProject path sets resp.ResourceCount before the project:created publish, so the WS event payload matches the HTTP echo. The hand-rolled response map collapses to an embedded ProjectResponse + resources array — one source of truth for the serialized shape. - packages/core/types/project.ts: Project gains resource_count: number to keep the TS contract aligned with the server response. Tests: - TestProjectResourceCountBreadcrumb extends to assert UpdateProject preserves the breadcrumb. - TestCreateProjectWithResourcesEchoesCount asserts the create echo carries resource_count matching the attached resources. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
f1082b10a4 |
feat(cli): add --assignee-id / --to-id / --user-id for unambiguous targeting (#2114)
* feat(cli): add --assignee-id / --to-id / --user-id for unambiguous targeting
`multica issue {create,update,list}`, `issue assign`, and `issue subscriber
{add,remove}` accepted only fuzzy name matching, which fails in workspaces
where one user's name is a substring of another (e.g. agent "J" vs
"Cursor - J" / member "Jiayuan"). #1642 added UUID acceptance through the
existing flags, but there was still no explicit path that signals "this is a
UUID, not a name" — important for scripts that read IDs from
`multica workspace members --output json`.
Adds an `-id`-suffixed counterpart for every assignee-taking flag:
- `issue list` : --assignee-id
- `issue create` : --assignee-id
- `issue update` : --assignee-id
- `issue assign` : --to-id
- `issue subscriber {add,remove}` : --user-id
The new flags route through `resolveAssigneeByID`, a strict resolver that
requires a canonical UUID and fails with a clear error when the entity is
not in the workspace (no name fallback). A shared `pickAssigneeFromFlags`
helper enforces mutual exclusion between the name and id flags so a script
that accidentally sets both never silently applies one over the other.
Refs MUL-1254.
Co-authored-by: multica-agent <github@multica.ai>
* fix(cli): detect assignee flag presence via Changed, not value-emptiness
`pickAssigneeFromFlags` previously branched on `flag value != ""`, so
explicitly passing an empty UUID silently routed through the "no flag set"
path:
multica issue list --assignee-id "" # listed every issue
multica issue create --assignee-id "" # created an unassigned issue
multica issue subscriber add --user-id "" # subscribed the caller
This is exactly the failure mode the strict-UUID flag was added to prevent —
a script interpolating `--assignee-id "$MAYBE_UUID"` against a missing env
var should fail loudly, not silently degrade to a different operation.
Switch the picker (and the assign-command top-level guard) to use
`Flags().Changed`, so an explicit empty value reaches `resolveAssigneeByID`
/ `resolveAssignee` and surfaces a clear "expected a canonical UUID" /
"no member or agent found matching" error.
Co-authored-by: multica-agent <github@multica.ai>
* docs(cli): cover --assignee-id / --to-id in user docs and quick-create prompt
Follow-up to the --*-id flag rollout: surface the new flags everywhere the
old ones are documented so users (and agents) can discover them.
- assigning-issues.{mdx,zh.mdx}: the page explicitly calls out the
duplicate-name footgun ("first one listed wins, so rename before
assigning") — replace that workaround with a --to-id <uuid> example
- cloud-quickstart.{mdx,zh.mdx}: add a --to-id hint after the substring-
match callout so first-time users learn about the strict path
- internal/daemon/prompt.go (quick-create injected prompt):
- default-to-self: pass --assignee-id <task.Agent.ID> instead of
--assignee <name>; the picker agent's UUID is already in scope and
UUID matching is unambiguous in workspaces with overlapping agent
names (J / Cursor - J / Pi - J etc.)
- user-named: tell the agent to prefer --assignee-id <uuid> using the
user_id/id from the JSON it already fetched; --assignee <name> stays
a fallback for unambiguous workspaces
Co-authored-by: multica-agent <github@multica.ai>
---------
Co-authored-by: multica-agent <github@multica.ai>
|
||
|
|
64c605e227 |
fix(execenv): write OpenCode skills to .opencode/skills/ for native discovery (#2016)
* fix(execenv): write OpenCode skills to .opencode/skills/ for native discovery * fix(repocache): exclude OpenCode skill directory |
||
|
|
862b0509df | feat: support repo checkout ref selection (#1988) | ||
|
|
da5dbc6224 |
refactor(repos): drop unused description + tighten create-project layout (#1930)
* refactor(repos): drop unused description + tighten create-project layout
Two related changes that touch the workspace-repos surface together.
1. Remove the per-repo `description` field everywhere it was threaded.
The only place it ever surfaced was a markdown table column the daemon
wrote into the agent runtime config, where most rows just read "—"
anyway. Agents already discover project structure by running
`multica project` / `multica issue` against the CLI, so the human-
readable description string carried no real value while taking up an
extra Settings input row and propagating through six layers (settings
UI → workspace.repos jsonb → handler RepoData → daemon RepoData →
repocache.RepoInfo → execenv.RepoContextForEnv).
- Settings → Repositories drops the description input; the URL field
now spans the whole row.
- WorkspaceRepo TS type loses `description`; backend RepoData /
RepoInfo / RepoContextForEnv all collapse to URL only.
- Daemon's runtime_config Repositories block changes from a
`| URL | Description |` markdown table to a simple bullet list.
- Tests updated; jsonb residue in existing workspaces is dropped at
normalize time, so no migration needed.
2. Tighten the Create Project modal footer: pull the Status / Priority /
Lead / Repos pills onto the same row as the Create Project button
(Linear-style single-row footer) instead of stacking them above it,
and swap the Repos pill icon from `FolderGit` to a real GitHub mark
(lucide-react v1 dropped brand icons, so the mark lives inline as a
small SVG component in this file).
I tried promoting Repos to its own "Resources" strip above the footer
to separate the resources abstraction from project metadata, but with
a single pill it looked too sparse — leaving a TODO comment in the
footer to revisit once we add Linear / Notion / Figma / Slack
resource types.
* fix(daemon test): drop residual Description field on RepoData literals
* fix(repos): drop Description residue surfaced after rebase on #1929
Project-resource github_repo lift path (#1929) and registerTaskRepos
both still constructed RepoData{...Description: ...} after the rebase.
Two test sites in daemon_test.go and execenv_test.go also reintroduced
the field. Strip them so the Description-removal change builds and
tests pass with the latest main.
|
||
|
|
44608713bb |
feat(projects): typed project resources + agent runtime injection (#1926)
* feat(projects): typed project resources + agent runtime injection
Adds a `project_resource` table that lets a project carry typed pointers
(github_repo today, more later) and surfaces them at agent runtime.
Server
- migration 065: project_resource (resource_type TEXT + resource_ref JSONB)
- sqlc CRUD + handler at /api/projects/{id}/resources
- claim handler attaches project_id/title + resources to issue tasks
Daemon
- TaskContextForEnv carries project context
- writes .multica/project/resources.json into workdir
- adds "## Project Context" block to CLAUDE.md / AGENTS.md / GEMINI.md
via type-dispatched formatter so new resource types just add a case
CLI
- multica project create --repo <url> attaches repos in one step
- multica project resource add/list/remove
Frontend
- Project create modal: Repos pill (workspace repos + ad-hoc URL)
- Project detail sidebar: collapsible Resources section with attach/remove
Docs
- New "Project Resources" chapter explaining the abstraction and
exactly what code to touch when adding a new resource type
Co-authored-by: multica-agent <github@multica.ai>
* fix(projects): transactional resources[] on create + generic CLI ref + test fix
Addresses review feedback on PR #1926:
1. CI red: TestProjectResourceLifecycle delete step called withURLParam
twice, which replaced the chi route context and dropped the project id.
Switched to the existing withURLParams helper from daemon_test.go.
2. POST /api/projects now accepts resources[] and attaches them in the
same transaction as the project. Invalid refs roll back the whole
create — no more half-attached projects on failure. Web modal + CLI
`project create --repo` both use the new bundled payload.
3. CLI `project resource add` now accepts a generic --ref '<json>' flag
so a new resource_type works without a CLI change. Per-type
shortcuts (--url for github_repo) remain as a convenience but are no
longer the only way in. Docs updated to drop the CLI from the
"files you must touch" list.
Adds two new server handler tests:
- TestCreateProjectAttachesResources (resources[] happy path)
- TestCreateProjectRollsBackOnInvalidResource (transactional rollback)
Co-authored-by: multica-agent <github@multica.ai>
---------
Co-authored-by: multica-agent <github@multica.ai>
|
||
|
|
365e84b920 |
fix(execenv): prefer stdin for formatted comment replies (#1851)
Co-authored-by: Devv <devv@Devvs-Mac-mini.local> |
||
|
|
54d895a210 |
fix(execenv): mandate comment-history read on assignment-triggered runs (#1843)
GitHub #1839: when an issue is reassigned from agent A to agent B, B often only reads the issue body and misses context A added in comments (e.g. which repo to clone). The assignment-triggered workflow injected into CLAUDE.md / AGENTS.md said "Read comments for additional context or human instructions" — vague enough that agents routinely skipped it. The comment-triggered branch already gives an explicit `multica issue comment list` invocation, so behavior diverged. Promote step 3 to a concrete CLI call, mark it mandatory, and surface the most common failure mode (stale instructions on reassignment) so the agent recognizes when it matters. Reorder so comments are read *before* flipping status to `in_progress`, matching how a human would catch up on a thread before claiming work. |
||
|
|
40a984c997 |
feat(quick-create): default assignee to picker agent when user didn't name one (#1836)
* feat(quick-create): default assignee to picker agent when user didn't name one The quick-create prompt previously told the agent to OMIT --assignee when the user's input didn't name a person. That left almost every quick-created issue unassigned, which doesn't match user intent — the user opened quick-create with a specific agent picked, so that agent is the obvious owner. Both prompt surfaces (BuildPrompt for the dispatched message, plus the workflow block in injected CLAUDE.md / AGENTS.md) now instruct the agent: if the input doesn't name an assignee, pass `--assignee "<your name>"`. The picker agent's name is interpolated into the prompt at task-build time so the agent has a literal value to use rather than guessing its own name. The "explicitly named assignee → resolve via members" branch is unchanged. * refactor(execenv): drop duplicated quick-create field rules from CLAUDE.md/AGENTS.md The quick-create field rules (title / description / priority / assignee fallback / project / status) lived in two places — the per-turn user message built by BuildPrompt, and the workflow block injected into CLAUDE.md / AGENTS.md by buildMetaSkillContent. Same content, two sources, easy to update one and forget the other (the assignee-default change in this PR had to touch both). Quick-create is one-shot, so the per-turn user message is always present and is the natural single source of truth. The injected file's quick-create section now keeps only the hard guardrails: "do exactly one issue create, no issue get / status / comment add, exit on CLI error". Those guardrails stay in BOTH surfaces because they're the safety net for providers that don't propagate the user message into resumed-session context. renderQuickCreateContext (issue_context.md) was already guardrails-only — no change needed there. |
||
|
|
576304519b |
docs(execenv): expose label/subscriber CLI + complete create/update flag list (#1830)
The agent-facing CLAUDE.md/AGENTS.md injected by InjectRuntimeConfig was
missing every doorway to non-core issue properties:
- `multica issue label list/add/remove` — the only way to label a newly
created issue from the agent. Without it, agents either give up
("no command for that, please add it manually") or hallucinate flag
names like `multica issue create --label foo` and fail.
- `multica issue subscriber list/add/remove` — same story for the
subscribe-on-behalf flow.
- `multica label list/create` — agents need to discover existing label
ids before they can attach one (we don't auto-create labels here).
- `issue create` flag list dropped `--project`, `--due-date`,
`--attachment` even though the CLI has supported them for a while.
- `issue update` flag list dropped `--status`, `--assignee`,
`--project`, `--due-date`, `--parent`, leaving agents thinking they
could only edit title/description/priority via update.
Also splits `issue status` from `issue update` in the doc so the agent
sees the shortcut, and notes the `issue create` body intentionally
does NOT accept labels/subscribers (use the post-create commands).
|
||
|
|
2d9c153695 |
feat: quick-create issue (async agent + inbox completion) (#1786)
* feat(server): add quick-create issue async task path Adds POST /api/issues/quick-create which validates the picked agent's reachability up front (not archived, has runtime, runtime online) then queues an issue-less agent task whose context JSONB carries the user's natural-language prompt + requester + workspace. Daemon claim resolves the workspace from the context, and the prompt builder switches to a quick-create template instructing the agent to translate the prompt into a single multica issue create call. Task completion writes a success inbox item to the requester pointing at the newly-created issue (located by querying the agent's most recent issue in the workspace since task start, so we don't depend on agent stdout shape). Failures write an action_required inbox item carrying the original prompt + agent id so the frontend can offer "Edit as advanced form" without losing input. * feat(views): quick-create issue modal + inbox failure CTA Adds a streamlined create-issue UI bound to the c shortcut: pick an agent, type one line, submit. The modal closes immediately and the agent translates the prompt into a multica issue create call in the background. Shift+c keeps the legacy advanced form for users who want every field. The "Advanced" button inside the new modal seeds the shared issue-draft store with the prompt + picked agent so switching mid-flow doesn't lose input. Last-used agent persists per (user, workspace) via a workspace-aware zustand store so frequent users skip the picker on every open. Inbox renders quick_create_done items with a status pin to the new issue and quick_create_failed items with an "Edit as advanced form" CTA that re-seeds the legacy modal with the original prompt. ApiError now carries the parsed JSON body so the modal can branch on the structured agent_unavailable code without parsing the error message. * fix(quick-create): execenv injection, claim race, private-agent permission Addresses GPT-Boy review on #1786: 1. execenv was rendering the assignment-task issue_context.md / runtime workflow even for quick-create, telling the agent to call `multica issue get/status/comment add` against an empty IssueID. Adds QuickCreatePrompt to TaskContextForEnv, plus a quick-create branch in renderIssueContext + the runtime_config workflow that instructs the agent to run a single `multica issue create` and exit, with explicit "do NOT call issue get/status/comment add" guards. 2. ClaimAgentTask serialized only on issue_id / chat_session_id, so concurrent quick-creates on the same agent (both NULL on those columns) ran in parallel — making the success-inbox lookup race over "most recent issue by this agent". Adds a third OR clause that treats "all four FKs NULL" as a serialization key for the same agent, so quick-create tasks on a given agent run one at a time. 3. QuickCreateIssue handler bypassed the private-agent ownership rule that validateAssigneePair enforces elsewhere — a user could POST a private agent_id they didn't own and trigger it. Now routes the picked agent through validateAssigneePair before the runtime liveness check. 4. Clarifies the quick-create-store namespacing comment to match the actual workspace-aware StateStorage convention used by the other issue stores (per-user is browser-profile-local). * fix(quick-create): branch Output section + deterministic origin lookup Addresses GPT-Boy's second-pass review on #1786: 1. The runtime_config.go Output section forced "Final results MUST be delivered via multica issue comment add" for every non-autopilot task — quick-create still got this conflicting instruction even though there's no issue to comment on. Switched the Output block to a three-way switch so quick-create gets a tailored "stdout is captured automatically; do NOT call comment add" branch matching the autopilot variant. 2. Completion lookup was "most recent issue created by this agent since task.started_at", which races against concurrent issue creates by the same agent (assignment task running alongside quick-create when max_concurrent_tasks > 1). Replaced with a deterministic origin link: - Migration 060 extends issue.origin_type CHECK to allow 'quick_create'. - Daemon sets MULTICA_QUICK_CREATE_TASK_ID env var when running a quick-create task. - multica issue create CLI reads the env var and stamps the new issue with origin_type=quick_create + origin_id=<task_id>. - Server CreateIssue handler accepts (origin_type, origin_id) from trusted callers (only "quick_create" is allowed; the pair is rejected unless both fields are provided together). - notifyQuickCreateCompleted now calls GetIssueByOrigin keyed on (workspace_id, "quick_create", task.ID) — no more time-window racing against parallel agent activity. The old GetRecentIssueByCreatorSince query is removed. |
||
|
|
9587a577e2 |
fix: guide codex multiline comments (#1795)
Co-authored-by: Devv <devv@Devvs-Mac-mini.local> |
||
|
|
c366cf2ba1 |
feat(agent): add Kiro CLI ACP runtime (#1780)
* feat(agent): add kiro cli acp runtime * fix(agent): align kiro acp prompt and notifications * chore(agent): clarify kiro acp args compatibility |
||
|
|
541aaa974d |
fix(server): clarify silent-exit prompt and pin handoff contract (#1775)
Follow-ups to #1765 review nits: - Tighten the per-turn prompt and AGENTS.md workflow instructions so that "exit with no output" only applies when the trigger is from another agent AND no actual work was produced this turn. If the agent did real work, the standard "post results as a comment" rule still applies — a result reply is not a noise comment. - Add TestAgentExplicitMentionStillTriggers as a positive control documenting the boundary the structural fix preserves: suppressing implicit parent-mention inheritance for agent authors does NOT block deliberate handoffs. An agent that explicitly @mentions another agent in its own content still enqueues a task for the mentioned agent and does not self-trigger. |
||
|
|
81231e06f8 |
fix(server): prevent agent-to-agent mention inheritance loops (BRI-34) (#1765)
When an agent replied in a thread whose root mentioned another agent, the reply inherited the parent mention and re-triggered the other agent. This caused 'No reply needed' ping-pong loops between co-assigned agents. Structural fix: - In enqueueMentionedAgentTasks, suppress parent-mention inheritance when authorType == 'agent'. Explicit @mentions in the agent's own comment still work for deliberate handoffs. Defense-in-depth (prompt): - Strengthen per-turn prompt and AGENTS.md workflow instructions to explicitly forbid posting 'No reply needed' noise comments. Regression test: - TestAgentReplyDoesNotInheritParentMentions covers both the fix (agent reply does not re-trigger) and the positive control (member reply still inherits mentions). Also updates TestBuildPromptCommentTriggeredByAgent to match the new prompt wording. |
||
|
|
d14265de2a |
fix(comments): preserve newlines from agent CLI writes (#1744)
* fix(comments): preserve newlines from agent CLI writes Agents (e.g. Codex) routinely emit `multica issue comment add --content "para1\n\npara2"` because Python/JSON-style string literals are their default. Bash does not expand `\n` inside double quotes, so the literal 4-char sequence flowed through the CLI into the database and rendered as text in the issue panel — comments came out as one wall of prose. Three coordinated fixes so the platform behavior no longer depends on whether a given model has strong bash-quoting intuition: - CLI: decode `\n / \r / \t / \\` in `--content` and `--description` for `issue create / update / comment add` (callers needing a literal backslash still have `--content-stdin`). - Agent prompt: rewrite the comment-add example in the injected runtime config to require `--content-stdin` + HEREDOC for any multi-line body, and call out the same rule for `--description`. The previous wording flagged stdin only for "backticks, quotes", which models read as irrelevant to plain paragraphs. - Renderer: add `remark-breaks` to the shared Markdown plugin chain so a bare `\n` becomes a visible line break instead of a CommonMark soft break — protects against models that emit single newlines for formatting. Tests: pin the new CLI helper, and pin the runtime-config guidance so the multi-line wording cannot decay back into a footnote. * fix(comments): address review feedback on newline-rendering PR - Cover the issue panel: ReadonlyContent (used by every comment card and the issue description) has its own react-markdown wiring; add remark-breaks there too so the renderer fix actually applies to the surface the bug was reported on, not just the chat panel. Pinned by ReadonlyContent line-break tests. - Make the prompt's `--description` guidance executable: add `--description-stdin` to `issue create` / `issue update`, refactor comment-add to share a single `resolveTextFlag` helper, and have the injected runtime config name the real flag instead of an imaginary "stdin / a tempfile" path. Pinned by the runtime-config guidance test. - Document the unescape contract on each affected flag's help text and pin the precise boundary in tests: `\n / \r / \t / \\` are decoded; `\d / \w / \s / \u / \0` and other unrecognised escapes pass through verbatim, so regex literals and Windows paths survive intact unless they embed a literal `\n` / `\r` / `\t`. Callers that need the literal sequence have `--content-stdin` / `--description-stdin` as the escape hatch. |
||
|
|
a89064d693 |
docs: clean up leftover .pi/agent/skills references (#1645)
PR #1632 updated the Pi project-level skill dir from .pi/agent/skills/ to .pi/skills/, but missed two references: - server/internal/daemon/execenv/runtime_config.go:20 — the comment block here lists project-level paths for every other provider, so using Pi's global path was inconsistent and misleading. - docs/docs-rewrite-plan.md:88 — planning doc still listed the old path in the Skills row. Follow-up to #1632. |
||
|
|
13d9d7df1b |
fix: pass autopilot run-only context to agents
Fix run-only autopilot tasks so agents receive autopilot context instead of empty issue instructions. Add regression coverage for run-only terminal event sync. |
||
|
|
e0e91fc792 |
feat(daemon): harden agent mention-loop instructions (#1581)
* feat(daemon): harden agent mention-loop instructions Two agents that mention each other via `mention://agent/<id>` can fall into an infinite reply loop — each says "I'm done" in prose but keeps `@mentioning` the other, which re-enqueues their run. Adding hard caps on agent-to-agent turns conflicts with Multica's design principle of giving agents the same authorship freedom as humans, so this change hardens the instructions that the harness injects instead. - Replace the terse "mentions are actions" blurb with a full Mentions protocol: `side-effecting` warning, explicit "when NOT to mention" (replying to another agent, sign-offs, thanks) and "when a mention IS appropriate" (human escalation, first-time delegation, user asked). - Add a pre-workflow decision step for comment-triggered runs: decide whether a reply is warranted at all, decide whether to include any `@mention`, and clarify that the post-a-comment rule is mandatory *if* you reply — silence is a valid exit for agent-to-agent threads. - Thread the triggering comment's author kind + display name (`TriggerAuthorType` / `TriggerAuthorName`) from the claim endpoint through the daemon task type, per-turn prompt, and CLAUDE.md workflow. When the author is another agent, both surfaces now name that agent and warn against sign-off mentions. - Soften the old closing line that told agents to `always` use the mention format — the word generalized to member/agent mentions and encouraged the very behavior that causes loops. Refs GH#1576, MUL-1323. * fix(daemon): remove MUST-respond conflict and sanitize trigger author name Addresses two blocking points on PR #1581: 1. buildCommentPrompt told the agent "You MUST respond to THIS comment" and unconditionally appended the reply command — directly conflicting with the new agent-to-agent silence-as-valid-exit workflow. Models were likely to keep following the older must-reply rule and fall back into the loop this PR is trying to close. Rewrite the header as "Focus on THIS comment — do not confuse it with previous ones" (keeps the anti-stale-comment signal) and change BuildCommentReplyInstructions to open with "If you decide to reply, post it by running exactly this command" so the reply command is available but conditional across both prompt surfaces. 2. Raw agent/user display names were being embedded directly into the high-priority prompt and CLAUDE.md via TriggerAuthorName. Agent and member names are only validated as non-empty at write time, so a name containing newlines, backticks, or fake mention markup would turn the field into a cross-agent prompt-injection surface. Add execenv.SanitizePromptField — strip control runes, collapse whitespace, drop markdown structural characters (backtick, asterisk, brackets, pipe, angle brackets, hash, backslash), truncate to 64 runes — and apply it at both embed sites (per-turn prompt and CLAUDE.md). Defense-in-depth at the consumption layer so this works for already-stored names without a migration. Tests: TestSanitizePromptField covers the policy; TestBuildPromptSanitizesAgentName plants an attack payload in TriggerAuthorName and checks the rendered prompt does not leak the newline-anchored injection or the fake mention markup. TestBuildPromptCommentTriggered*{,ByMember} updated to lock in the conditional reply-command framing. * refactor(daemon): trim redundant CLAUDE.md preamble and drop name sanitizer Per PR #1581 feedback: 1. Remove the `if ctx.TriggerAuthorType == "agent"` preamble block in runtime_config.go. It duplicated what workflow steps 4 and 5 already say ("Decide whether a reply is warranted", "Never @mention the agent you are replying to as a thank-you or sign-off"), so the signal lands the same without the extra ~7 lines of CLAUDE.md. The per-turn prompt preamble in prompt.go stays — that surface has no numbered workflow below it and would otherwise lose the silence-as-exit signal. 2. Delete execenv.SanitizePromptField + its test. Workspace agents are created by trusted team members, so the cross-agent name-injection surface it defended isn't realistic in the current trust model. 3. Drop TriggerAuthorType/Name from execenv.TaskContextForEnv and stop populating them in daemon.go — they're no longer read by the execenv package. The same fields on daemon.Task stay because prompt.go still needs them to label the triggering author in the per-turn prompt. Tests simplified to match the leaner shape: CLAUDE.md regression guards now assert that the anti-loop phrases live in the numbered workflow, and the sanitizer-specific tests are removed. |
||
|
|
d97aec83d7 |
fix: pass model to Hermes ACP and add hermes to InjectRuntimeConfig (#1203)
* fix: pass model to Hermes ACP session/new and add hermes to InjectRuntimeConfig - hermes.go: include opts.Model in session/new params so Hermes uses the configured model instead of its default (fixes local LLM failures) - runtime_config.go: add "hermes" to the AGENTS.md provider list so Hermes receives the Multica runtime instructions and skill discovery Fixes: https://github.com/multica-ai/multica/issues/1195 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(hermes): drop false native-skill claim and add regression tests The previous change added 'hermes' to the 'skills discovered automatically' branch of buildMetaSkillContent, but resolveSkillsDir has no Hermes case so skills still land in the .agent_context/skills/ fallback. AGENTS.md ended up claiming native discovery while the files were somewhere else, which would mislead Hermes (and future debuggers). - Move 'hermes' to the fallback branch alongside 'gemini' so AGENTS.md points Hermes at .agent_context/skills/ — matching where writeContextFiles actually writes them. - Extract buildHermesSessionParams so the session/new payload is unit-testable. - Add regression tests covering: * buildHermesSessionParams includes/omits 'model' correctly * InjectRuntimeConfig('hermes') writes AGENTS.md with the fallback hint * writeContextFiles('hermes') writes skills to .agent_context/skills/ Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: CC-Girl <cc-girl@multica.ai> |
||
|
|
9e47b83f02 |
feat(agent): add Kimi CLI as agent runtime (#1400)
* feat(agent): add Kimi CLI as agent runtime
Adds support for Moonshot AI's Kimi Code CLI (https://github.com/MoonshotAI/kimi-cli)
as a new agent runtime, alongside Claude, Codex, OpenCode, OpenClaw, Hermes,
Gemini, Pi, Cursor and Copilot.
Kimi Code CLI implements the standard Agent Client Protocol (ACP) via the
`kimi acp` subcommand, so the new `kimiBackend` reuses the existing
hermesClient JSON-RPC transport in the agent package — only the binary,
client identity, log prefix, and tool-name extraction differ.
Wiring:
- server/pkg/agent: new kimiBackend + kimi_test.go; registered in New(),
LaunchHeader map, and the supported-types coverage test.
- server/internal/daemon/config.go: probes `kimi` (overridable via
MULTICA_KIMI_PATH / MULTICA_KIMI_MODEL).
- server/internal/daemon/execenv: writes AGENTS.md as the runtime context
file (Kimi reads AGENTS.md natively via /init), and writes skills under
`.kimi/skills/` so they are auto-discovered by the project-level skill
loader.
- packages/views/runtimes: ProviderLogo gains a Kimi mark.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(agent/kimi): support per-agent model selection via ACP set_model
Wire Kimi into the model dropdown introduced in #1399:
- ListModels gets a 'kimi' case that drives the same ACP
initialize + session/new handshake as Hermes; both share a new
discoverACPModels helper and parseACPSessionNewModels parser
so future ACP backends only need a small provider entry.
- kimiBackend now issues session/set_model after session/new when
opts.Model is non-empty, mirroring the Hermes flow. Failures
fail the task instead of silently falling back to Kimi's
default model — silent fallback would hide that the dropdown
pick wasn't honoured.
Verified: go build ./..., go test ./pkg/agent/... ./internal/daemon/... ./internal/handler/..., pnpm typecheck and pnpm test (138 passed).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* refactor(agent): address code review feedback on Kimi runtime
- Share ACP provider-error sniffer between hermes and kimi. Previously
only hermes promoted stderr-observed 4xx/5xx into a failed task;
kimi would report "completed + empty output" when the Moonshot
upstream rejected a request (expired token, rate limit, …). Rename
hermesProviderErrorSniffer → acpProviderErrorSniffer and parameterise
the provider name; wire it into kimiBackend.Execute the same way.
- Rename extractHermesSessionID → extractACPSessionID (shared by all
ACP backends) so the name matches parseACPSessionNewModels.
- Drop the redundant second argument to kimiToolNameFromTitle; the
Message struct has only one relevant field (Tool), so passing it
twice was a dead fallback. Document that the function normalises
residual capitalised kimi titles not caught by hermesToolNameFromTitle.
- Remove kimi-only cmd.WaitDelay override; the hermes baseline is
fine for both and divergence adds noise.
- Add TestKimiBackendSetModelFailureFailsTask: fake `kimi acp` binary
that returns a JSON-RPC error for session/set_model, asserts that
the task result surfaces status=failed with the model name + upstream
message and preserves the session id.
- Fix stale agent listings in agent.go / daemon/config.go doc comments
(missing cursor, gemini, copilot).
All: `go build ./...`, `go vet ./...`, `go test ./pkg/agent/...
./internal/daemon/... ./internal/handler/...` green.
* fix(agent/kimi): pass --yolo so Shell tools don't hang on approval
Kimi's default config has `default_yolo = false`. Every Shell/file-mutating
tool call causes kimi acp to send a `session/request_permission` request
and block (up to 300s) waiting for a response. The daemon's hermesClient
only handles `session/update` notifications — permission requests go
unanswered, the tool call times out, and the UI loop eventually dies
("UI loop timed out"). Observed with the first real kimi task: agent sat
as Live for ~7 minutes before the daemon killed it.
The fix mirrors hermes' HERMES_YOLO_MODE=1 override: pass `--yolo` to
`kimi` so it auto-approves everything. `--yolo` is a top-level flag on
the `kimi` CLI (not a flag on `kimi acp`), so it must come before the
`acp` subcommand in argv. Added to kimiBlockedArgs so user custom_args
can't strip it.
While here, fix a related bug that made kimi tool names show up empty
in the daemon log ("tool #1: "): hermesToolNameFromTitle's fallback
returned `kind` when neither title-with-colon nor kind matched a known
tool. Kimi's ACP `tool_call` emits bare titles like "Shell" or "Read
file" with no `kind` at all, so we'd drop the title on the floor before
kimiToolNameFromTitle ever got a chance to map it. Now: preserve the
title when kind is unclassified; hermes titles always carry a colon so
this branch never fires for hermes.
Tests:
- TestKimiBackendPassesYoloFlag — fake binary that records its argv,
asserts --yolo comes before acp.
- TestHermesToolNameFromTitle rows for bare kimi-style titles.
- Existing suite green: go build, go vet, full pkg/agent + daemon +
handler test packages.
* fix(agent/acp): auto-approve session/request_permission from agent
The previous attempt (`kimi --yolo acp`) was a no-op. Inspected the
kimi-cli source: the `acp` Typer subcommand takes no parameters, so
flags on the root `kimi` command are dropped before `acp_main()` runs
— it's impossible to opt into YOLO mode through CLI flags for ACP.
The real fix is on our side: respond to session/request_permission.
ACP is bidirectional. When kimi runs a Shell or file-write tool, it
sends `session/request_permission` (agent → client, JSON-RPC request
with id + method) and waits up to 300s for a response. Our existing
hermesClient.handleLine only dispatched: (id + result/error) →
handleResponse, and (no id + method) → handleNotification. A request
with BOTH id and method fell through and got silently dropped — kimi
timed out, UI loop died, task sat stuck for 7 minutes.
Add handleAgentRequest: for session/request_permission, echo the id
and respond with outcome=selected, optionId=approve_for_session. The
daemon is headless; there's no user to prompt. `approve_for_session`
lets the agent remember the action so subsequent identical calls
(every Shell, every file write) skip the round-trip entirely. For any
other agent → client method, reply with standard -32601 method-not-
found so the agent doesn't block.
Also:
- Add writeMu so request() (main goroutine) and handleAgentRequest
(reader goroutine) don't interleave JSON frames on stdin.
- Revert the `--yolo acp` flag — it's a no-op, and carrying it in
kimiBlockedArgs gives the wrong impression that it does something.
Comment in kimi.go now points at handleAgentRequest as the real fix.
Tests:
- TestHermesClientAutoApprovesPermissionRequest: inject a
session/request_permission, assert the reply echoes the id and
carries {outcome: selected, optionId: approve_for_session}.
- TestHermesClientReplesMethodNotFoundForUnknownAgentRequest: confirm
unknown agent → client methods get JSON-RPC -32601 instead of silence.
- TestKimiBackendInvokesACPSubcommand replaces the yolo-flag assertion
with a negative assertion: no dead --yolo / --auto-approve / -y on
argv, since they'd pretend to do something they can't.
All: go build ./..., go vet ./..., go test ./pkg/agent/... green.
* fix(agent/acp): surface kimi tool input/output via content blocks
Kimi-cli emits tool_call and tool_call_update ACP frames with the
input/output inside a `content` array of ContentToolCallContent
blocks (shape: {type:"content", content:{type:"text", text:"..."}}),
not in the hermes-style `rawInput` map / `rawOutput` string. Our
parser only looked at rawInput/rawOutput, so the daemon recorded
empty Input and Output for every kimi tool — the execution-history
UI showed blank terminal panels even for commands that ran fine.
Add extractACPToolCallText() and a fallback in handleToolCallStart /
handleToolCallUpdate: when rawInput is nil / rawOutput is empty, pull
the text out of the content blocks. rawInput / rawOutput still take
precedence so hermes' behaviour is untouched. Terminal /
FileEditToolCallContent blocks are skipped (we have nothing to render
them as — kimi only emits TerminalToolCallContent when the client
advertises terminal capability, which we don't).
Tests:
- TestHermesClientHandleToolCallStartKimiContent — content array →
Input.text populated.
- TestHermesClientHandleToolCallCompleteKimiContent — multi-block
content → Output concatenated with newline separator.
- TestHermesClientHandleToolCallRawOutputTakesPrecedence — hermes
rawOutput still wins when both are present.
- TestExtractACPToolCallText — unit coverage for the helper
(single/multiple text blocks, terminal-block skip, empty input).
* fix(agent/acp): buffer streaming tool args so Input isn't empty in UI
kimi-cli streams tool args token-by-token via tool_call_update frames
— the initial tool_call carries an empty content block and each
subsequent in_progress update carries the cumulative JSON so far
(`{`, `{"comma`, `{"command": "echo`, …). The final completed update
then carries the tool's stdout, not the args. Observed per kimi-cli
acp/session.py::_send_tool_call{,_part,_result} and confirmed by
driving a real Shell call end-to-end: 10 in_progress frames, last
with `{"command": "echo hello world"}`, then completed with `hello
world\n`.
Our previous handleToolCallStart emitted MessageToolUse on the first
tool_call frame, capturing the empty content — so every kimi tool
appeared in the execution-history UI with a blank input. Output was
correct (fix
|
||
|
|
c76c790b32 |
fix(daemon/execenv): make posting result comment an explicit workflow step (#1372)
Agents were silently finishing tasks without ever posting results to the issue — their final reply stayed in terminal/log output only. See MUL-1124. Root cause: the injected CLAUDE.md / AGENTS.md put "post a comment with results" inside the body of step 4 (a nested clause in the default workflow description), so skill-driven flows jumped straight from "do the work" to `status in_review`. - Hoist posting the result comment into its own explicit, numbered step in both assignment-triggered and comment-triggered workflows, with the exact `multica issue comment add` invocation inlined. - Add a hard warning at the top of the Output section that terminal / chat text is never delivered to the user. - Add regression test covering both workflow branches. |
||
|
|
951f51408a |
fix(agent/comments): prevent resumed sessions from reusing stale --parent UUID (#1374)
* fix(agent/comments): re-emit trigger comment id every turn + server-side parent_id guard Resumed Claude sessions keep prior turns' tool calls in context, so a comment-triggered task could reuse the PREVIOUS turn's --parent UUID instead of the current trigger's. The reply landed in the wrong thread (MUL-1125): backend stored exactly what the agent sent, but the agent pulled a stale UUID from its own conversation memory. Two layers of defense: 1. Extract BuildCommentReplyInstructions so daemon.buildCommentPrompt and execenv.InjectRuntimeConfig emit the same "use this exact --parent, do not reuse values from previous turns" block. The per-turn prompt now carries the current TriggerCommentID, which it previously relied on CLAUDE.md for (and CLAUDE.md isn't re-read mid-session). 2. Handler-side guard in CreateComment: when an agent posts from inside a comment-triggered task (X-Agent-ID + X-Task-ID, task has TriggerCommentID), require parent_id == task.TriggerCommentID or return 409. Assignment-triggered tasks are untouched. * fix(agent/comments): scope parent_id guard to the task's own issue Two issues from CI + GPT-Boy's review: 1. Guard was too broad: the CLI stamps X-Task-ID on every request, so an agent legitimately commenting on a different issue while its current task was comment-triggered would get 409'd with the wrong issue's trigger comment id. Narrow the guard to fire only when the request's issue matches the task's own issue — cross-issue agent activity stays unblocked. 2. The integration test tried to insert a second queued task for the same (agent, issue), which hits the idx_one_pending_task_per_issue_agent unique index. Replace the assignment-triggered-task sub-case with a cross-issue regression test (the scenario we now need to cover anyway): post on issue B while X-Task-ID points at a comment-triggered task on issue A, expect 201. |
||
|
|
e198a67f8f |
docs(prompt): warn agents that mention syntax is an action, not a text reference (#1306)
Agent mentions enqueue a new task; member mentions send a notification. Without this warning, agents have used `[@Name](mention://agent/<id>)` in prose (e.g. "GPT-Boy is correct") and accidentally re-triggered the agent. Adds a caveat under `## Mentions` in the prompt injected into agent runtimes, plus tightens the Agent bullet to make the side-effect explicit. |
||
|
|
b2307a5ee9 |
fix(execenv): write Copilot skills to .github/skills/ for native discovery (#1270)
GitHub Copilot CLI scans project-level skills from .github/skills/<name>/SKILL.md (per the official cli-config-dir-reference docs), not from .agent_context/skills/. Previously, skills injected for the copilot provider were placed under .agent_context/skills/ and only referenced by name in AGENTS.md, meaning Copilot would not actually pick them up. - resolveSkillsDir: add a dedicated copilot case writing to .github/skills/ - Update doc comments in context.go and runtime_config.go - Add TestWriteContextFilesCopilotNativeSkills covering the new path and ensuring .agent_context/skills/ is not created for copilot Co-authored-by: Devv <devv@Devvs-Mac-mini.local> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
9e15b17c92 |
feat(cli): add autopilot commands (#1234)
* feat(cli): add autopilot commands Expose the existing autopilot REST API through the multica CLI so users and agents can list, get, create, update, delete, trigger, and inspect autopilots, plus manage their triggers (schedule/webhook/api). Also surface the read + core write commands in the agent meta skill prompt so agents discover them without needing --help. - new cmd_autopilot.go (+ test) wiring /api/autopilots endpoints - add APIClient.PatchJSON (autopilot update uses PATCH) - expose autopilot in CORE COMMANDS group - extend runtime_config.go meta skill with autopilot entries - document autopilot command group in CLI_AND_DAEMON.md * fix(autopilot): address code review — restrict run_only, validate workspace on update Code review caught two issues with the initial CLI PR: 1. run_only mode is broken end-to-end. The daemon-side resolveTaskWorkspaceID() in internal/handler/daemon.go only resolves workspace from issue/chat, so run_only tasks (which have neither) return 404 from /start. BuildPrompt() would also emit an empty issue ID. The service-level resolver in internal/service/task.go already handles AutopilotRunID, but the daemon endpoint uses the handler copy. Fixing that path is out of scope for the CLI PR; drop run_only from the CLI and docs so we don't recommend a mode that cannot complete. Server continues to accept it for the existing UI. 2. UpdateAutopilot did not verify that a new assignee_id belongs to the workspace, unlike CreateAutopilot. This let a PATCH swap in an agent from a different workspace. Mirror the same GetAgentInWorkspace check. |
||
|
|
cd50c31201 |
feat(agent): add GitHub Copilot CLI backend (#1157)
* feat(agent): add GitHub Copilot CLI backend Integrate Copilot CLI as a new agent backend using the stable `-p` JSONL mode (`--output-format json`), following the same spawn-CLI-scan-JSONL pattern established by claude.go. Backend (server/pkg/agent/copilot.go): - Spawn `copilot -p <prompt> --output-format json --allow-all-tools --no-ask-user` - Parse streaming JSONL events (system/assistant/user/result/log) - Extract session ID for resume support (`--resume <id>`) - Accumulate per-model token usage for billing - Filter blocked args to prevent protocol-critical flag overrides Daemon config: - Probe MULTICA_COPILOT_PATH / MULTICA_COPILOT_MODEL env vars - Copilot uses AGENTS.md (native discovery) and default skills path Frontend: - Add Copilot logo SVG and provider switch case Tests: 14 unit tests covering arg building, event parsing, usage accumulation, and edge cases. All Go + TS checks pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(daemon): add restart subcommand, make daemon uses it - `daemon start` keeps original behavior: errors if already running - `daemon restart` stops existing daemon then starts fresh - `make daemon` now runs `daemon restart --profile local` Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(copilot): address review nits 1-5 - Nit 1: Add MinVersions["copilot"] = "1.0.0" - Nit 2: Seed activeModel from session.start.data.selectedModel (falls back to opts.Model, then "copilot"). First-turn tokens now get correct model attribution. - Nit 3: Handle assistant.reasoning/reasoning_delta → MessageThinking, reasoningText in assistant.message → MessageThinking, session.warning → MessageLog{warn} - Nit 4: Extract handleCopilotEvent() method shared by production and tests — no more duplicated switch body that can drift - Nit 5: Deltas write to output buffer as defense-in-depth; if process dies before assistant.message, output is non-empty Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
c0b4e7e8b8 |
feat(agent): add Cursor Agent CLI runtime support (#1057)
* feat(agent): add Cursor Agent CLI runtime support Add cursor-agent as a new agent backend, following the same pattern as existing providers. The implementation spawns cursor-agent CLI with stream-json output, parses JSONL events into the unified Message type, and supports session resume, usage tracking, and auto-approval (--yolo). Changes: - server/pkg/agent/cursor.go: cursorBackend implementation - server/pkg/agent/cursor_test.go: unit tests for args, parsing, errors - server/pkg/agent/agent.go: register "cursor" in New() factory - server/internal/daemon/config.go: probe cursor-agent in PATH - server/internal/daemon/execenv/context.go: cursor skill discovery path - server/internal/daemon/execenv/runtime_config.go: AGENTS.md injection - packages/views/.../provider-logo.tsx: cursor logo in UI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(agent): address PR review for cursor backend 1. Fix token usage double-counting: usage is now taken exclusively from "result" events (session totals). Per-message usage in "assistant" events is intentionally ignored. "step_finish" usage is only used as fallback when no "result" usage is available. 2. Remove dead code: isCursorUnknownSessionError() and its regex were defined but never called. Removed along with corresponding test. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(agent): add missing CustomArgs, SystemPrompt, MaxTurns, and debug logging to cursor backend - Add cursorBlockedArgs and filterCustomArgs support for safe custom arg passthrough - Add --system-prompt and --max-turns flag support to buildCursorArgs - Add debug logging of command args before execution (consistent with all other backends) - Move stdout-close goroutine inside main goroutine (consistent with claude.go pattern) - Add tests for SystemPrompt/MaxTurns and CustomArgs filtering Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: make daemon uses local profile & update Cursor logo to official brand - Makefile: make daemon now runs 'daemon start --profile local' for local dev - Replace Cursor runtime logo with official brand SVG (removed background rect) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(agent): remove unsupported --system-prompt and --max-turns from cursor-agent cursor-agent CLI does not support these flags. Instructions are already injected via AGENTS.md and .cursor/skills/ files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(agent): prevent step_finish + result usage double-counting in cursor Split usage accumulation into separate stepUsage and resultUsage maps. After stream ends, use resultUsage if available (session totals from result event), otherwise fall back to stepUsage (sum of step_finish). This prevents 2x counting when result.usage already includes totals. Added table-driven test covering: result-only, step_finish-only, step_finish+result (no double count), and multi-model scenarios. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs(agent): fix misleading comment on cursor -p flag Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Devv <devv@Devvs-Mac-mini.local> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: yushen <ldnvnbl@gmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
8c518c350a |
feat(agent): add Pi agent runtime support (#1064)
* feat(agent): add Pi agent runtime support
Add Pi as a new agent runtime provider, following the established adapter
pattern. Pi CLI outputs JSONL events which are parsed for messages, tool
calls, and usage tracking.
Backend:
- New piBackend implementing the Backend interface (pi.go)
- Pi CLI discovery via MULTICA_PI_PATH env var or PATH lookup
- JSONL event stream parsing (agent_start, message_update, thinking_update,
tool_execution_start/end, agent_end)
- Usage scanner for ~/.pi/sessions/*.jsonl files
- Runtime config injection via AGENTS.md
- Skill injection to .pi/agent/skills/
Frontend:
- Pi provider logo (teal π icon)
- Pi label in transcript dialog
Docs:
- Updated all provider lists in README, CLI_INSTALL, and docs
* fix(agent): filter Pi usage scanner to agent_end events only
Address review feedback: restrict usage parsing to agent_end events
which contain cumulative totals, preventing potential inaccuracy if
Pi adds usage fields to other event types in the future.
* fix(agent): align Pi runtime with real CLI flags, event schema, and custom_args
- Flags: Pi's CLI uses `--mode json` (not `--output-format jsonl`), has no
`--yolo` (explicit `--tools` allowlist instead), takes the prompt as a
positional argument (not `-p <prompt>`), splits model as
`--provider <name> --model <id>`, and treats `--session` as a file path
that must exist before spawn.
- Event parsing: rewrite the stream event struct to match Pi's actual
JSON event schema (`message_update.assistantMessageEvent.delta`,
`turn_end.message.usage.{input,output,cacheRead,cacheWrite}`, etc.).
- Sessions: generate/persist session files under ~/.multica/pi-sessions/
and use the file path as the opaque SessionID returned to the daemon.
- Usage scanner: read assistant `message` events from the same session
files (Pi's session-file schema, distinct from the stdout stream).
- Custom args: consume `ExecOptions.CustomArgs` via `filterCustomArgs`
with a Pi-specific blocked set (`-p`, `--print`, `--mode`, `--session`)
so Pi matches the pattern shared by every other agent backend.
|
||
|
|
08c3513eef |
fix(cli): add pagination metadata to issue list JSON output and update agent prompt
Issue list JSON now includes total, limit, offset, has_more fields so agents can detect truncated results and paginate. Also documents --limit/--offset in the agent prompt and emphasizes mention format in Output section. Closes MUL-837 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
9ba9ea66f8 |
fix(daemon): emphasize NEW comment in trigger prompt to prevent session confusion
When a comment-triggered task resumes an existing session, the agent may mistake the new comment for a previous one and skip it. Add [NEW COMMENT] tag to the prompt and reinforce in AGENTS.md workflow that the agent must respond to THIS specific comment, not prior ones. |
||
|
|
2cd6024851 |
Merge pull request #820 from zoharbabin/feat/local-storage-and-stdin
feat(cli): add --content-stdin flag to issue comment add |
||
|
|
bc1185f525 |
Merge pull request #755 from sanjay3290/feat/gemini-backend
feat(daemon): add Google Gemini CLI backend |
||
|
|
1d71df8622 |
fix(daemon): include dispatched agent identity in CLAUDE.md (#877)
When an agent is triggered via @mention (not as the issue assignee), the generated CLAUDE.md had no explicit agent identity. The agent would infer its identity from the issue's assignee field, causing it to skip work intended for it. Now CLAUDE.md always includes "You are: <agent-name> (ID: <agent-id>)" so the agent knows exactly who it is regardless of the issue assignee. Closes MUL-709 |
||
|
|
77dbcaefad |
feat(cli): add --content-stdin flag to issue comment add
Allow agents to pipe comment content through stdin instead of the --content flag, avoiding shell escaping issues with backticks, quotes, and other special characters in markdown content. Usage: cat <<'COMMENT' | multica issue comment add <id> --content-stdin Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
f99f50eb0c |
feat(daemon): add Google Gemini CLI backend
Registers `gemini` as a sixth supported agent provider alongside claude, codex, opencode, openclaw, and hermes. - Daemon config probes for `gemini` on PATH (MULTICA_GEMINI_PATH / MULTICA_GEMINI_MODEL env overrides mirror the other providers). - New agent.geminiBackend in pkg/agent/gemini.go: spawns `gemini -p <prompt> --yolo -o text [-m <model>] [-r <session>]`, reads stdout to completion, and returns a single MessageText plus the standard Result struct (Status / Output / DurationMs). - Execution environment writes a GEMINI.md file into the task workdir (mirroring the existing CLAUDE.md / AGENTS.md injection for other providers) so Gemini discovers the Multica runtime meta-skill through its native mechanism. Tests: - pkg/agent/gemini_test.go — unit coverage for buildGeminiArgs (baseline, model override, resume session, omit-when-empty). - internal/daemon/execenv/TestInjectRuntimeConfigGemini — verifies GEMINI.md is written and that CLAUDE.md/AGENTS.md are NOT. Scope (intentional for v1): - Text output only (`-o text`). Streaming tool events via `--output-format stream-json` is a follow-up once we have a reliable reproduction of Gemini's event schema. - No MCP config plumbing. Gemini's `--allowed-mcp-server-names` filter pairs well with the per-agent MCP work on feat/per-agent-mcp; stacking the two can land as a follow-up. - No token usage scraping (Gemini's accounting lives on the Google Cloud side, not a local JSONL log like claude/codex). - No session resume wiring beyond accepting the ExecOptions field — the daemon does not yet persist Gemini session IDs because the text output mode does not expose them. Migration / env changes: - New optional environment variables MULTICA_GEMINI_PATH and MULTICA_GEMINI_MODEL. Default path is the string "gemini" (resolved via PATH at daemon startup). If no Gemini install is detected, the provider is simply absent from the runtime — no behavior change for existing deployments. |
||
|
|
cc9a8ad6ec |
fix(daemon): make meta-skill workflow defer to agent Skills instead of hardcoding (#675)
Replaces the hardcoded assignment-triggered workflow in buildMetaSkillContent() with a minimal version that defers to agent Skills and Identity. Keeps platform capability docs and status management steps intact. Fixes #669 Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |