mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-31 08:59:12 +02:00
template-require-runtime-model
7 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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> |
||
|
|
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> |
||
|
|
365e84b920 |
fix(execenv): prefer stdin for formatted comment replies (#1851)
Co-authored-by: Devv <devv@Devvs-Mac-mini.local> |
||
|
|
9587a577e2 |
fix: guide codex multiline comments (#1795)
Co-authored-by: Devv <devv@Devvs-Mac-mini.local> |
||
|
|
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. |
||
|
|
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. |