Repository navigation
Conversation
…iptor sidechat.start passed inheritedEventCount = seed.length while the thread's own subagent/descriptor was the last seed event, so the descriptor sat inside the inherited prefix. The host reads a child's descriptor only from events.slice(inheritedEventCount), so side threads had none: they listed as corrupt and could not be resumed as continuable children. Build the seed the way the host's own session.fork does (buildForkSeed): the copied parent history, an inherited session/end-seed marker at the cut, then the thread's own working-directory stamp and descriptor. The cut is the copied history's length.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
sidechat.startcreates the side thread withinheritedEventCount: seed.length(src/sidechat-routes.ts:713 ondevat 0fab0af), and the seed ends with the thread's ownsubagent/descriptor(line 673:[...inheritance.seed, ...directorySeed, descriptorEvent]). The cut is therefore one past the descriptor, so the descriptor sits inside the inherited prefix.This PR builds the seed the way the host's own
session.forkdoes (buildForkSeedin@deepseek-ai/dsh-session): the copied parent history, then asession/end-seed {inherited: true}marker at the cut, then the thread's ownworking-directory/changestamp and descriptor.inheritedEventCountbecomes the length of the copied history. The seededSessionconstructor accepts a cut shorter than the seed only when the event at the cut is that marker, so the marker is required, not decorative.Why
The host looks for a child's descriptor only in the child's own events,
events.slice(inheritedEventCount):@deepseek-ai/dsh-subagent(packages/subagent/subagent/src/manager.ts,foldSubagentDescriptor(source.events.slice(source.inheritedEventCount)), about line 1348 at 0.2.1-rc.2; the same code ships in the 0.2.1-rc.1 package this repo pins)packages/session/session-format-v3-to-v4/src/facts.ts:17at 0.2.1-rc.2)With the descriptor inside the prefix, a side thread has no descriptor as far as the host can tell. It lists as corrupt, cannot be resumed as a continuable child, and is never cached, so every read decodes the whole log again. Measured on DSH 0.1.7 with a large thread (about 47k events), each decode blocked the event loop for about 1.8 s.
Existing threads keep their header. Threads created after this change get the corrected layout. The plugin's own readers already handle it: the transcript cuts at the last
session/end-seed, the cold-resume sweep folds only[0, inheritedEventCount), and the inbox clear after create is unchanged.Test plan
tests/sidechat-routes.spec.ts("puts the side thread descriptor after the inherited cut, where the host looks for it"). It runssidechat.start, feeds the options into the realSession.createfrom@deepseek-ai/dsh-session0.2.1-rc.1, and asserts thatfoldSubagentDescriptor(events.slice(inheritedEventCount))finds thecontinuable/sidechatdescriptor. It also checks that the inherited prefix is exactly the copied parent history and that the own range issession/end-seed,working-directory/change,subagent/descriptor.expected undefined to match object { mode: 'continuable', … }.tests/sidechat-routes.spec.ts, the route-shaped helper intests/sidechat-seed-validation.spec.ts, and the source-text guard intests/host-runtime-imports.spec.ts.docs/host-contracts.mditem 10: one paragraph on where the cut goes and why.pnpm test: 221 files, 2683 passed, 17 skipped.pnpm typecheckandpnpm lintare clean.This is independent of #936. That PR also touches
sidechat.start(the create timeout), but different lines, and the two do not conflict.🤖 Generated with Claude Code