Replies: 3 comments
|
Verified the structural argument against Confirmed at source
The gap is a sibling of the orphan-tree class this repo already tracks
On your two questions — the design answer is probably "2", with a twist
So the sharper fix shape is: on dispose, drain continuable descendants (your option 2); on settle, when the parent has no Activation (this exact case), either (a) re-parent the ownership edge to the nearest continuation-managed ancestor, or (b) at minimum surface the orphan in the parent's own closing message / settlement summary so it isn't silently forgotten. (a) is the closer match to the "waiting graph" invariant; (b) is the cheap fallback. A persistent-orphan variant worth noting: the grandchild's Session is durable, so a restart doesn't free it either — cold resume of a child whose recorded parent is gone has the same "no live owner" shape. Whatever fix lands for the runtime case should state its cold-resume stance explicitly, mirroring the route-inheritance family's lesson (there, resume stability vs liveness was the open question). The runtime reproduction you sketched — dispose the parent mid-flight and check whether the child's Activation survives — is worth someone doing; the 中文摘要:已对照 b150a55 逐条验证你的结构性论证——acquireOwnership 早退(:1162-1174)、notifySettlement 丢弃(:1462+)、agent/disposed 只清 scope(:385-386)、liveLineage 在消失的中间层处断裂(:881-891),全部成立;预设确实混合了 continuable(:191/:198)与 one-shot(:210/:219),可达性真实。家族关联:这是 #3911(pwsh 结算不杀树、孤儿孙进程)的 Activation 树同构——结算/销毁边界只清理自身、不清理子树。两个设计问题的回答:① 不应拒绝(工具描述推荐 run_in_background:false,拒绝会破坏合法组合);② 应该 drain——但区分 dispose(应 drain,否则无人再能触达)与 settle(continuation 是有意语义,真缺陷是 notice 通道断裂);修复形态 = dispose 时 drain + settle 时无 Activation 则重挂载到最近的 continuation 祖先或至少在关闭消息中显式呈现孤儿。冷恢复变体:child Session 持久 → 重启也不释放,恢复时同样无主。运行时复现(dispose 父代理检查 child Activation 是否存活)值得做——agentLoop.create 的 disposer 正是 continuation 测试未覆盖的能力缺口。 |
|
I reproduced the runtime failure against current The failing composition is concrete: create an exact non-continuation parent through Reference implementation:
The patch keeps mixed one-shot/continuable composition valid. Before the first continuable materialization below an exact non-continuation parent, the manager installs one WeakSet-guarded cleanup effect in that parent Agent's own scope. Scope cleanup then reuses the exact-root drain machinery:
The public This intentionally fixes disposal only. Normal settlement, reparenting, notice routing after settlement, and cold-start/durable orphan recovery are not changed. Verification:
中文摘要:已在 |
|
This thread did exactly what I hoped filing it as a question would do — thank you both. @argszero: the #3911 cross-link is the observation that elevates this from a bug to a class. "The settle/dispose boundary cleans up what it owns directly, not the subtree beneath it" is a better statement of the root shape than anything in my report, and it predicts where else to look. Your design answer also matches what the preset ships: refusing mixed composition would break the documented @Jstn-1g: reviewed and adopted. Your reproduction closed the exact gap I declared — I stopped at
Two design choices worth calling out as right, having tried to break them: installing the effect in the parent Agent's OWN scope means disposal ordering comes from Cordis rather than from a listener race — the drain is sequenced before scope teardown completes by construction, not by hoping The disposal-only scope is the correct cut. Settlement-path notice routing and durable orphan recovery are separable, and #3911's process-tree analogue stays its own fix. Adopted downstream after that verification. For the maintainers: the reference branch applies cleanly to |
Uh oh!
There was an error while loading. Please reload this page.
Raising this as a question about intended ownership rather than a defect claim, because the individual behaviours are each documented and the gap is in how they compose. I was not able to demonstrate it at runtime — see the last section — so this is a structural argument plus a reachability check.
The composition
startContinuableaccepts any liveAgentas parent, with no gate on whether that parent is continuation-managed. Ownership is then acquired like this:The early return is deliberate and its JSDoc says so: a top-level or otherwise non-continuation agent "has no Activation and stays outside the waiting graph". Likewise
notifySettlementdocuments dropping the notice when the parent is no longer live: "A parent that is no longer live is not an error; the child's own Session remains the durable record either way."Both are reasonable in isolation. Composed, they describe a child that nothing owns and whose settlement nothing hears.
Why it looks reachable in the shipped preset
apps/cli/config/agent-presets/standard/agent.cordis.ymlcomposes both subagent tools withbackgroundMode: continuableand nomaxDepth(default 3), and a child inherits the same composition throughapplyChildComposition. So:subagentwithrun_in_background: false— which is exactly what the tool description recommends when the next action depends on the result. That child is one-shot, and has no Activation.subagenton the default background route, so the manager materializes a continuable grandchild.acquireOwnershipearly-returns, because the one-shot parent has no Activation. Nothing owns the grandchild.The grandchild then keeps running model turns — billed, doing work — until it finishes on its own or app teardown cancels it, and its settlement notice is dropped because the parent is no longer registered.
The
agent/disposedlistener closes the scope but does not drain the disposed agent's continuable descendants, anddrainDescendantsis only reached for the host's top-level agents via the ACP bridge. Once the intermediate parent is gone,liveLineagecannot climb through it, so a later drain no longer sees that subtree.What I could not establish
I did not get a runtime reproduction.
agentLoop.create()returns theAgentand discards the disposer — the owned handle is a separate capability — so disposing a parent mid-flight needs more scaffolding than the existing continuation tests carry, and I did not want to assert a leak I had only reasoned about. If someone with that harness closer to hand can dispose the parent and check whether the child's Activation survives, that settles it either way in a few lines.The questions
startContinuablerefuse it? Note the existing tests use a plainagentLoop.createparent, so refusing would change what they exercise.agent/disposeddrain the disposed agent's continuable descendants rather than only closing the scope?Not proposing a patch: both answers change ownership semantics, and picking one from outside is guesswork.
All reactions