* fix(lark): resolve outbound references to workspace links
* test(wecom): use normal budget for no-holder state assertion
* fix(channel): preserve Markdown reference source boundaries
* fix(lark): format references with one workspace lookup
* fix(deps): update x/net for HTML parser advisory
---------
Co-authored-by: Frezc <[email protected]>
A merged PR moved its issue to the workspace's pr_merge_status even when the
issue's project workflow does not list that status, bypassing the check a
manual status change goes through. The decision now loads the project
workflow and returns not_in_workflow instead; the issue page renders nothing
for it, so the issue silently stays where it is. Default workflows and issues
outside any project are unchanged.
Co-authored-by: Multica Eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* MUL-8292: refactor(brief): move Available Commands detail behind --help pointers
Each bullet keeps its signature and the behaviour needed at the call site;
wakeup condition catalogue, PR snapshot mechanics, actor filters and the
repeated file rules now live only in --help and the multica-platform
references. Issue brief 17,300 -> 16,372 bytes (-928); chat and autopilot
briefs drop by the same amount. New TestAvailableCommandsKeepsCompressedContracts
pins the kept contracts and guards the moved detail from drifting back.
Co-authored-by: multica-agent <[email protected]>
* MUL-8292: fix(brief): keep Available Commands rules self-contained in every kind
Review follow-up on #9596:
- comment list / issue create / comment add pointed at Workflow step 2 and
Comment Formatting, which chat and autopilot briefs do not have; the
bounded-read recipe and the workdir file rule are inline again.
- repo checkout restores both keep conditions and the clean-checkout case.
- wakeup scopes last_error to missing PR links/snapshots; a pending check or
unmet condition records nothing.
Contract test now runs every kind x provider, and a new test fails when the
shared section references a heading or workflow step the kind lacks.
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-agent <[email protected]>
* fix(runtimes): spell out Qoder Cloud Agents instead of QCA
The UI, docs, landing copy and server messages called the product "QCA"
in labels such as "QCA Agent", "QCA runtime" and API errors like "QCA
token is required". Use the full name Qoder Cloud Agents everywhere a
person reads it, and add it to the brand list in the naming conventions
so the abbreviation does not come back.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(runtimes): refine Qoder Cloud Agents copy (MUL-8282)
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: 安辰 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Sol-Boy <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* fix(runtimes): price Bedrock-served Anthropic ids and DeepSeek-V4.1-Flash
Bedrock reports Anthropic models as `<region>.anthropic.<id>` with an
inference-profile version on snapshots (`us.anthropic.claude-sonnet-5`,
`eu.anthropic.claude-sonnet-4-5-20250929-v1:0`). Neither resolver accepted
that shape: the backend alias rules anchor on `/`, `:` or start-of-string,
and the frontend `canonicalCandidates` only strips `/` and `:` routing
prefixes, so the vendor and region prefixes survived into the lookup. Every
Bedrock-served turn therefore missed the rate table and rendered as $0.00
plus an "unmapped models" warning, while the tokens were still counted in
`task_usage` (cost_usd_ticks is provider-reported and NULL for Anthropic, so
nothing else was pricing them either).
Strip the Bedrock vendor/region prefix and the `-v<digit>:<digit>` profile
version on both sides, before the existing dot↔dash normalisation. Only
`anthropic.`-prefixed ids are rewritten, so non-Anthropic Bedrock models
(`us.amazon.nova-*`) stay unmapped rather than borrowing a Claude rate.
DeepSeek-V4.1-Flash is also missing: the catalog lists v4-flash and v4-pro
only, and there is deliberately no family fallback, so `deepseek-v4.1-flash`
and OpenRouter's dated `deepseek-v4.1-flash-1017` both went unpriced. Add the
official `deepseek-flash` sheet rate ($0.15/$0.60, cache read $0.003, cache
write mirrors input) and one alias rule that admits the dated snapshot —
DeepSeek documents that id as the model actually served for the undated slug,
so it is the same SKU at the same rate, not a second tier.
Both tables are updated together (server/internal/metrics/pricing.go drives
the Prometheus counters, packages/views/runtimes/utils.ts drives the
dashboard) and both suites pin the new ids, including the negative cases: a
later SKU, a `-preview` qualifier or a non-Anthropic Bedrock id must stay
unmapped.
* ci: re-run (previous backend-tests failure was in channel-media reconciler timing tests, unrelated)
* fix(runtimes): align Flash pricing and constrain Bedrock aliases (MUL-8269)
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: Sol-Boy <[email protected]>
Co-authored-by: multica-agent <[email protected]>
When a resume's baseline capture fails, the result event's modelUsage
was stored as the task's usage as-is. On newer Claude Code a resumed
session restores its cumulative counters, so that reported total spans
every prior task in the session: the #8781 inflation returned for every
baseline-unavailable mode. The reported trigger is a custom runtime
profile whose command is a wrapper script that sets CLAUDE_CONFIG_DIR
internally - the daemon resolves the transcript from its own env
(os.Environ() + agent custom_env) and never sees it, so every resume
logged 'claude usage baseline unavailable' and stored the running
session total (observed ~10x on a session's 7th task).
The code already distinguishes a transcript that exists but has no
cost-state (older Claude Code reports per-run totals there; behavior
preserved, pinned by existing tests) from one that cannot be found at
all. For the latter the reported modelUsage is now treated as
non-authoritative: usage keeps the stream-derived per-run totals, and
the reported totals are only recorded for the resume-rejected recapture
where the run is known to have started a fresh session of its own.
Affected modes now degrade to a conservative per-run approximation
instead of silently storing a cumulative total; the WRN stays, reworded
to say what actually happens.
Out of scope, per the adjudication on the issue: per-profile env vars
(schema/feature) and expanding $HOME/~ in custom_env values (makes the
manual workaround portable but leaves the default inflation path live).
Ablation: reverting only server/pkg/agent/claude.go on this branch makes
both new tests fail again with the cumulative total stored
(700/30/800/80 instead of the per-run 70/0/80/8).
Fixes#9564
Generated-by: GLM-5.3-Flash (ZCode)
* feat(wakeups): accept blocked in --until-issue-state
Lets a coordinator wait for a specific child to reach blocked, alongside the
existing in_review handoff, without waiting for the child to close.
Exact status key match, same as in_review.
Refs #9344
Co-authored-by: multica-agent <[email protected]>
* fix(i18n): keep blocked state key in lowercase English in zh wakeup copy
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-agent <[email protected]>
* fix(execenv): let a re-dispatched task fork past its own busy-branch fallback (#9198)
When the conversation branch is held by another worktree, a local_directory worktree task forks onto <branch>-<task key>. That name is deterministic, so the same task re-dispatched while the branch is still busy hit 'a branch named ... already exists' and failed every attempt. Fork a fresh timestamped branch instead, leaving the earlier attempt's branch (and any partial work on it) untouched.
Co-authored-by: multica-agent <[email protected]>
* fix(execenv): keep picking an unused fallback branch name on repeated re-dispatch
A second-granular suffix still collides when the same task is prepared more than twice within one second. Try a bounded sequence of fallback names (<alt>, <alt>-<unix>, <alt>-<unix>-N) instead of failing on the first collision; earlier attempts' branches stay untouched. Addresses review on #9547.
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* fix(workflow): brief a handler-less step as the assignee's span to the next handoff (MUL-8231)
Co-authored-by: multica-agent <[email protected]>
* fix(workflow): leave done to a human from an unconfigured in_review (MUL-8231)
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-agent <[email protected]>
UpdateProjectResource derives every column it writes back from the row
read at the top of the request, but the read and the write ran outside
any transaction. Two concurrent partial updates could interleave as
read-read-write-write, and the second writer's stale snapshot silently
reverted the first writer's change: a label-only PUT reverted a racing
ref update to an old Git ref, and a label-only PUT on a local_directory
restored a stale execution mode (reported in #9238 with a deterministic
interleaving that fails 15/15 under -race -count=3).
Serialize the flow with SELECT ... FOR UPDATE inside a short transaction
(GetProjectResourceInWorkspaceForUpdate + Begin/WithTx, same shape as
CreateAgent). Competing updates block on the row until the write lands,
then re-read fresh. This keeps the local_directory legacy label-mirror
writeback intact, which a presence-aware column rewrite alone would have
broken for mirror-triggered ref updates.
Review follow-ups:
- The regression test no longer leaves the race to the scheduler. Before
resuming the paused label request, it waits until the ref request has
either completed outright (the only exit on the pre-fix code, which
never blocks) or is blocked waiting for the label request's row lock
(the only exit with the fix, seen as wait_event_type='Lock' on the
backend running the FOR UPDATE query, checked via pg_stat_activity).
The two signals are mutually exclusive, so the test fails
deterministically without the lock and passes with it.
- findLocalDirectoryConflict and requireWorktreeCapableDaemon now read
through caller-provided queries instead of the handler's pool: on the
update path they run inside the transaction, so a locked request no
longer borrows a second pooled connection per request.
- Added a local_directory concurrency case: a label-only update racing
an execution_mode update through the label-mirror ref rewrite.
The regression tests fail on the pre-fix code ("stored ref = main, want
the concurrent ref update (release)" / "stored execution_mode = worktree,
want the concurrent mode update (cleared = in_place)") and pass with the
lock, 20/20 runs each way.
Fixes#9238
Generated-by: GLM-5.3-Flash (ZCode)
* fix(issues): stop repeating "wake" in a time wakeup's timeline entry
A time rule's label already reads "wake at ..." (or "every hour", "每小时唤醒"),
so the created entry said "…09:00 唤醒唤醒 Agent" in Chinese and
"Wake at 09:00 · wake Agent" elsewhere. Time rules now use a sentence that
names the agent first: "为 Agent 添加了唤醒:…09:00 唤醒".
* refactor(daemon): remove the unused MULTICA_KEEP_ENV_AFTER_TASK setting
Its only reader went away when run directories stopped being removed at the
end of a run so later runs on the same issue can reuse them (ce2b263ea).
Retention is controlled by the MULTICA_GC_* settings.
* feat(agent): add host-local Devin CLI over ACP
Spawn `devin acp` via the shared ACP client. Local CLI only — not
cloud Devin VMs/Playbooks/Secrets. Model via `devin acp --model`.
Catalog from `devin models list --format json` (families/variants).
* fix: skip Devin ACP handshake when browser auth is also offered
* fix: close dim case string so Devin compiles
Missing quote after dim broke runtime_config.go and every Go CI job.
* fix: stop runtimeConfigPath recursion on Devin
Devin is a builtin whose ProtocolFamily is itself. Recursing on that
overflowed the stack and killed backend-tests.
* test(agent): cover Devin model failures and final-answer delivery
Co-authored-by: multica-agent <[email protected]>
* fix(agent): honor Devin model aliases and stored login (MUL-6623)
Co-authored-by: multica-agent <[email protected]>
* fix(agent): drain Kiro panic details before terminating runtime (MUL-6623)
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: Sol-Boy <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* feat(i18n): add zh-Hant locale to web and desktop
Add Traditional Chinese (zh-Hant) as a supported locale with a full
translation of the shared views bundles. Browser tags zh-TW/zh-HK/
zh-Hant now resolve to zh-Hant instead of collapsing to zh-Hans; the
document lang is zh-TW, and docs/use-case links reuse the /zh pages.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit a6b3f6ba53a605c3e06fd6c4d30fd9df6fca8645)
* feat(mobile): add zh-Hant locale
Ship Traditional Chinese bundles for mobile and resolve zh-Hant/TW/HK/MO
device locales to them instead of falling back to English.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit 04563c22662bdb735a9a6c04be784abd9f87da06)
* feat(ui-lab): add zh-Hant locale
Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit 1351ea572ed5d885d8a29034d6c0317de58641ea)
* feat(server): accept zh-Hant as user language
Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit 50dfe77a4c45452fc877c62497aba226d5aaa426)
* fix(i18n): sync zh-Hant with upstream copy changes
Upstream added conversation-starter, instruction-preview and board column
strings, removed restated descriptions (MUL-8162) and renamed Qoder Cloud
Agents. Mirror those key additions, removals and rewordings in zh-Hant so
the locale parity test passes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit 3a97dfc7e0bbf7568925f735c00fc69b6d94313d)
* fix(i18n): add zh-Hant copy for member-scoped runtime visibility
Mirror the runtime visibility "members" choice and the reworded revoke
dialog in zh-Hant so the locale parity test keeps passing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(i18n): replace Mainland terms in zh-Hant copy with Taiwan usage
Several zh-Hant strings were converted character by character from
zh-Hans and kept Mainland vocabulary, which reads wrong to Traditional
Chinese users: 郵箱 → 信箱, 賬單/賬號 → 帳單/帳號, and similar. Values
only; no keys added or removed.
* fix(i18n): polish zh-Hant copy from review
- 分割槽 → 分區 and 許可權 → 權限 (OpenCC phrase mis-conversions)
- drop stray blank lines before zh-Hant workspace names
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Multica Eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* feat(lark): deliver reply images and files
Based on #8332 by @zprial, addressing its review of bounded delivery,
resource uploads, uncertain sends, file fallback and HTTP boundary tests.
Render bound images with the reply text in one card, pair video/audio
uploads with their message types, preserve pending-send uncertainty and
normalize Markdown destinations before matching bound attachments.
Keep runtime artifact ingestion and deployment-specific configuration
outside this change. Add transport, ownership and rendering regressions
and document the behavior in all supported languages.
* refactor(lark): scope attachment transfer slots to the patcher
The concurrency semaphore was a package-level global while the admission
and pending tiers lived on each Patcher. Move it onto the Patcher so all
three limits share one scope and tests no longer mutate global state.
MUL-8205
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* fix(daemon): defer issue status rules to the project workflow (MUL-8145)
When the claim carries a project workflow, the runtime brief no longer
tells the agent to set in_progress first and in_review on delivery. Those
statuses may be outside the workflow (the server rejects them), and
entering a handoff step reassigns the issue. The brief now points at the
per-turn [Project workflow] block, and the workflow brief carries the
entry/exit checks in workflow terms. Briefs without a workflow are
byte-identical to before.
Co-authored-by: multica-agent <[email protected]>
* fix(squad): word the owning leader's status moves by the project workflow (MUL-8145)
The squad protocol (Agent Identity, which outranks the runtime brief) still
told an owning leader to move the parent to in_progress and then in_review,
which a workflow may not list and which can hand the parent to another
step's handler. When the claim carries a project workflow, responsibility 6
now keeps the ownership grant but moves the parent only through the
[Project workflow] commands. The claim resolves the workflow before the
briefing to choose the wording. Guest leaders and issues without a
workflow are unchanged.
Co-authored-by: multica-agent <[email protected]>
* refactor(daemon): list no status keys in the brief for a workflow issue (MUL-8145)
With a project workflow, [Project workflow] already lists the issue's
statuses and the commands that move it, and the built-in keys may not be
accepted there. The status command line now keeps only the command and
points at that block; other issues learn valid statuses from the error.
Co-authored-by: multica-agent <[email protected]>
* refactor(workflow): stop forbidding status moves nothing asks for (MUL-8145)
Under a project workflow the current runtime brief no longer states the
built-in in_progress / in_review moves, so the lines that forbade them
(in [Project workflow], the runtime brief and the squad protocol) only
named statuses the agent had no reason to use. Drop them, and the exit
check the runtime brief already states.
Older daemons still state the built-in rules, so the daemon now
advertises workflow-status-rules-v1; without it the claim keeps the
override lines in [Project workflow] and the squad protocol. The
multica-platform skill notes that [Project workflow] supersedes its
built-in status arc.
Co-authored-by: multica-agent <[email protected]>
* refactor(workflow): shorten the step's moves in the workflow brief (MUL-8145)
Replace the handoff preamble with a plain "Your moves:" heading, name
who each move hands off to on its own line, and drop the "this
workflow has no blocked status" explanations from Can't proceed.
Co-authored-by: multica-agent <[email protected]>
* refactor(workflow): state each step outcome as the status to set (MUL-8145)
The moves block read like raw CLI ('Step done → multica issue status MUL-7 x').
Each outcome now says which status to set and that the server assigns the
next handler; the CLI form is given once.
Co-authored-by: multica-agent <[email protected]>
* Revert "refactor(workflow): state each step outcome as the status to set (MUL-8145)"
This reverts commit 2d82d2d342a99afe88cc40c48516b7ea8e860d1e.
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
Preserve workflow-selected assignees when description updates refresh untouched nullable fields under the issue row lock. Apply the same behavior to single and batch updates while retaining explicit-assignee precedence.
Add regression coverage for single and batch handoffs, unchanged-status retries, and explicit member assignments. All applicable PR CI checks passed on 3c5f8d4.
Co-authored-by: Multica Eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
Use one slash-delimited path contract for skill authoring endpoints and daemon bundle validation. Cover canonical path handling and all public file-write routes with regression tests.
* fix(openclaw): carry per-run model override on the run input (INFRA-71)
Reimplement MUL-7376 (#8419) on this fork and address the review points.
Resolve runtime_config.model_override once, at claim time, and carry it on the run input (model_override); the daemon no longer decodes it from mutable agent config.
OpenClaw keeps agent.model as the registered agent id (--agent) and the override as the underlying model (--model); arbitrary --model from custom_args stays blocked. Usage attribution falls back runtime model -> per-run override -> unknown, never the agent id.
Round-trip model_override through the runtime-config schema, serializer, equality check and the runtime-config tab (i18n in all locales). Docs updated.
Co-authored-by: multica-agent <[email protected]>
* docs(openclaw): clarify persistent agent model overrides
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-agent <[email protected]>
Co-authored-by: Multica Eve <[email protected]>
* fix(daemon): tell resumed runs when their trigger comment was edited (MUL-8218)
A comment edit cancels the affected run and retriggers from the edited
comment, but the follow-up resumes the cancelled run's session, whose
history still quotes the pre-edit text (GH #9492).
The claim now sets trigger_comment_edited_at / trigger_comment_revision
when the trigger was created before the resumed run started and edited
after it. The daemon renders an [EDITED COMMENT] header plus a note that
the quoted text is the current version, while work already done in the
session still happened.
Co-authored-by: multica-agent <[email protected]>
* fix(daemon): keep edit flag after resolve and for coalesced comments (MUL-8218)
Review of #9526 found two paths that resumed a session holding the old
text without flagging the edit:
- A resolve after the edit moved updated_at to resolved_at and the
resolve exclusion hid the edit. Only a single resolve on a first-
revision row (revision 2, resolved_at == updated_at) is now treated
as resolve-only; anything else errs toward the note.
- An edited comment folded behind a newer, unchanged trigger lost its
flag because only the trigger was checked. Every delivered coalesced
comment now carries edited_at / revision, and the prompt marks it
EDITED with the shared current-version guidance.
Adds the reviewer's two regressions.
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-agent <[email protected]>
* fix(skills): preserve complete bundles when source fetching fails
* fix(skills): skip submodules in the contents-crawl fallback
The contents API lists submodules inside a directory as type "file" with
a null download_url. The crawl fallback rejected them as a missing
download URL, so a skill containing a submodule failed with 503 whenever
the repository tree was truncated, while the tree path skips them. Detect
submodules via their git_url tree pointer and skip them; a regular file
without a download URL still fails the import.
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: Multica Eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* fix(autopilot): settle create_issue runs by first task outcome, not issue blocked (MUL-8200)
Co-authored-by: multica-agent <[email protected]>
* fix(autopilot): settle create_issue runs only from their own dispatch chain (MUL-8200 review)
Co-authored-by: multica-agent <[email protected]>
* fix(autopilot): root create_issue dispatch chain at the recorded task and let its outcome win (MUL-8200 re-review)
Co-authored-by: multica-agent <[email protected]>
* fix(autopilot): resolve unlinked create_issue dispatch by agent and wait for pending enqueue (MUL-8200 review)
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-agent <[email protected]>
* MUL-8177: fix(daemon): label worktree finalization failures as local_directory_error (#9447)
Co-authored-by: multica-agent <[email protected]>
* test(wecom): read quoted-bind body and attachment count from one snapshot
The poll read the message body and then counted its attachments in a
second statement. When the bind transaction committed between the two,
the count said binding was done while the body still held the unbound
"[Image: unavailable]" markers, failing TestAQuotedPictureIsNamedInTheStoredBody
intermittently in CI.
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* fix(opencode): treat process exit as the terminal signal on 2.x
OpenCode 2.x stops consuming step events once the session settles, so the
final step_finish is routinely never written. The daemon failed those
completed runs as 'step still open at EOF' / 'continuation never started',
classified them provider_network and auto-retried, repeating side effects.
On built-in 2.x runtimes, waive the two step-bracketing guards and let the
process outcome decide: exit code, error events, prompt write, cancel /
timeout and scanner errors still fail the run. The void-step guard stays.
1.x and custom runtimes are unchanged.
Also decode the 2.x error payload ({type, message}) so failures surface
their real cause instead of 'unknown opencode error'.
Refs #8892, #9456
Co-authored-by: multica-agent <[email protected]>
* fix(opencode): address review on 2.x terminal-signal relaxation
- Clear the void-step flag on 2.x once later text or a tool call shows the
run recovered, since the recovering step may never report step_finish.
1.x still requires that step to close.
- Never report stream-level completion (sawTerminalSignal) on 2.x: an
intermediate step_finish is not proof the run finished, so a failed
prompt write keeps failing the run.
Refs #9463
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: Multica Eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* fix(agent): preserve runtime config boundaries after formatting
Keep original trailing newlines inside the managed block so formatter normalization does not leak cleanup changes into task commits.
Constraint: Preserve user edits outside the managed brief and legacy block behavior.
Rejected: Restore the entire original file | Discards agent-authored changes.
Confidence: high
Scope-risk: narrow
Tested: execenv suite with race and shuffle; go vet; Prettier 3.6.2 and Oxfmt 0.40.0 file round trips.
Not-tested: Full daemon auto-finalize integration.
* fix(agent): don't restore boundary into replaced body; keep CRLF consistent
Follow-up to the formatter-boundary fix (MUL-8114, review on #9382):
- Record whether the pre-existing file was blank in the boundary metadata.
An empty prefix at cleanup only means "blank original" when that flag is
set; otherwise the agent removed or moved the original content, and
re-adding the old trailing newline produced "\n# New\n".
- Treat any two line breaks (LF or CRLF) as the collapsed separator and
restore the recorded boundary in the formatter's line-ending style, so
CRLF files no longer end up mixed or with extra CRLFs.
- locateMarkerBlock consumes a CRLF after the end marker.
- Restore the removed test doc comment and update the inject/cleanup docs
to describe the metadata line.
Co-authored-by: multica-agent <[email protected]>
* fix(agent): don't restore blank-file boundary in front of new content
A newline-only CLAUDE.md that the formatter stripped and the agent then
wrote a body into (after the managed block) got the recorded newline
restored in front of the new body, failing prettier --check. Only restore
a blank file's boundary when nothing follows the block (MUL-8114).
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: shaun0927 <[email protected]>
Co-authored-by: Multica Eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* fix(kiro): fail fast when the runtime panics mid-turn (MUL-8188)
Kiro can panic inside its agent loop while the ACP process stays alive, so
session/prompt never answered and the task hung until the 2h idle watchdog
or a manual cancel. Watch stderr live for a panic header, abort the prompt,
and surface the panic text. A resumed session that panics before its first
message signals ResumeRejected so the daemon retries once on a fresh session.
Co-authored-by: multica-agent <[email protected]>
* fix(resume): stop re-resuming sessions the runtime cannot drive (MUL-8188)
- taskfailure.UnresumableHistory matches "invalid conversation history",
so the daemon's fresh-session retry, classifyPoisonedError and
ResumeUnsafeFailure all treat Kiro's panic as resume-unsafe; the
GetLastTaskSession / GetLastChatTaskSession text guards mirror it for
rows written by older daemons.
- A resumed run cancelled after >=5 minutes with zero agent output now
retires its session: the daemon sends retired_session_id on cancel-ack
and the server records it on the cancelled row (status CAS, never
overwrites) and clears a chat pointer still naming it.
Co-authored-by: multica-agent <[email protected]>
* fix(resume): address review on retired sessions (MUL-8188)
- Manual retry no longer revives a retired session: the exact-source rerun
claim path now checks IsTaskSessionRetired (fails closed), still reuses
the source workdir, and sets PriorSessionResumeUnavailable.
- Silent-resume retirement measures awakeclock time, so host suspend
(Windows monotonic clock counts through sleep) cannot retire a healthy
session; adds a suspend regression test.
- Handler tests use dbfx.Issue/Task and testutil.Call per AGENTS.md.
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-agent <[email protected]>
The failure classifier's status-code regexes guarded against longer
numbers only: `(^|[^0-9])(429|529)([^0-9]|$)` and friends. Workspace
and task IDs are hex, so a path like
~/multica_workspaces_x/<slug>-0000c429f4b/<task>/worktree embeds "429"
between letters, which passed the digit-only guard — and the capacity
rule runs before the process-failure and unknown rules. Every failed
task quoting such a path was labeled
agent_error.provider_capacity_or_rate_limit, sending users to check
their provider accounts for a 429 no provider ever returned. The same
gap misfiled paths containing hex "401"/"402"/"403"/"5xx" into the
auth, quota and server-error buckets.
Widen all five boundary guards (capacity, auth, quota, 5xx, and the
403 usage-limit regex shared with NormalizeDaemonReason) from
`[^0-9]` to `[^0-9a-z]`. The classifier lowercases its input before
matching, so letters are the only remaining embedded context to
exclude. Genuine status codes (space/punctuation-delimited, or bare)
still match.
The MUL-1949 offline backfill SQL these patterns mirror is not in this
repo; regenerating it needs the same widened boundary to stay in
lock-step (noted in the pattern comments).
Fixes#9146
Generated-by: GLM-5.3-Flash (ZCode)
* feat(server): record structured claim decision outcomes
Every daemon poll decides whether a task is dispatched, skipped, or left
waiting, but most of those decisions were invisible: claimTask's outcome
string only surfaced through the >=300ms slow log, two outcomes had
scattered debug lines, and no_runtime / runtime_mismatch had nothing at
all. The worst blind spot is no_tasks, which collapses ClaimAgentTask's
SQL fences (wakeup gate, runtime liveness, per-(issue, agent)
serialization, rebind) into one value even though the caller's candidate
list knew whether work was visible (#8551).
Add a single observability funnel, recordClaimDecision, called once per
claim attempt from claimTask's defer and once per claim-poll exit that
never reaches claimTask. It emits one bounded-cardinality counter
(multica_agent_task_claim_decision_total{source, outcome, reason}) and a
structured debug log. The reason split inside no_tasks —
candidates_blocked vs no_candidates — attributes a queue wait to a fence
rather than to an empty queue. Idle-poll outcomes are counted but not
logged to keep the hot path quiet. No decision logic changes: outcomes
mirror strings the claim paths already produce, and the caller-supplied
claimScope is informational only.
Co-Authored-By: Claude Code <[email protected]>
* feat(server): cover batch claim polls and sharpen decision reasons
Address review feedback on the claim decision funnel:
1. Batch poll decisions now reach the counter: ClaimTasksForRuntimes
records its promote/reclaim/list error exits, its empty-cache
short-circuit, its zero-candidate result, and each recovered
stale-dispatched task. The counting unit is documented in the funnel:
one increment per claim attempt plus one per poll-level decision,
which are distinct outcomes and cannot double-count.
2. Reason labels now state only what the evidence supports. The
candidate queries already filter wakeup validity and agent/runtime
binding, so an empty result is no_visible_candidates, never
empty_queue. Candidates observed with no_tasks is
candidates_unclaimed — the exact cause (SQL fence vs lost race)
is unknown from the Go side.
3. The source label is renamed to claim_path so it cannot be mixed with
the task-origin source label of the other multica_agent_task_*
metrics in shared dashboard filters.
Adds batch-path DB coverage (claimed, no visible candidates,
candidates unclaimed) alongside the existing singular-path cases.
Co-Authored-By: Claude Code <[email protected]>
---------
Co-authored-by: Thneoly <[email protected]>
Co-authored-by: Claude Code <[email protected]>
* fix(daemon): let a worktree turn repair a delivery it moved off its branch (MUL-8139)
Worktree-mode turns now get told which branch and starting commit their
delivery must stay on. When a turn still ends with its work squashed past
the baseline or committed off the task branch, the daemon runs one repair
turn asking the agent to put the work back before Finalize decides.
The MUL-6881 delivery guard is unchanged; resets past the baseline and
repairs that would revert the baseline's content are never offered.
Refs #9404
Co-authored-by: multica-agent <[email protected]>
* fix(daemon): refuse repairs that would revert baseline edits; no supplements in repair turn (MUL-8139)
Review follow-up on the worktree delivery self-repair:
- repairWouldRevertBase now proves at the content level that the agent's
files already carry every change the baseline made relative to its merge
base with tip (equal content, or a clean three-way merge that leaves the
agent's file unchanged). A path-level "both sides touched it" check let a
repair silently revert a user's edit in a file the agent also changed.
Deletions, non-regular or binary files, conflicts and git errors refuse.
- The repair turn no longer accepts additional messages: its reply is
discarded, so an accepted message would be acked as delivered and its
answer lost. Unclaimed, it settles as turn_ended and the receipt says so.
Co-authored-by: multica-agent <[email protected]>
* fix(daemon): keep file modes in the worktree repair preservation check (MUL-8139)
repairWouldRevertBase compared only text, so a user's chmod +x recorded in
the baseline was lost when the agent worked from an older checkout: the
repair re-committed run.sh as 100644. The agent's side is now read from the
tree `git add -A` would stage (built in a private copy of the worktree
index), which is exactly what RepairSteps commits, and every path the
baseline changed must keep the baseline's mode. A mismatch, or an index that
cannot be copied, refuses the repair.
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* feat: share runtimes with selected workspace members
Co-authored-by: multica-agent <[email protected]>
* refactor(runtimes): one-click private/public and popover member picker
Private and Public apply immediately again (restrictions still go through
the reviewed revocation dialog). Specific members opens a draft popover
with search, Cancel and Save; selected recipients are summarized under
the scope control with an Edit entry point. The editor moves to its own
runtime-access-editor component.
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-agent <[email protected]>