Commit graph

2481 commits

Author SHA1 Message Date
Shaojin Wen
e584d4d197
docs(managed-agent): add the FG12b and FG12e fault-gate designs (#14447) (#14560)
* docs(managed-agent): draft the FG12b and FG12e fault-gate designs

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* docs(managed-agent): correct the FG12b and FG12e designs after audit

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* docs(managed-agent): address the FG12b and FG12e design review

* docs(managed-agent): close the remaining FG12b/e review findings

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-10-18 09:43:26 +00:00
易良
0fac9973df
fix(managed-agent): verify ambiguous Linux workspace birth times (#14505)
* fix(managed-agent): verify ambiguous Linux workspace birth times

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* test(managed-agent): pin the #14461 recovery accessors in the session lock-order audit (#14505)

#14461 added hasLiveTurn and markTurnRecoveryBlocked to
ManagedAgentStore without updating the pinned turn-row accessor set in
SessionRowLockOrderTest, so the exact-set assertion fails on any tree
containing both — including this PR's merge commit, whose fast tier is
red because of it. hasLiveTurn is a read-only helper and
markTurnRecoveryBlocked is fenced by the live dispatch lease rather than
the session-row lock, and the audit finds no lock-ordering violation in
either, so both belong in the pinned accessor set.

* docs(managed-agent): note Maven report output breaks the root ESLint sweep (#14505)

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: qwen-code-autofix <qwen-code-autofix@localhost>
2026-10-18 17:34:18 +08:00
易良
1151c56ce0
fix(managed-agent): preserve rotated credential keys during operator recovery (#14509)
* fix(managed-agent): preserve rotated keys during operator recovery

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* test(managed-agent): scope-key the Hosted lane's ma_resource helper reads

The FG7c adapter-stop and FG6c crash gates timed out in the hosted
process-fault lane when their test helpers read ma_resource by raw
tenant/session/resource IDs: the table's indexes are resource_key and
(session_scope_key, resource_id), so those reads full-scan a table the
whole shared fork accumulates into. Lead the same reads with the
session scope key, keeping the raw IDs as residual predicates — the
shape production reads and HostedGateTestSupport already use.
Timeouts and assertions are unchanged.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
2026-10-18 17:18:24 +08:00
Shaojin Wen
970b887629
test(managed-agent): fix the FG12d post-merge evidence and ledger P3s (#14553)
* test(managed-agent): fix the FG12d post-merge evidence and ledger P3s

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* test(managed-agent): resolve the FG12d case letter before boot can fail

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-10-18 09:05:08 +00:00
jinye
97ebffaa70
feat(managed-agent): support Plan mode and inline attachments (#14531) 2026-10-18 16:58:07 +08:00
Shaojin Wen
d1c6f02ff6
test(managed-agent): add the FG11b Spring-side automation store-fault gates (#14446) (#14551)
* test(managed-agent): add the FG11b Spring-side automation store-fault gates (#14446)

A second method of HostedAutomationFaultGatesIT, on the FG11a boot, with
six cases sharing one minute boundary:
- occurrence-insert: a refused occurrence record fires nothing, and the
  slot fires once after the refusal drops;
- occurrence-settle: a refused settle keeps the occurrence firing and
  re-driven against the same run, one run chain and one model Turn;
- watermark: a refused watermark advance re-derives the slot every tick,
  and its recorded decision stands;
- receipt-claim / receipt-settle: a refused request receipt relays
  nothing, or re-drives the same operation id once it drops;
- revision-race: a fire parked while an update commits revision 2 meets
  automation_revision_stale and is re-pinned and fired once, never
  skipped.

Refusals are tenant-fenced MySQL triggers (the FG9b/FG10c pattern); the
relay gains a hold that parks an operation before the Harness. Six
mutation witnesses are red; one single-layer watermark mutation is
equivalent and recorded. The Harness-side store faults are slice 2.

The method measures 30.8-64.5 s locally and is budgeted at ~130 s: the
healthy sum moves to 4738 s and the fork to 5348 s, inside the Verify
step's 100 minutes.

* test(managed-agent): pin FG11b's recovered occurrence to the refused slot (#14446)

Review round 1: occurrence-insert took the first recorded scheduled
occurrence after the refusal dropped, without checking it was the slot the
trigger refused. A regression that skipped past the refused slot, letting
the watermark advance, would pass on the next minute's fire.

The refusal row now records what it refused (the occurrence key, or the
watermark slot). occurrence-insert and occurrence-settle check that every
refusal names one slot and that the slot they recover is that one.

A/B with a mutation that swallows the refused decision so the watermark
advances past it: the previous assertions stayed green (163.3 s, the next
minute fired in its place); the new ones go red at "every refusal is the
one slot's record".

---------

Co-authored-by: 易良 <1204183885@qq.com>
2026-10-18 08:53:49 +00:00
Shaojin Wen
c7061b2c3a
test(managed-agent): add the FG12d MCP release fault gate (#14447) (#14549)
* test(managed-agent): add the FG12d MCP release fault gate (#14447)

* test(managed-agent): pin the FG12d release and settle witnesses

Require the held release dispatch to answer running before the kill, so
a synchronously settled release cannot satisfy the lookup witness; filter
the status witness to a settled 200 for the original release id; assert
the released server exits once instead of claiming it survives; accept
the lifecycle 409 conflicts while retrying settle_resources; and correct
the docs' saveEffects/status/timing claims.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* test(managed-agent): tighten the FG12d settle retry and status target

Retry only hosted_turn_active on the lifecycle route; a
hosted_lifecycle_operation_conflict cannot self-heal and should fail
with its body. Join the settled-status witness to the original
runtimeSessionId, and correct the docs' latest measured seconds.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: 易良 <1204183885@qq.com>
2026-10-18 08:13:59 +00:00
Shaojin Wen
5dc4b0e501
test(managed-agent): add the FG13d stopped-worker cancel gate (#14541)
* test(managed-agent): pin the FG13d cancel against a stopped worker

SIGSTOP the durable worker while the command holds, then cancel through
the Harness route. The owner cannot answer the lookup, so the record must
stay recovery_blocked/outcome_unknown and keep the unit populated instead
of inventing an end. Adds 150 s to the shared fork budget and records the
matching mutation witness in the design.

* test(managed-agent): hold the stopped-worker fixture indefinitely

SIGSTOP freezes the worker, not the fixture's own hold timer: a 60 s hold
would end during the two 30 s owner lookups, kill the child and write the
allow, so a correct implementation could fail on scheduling. Hold until
teardown instead, matching the worker-kill cells.

---------

Co-authored-by: 易良 <1204183885@qq.com>
2026-10-18 08:03:03 +00:00
qqqys
715c0a2e60
fix(mods): skip catches for re-entered registrations (#14489)
Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com>
2026-10-18 15:57:46 +08:00
Shaojin Wen
7a2ce9b99a
test(managed-agent): add the FG11a automation lost-reply fault gates (#14446) (#14534)
* test(managed-agent): add the FG11a automation lost-reply fault gates (#14446)

The first packaged lane that turns automation on: in-JVM Spring with the
scanner running, a real Harness behind a relay that drops one committed
answer, real MySQL and a model stub.

Eight cases share one boot and one minute boundary:
- create-reply, update-reply (a newer update lands meanwhile),
  noop-update-reply and retire-reply: the retry answers the committed
  revision and never commits again or over a newer one;
- manual-run-reply and scheduled-fire-reply: the re-drive meets the
  same arun_ run, with one run chain and one model Turn;
- reconcile-reply: a lost reconcile answer leaves the slot skipped
  under its own refusal, never unknown;
- public-reply: a lost public 202 replays the receipt without a second
  Harness operation.

Five mutation witnesses are red; one equivalent mutation is recorded in
the design. The fixture runs one Turn per Session first: the automation
verbs only load a Session on the Harness, so one created without input
is refused 404 managed_session_not_found.

The class measures 46.3-85.0 s locally; it is budgeted at ~170 s, so the
shared fork's healthy sum moves to 4359 s and its timeout to 4969 s,
inside the Verify step's 95 minutes.

* test(managed-agent): keep FG11a's clocked cases off the scanner until armed (#14446)

Review round 1:
- The scanner ran from boot, so a minute boundary between a definition's
  create and its armed fault, or its held manual run, let a slot fire
  first: attempts 0 for scheduled-fire-reply, 409 automation_run_skipped
  for reconcile-reply. SchedulerHold now parks the scanner's only thread
  while the three clocked cases set up; manual runs fire on the request
  thread and are unaffected.
- assertOneRun collected every fire and run command of the schedule, so a
  legitimate second slot on a slow runner read as a duplicate. The
  exactly-once checks are now scoped to the occurrence under test.
- The design docs give the Verify step's 95 minutes, describe both fixes,
  and say a first red skips the later cases, which are re-run per case.
2026-10-18 07:22:12 +00:00
Shaojin Wen
e5ceeb4bea
test(managed-agent): add the FG13d Hook cancel and timeout fault gates (#14533)
* test(managed-agent): add the FG13d Hook cancel and timeout fault gates

Extend the packaged Hook rig with a descendant that ignores SIGTERM, and
pin two arms against the real stack: a cancel must escalate to cgroup.kill
after the grace and settle cancelled only once the unit is empty; a 2 s
timeout must kill the unit, keep the output unapplied, and block the tool.
The class adds 200 s to the shared fork budget and is registered in the
pinned Hosted class list.

* test(managed-agent): drop the unused FG13c helpers from the FG13d class

The new class was copied from the owner-restart rig, so it carried the
Harness-kill, worker-kill and status helpers those cells used. This slice
cancels through the Harness route and waits on JDBC, so those helpers are
dead; removing them keeps the class to what it exercises.

* docs(managed-agent): cite the whole Hook unit terminate() range in the FG13d design

* test(managed-agent): check the drained unit at the first terminal observation

The cells waited for the terminal record and only then waited out the
descendant, so a regression that settled early while the drain continued
still passed. Inspect the first terminal observation instead: the
descendant must be gone and the unit empty at that instant, and document
the early-terminal mutation witness.
2026-10-18 06:40:11 +00:00
Shaojin Wen
193e365290
test(managed-agent): add the FG12c MCP restart fault gates (#14447) (#14524)
* test(managed-agent): add the FG12c MCP restart fault gates (#14447)

Kill the packaged Harness, the durable Runtime worker and the stdio MCP
server, and prove that the surviving generation never mints a second
owner, never resends a control and never fabricates a settled result.
The fixture parks a held resource read until a release file appears and
records stdin close, so the worker-kill cells assert the child's fate.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(managed-agent): close FG12c review gaps and re-pin the fork budget

Wait for dispatch_started and a held configure before killing the
Harness; drive a real recovery on the same Session after an idle worker
kill and assert the identity is unchanged; validate the operation status
response and its published result instead of discarding the lookup. The
five cases now measure 49 s, so the healthy sum moves to 3938 s and the
CI fork to 4548 s.

* fix(managed-agent): wait for the refused recovery acquire in FG12c

A 202 only admits the prompt; the MCP refresh and acquire run later in
the turn's declarations. The idle-worker cell now waits for the recovery
acquire to be refused with 409 by the dead generation before it reads
the configuration identity, so a fast assertion cannot observe only the
pre-crash state. A direct 409/503 refusal must name a recovery error.

* test(managed-agent): harden the FG12c witnesses against false-green paths

Sequence the proxy wire so baseline markers cannot race an in-flight
relay; pin the idle-worker recovery acquire to the dead generation's
409 body; count invokes and server starts in the worker/stdio cases; and
pair the configure status answer with the persisted record state.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* test(managed-agent): close the FG12c round-2 high findings

Wait for the successful status entry in the recorded wire instead of the
relay-entry control, so a slow responder cannot produce a false red; and
require a direct 409/503 refusal to be backed by the dead generation's
own refusal. Wait for the fixture to exit before asserting it is gone.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* test(managed-agent): accept both dead-generation acquire refusals

The killed worker can refuse the recovery acquire with 409 'Runtime
generation is no longer live' or 503 'Managed Runtime process is not
alive.'; accept both, anchored to the :acquire request. Require the
configure status answer to be terminal before pairing it, and await the
durable worker registration before killing it.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* test(managed-agent): make the FG12c status witness terminal-aware

Select the first terminal status answer instead of locking the first
200 (which can be running), stop requiring an acquire that a
pre-admission refusal never issues, and accept the third legitimate
dead-generation refusal message.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* docs(managed-agent): sync the FG12c budget with the merged FG13c ledger

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* test(managed-agent): only retry the dead writer lease on reload

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-10-18 06:06:25 +00:00
Shaojin Wen
a703a76ae2
test(managed-agent): add the FG13c Hook owner-restart fault gates (#14449) (#14490)
* test(managed-agent): add the FG13c Hook owner-restart fault gates (#14449)

* test(managed-agent): tighten the FG13c hook fault gates after audit

* test(managed-agent): address the FG13c review findings

* test(managed-agent): settle the FG13c harness-kill hook after a Turn cancel

* test(managed-agent): address the FG13c review census

* test(managed-agent): reconcile the FG13c hook before the terminal wait

* test(managed-agent): tighten the FG13c fresh-prompt reconcile after audit

* test(managed-agent): drop the stale SIGTERM claim from the FG13c fixture

* test(managed-agent): settle the FG13c Harness-kill Hook via the Harness status route

The public prompt cannot trigger reconciliation: the control plane's
active-Turn gate answers 409 in insertTurnCommand before any Harness
dispatch, and the cancelled Turn never leaves CANCELLING. Re-attach the
Session to the replacement Harness with the passive recovery load and
read the operation through the authenticated hooks/operations route,
which asks the record's original runtimeSessionId and settles it from
the receipt the worker still holds.

* test(managed-agent): pin the FG13c kill window and the settled owner

The Harness-kill cell could pass without proving anything if the Hook
had already allowed before the kill: the old Harness would have settled
the record and the status read would see a terminal one. Hold longer and
refuse to kill while an allow is already on disk. Assert after
settlement that the binding still names the same worker and generation,
which is what the design claims.

* test(managed-agent): reuse the replacement attachment instead of forcing a load

A Session the coordinator already attached is resident with a Hook-pending
Turn, so a second passive load of it is refused with takeover_unsupported;
the forced call sat outside the wait and exited the cell before the status
route could run. Probe the status route first, reuse whatever attachment
serves it, and ask for a passive re-attach only on 404, at most once a
second.

* test(managed-agent): keep a refused re-attach inside the wait

untilAsserted retries an AssertionError, not a RuntimeException, so a
takeover_unsupported refusal would still have exited the cell. Swallow it
at the call site and report the message on the assertion instead.

* test(managed-agent): send the Harness contract headers on the Hook status probe

Every /session route runs the hosted-harness contract middleware, so the
status probe needs X-Qwen-Harness-Protocol-Version and the boot id of the
process it is talking to. The identity now carries both from the attachment
ref, and a stale generation counts as an attachment that cannot serve the
route, like a missing Session.

---------

Co-authored-by: 易良 <1204183885@qq.com>
2026-10-18 13:03:10 +08:00
Shaojin Wen
2d05b041a0
test(managed-agent): add the FG12a MCP reply-loss fault gates (#14447) (#14499)
* test(managed-agent): add the FG12a MCP reply-loss fault gates (#14447)

* test(managed-agent): stop the FG12a fixture's durable worker (#14447)

* test(managed-agent): address the FG12a gate review (#14447)

* test(managed-agent): stop the FG12a workers off Linux too (#14447)

* test(managed-agent): reuse the shared worker teardown on every host (#14447)

---------

Co-authored-by: 易良 <1204183885@qq.com>
2026-10-18 03:28:00 +00:00
Shaojin Wen
b3c5d3ed1e
fix(managed-agent): keep a crash-blocked workflow run unknown and honestly notified, gated by FG10d (#14165) (#14506)
* fix(managed-agent): keep a crash-blocked workflow run unknown and honestly notified, gated by FG10d (#14165)

FG10d, the last FG10 letter, SIGKILLs the packaged Harness under an
attached workflow run, with its agent() request in flight and with its
work done but the child Turn's settlement swallowed at the store, then
starts a cold one. The run must never be re-executed or settled as if
observed: the Turn parks marked hosted_workflow_recovery_blocked, the
cold load refuses, and the record settles recovery_blocked over
outcome_unknown with one blocked notification to the lead.

The gate found two defects in that honest-settlement path:

- A retained close debt wrote the walk done and its CHILD_RESULT row
  delivered whatever the arm proved, so the unknown the recovery-blocked
  arm and the give-up chain intend read as a delivered result wherever
  the host cannot close the child at once. The debt now parks under the
  walk's own classification (retainCloseDebt), as classify does.
- settleRecoveryBlocked rendered the parent's notification from the
  record it replaces, so under <status>blocked</status> the result line
  read "run running (unknown)". It now renders the blocked revision.

The store proxy gains a SWALLOW disposition and the class a killHarness
helper. The Hosted fork's healthy allowance composes 3743 + 131 = 3874 s
and the fork moves 4353 -> 4484 s; the Verify step and job ceiling hold.
The relay design gains FG10d's section; the a+d design points at it.

* fix(managed-agent): release a parent whose child walk retired unknown, and sweep FG10d's review nits (#14165)

Session resource collection held a retired parent while a CHILD_SESSION_START
walk was not done/orphaned, but the relay's `unknown` is just as terminal and
is never rewritten, so the hold had no release; the close-debt fix carried it
to hosts without close capability. The collector now treats `unknown` as
terminal; an owed close rides its own CHILD_SESSION_CLOSE row and reads no
parent resource.

Also: pin the give-up arm's no-close classification, refuse a close row in
retainCloseDebt itself, correct the give-up javadoc and test comment that
still described the old either/or, and sweep the stale four-letter and
FG10d-selector text in the IT header, the CI comment and both designs.
2026-10-18 02:00:49 +00:00
jinye
fbc698a651
feat(managed-agent): support approval changes for stored agents (#14484) 2026-10-18 00:54:29 +00:00
Shaojin Wen
428f686ade
docs(managed-agent): record M6 as implemented in #14483 (#14491)
* docs(managed-agent): record M6 as implemented in #14483

The design still said the last slice registers and enables nothing and that
neither code slice is implemented. Registration and the workspace-change
acknowledgement landed in #14483; the review fixes and the remaining test and
end-to-end coverage are in #14488.

Refs #12737

* docs(managed-agent): correct the M6 follow-up coverage claim

The status said the remaining tests and the end-to-end coverage are in #14488. They are not: #14488 carries the review fixes and one bridge assertion. Say that the workspace-change tests and the end-to-end pass are still open.

Refs #14491

---------

Co-authored-by: 易良 <1204183885@qq.com>
2026-10-18 02:09:38 +08:00
Shaojin Wen
f110a6b7fa
fix(managed-agent): never re-drive an unsettled workflow Turn on the recovery path (#14387) (#14461)
Some checks are pending
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Lint & Static (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / web-shell E2E Smoke (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Integration Tests (no-AK, No Sandbox) (push) Blocked by required conditions
Qwen Code CI / Integration Tests (CLI, No Sandbox) (push) Blocked by required conditions
E2E Tests / Build for E2E (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/1 (push) Blocked by required conditions
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/1 (push) Blocked by required conditions
E2E Tests / E2E Test - macOS - shard 1/2 (push) Blocked by required conditions
E2E Tests / E2E Test - macOS - shard 2/2 (push) Blocked by required conditions
E2E Tests / E2E Interactive - OpenTUI renderer (bun) (push) Blocked by required conditions
E2E Tests / channel-plugin E2E (nightly) (push) Blocked by required conditions
E2E Tests / cron-interactive E2E (nightly) (push) Blocked by required conditions
E2E Tests / web-shell Browser Regression (push) Waiting to run
Managed Agent gate / SDK Java () (push) Blocked by required conditions
Managed Agent gate / Select the Managed Agent tier (push) Waiting to run
Managed Agent gate / Managed Agent gate (push) Blocked by required conditions
SDK Java Contract Open PRs / Re-check open PRs against main (push) Waiting to run
SDK Java Flyway Open PRs / Re-check open PRs against main (push) Waiting to run
verify Maven repo producer / Save verify Maven repo (push) Waiting to run
* fix(managed-agent): never re-drive an unsettled workflow Turn on the recovery path (#14387)

A workflow Turn's work is one deterministic in-process script under
node:vm, and a Harness crash leaves no witness to its in-process
subagents. Re-driving it after the crash would re-run the script and
re-dispatch the subagents, spending the work twice while the first
run's outcome stays unobserved — and the recovery attach would settle
the Turn FAILED as if that outcome had been observed.

The coordinator now refuses to re-drive a workflow Turn once its
Session is bound: the Turn stays unsettled, the relay classifies the
record unknown/blocked, and the cold-load refusal keeps the child
Session's reopen refused. A cancel still drives — settling the Turn
cancelled is the cancel's own act, not a re-run. The first launch is
unaffected: harness_boot_id is written only by the first attach, so
the refusal can only fire on a genuinely previously-driven Turn.

* fix(managed-agent): scope, park, and settle the refused workflow Turn (#14387 review)

Review on #14461 found the refusal too broad and too quiet. The refusal
now fires only where re-execution would actually run — the takeover
continue of a parked snapshot and the resubmit of a Turn whose admission
the lost generation never recorded — so a healthy same-boot reconnect
still resumes the event stream of an admitted workflow Turn instead of
being refused with it.

Refusing no longer ends in a bare lease release either: the Turn is
durably marked (still active, never settled as if observed) and parked
out of the dispatch sweep — the claim-refuse-release tick would
otherwise re-qualify every second forever. A cancel still drives:
CANCELLING rows stay scannable, and settling the Turn cancelled is the
cancel's own act.

The child result relay reads the mark off the Turn line and settles the
child_run record recovery_blocked over outcome_unknown through a new
child operation, classifying the ledger unknown — the honest terminal
shape for an execution whose outcome no witness survived.

* fix(managed-agent): close the workflow refusal's replay, refusal-route, close, and lease gaps (#14387 review)

Round two of review on #14461 found four gaps in the refusal path. The
resubmit refusal now requires evidence the generation changed — the
attach's boot differing from the Session's — so a lost submit reply on
the same live Harness still replays through the daemon's admission
dedupe instead of parking forever. Takeover loads that refuse before
returning a snapshot (the decline and the coded 409) route to the same
durable block instead of exhausting retries into a dishonest FAILED.
The lifecycle gate counts only live Turns, so the parked workflow Turn
no longer blocks the child Session's close admission — without it the
blocked settlement could never land. And the blocking verdict is fenced
by the live dispatch lease, so an owner whose lease expired parks
nothing.

* fix(managed-agent): keep transient takeover refusals out of the workflow-loss verdict (#14387 review)

A 409 hosted_turn_recovery_required also covers retryable restore read
failures, which are not evidence the workflow cannot be observed — a
completed result may already sit journaled behind them. The refusal now
parks the Turn blocked only on the daemon's terminal
takeover_unrecovered verdict; every other reason keeps the retry path,
so one transient read hiccup can no longer strand a committed workflow
into the unknown/blocked settlement.

* fix(managed-agent): keep the workflow block off a cancelling Turn (#14387 review)

The block sites in the two takeover-refusal catches gated only on the
recovery path and the workflow input, so a user-cancelled workflow Turn
whose takeover load kept refusing was marked too — but the mark cannot
park a CANCELLING row (the dispatch sweep re-admits it so the cancel can
drive), and the write only re-opened the claim-refuse-release spin with
the cancel never settling. Both catches now carry the same
non-cancelling guard the recovery arm already had: a cancel keeps its
own bounded retry/fail path.

---------

Co-authored-by: 易良 <1204183885@qq.com>
2026-10-17 17:28:57 +00:00
Shaojin Wen
6ad896c20b
feat(managed-agent): reconcile parked Session title deliveries (D7-2) (#14475)
* feat(managed-agent): reconcile parked Session title deliveries (D7-2)

A title delivery whose submission went unanswered now parks
recovery_blocked like every other acceptance-unknown kind, and the
operation reconciler settles it from the Session journal's
hosted-title:<revision> commit. Parking retires the delivery's receipt,
so a crash-parked title no longer wedges later titles or the Session
lifecycle; legacy UNKNOWN rows are converted by the delivery sweep.

* test(managed-agent): cover parked Session title reconciliation (D7-2)

* docs(managed-agent): describe the D7-2 Session title phase
2026-10-17 15:04:48 +00:00
Shaojin Wen
910a2909a7
ci(sdk-java): run the Hosted failover arms in their own job (#14467)
* ci(sdk-java): run the Hosted failover arms after an unrelated Verify failure

The 15 failover E2E steps had no if:, so any failing test in the Verify
step skipped them all. Since #14181 merged, four consecutive main runs
failed Verify on four different tests, and the seven G3 Step 3 arms have
never executed in CI.

The MySQL install step now runs whenever the Managed Agent dependencies
installed, and first checks that the server jar was packaged; each arm
runs when that step succeeded. A failing arm still fails the job.

* ci(sdk-java): move the Hosted failover arms into their own job

Conditions on the arm steps let them run after an integration-test
failure, but they still waited behind the 80-minute Verify step and
depended on its packaged jar. The fifteen arms, the MySQL binaries
install, the runner-only QWEN_E2E_REGISTER_STORAGE and the Step 3
evidence upload now form hosted-failover-e2e, which bundles the CLI,
installs the SDK and packages the server with -DskipTests itself, with
no needs: on the Hosted MySQL job. Each arm still runs when an earlier
one failed.

The Hosted MySQL job ceiling drops from 325 to 175 minutes; the new job
has 165. The pins move with the steps, the long-answer arm joins the
arm table, the SpotBugs staging count goes to five, and D11, the Step 3
design, the server README and the runner's Linux-only hint name the new
job.

* docs(managed-agent): point the remaining failover-arm references at the new job

The D11 Status header, the 275/290 ceiling of the Step 3 slice and the
ubuntu-only lane note still named the Hosted MySQL job as where the
failover arms run, and so did the G1 failover design, the Broker fault
gates scope and the review-corrections gate list (whose Workspace
file-tool Turn and durable-store adapter do stay in that job, so the
sentence splits). Point-in-time notes keep their history and add where
the arms run now.

The Hosted MySQL report upload has to precede the two steps that run
`clean verify` on the module, or it ships an empty artifact with only a
warning; the test now pins that order. `--big-output` joins the mode
flags each failover row must own alone.
2026-10-17 14:48:59 +00:00
Shaojin Wen
ace759b061
docs(managed-agent): specify the M6 engine registration and workspace-change contract (#14479)
Record the last slice of the ordinary-host Managed engine: registration of
the M2 host with the M3 compatibility evaluation behind
--experimental-paired-engines, the workspace-change acknowledgement a Managed
channel currently lacks, and the lifecycle and quarantine contract.

The section is marked design only. No production caller builds the engine and
the Managed host does not answer qwen/control/workspace/change yet; the two
code slices land separately. It also corrects the permission-change path: a
settings reload does not touch live permission managers, so the child must
re-read the rule sets and sync them the way the permission-rule control
method does.

Refs #12737
2026-10-17 22:45:29 +08:00
jinye
95b8d377ad
feat(serve): attach private CSI Sessions to Hosted text turns (#14081)
* feat(serve): attach private CSI Sessions to Hosted text turns

Extract the first private Hosted attachment and native no-tool text turns from the CSI proposal, stacked on the original Session authority. Keep original ownership, exact prompt replay and local-only stop semantics, with producer-to-SQL grammar and boundary tests.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* docs: record CSI text validation on main

Update the synchronized design status after the private Session authority and 32-table baseline landed, preserving the text-only extraction scope.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(test): qualify the CSI fixture AbortController

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-10-17 14:29:09 +00:00
Shaojin Wen
7eb6e00441
test(managed-agent): add the FG10c/FG10e workflow relay store-failure and cancellation fault gates (#14165) (#14473)
* test(managed-agent): add the FG10c/FG10e workflow relay store-failure and cancellation fault gates (#14165)

Slice 2 of FG10b/c/e/f on HostedWorkflowRelayFaultGatesIT. No production
code changes.

FG10c refuses, at the store, the lead's result revision and the
child_acceptance insert that lands the notification, armed only after
the subagent hold let the lead's own Turn settle and the child Turn
complete, so the refusal latches an idle lead and never crosses a running
workflow Turn. While latched the record never advances and nothing is
delivered; after the trigger drops and the Harness restarts cold, the
relay's retry finishes the walk once and the script never runs again.

FG10e cancels the workflow task with its agent() request held: the child
Turn ends CANCELLED, the record settles cancelled/stop_requested, the held
answer never reaches the child journal and no notification lands. A lost
cancel reply replays the admitted cancel under the same key, and a cancel
of a settled task answers 409 task_action_unavailable.

The four-letter class measures 181.2-181.6 s, so the Hosted allowance
composes 3545 + 198 = 3743 s: fork 4155->4353 s, Verify step 85->90 min,
job 330->335 min. The design records the landed letters, the mechanics
and five mutations (two narrower ones equivalent).

* test(managed-agent): witness that a workflow cancel aborts the held subagent request (#14165)

FG10e's cancel-subagent-in-flight case observed only the run settling
cancelled, which the sandbox's abort race produces even when the stop
never reaches the agent() dispatch. The held subagent request is now a
live SSE stream (headers at once, a comment every 250 ms), and the case
asserts the request was aborted while the hold was still closed.

A mutation handing createProductionDispatch a fresh signal instead of
the run controller's turns the case red at the new witness; the case
without it stayed green. Recorded as mutation 9 in both design docs.

Also correct the store retry bound's quoted source line in both relay
gate classes and take the task id from ManagedExtensionProjection.taskId.
2026-10-17 13:49:09 +00:00
易良
1ab68e951e
feat(ci): gate Managed Agent changes on a full SDK Java run before merge (#14397)
* feat(ci): gate Managed Agent changes on a full SDK Java run before merge

Changes to the Java modules or the harness they drive now run a fast
SDK Java subset on every push and the full suite on the first approval
of the current head. An always-reporting `Managed Agent gate` check can
be made required: a touched PR reports it only from a full run.

Refs #14392, #14381

* fix(ci): route the Managed Agent gate's own jobs off the hosted queue

The scope and gate jobs report the would-be required check; on the first
run they sat in a hosted queue holding 123 runs. Trusted runs now take
the ECS pool with the shared pre-checkout hygiene, forks stay hosted.

* fix(ci): register managed-agent-gate.yml in the PR-stage CI budget ledger

managed-agent-gate.yml triggers on pull_request and merge_group, so it is
PR-stage and needs an entry in .github/workflows/.ci-budget. That ledger is a
fail-closed whitelist: a missing entry makes check-ci-budget.mjs error out and
short-circuits the remaining Lint & Static steps, so no linter ran.

Recorded exactly as measured (3 jobs, 15 declared timeout-minutes, 1 job
without a fixed timeout) so no slack is left against the ratchet.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmvb6htxaa7

* fix(ci): run the full Managed Agent unit suite in the fast tier

The fast tier skipped mysql-integration, the only lane that runs the
module's ~4,200-test H2 suite in full, so a PR push got no unit
feedback until the review-triggered full run. In the fast tier the
Java 21 leg now runs the whole module suite instead of its Runtime
Broker subset, after the same workspace install mysql-integration uses:
part of the suite loads the built Core and CLI JavaScript.

* fix(ci): harden Managed Agent gate tier selection and cancellation reporting (#14397)

* fix(ci): gate the Managed Agent full tier on a maintainer approval

The full tier was reachable by any approved review, including the
deferred APPROVE that qwen-triage-finalize.yml posts once a PR's checks
land green. That workflow now watches this gate, so its own fast tier
could buy the most expensive run in the repository with no human
involved. tier=full now also requires the reviewer's author_association
to be OWNER, MEMBER or COLLABORATOR, and fails closed when it is absent.

The concurrency group embedded the event name, so a push could never
cancel the approval run it superseded: that run kept going for about 125
minutes and then reported a head that branch protection no longer reads.
The group is now keyed on the pull request, with cancel-in-progress still
true only for pull_request, so approvals keep queueing behind each other
instead of displacing a pending full run.

The design docs also described a Maven cache composite that is not in the
tree, called the untouched-PR cost hosted-only when trusted runs take the
ECS pool, and carried a touched-push figure from before the fast tier took
on the full module suite. Non-goals now record that this gate supersedes
the separate compile gate proposed in #14385, and that the Java SDK
checkstyle step stays pinned to the Java 11 leg, so only the full tier
runs it.

* docs(ci): stop citing the Maven cache save the merge from main removed

Merging main brought in #14396, which suppresses setup-java's post-job
Maven cache save on self-hosted runners by making the cache input
conditional on runner.environment. That was the measured cause behind two
claims this branch carries, and both went stale the moment it landed.

The design doc's Cost section said removing the 14 to 37 minute save was
planned work with no implementation in the tree. It is now merged, so the
330 machine minute full run is a figure measured before the saving went
away and should come in lower; it is stated that way, without a
re-measured number this branch does not have.

The daemon-e2e budget comment and its test justified the fast tier's 90
minutes entirely by that save. The value stays: the leg's own sizing puts
stretched setup at about 44 minutes against a 45 ceiling, and the fast
tier now runs it on every touched PR push rather than nightly only, so it
meets that tail far more often. The comment now says that instead of
citing a save that no longer happens.

* fix(ci): diff a review event against its merge commit's first parent

A pull_request_review run checks out the same test merge commit as a
pull_request run (run 38997396688: "Merge 7d918f7 into 2746553"), so
HEAD^1 is already the base. The payload's base sha can lag main and pull
main's own Managed Agent changes into an untouched PR's diff, starting
needless full runs.

* fix(ci): keep comment reviews out of the gate's pull request group

A concurrency group keeps one pending run, and a newcomer cancels it
whatever cancel-in-progress says. With every event of a PR in one group,
a comment review arriving while an approval run waits behind the fast
tier cancelled that approval run, and the head's required check stayed
expected until someone approved again. Reviews that are not approvals
now run in a group of their own.

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
2026-10-17 21:17:23 +08:00
Shaojin Wen
dac04ed35a
feat(managed-agent): add the opt-in java_durable admission profile (D7-3) (#14470)
* feat(managed-agent): add the opt-in java_durable admission profile (D7-3)

A deployment switch, qwen.managed-agent.java-durable-admission.enabled,
off by default, creates the only profile bean through a conditional
configuration, so a flag-off deployment answers every admission as
before and advertises nothing.

With it on, Sessions advertise durable_operations, and a Turn input or
cancel that asks for admission_profile java_durable (admissionProfile on
WebShell) is answered with operation_id, admission_stage and
delivery_state of the operation its admission saved, built before the
dispatcher runs. A retry under the same key answers the same operation;
the request digest leaves the profile out. harness_confirmed stays the
default, and asking for java_durable where the profile is off answers
400 unsupported_feature before anything is read or written.

Contract 1.61.0.

* test(managed-agent): pin the java_durable admission profile on both switch states

The configuration test pins that a flag-off deployment has no profile
bean. The contract suite now also runs whole with the profile on, on its
own database: every default admission answers as with it off. Profile
off, asking for java_durable answers 400 unsupported_feature on the
public event route and both WebShell Turn routes, with no Turn or
operation admitted; profile on, a Turn input and its cancel name their
own operations, a retry under the key names the same one, and the
default stays harness_confirmed. The Session capability check follows
the switch.

* docs(managed-agent): record the java_durable admission profile design (D7-3)

The design note states the switch, the per-command request, the refusal
where the profile is off, what the response carries on admission and
replay, and what stays out: the internal admission-stage writes, the
Turn read's operation fields and the harness_confirmed name. The D7-1
note's follow-up now points at it.

* test(managed-agent): drop two unused imports from WorkspaceRuntimeTest

#14435 left HarnessSessionResolver and RuntimeLifecycleAuthority
imported but unused, so mvn verify stopped at Checkstyle on main. No
pull-request lane runs the Java build, so it went unnoticed.
2026-10-17 12:28:16 +00:00
Shaojin Wen
d21b861fdf
test(managed-agent): add the FG10b/FG10f workflow relay lost-reply and delivery fault gates (#14165) (#14468)
* test(managed-agent): add the FG10b/FG10f workflow relay lost-reply and delivery fault gates (#14165)

Slice 1 of FG10b/c/e/f: a new HostedWorkflowRelayFaultGatesIT on the FG9
coordinator-owned fixture, where a team lead's workflow launch walks the
whole child result relay (create, bind, the child Turn's in-process
agent() call, the result revision, the notification and the lead's wake
Turn) on a real Spring, MySQL, embedded broker and packaged Harness. No
production code changes.

FG10b holds the replies to the relay's dispatch and result revisions past
the relay's request timeout, so the relay re-drives each step while the
first is in flight, and drops the reply to the child Turn's own journal
commit: one child Session, one script run, one agent() dispatch, each
child_run revision committed once, one notification and one wake round.
FG10f holds the notification commit the same way (delivered at most once)
and cuts the workflow child's public and WebShell streams while its
subagent is in flight: each resumes with no gap and no duplicate.

The store proxy now logs each commit's command id beside its operation.
The Hosted fork's healthy allowance composes 3380 + 165 = 3545 s for the
measured class (81.6-81.7 s), so the fork moves 3990->4155 s, the Verify
step 80->85 min and the job ceiling 325->330 min. The bilingual design
covers all four remaining letters; FG10c/FG10e follow as slice 2.

* test(managed-agent): bind the FG10b/FG10f timeout witness to the held call (#14165)

The relay's CHILD_SESSION_START attempt count is cumulative over the
whole creation, so an earlier deferral (a bind that met a binding not yet
visible) could satisfy `>= 1` before the held call timed out, releasing
the hold early and letting the case pass on the ordinary path. The
witness now reads the count as the hold begins and waits for one more
attempt, the FG9f baseline-plus-one; while the call is held the relay's
one scan thread is blocked in it, so nothing else of the row can spend
that attempt.

childRunCommands now asserts every counted commit names its command id
instead of falling back to the whole log line, so a missing cmd= tag
fails loudly rather than degrading the once-per-revision check. The
design records both, and that slice 2 re-measures the class before
composing its budget (the per-letter slack is 10 s after slice 1).

---------

Co-authored-by: 易良 <1204183885@qq.com>
2026-10-17 20:14:53 +08:00
Cenab Batu Bora
17aa2d9db7
docs: clarify safe mode tool schemas for small contexts (#14445)
Co-authored-by: 易良 <1204183885@qq.com>
2026-10-17 19:35:06 +08:00
jinye
fa020303b5
feat(runtime-broker): Workspace-resident Tool Runtime for bound Sessions (#14435) 2026-10-17 11:02:35 +00:00
jinye
6c7c044533
fix(managed): Recover cancellation after process restarts (#14377) 2026-10-17 19:02:11 +08:00
Shaojin Wen
fbc6c6dd0d
feat(managed-agent): reconcile a parked SSH Workspace registration (D7-2) (#14456)
* feat(managed-agent): reconcile a parked SSH Workspace registration (D7-2)

A non-retryable SSH registration failure used to park recovery_blocked
for good and keep the physical directory reserved. Now only a reserved
registration parks, since only one that reserved can have reached its
node through register-storage; an unreserved one fails outright.

A parked registration joins the D7-2 reconciler. Its evidence is a
read-only probe of its node one delay after the last attempt: the
reserved directory with this registration's marker completes it, as
the original attempt would have; no marker, or another directory at the
root, fails it and releases the storage alias and the directory's
owner; an unreadable node, a revoked actor or a foreign marker keeps it
parked. Rows parked before this change reconcile the same way.

The reconciler now takes stored kind names, lets a source settle with
effects of its own, and lets a kind outside any Session list its own
parked rows.

* test(managed-agent): pin the parked SSH registration evidence and settlement

Store: an unreserved registration fails outright, a reserved one parks
for the delay, completes once when its marker landed, and fails with
its directory released when it cannot land, without touching an owner
another registration finished. Service: the node is only ever probed,
and each verdict follows from what it holds. Reconciler: a kind outside
any Session scans and settles through its own source.

* docs(managed-agent): state the SSH registration reconciliation in contract 1.60.0

The registration operation no longer calls recovery_blocked terminal:
an unreserved refusal fails, and a reserved registration stays blocked,
with its directory, until a read-only probe of its node completes or
releases it. The WebShell message says so instead of promising an
administrator review in every case, and the D7-2 design doc gains its
phase-2 section.

* test(managed-agent): expect schema V8 in the SSH workspace MySQL IT

D7-1 (#14404) moved the schema to V8 but left this IT asserting V7
after the full Flyway chain, so it failed on main under the
mysql-integration profile, which no pull-request lane runs.

* test(managed-agent): reserve before parking in the registration route fixtures

The surface and contract fixtures failed an unreserved registration and
expected recovery_blocked; that refusal is now final. They reserve first,
so the route still reads a parked registration. The lock-order audit
pins the record accessor the parked registration adds.

* fix(web-shell): keep querying a blocked SSH registration

The server now completes or releases a blocked registration once it
reads the machine again, but the dialog stopped querying on
recovery_blocked and dropped its continue action, so it stayed frozen on
the blocked message until a reload. A blocked registration keeps
"Continue confirming registration" beside "Start another registration",
and a saved registration is queried each time the dialog opens; a later
completed or failed result clears the blocked state.

* fix(managed-agent): keep parked-operation evidence off the shared scheduler

OperationReconciler read each parked row's evidence on the shared
single-thread taskScheduler, and an SSH registration's evidence is a
probe that can wait a minute on its node, so one slow node stalled every
other coordinator's recovery tick. The scan now only lists due rows and
hands each to the managed-agent executor; a row still being reconciled
is not handed out again.

The regression test runs the scan on a single-thread scheduler with a
probe that blocks: the next task on that thread still runs, a second
scan does not read the row again, and the settle follows the release.
Run synchronously, it times out waiting for the other task.
2026-10-17 10:51:06 +00:00
Shaojin Wen
8e100eecdd
fix(managed-agent): answer a full tool publication reservation with its own code (#14451)
A reservation refused at the tenant byte, Session byte or active-capture
limit answered the generic 400 invalid_request, so the Harness logged
only "The request is invalid." and could not tell a full tenant from a
malformed request. The store now names the limit in the error envelope
(`limit`) and in the message, and the status says whether the refusal
can clear while the Session lives:

- 409 managed_tool_publication_retention_exhausted when used bytes alone
  fill a byte limit, whichever limit failed first. Only the Session's
  retirement and the collection of its output free used bytes.
- 429 managed_tool_publication_capacity_exhausted for every other
  refusal: open captures give back what they hold beyond their used
  bytes when they finish, are fenced, expire or are referenced, and
  leave the active count.

Each refusal logs one WARN line with the limit, its class, the tenant
and Session scope keys, and the held, used, requested and allowed bytes.
The FG8d note and its design doc now say the Harness logged the old
refusal as "The request is invalid.".

Refs #14434
2026-10-17 08:30:04 +00:00
Shaojin Wen
06a5ffe0e0
docs(managed-agent): state the read-profile step-3 acceptance (#13905 item 45) (#14450)
The step-3 recovery design's acceptance for committed native results
asks for a same-Session next-Turn Write/Read on a newer generation. The
read profile that #13753 I3 added to that path cannot write, and in the
ordinary flow its Session gets no next Turn: it belongs to a snapshot
child, which takes no messages, and the relay closes it once its result
is delivered. A continuation runs in a new child Session.

The paragraph now states the read profile's acceptance: the run's end
replaces the next-Turn checks (the resumed answer settles the task
Turn and reaches the parent as the child's result, the child Session
closes with physical worker retirement, and its copy is discarded),
and the new-tool refusal is exercised with a Read or Glob. Both
languages are updated. No behavior changes.
2026-10-17 08:06:55 +00:00
Shaojin Wen
2035ad35db
feat(managed-agent): reconcile acceptance-unknown operations through one rule (D7-2) (#14443)
* feat(managed-agent): reconcile acceptance-unknown operations through one rule (D7-2)

A recovery_blocked operation of an acceptance-unknown kind is never
claimed or sent again; one reconciler settles it from its kind's keyed
evidence: committed, not committed with the proven code, or parked for
another recheck. TASK_CANCEL's record verdict moves onto it unchanged.

claimOperation no longer takes a parked task cancel once it is due; only
lifecycle waits (CLOSE, ARCHIVE, DELETE, CWD_CHANGE) are re-driven from
recovery_blocked.

A Turn input whose submit no Harness answered now parks as
submit_input_unconfirmed instead of failing: a null epoch does not prove
non-admission. The Session journal decides it: the prompt's submitInput
commit completes it, and a complete journal that no writer can extend
fails it with the Turn's code.

* docs(managed-agent): state the D7-2 reconciliation rule in contract 1.59.0

The contract says which meaning recovery_blocked carries for which kind,
names submit_input_unconfirmed, and adds the v1.59 note. The design doc
records the rule, the journal evidence for Turn input and the phases
left for later. A parked task cancel still holds a storage migration,
now with a comment and a test that say why.

* test(managed-agent): pin the D7-2 evidence read and the migration barrier

turnInputAcceptance reads its Turn without a lock and joins the pinned
Turn-row accessor set. The migration case now checks that a settled
cancel releases the idle check rather than asserting the whole move.

* test(managed-agent): order the D7-2 test imports

* fix(managed-agent): read the journal evidence for a parked input in one statement

turnInputAcceptance read the submitInput journal row and the session
head in two statements and relied on @Transactional(readOnly = true)
for one snapshot, but that keeps the connection's isolation. Under READ
COMMITTED an admission and then the deletion could commit between the
two reads, which looked like a complete journal without the admission
and failed an admitted input for good. Both facts now come from one
statement, whose single read view holds under any isolation level.

The new test runs the store with no transaction at all, so every
statement autocommits, and commits the admission and the deletion right
after the first statement that reads the journal; the two-statement
read fails it with NOT_COMMITTED.
2026-10-17 07:50:26 +00:00
曹潇缤
a6ab061fe5
feat(qqbot): accept inbound files and voice messages (#14343)
* feat(qqbot): accept inbound files and voice messages

QQ delivers a document as `content_type: file` and a voice message as
`content_type: voice`, but the attachment classifier only knew images and
videos, so both were dropped and a file-only or voice-only message started
no turn.

- Classify `file` and `voice` and give each a 20 MB cap.
- Keep the sender's extension for a file (restricted to letters and digits,
  `.bin` otherwise): QQ reports no MIME type, and the read tool classifies
  documents by extension.
- Prefer `voice_wav_url` over the SILK original and label the download
  `audio/wav` / `audio/silk` accordingly; deliver voice by path as an
  `audio` attachment, since the base layer has no audio content part.
- Extend the media-only placeholder to `(file)` and `(voice)` and document
  the new kinds in the design docs and the channel docs.

* test(qqbot): cover inbound file and voice delivery

Pin the classifier, the per-kind caps, the extension fallback, the WAV
preference and the placeholders for file-only and voice-only messages, in
both a C2C and a group mention. The two "unsupported attachment" cases now
use `application/octet-stream`, since `file` and `voice` are supported.

* test(qqbot): pin the file and voice byte caps

The per-kind cap selection was not covered: the over-limit cases each add
one byte to their own constant, and the video, file and voice caps are all
20 MB, so routing a file or a voice message to the 8 MB image cap kept the
suite green.

Add one case per kind with a declared size of `QQ_IMAGE_MAX_BYTES + 1`: it
succeeds only when the kind routes to its own cap, which is what the two
new assertions pin.

---------

Co-authored-by: Eric-GoodBoy-Tech <Eric-GoodBoy-Tech@users.noreply.github.com>
2026-10-17 15:49:32 +08:00
Shaojin Wen
42659a3975
docs(managed-agent): keep hooks on snapshot children (#13905 item 37) (#14440)
* docs(managed-agent): keep hooks on snapshot children (#13905 item 37)

Item 37 of #13905 asked whether a snapshot child should run hooks.
Checked against main:

- A child Session copies its parent's agent definition, so a snapshot
  child runs the parent's hook catalog, which is operator policy.
- A PreToolUse hook can deny or rewrite a read_file or glob call.
  Dropping hooks on the read profile would let the model read what the
  same agent is refused everywhere else, by choosing snapshot isolation.
- What hooks cost a snapshot child applies to every child: a PreToolUse
  ask waits on an approval, a hook command runs against the copy and
  its writes are discarded, a turn resumed after its Runtime was
  replaced reacquires through the Hook owner instead of the step-3
  path, and a non-prompt hook holds the storage until the child closes.

Decision 4 of the snapshot design now records the decision and these
costs, in both languages. No behavior changes.

* docs(managed-agent): bound two hook costs on snapshot children

Review of the decision 4 bullet found two overstated guarantees:

- A hook command only runs with the copy as its working directory; it
  is not isolated. A write outside the copy, or any other side effect,
  outlives the discard.
- A turn blocked for recovery can hand the Hook owner's mount back once
  its hook work is proven settled or never dispatched, so the storage
  is not always held until the child Session closes.

Both languages are updated.

* docs(managed-agent): limit the snapshot step-3 promise to Sessions without hooks

Decision 8 promised that committed read results survive a replaced
Runtime, but the step-3 path it relies on serves only Sessions with
neither hooks nor MCP, as decision 4 now says. Both languages are
updated.

---------

Co-authored-by: 易良 <1204183885@qq.com>
2026-10-17 15:11:16 +08:00
Shaojin Wen
397fd2ce74
refactor(integration-tests): extract the shared helpers duplicated across the hosted gate drivers (#14147) (#14439)
* refactor(integration-tests): extract the shared helpers duplicated across the hosted gate drivers (#14147)

The FG6e/FG8/FG10a rounds each landed their own private copy of the same
machinery (flagged in #14120's review, deferred there to keep its diff
scoped). One sweep moves every carrier onto one home: the SSE Observer and
event projection and the POSIX quote escape go to two single-concern TS
modules; the tenant-pinned authentication filter, the probe SSE relay's
client/stream/project/append mechanics, the tool-publication tuning block
and its OSS stub, and the extension-record JDBC read go to a package-private
HostedGateTestSupport. The extension-record extraction also restores the
scope-key index-leading predicate that FG10a's copy had dropped.

Semantics-preserving by construction; no production code touched. Verified:
the full hosted-harness-mysql lane run locally is byte-identical before and
after (the FG8/FG6/public-workspace classes are environmental-red on macOS
— Linux-only workspace execution and identity gates — unchanged by this
diff; the classes that run on macOS stay green, including FG9 and FG10a,
the latter exercising the migrated extension-record read). Static pins
(hosted-process-ci, hosted-relay-headers), checkstyle, ESLint and the
integration-tests typecheck stay green.

* refactor(integration-tests): restore the SSE id pre-conversion check and the relay's multi-resource close (#14147 review)

Review on #14439 found the sweep dropped two guarantees the private
copies carried. The shared SseObserver validated the frame id after
Number() conversion, which let malformed cursors (1e0, 0x1, +2) coerce
to valid numbers and mask the cursor-format regression the continuity
assertions pin; it now validates the raw id string first, as FG6e's
original did. And the extracted RelayStream.close() ran stream.close()
then client.close() in sequence, so a DaemonTransportException from the
stream left the client's scheduler, heartbeat and HTTP executors
unshutdown; it now closes the stream in a try/finally so the client is
always closed, matching the probes' nested try-with-resources.
2026-10-17 15:05:45 +08:00
Shaojin Wen
bb95f75758
fix(managed-agent): address the H4f task cancel review follow-ups (#14437)
* fix(managed-agent): give an abandoned background stop's thread back

The Broker stop waited with join(), which ignores the interrupt the
attempt deadline abandons a send with, so a Broker stage that never
settled held the send's thread until it did. Wait with get() instead,
wrapping failures as join() did so the error handling is unchanged.

Also name the child_run record invariant that keeps the verdict's
stopReason clause from changing child_agent, workflow and
background_shell outcomes.

* test(managed-agent): pin a delivery cancel the Workspace authority refuses

The channel operation route re-runs the route Session's Workspace
execution authority on every operation, a cached attachment included,
and a delivery's task cancel takes it like submit_input. Pin that the
connector forwards nothing while it fails, and that the cancel retries,
parks, is never sent again and settles from the record. Document the
scope in the delivery task note and the H4f note: the child operation
route runs no Workspace check of its own on a cached attachment.

* refactor(web-shell): type the task panel's kind labels by the contract union

TASK_KINDS was an untyped Set<string>, so a label for a kind the
generated TaskKind union does not name type-checked silently. Type it
by ManagedTask['kind']; a newer server's kind still reads as other.

* docs(managed-agent): drop the provisional contract numbers from the H4f notes

H4f-c, the child Session link and the delivery task landed as 1.54.0,
1.55.0 and 1.57.0, so the notes no longer need to say where main was or
when to renumber.
2026-10-17 14:48:18 +08:00
jinye
3b6136a5a9
feat(managed-agent): support native macOS workspace runtime (#14442) 2026-10-17 06:35:40 +00:00
Shaojin Wen
7d6dcc8c95
feat(managed-agent): record Turn input and cancel as durable command operations (D7-1) (#14404)
* feat(managed-agent): record Turn input and cancel as durable command operations (D7-1)

Every Turn submit and cancel admission now saves a submit_input or cancel
operation in the transaction that admits it, carrying the command's
idempotency key and request digest. The command receipt names the
operation, so a retry under the key resolves to the original operation
and a different payload still conflicts. The operation query serves both
kinds on the public and WebShell surfaces.

The Turn dispatcher stays the only deliverer. Each operation settles from
the Turn's own committed facts in the transaction that commits them: input
completes harness_confirmed once a Harness admits its Turn, and a cancel
completes when its Turn is cancelled or fails turn_already_settled when the
Turn ended otherwise. The new kinds target the Turn, so they are never
Session barriers. Admission responses do not return the operation yet;
that is the D7-3 profile.

Schema V7 widens ck_ma_operation_kind, and the schema initializer now
replaces any CHECK that a migration drops and re-adds. Contract 1.53.0
(provisional: open PRs already claim 1.52.0) documents the operation
route's kinds and the new failure codes, and the
known-gaps header points at #14122.

Closes #14168

* fix(managed-agent): pin the Turn command settle scan and address D7-1 review

- Pin the settle's locking scan to uq_ma_operation_identity with a MySQL
  executable-comment FORCE INDEX (H2 skips it). Left to the optimizer, a
  Turn with a long settled history steers it to ix_ma_operation_claim,
  whose (kind, state) range holds every Session's pending rows, and two
  Sessions' settles deadlock. A new MySQL IT reproduces that on MariaDB
  10.11 and MySQL 8.4 without the pin.
- Pin the snapshot race witnesses to REPEATABLE READ so a server default
  cannot void their premise.
- Add an H2 case for a pending cancel whose Turn the dispatcher fails.
- Pin the operation-kind CHECK union: every shipped kind inserts after the
  full chain (Broker initializer and Flyway), and a V7 catalog holding a
  WORKSPACE_CREATE row upgrades to V8.
- State input and cancel completion consistently in both command operation
  schemas and regenerate the WebShell types.
2026-10-17 05:08:17 +00:00
Shaojin Wen
2c700670fc
test(managed-agent): add the FG9b/FG9c session relay store-failure and crash fault gates (#14164) (#14420)
Slice 3 of 3 of the FG9 round: two letters / five cases join
HostedSessionRelayFaultGatesIT; no production code changes.

FG9b refuses one journal write of the relay pass at the store with a
test-only trigger on ma_session_record (the sender's outbound, the lead's
receipt, the copy-back), armed until the case drops it: the store client
retries a 5xx commit three times, so a one-shot arm never reaches the
Session. Past the third refusal the Session latches its writes and only a
cold Harness heals it; each case restarts it and pins that the healing
runs through the original command: the refused outbound is answered "not
sent" from the journal and never minted afresh, the re-execution after a
refused receipt finds the minted position, and the refused copy-back lands
once with the receipt's reply byte for byte.

FG9c SIGKILLs the Harness on a commit the store accepted whose reply the
store proxy holds: mid-fanout, the re-execution reports the minted
position sent from the journal and mints only the next one; right after
the sender's outbound landed, the takeover resumes the parked wait and the
unacknowledged entry stays the one command.

The scripted lead's closing round now answers with text: an empty
response is retried for ~20 s, which kept the lead's Turn open across the
arms, and a lead Turn open across a latch or a kill leaves the cold lead
churning on workspace_unavailable. FG9b/FG9c wait for it to settle. The
store proxy logs each commit's operation and status for the never-minted-
twice witness. The Hosted fork's healthy allowance composes with FG8c
(#14407) and FG10a (#14390): 2963 - 534 + 950 = ~3380 s for the measured
six-letter class (528-557 s), so the fork moves 3600->3990 s, the Verify
step 75->80 min and the job ceiling 320->325 min.
2026-10-17 12:48:06 +08:00
Shaojin Wen
c909af765f
feat(managed-agent): project channel deliveries as tasks and serve their cancel (#13746 F3) (#14414)
* feat(managed-agent): H4f-c public task cancel for background Shell and Monitor tasks

The H3 kinds join the public task cancel path now that #13889 lifted their
gates. A background_shell or monitor task in pending, running, waiting or
degraded, in an active Session, advertises cancel. Its delivery stops the
task's process through the embedded Runtime Broker (the drain sweep's
status, stop, status for that one hold row, unfenced), and the operation
completes from the committed record: a Shell's stop request, which the A8
publisher writes on the worker's stop-marked end, or a Monitor's
stop_requested settlement, since monitor_run carries no request. A process
that ended first fails the operation with task_already_settled; an
unproven stop retries within the budget and then parks.

Adds FG8g, a real-stack gate that cancels a live background Shell and a
live Monitor watch through the public route. Contract 1.53.0
(provisional).

Part of #13746.

* feat(managed-agent): project channel deliveries as tasks and serve their cancel (#13746 F3)

A channel delivery now projects as a task of its route Session (kind
channel_delivery, in both languages and the shared projection fixtures),
so the one public task cancel path serves it: no new route, operation type
or failure code. The task is pending while the delivery is planned,
running while an adapter sends it, waiting while its outcome is unknown,
and completed, failed or cancelled at its end. Cancel is advertised and
admitted only while the delivery is pending; the delivery arm sends the
Hosted cancel_delivery verb, and the operation completes from the
record's cancelRequested flag, so an adapter that claimed the delivery
first keeps sending it to its natural outcome. Deliveries planned before
this change keep projecting no task.

FG7d gains two real-stack cases through the public route: a planned
delivery cancelled before any send, and a claimed one refused with 409.
Contract 1.55.0 (provisional).

Part of #13746.

* docs(managed-agent): note the delivery task contract number against main

* docs(managed-agent): renumber the delivery task contract to 1.57.0
2026-10-17 12:34:53 +08:00
Shaojin Wen
fce91ce444
fix(managed-agent): run a merge recorded on a snapshot as a discard (#13905 items 33-36) (#14432)
A relay from before snapshots asks a merge of any ready child Workspace
row, and its store records it as asked, so the scan could merge a
snapshot's copy into the parent. The scan now starts a snapshot row's
recorded merge as a discard.

The snapshot design's rollout procedure is corrected in both languages:
a server from before #14265 refuses to start on a database a server
from #14316 on has migrated, which bounds the upgrade window and
control-plane rollback, and Workspace recovery checks closed Sessions
too, so closing a snapshot child does not let an older CLI recover its
storage.
2026-10-17 12:19:04 +08:00
Shaojin Wen
e847c14496
test(managed-agent): add the FG10a workflow child_run digest-integrity fault gate (#14165) (#14390)
* test(managed-agent): add the FG10a workflow child_run digest-integrity fault gate (#14165)

Stage F round FG10 (umbrella #14054), the workflow child_run kind enabled by
#13858. This slice lands FG10a — the digest-integrity gate — following the
FG7/FG8 shape: a real packaged Harness, a per-case TypeScript driver, an
in-JVM Spring control plane against a real MySQL, and JDBC ground truth.
No production code changes.

Three cases prove the launch pin under fault: a tampered envelope (bytes
rewritten after the record committed, before the relay reads it) is refused
by the child Turn's re-verification naming both digests, before any model
spend; a same-length substitution is refused the same way; and a launch
whose args carry fractional and subnormal values (0.1, 5e-324) crosses the
daemon/SDK admission digest byte-identically, never wedging on
invalid_hosted_prompt. The mutation check (neutralizing the re-verification)
turns the refusal cases red, and the gate is green again once restored.

The gate's assertions sit at the child Turn, not the record's terminal
settlement: the digest refusal fires inside the Turn and runs independently
of the relay's later bind/deliver walk, which a driver-held parent cannot
drive to completion without a coordinator-owned attach (a Linux-only
topology). FG10d (process crash) is deferred to #14387: its gate caught the
recovery re-driving an unsettled workflow Turn after a Harness kill, which
violates 'never re-executed on reopen' and needs adjudication outside this
test-only slice.

* test(managed-agent): prove FG10a's substitution is digest-attributable and args reach the child

Review feedback on #14390 found the gate could not prove what it
claimed: the same-length substitution rewrote the script into text the
real compiler rejects with a SyntaxError, so removing the digest check
would have failed on a parse error rather than executing the substitute;
and the fractional-args case passed without the special values ever
being observed at the execution end.

The substitution now swaps in a driver-authored script that is
syntactically valid, byte-length equal, and proven runnable by a new
substitute-script-baseline case, so the refusal is attributable to the
digest alone. The positive case reads the child Turn's final content
through a new probe endpoint and compares the echoed args value-exactly
— the wire rendering varies by hop (the store renders the subnormal
4.9E-324, the sandbox echoes 5e-324, keys arrive sorted) but every hop
parses to the identical double.

Verified: FG10a 4 cases green; with the digest check neutralised, both
refusal cases turn red with the tampered/substitute script executing to
COMPLETED; checkstyle, relay-headers and hosted-process-ci pins green.

---------

Co-authored-by: 易良 <1204183885@qq.com>
2026-10-17 03:42:41 +00:00
Shaojin Wen
ed7d034f19
test(managed-agent): give FG8d Sessions a 10 s writer lease (#14425)
FG8d runs six cases in one tenant, and the fixture admits four active
captures. A capture a case leaves OPEN keeps the expiry its last grant
renewal set, so under the default 60 s lease the earlier cases still held
all four slots when drain-release reserved its own on a fast host: the
store refused it ("Publication capacity exhausted", logged by the
Harness as "The request is invalid."), the run never started, and the
case timed out after 120 s. A 10 s lease, the length FG8c's crash
Sessions already use, lets a capture stop counting within ten seconds
of its last renewal.

The design doc records the lease and why, in both languages.
2026-10-17 11:35:40 +08:00
易良
8f8cab9d4c
fix(runtime-broker): support credential key rotation (#14317)
* fix(runtime-broker): support credential key rotation

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* docs(managed-agent): document the key ring's YAML-only configuration

The previous-credential-keys map cannot be supplied through a
QWEN_MANAGED_AGENT_* environment variable: relaxed binding silently
resolves an env-var form to an empty or mis-keyed ring, so the inert
application.yml literal is removed and the rotation runbook, README,
and zh-CN runbook now say the ring is configured only in an overriding
application.yml. They also warn that the operator-recovery and
CSI/migration maintenance mains accept a single credential key pair
and must be launched with the previous key as the active pair when
recovering pre-rotation bindings.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* docs(managed-agent): quote key-ring map keys and fix README link depth

An unquoted YAML map key containing /, : or a space is silently rewritten
by parsing, so the previous-credential-keys lookup never matches the
persisted key ID and rows stay unreadable even with the key configured.
Quote keys in the README, the rotation runbook (EN/ZH) and the design
docs, with the warning spelled out adjacent to each example. The README
rotation-design link also still pointed one directory too deep after
the server/broker source consolidation.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* docs(managed-agent): write key-ring map keys in Spring bracket form

Merely quoting a YAML map key is not enough: Spring's relaxed binder
normalises plain keys, so an ID like legacy/v1, legacy:v1 or legacy v1
binds as legacyv1 whether the YAML quotes it or not, and the ring then
never matches the persisted key ID. All examples now use the bracket
form ('[...]'), the runbook (EN/ZH), README and both design docs say
why, and the requirement is pinned next to each example.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* test(managed-agent): pin bracket-form key binding end to end

The rotation runbook now instructs bracket-form previous-credential-keys
entries; this drives the documented YAML shape through the real
YamlPropertySourceLoader and Spring Binder and asserts the map carries
legacy/v1, legacy:v1 and legacy v1 verbatim, then that a protector built
from the bound ring decrypts ciphertext written under the original ID
and keeps writing under the active one.

Local compile deferred to CI (no JDK on the authoring host); the test
mirrors the classes used in the reported reproduction.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(managed-agent): use the Boot 2 BindResult.orElseThrow supplier form

* fix(managed-agent): declare the YAML loader's checked IOException

* style(managed-agent): match the enforced ImportOrder grouping

* fix(managed-agent): name the offending key in key-ring failures

A ring with several previous keys reports "AES-GCM key must contain 32
bytes" or "encodedKey must be valid base64" without saying which entry
failed, so locating a bad key means bisecting the ring -- and the
quickest way is to delete keys until startup succeeds, which can drop a
key that persisted rows still need.

Carry the key ID into both messages and assert it in the existing
regression test.

* docs(managed-agent): give a runnable retirement check

The runbook and the rotation design both required database evidence that
no persisted row references a previous key without saying how to obtain
it. Both key-ID columns on ma_runtime_binding are the only persisted
references and store UTF-8 bytes, so read them as text and retire a key
only when the result no longer lists it; the design links to that query
instead of restating the requirement.

* fix(runtime-broker): tighten the key-ring change after round-5 review

Three corrections, each shrinking or pinning what this PR already ships:

- Drop the active-key guard from the private ring constructor. Both call
  sites put the active ID before delegating (`singleKey` and `fromBase64`,
  whose `decodeKey` throws rather than returning null), and
  `BrokerValues.requireId` returns its argument unchanged, so no input
  reaches the throw. A future caller that omits the active ID still fails
  closed in `protect()`.
- Pin the fail-closed message in `encryptsWithTheActiveKeyAndDecryptsPreviousKeys`.
  Asserting only `IllegalStateException` also passes a fallback-to-active-key
  mutant, which the design doc forbids.
- Select the retirement query's key-ID columns without `CONVERT` in both
  runbooks. `CONVERT(expr USING utf8mb4)` carries the character set's default
  collation, so `DISTINCT`/`UNION` deduplicate case- and accent-insensitively
  while key selection is exact; an under-reported ID invites removing a key
  that rows still reference.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmvbcxb9baj

* docs(managed-agent): give the key ring one normative home and fix the config claim

R1-5: the README rotation block restated four claims the deployment runbook
already owns, in the same order and with the same examples. Keep what is
local-dev specific (the local-dev-v1/v2 example, the active/previous summary)
plus the bracket-form reason beside the example, and link to the runbook anchor
for the rest. Net -6 prose lines.

R1-3: "Configure previous-credential-keys only in an overriding application.yml"
named a surface the reference distribution neither ships nor documents -- the
packaging script stages only bin/, lib/, docs/, LICENSE, manifest.json and
SHA256SUMS, the launcher is a bare `exec java -jar "$base/lib/${jar}" "$@"`
with no cd, and spring.config appears nowhere in the repo. State where Spring
actually resolves an external application.yml (the process working directory,
not the distribution root) and that the launcher forwards its arguments, so
--spring.config.additional-location= pins the location.

R1-4: sweep the credential-key rename into the two operator-recovery runbooks.
The deployment runbook links to them for recovery, the packaging script stages
both into the archive, and neither mentioned rotation, so an operator following
them after a rotation exports the new active pair, which cannot decrypt a
pre-rotation row.

Docs only; no production or test change. prettier --check clean on all five.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmvbh7mrzas

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-10-17 11:11:28 +08:00
jinye
ea6d67390a
feat(managed-agent): Improve workspace selection and tool progress (#14405)
* feat(managed-agent): Improve workspace selection and tool progress

Allow selecting existing authorized Linux project directories, expose safe tool lifecycle progress, honor the configured model-round limit, and reuse daemon WebShell presentation.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(managed): Allow late model rounds to persist and recover

Remove obsolete round ceilings from persistence and continuation validation. Cover late rounds with recovery regressions and complete real-model prompt evidence.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* test(managed-agent): Order workspace contract imports

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(managed-agent): Correct directory truncation reporting

Count an omitted eligible directory before marking the page truncated while preserving the raw-entry scan bound. Pin the unsafe-selection refusal codes and add directory limit regression coverage.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-10-17 10:35:25 +08:00
易良
205eb78acb
fix(daemon): preserve active work when deleting scheduled tasks (#14276)
* fix(daemon): preserve work when deleting scheduled tasks

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* docs(daemon): correct comments falsified by the conditional-close change

Four review findings from the bot round anchored at ea2702e08b, all
comment/doc-only, no behaviour change:

- bridge.test.ts: "only ordinary automatic cleanup is fail-closed" was
  falsified by the conditional-close assertions added directly above it.
- session-control-plane.ts: maybeCloseIdleSession's doc still claimed the
  preservation rule lives in exactly one place and that explicit close never
  reaches it; the new closeIfUnheld caller does.
- scheduled-tasks.test.ts: "no resident leak" no longer holds now that
  production retains the session while work holds it.
- design doc (EN + zh-CN): the Risks bullet dropped the "unattached"
  qualifier the Lifecycle section carries, promising a reaper convergence
  the subscriberCount gate cannot deliver.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmvb02cj89w

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
2026-10-17 10:04:47 +08:00
Shaojin Wen
809792a198
feat(managed-agent): link a child task to its child Session (#13746 F2) (#14413)
* feat(managed-agent): link a child task to its child Session (#13746 F2)

A child_agent or workflow task now names the child Session that runs
it: PublicTask.child_session_id and WebShellTask.childSessionId, present
once the run's committed child_run record names the child (its attach,
or a terminal verdict that names a minted child) and absent for every
other kind. The child is a plain Session in the parent's Workspace, so
the callers that can read the task already read the child's Turns,
items and events; the link adds no authorization rule, route or event
type.

The task view reads the id from the record's committed body rather
than a new projected column, so there is no migration or backfill. The
cancel admission's task lookup stays a pure projection read.

Contract 1.54.0 (provisional) adds the optional field with a kind
conditional on both task schemas; the WebShell types are regenerated.

* docs(managed-agent): note the child Session link contract number against main
2026-10-17 01:43:45 +00:00
Shaojin Wen
f597df5a60
docs(managed-agent): stop saying no lifecycle operation sets failure_code (#14422)
Section 4.1 records the v1.18 contract, where failure_code stayed planned
because no lifecycle operation set it. Since then v1.25 implemented the
field for permission Action responses, and the retry budget (4.5) makes a
lifecycle operation set it, as 4.6 documents. Say the 4.1 remark held at
v1.18 and point at 4.5 and 4.6, in both languages.
2026-10-17 09:34:38 +08:00
Shaojin Wen
d7a7aba9dc
test(managed-agent): ground the FG8a/FG8b/FG8d gate choreography in Linux truth (#14398)
* test(managed-agent): ground the FG8a/FG8b/FG8d gate choreography in Linux truth (#14163)

Follow-up to #14334. That PR's letters had only been run cgroup-free on
macOS; on a Linux + cgroup v2 rig with MySQL 8.4 (Orange Pi, aarch64) the
merged head fails fg8a (dispatch-reply), fg8b (page-envelope-commit), fg8d
(cancel-status-unavailable, cancel-reply-lost, the release arms) and,
intermittently, fg8f (receipt-drop-after-watermark).
Each fix below closes a review finding left open on #14334 or a failure
the rig reproduced; none touches production code.

- dispatch-reply: the Broker answers :start once beginDispatch
  committed, before the worker necessarily wrote the producer's marker.
  A single look at bg-started.txt left the run-gate shut and the case
  timed out at 180 s on the rig; wait until the run started or settled
  never-started first.
- The store proxy's commit log resets per case like every arm, so
  capture-commit-reply's dropped page commit no longer surfaces in
  observation-advance-reply's droppedCommitOps.
- page-envelope-commit: the refused page rides the first output-advance
  commit after the stream seals, and every later manifest still names
  it, so the run is blocked exactly like manifest-commit — never a
  natural exit. Both arms now share the blocked branch; the witness is a
  refused advance that carried the page. The zero-page assertion is
  kept for the page arm only (the manifest arm's page lands with the
  seal-time advance).
- cancel-reply-lost: every shell revision commits as
  commitChildRunRecord, so the arm now drops only the revision that
  first sets stopRequested, read from the staged managed-child_run
  resource, and the IT asserts one distinct stop-revision command id.
- cancel-status-unavailable: the captive read was GET :status, a route
  the Broker does not serve (404 passed as a recovery). It is now the
  Harness's own GET /executions/<call>:process, the proxy names it as
  parked, and it must answer 200 after the restore. The Broker's own
  shell-status to the worker never crosses this proxy; faulting that
  link needs a production seam and is left to a follow-up.
- Every driver release (routine or drain) waits until the dispatching
  turn handed the Runtime Session back: before that the Broker answers
  runtime_session_busy, or joins the release to the Harness's in-flight
  turn-end routine release and answers it runtime_session_held, even
  for a drain.
- fg8f receipt-drop-after-watermark: the bare next-commit drop arm was
  taken by the Session's activation renewal on the rig (droppedOp
  renewActivation instead of the manifest commit the IT pins); the arm
  is now keyed to commitChildRunRecord and the unkeyed arm is gone.

Design doc (EN + zh-CN) rows updated to match.

* test(managed-agent): drop only accepted commit replies in the FG8 store proxy

A reply-loss arm now fires only on a commit the store accepted, so a
refused commit relays and the arm stays armed for the one that lands.
The stop-revision arm is consumed by its own condition rather than by
the page-envelope branch, and the cancel-status-unavailable case names
a missing :process row instead of throwing a TypeError.

* docs(design): note that FG8 reply-loss arms fire only on accepted commits

---------

Co-authored-by: 易良 <1204183885@qq.com>
2026-10-17 01:27:12 +00:00