Skip to content

feat(session): 会话格式 v4、外部 Session 导入与模型设置(含暗病修复) - #2

Merged
tyza66 merged 34 commits into
mainfrom
feat/session-format-v4
Oct 21, 2026
Merged

tyza66 merged 34 commits into
mainfrom
feat/session-format-v4

Conversation

@tyza66

@tyza66 tyza66 commented Oct 21, 2026

Copy link
Copy Markdown
Member

Motivation

把会话格式 v4 的移植收尾并落到可合并状态:v4 词汇、外部 Session 导入、模型设置页的推理档位与输入兜底,并修掉本轮全面排查中发现的暗病(详见 Changes)。版本号已是 1.0.20261021。

Changes

持久化 / 会话格式

  • SESSION_FORMAT_VERSION = 4;catalog payload 词汇 v0(local,一次性/可续)/ v1(新增 unknown)/ v2(external)。
  • 新增后继记录 2026-10-21-native-catalog-payload-vocabulary(same-version,链到 2026-09-19-external-subagent-catalog),补齐 .schema.json 与 i18n 记录,摘要与 finalized/v4.json 一致。
  • 修掉后继记录里重复的 <a id> 锚点与失配摘要;historical-formats/README 由 persistence-formats.ts --write 重新生成。

外部 Session 导入

  • 新增 Host 网关:列出另一套 harness 的 Session,按「一个 Session 一次提交」采用;已存在、并发抢先、损坏、头不可读分别归类,拒绝时给出原因且不暴露非 Error 拒绝。
  • 新增 ui-settings-import-sessions 设置页(列表、根目录覆盖、刷新)。

模型设置

  • pi-ai 手写路由新增推理档位控件(档位来自命名空间自身 schema),DeepSeek 卡片支持路由级 defaultInput 兜底。
  • 附件拖放:分类平台未给出 entry 路径的文件夹与文件条目。

暗病修复

  • agent-loop 的步骤中断此前只对已产出首块的服务商生效:中断控制器没有并入请求信号,interruptModelStep() 对「一个块都没回」的等待不起作用,回合会一直挂到服务商返回。现在 AbortSignal.any([turnSignal, interrupt.signal]) 参与请求,立即切断在途等待。
  • llm-deepseek 对 defaultInput 重复校验了 schema 已经保证的模态取值,删除该冗余守卫,保留空表与重复项校验。
  • ModelRow 把同一个可空回调同时给 id 与 name 输入,两个编辑器因此各带一处不可达的 ?? '';拆成 onIdChange / onNameChange 后两处死分支消失。
  • 恢复被后续改动架空、但仍被完整 lint 需要的 oxlint-disable 指令位置(prefer-promise-reject-errors 与 no-deprecated),使 staged 与全量 lint 同时为 0 error。

测试

  • 新增 model-step-interrupt.spec.ts、failure-code.spec.ts、harness-import.{host,client}.spec.ts、foreign.spec.ts、image-capability.client.spec.ts、model-reasoning-efforts.client.spec.tsx,并为上述缺陷补齐回归用例。
  • 宿主负载下过紧的墙钟假设放宽:原生文件系统事件等待、pi-ai 空闲看门狗关闭观测。

Testing

  • tsc -b tsconfig.client.json(typecheck:contracts-ready)+ 全部项目 typecheck:通过,pre-push hook 亦为绿。

    Proof
    $ tsc -b tsconfig.client.json
    (无输出,exit 0)
    ✓ typecheck (7.86 seconds)   # pre-push hook
    
  • pnpm run lint:contracts-ready(全量 oxlint,4241 文件):0 warnings / 0 errors。

    Proof
    $ tsx scripts/run-oxlint.ts .
    Found 0 warnings and 0 errors.
    Finished in 27.6s on 4241 files with 90 rules using 14 threads.
    
  • run-gates ci-static:55/55 通过(含 Session 格式引用、包路径、各类生成目录一致性)。

    Proof
    run-gates: 55 passed, 0 failed, 0 skipped
    
  • run-gates ci-coverage(本机 macOS,非分区模式,逐文件 100%):27174 passed / 0 failed;仅 subprocess-local/src/linux-execve.ts 与 ptc-runtime-python/src/index.ts 的 /proc 读取分支在本机不可达(Linux 通道覆盖)。

    Proof
    Tests  27074 passed | 1 expected fail | 116 skipped (27191)
    ERROR: Coverage … linux-execve.ts
    ERROR: Coverage … ptc-runtime-python/src/index.ts
    
  • doc-quick 20/20、verify-translation-pairing(1026 对)、verify-doc-links(2050 文件)、persistence-formats v0–v4、type-equiv 455/455:通过。

    Proof
    run-gates: 20 passed, 0 failed, 0 skipped    # doc-quick
    translation pairing: 1026 pair(s) all consistent
    persistence-formats: v0 through v4 verified (5 complete references)
    

tianyicui and others added 30 commits October 20, 2026 14:40
# Conflicts:
#	docs/config-catalog.i18n.yaml
#	docs/persistence-changes/historical-formats/README.i18n.yaml
#	packages/session/session-format-catalog/README.i18n.yaml
# Conflicts:
#	.agents/notes/implemented/architecture/2026-08-31-released-session-format-migrations.i18n.yaml
#	packages/experimental/webworker-packer/README.i18n.yaml
#	packages/experimental/webworker-packer/src/preview.ts
#	packages/experimental/webworker-packer/tests/preview.spec.ts
# Conflicts:
#	docs/event-producer-consumer.i18n.yaml
#	docs/persistence-catalog.i18n.yaml
#	packages/core/session/README.i18n.yaml
Represent tool results as tool-role messages and migrate released wrappers
without losing outer own JSON fields, call identity, error state, or writer
field order. Refuse nested or wrapper-only information that has no lossless
current representation before successor publication.

Validate current event relationships directly instead of projecting V4 data
back into V3. Keep lifecycle, PTC, title/command, compaction, system-field and
retired-syntax checks on catalog restoration and ordinary JSONL opens. The
Worker JSON equality helper must inspect own keys so it cannot admit PTC
arguments that Node rejects.

Keep the source role change, required native admission and consumer fixture
updates in one complete review layer. Explain shared-type changes in the
persistence record and the native reader rationale in its existing note.
Use checked branded fixture identities and discriminant narrowing instead
of unknown double casts; fixture values and assertions remain intact.

This recomposes the original tool-role work by _Kerman with the independently
validated migration and admission repairs by Tianyi Cui. Frozen generations
and snapshot input version selection are retained.

Co-authored-by: Tianyi Cui <53024+tianyicui@users.noreply.github.com>

# Conflicts:
#	apps/web/tests/web-search-round.e2e.ts
#	docs/persistence-catalog.i18n.yaml
#	docs/persistence-changes/historical-formats/README.i18n.yaml
Give durable user context its producer-owned source kind instead of a generic plugin wrapper. The V3-to-V4 conversion preserves the frozen rename and collision rules, all own JSON metadata, event coordinates, and historical request-only kinds. Native catalog and JSONL admission retain source validation so unknown attribution can survive without its producer while malformed owned sources are refused.

Use an explicit user-only RequestUserInput for auxiliary LLM prompts that do not own durable message identity. Auto Review and compaction remove their request-only source registrations while retaining prompt content and freezing; provider serializers and projections keep the same wire data, assistant replay isolation, image ordering, and tool correlation. Session writes, Agent delivery, and recorded title requests continue to require identified durable messages.

Bind the saved compatibility policy only to user sources and qualify tmux location attribution without weakening fixed system, model, or tool semantics. Rebuild the current inventory and unpublished V4 acknowledgement on the actual tool-role parent, preserving its rationale and all accepted historical records. The catalog format version records tooling metadata; Session remains V4. Developer-role policy and activation belong to their own feature.

The latest-master Messages adapter, serialization, request-input typing, EpochHeader reservation, and attribution checks pass 103 tests across five files. The retained V3 Messages input-history snapshot passes through the source profile; Host/Client typechecks and all 41 documentation gates pass. The generated comparison records four changed roots and 447 unchanged type fingerprints. Latest-master ARIA changes are retained, with producer source labels applied independently; new Messages fixtures retain their producer names as declared source kinds and use the fixed system-message helper; recorded Session generations are unchanged by this regrouping.

Co-authored-by: Tianyi Cui <53024+tianyicui@users.noreply.github.com>

# Conflicts:
#	docs/event-producer-consumer.i18n.yaml
#	docs/persistence-changes/2026-09-16-session-format-v4.i18n.yaml
#	docs/persistence-changes/historical-formats/README.i18n.yaml
#	packages/llm/llm-deepseek/tests/messages/fixtures/input-history.ts
#	packages/llm/llm/README.i18n.yaml
#	snapshots/web/feedback-release/ack-expanded.expected.md
#	snapshots/web/web-search-round/ui.expected.md
The producer-source refactor introduced the unreleased V4 discriminator
code-mode even though this source identifies images deferred from successful
PTC subcalls. Name it ptc-mode before V4 ships so persisted attribution
matches the producer and does not freeze obsolete terminology into new logs.

Keep the live writer, V3-to-V4 migration output, collision guard, and reserved
producer policy aligned. Both released tools-code-mode and tools-ptc plugin
lookup keys map to ptc-mode. Preserve those historical keys, opaque message
text and IDs, all frozen V0-to-V3 generations, and recorded Session inputs.

Update the owning tests, producer documentation, current catalog, and V4
acknowledgement. The canonical schema audit identifies exactly three changed
roots; reversing only the source literal restores each prior digest. The
large generated JSON diff comes from graph ordering, with 61 roots and 572
types retained.

Validation: the seven-file focused run passed 298 tests and exposed four
stale current-output expectations in two migration files. After correcting
those expectations, both files passed all 23 tests. All 41 documentation
gates and full lint passed; frozen generations and recorded inputs are
unchanged.

# Conflicts:
#	scripts/persistence-source-policy.ts
Session V4 reserves developer-role messages for incremental model-visible
session changes. The role, tool addition/removal blocks, and deferred-loading
marker need a durable representation before independent provider and UI
integrations can emit or consume them. Known developer events must match an
open turn and step; unsupported providers and UI consumers fail explicitly.

Request headers already capture full tool definitions. Persisting another
ToolSchema in each addition would duplicate Session information and create
two definitions that could drift. Additions therefore store toolName, and
the containing developer event uses headerSeq to select one earlier known
request/header for all its additions. A same-name replacement keeps its
historical definition identity through restart, fork and surface changes.
Removals require no definition copy, and ordinary developer messages omit
headerSeq. Generic sourceEventSeqs remains independent derivation metadata.

The rejected inline tool field is optional never with @persistenceReserved.
The schema extractor retains that uninhabited slot, so permitting a future
value requires a format bump instead of being mistaken for compatible
optional metadata. Unrelated optional fields remain additive and readable.

Native and Session admission reject missing, forward, non-header, unknown,
ambiguous and incomplete referenced definitions, and reject the retired
inline tool field. Validate complete schema fields only for referenced
definitions, preserving unrelated header behavior and extra JSON metadata.
Unknown ignorable records remain opaque. Empty developer nodes retain their
surface position without producing a message or shadowing the system head.
No automatic emitter, provider activation, UI implementation, or speculative
public resolver is added.

Keep the core-owned developer source binding beside the introduced role,
including the request-only type rejection and attribution round trips. Update
consumer guards, SDK/snapshot handling, bilingual owner documentation, and
the same unreleased V4 acknowledgement. Keep earlier tool/source migration
work and its documentation in the parent. Preserve the existing recorded
Cordis inspection generation from the original developer change.

Integration keeps the final source layer as the sole parent and preserves
master's Messages input filtering, empty-user omission, empty tool results,
and system-update placement. Developer/deferred guards still run before
conversion. Native V4 admission, header references, reservation declarations,
and generic tooling match the validated marker candidate. Parent-owned
fixture and generator fixes remain in the parent.

Earlier feature validation passed full build, full lint, 41 doc-sync gates,
and 16 hygiene gates. The unchanged native implementation retains that evidence.
The focused Session/V4/request-input run passes 884 tests across 38 files with
100% statement, branch, function and line coverage for the V3-to-V4 package
and Session surface. Consumer tests pass 482 with one existing skip; tool,
message and optional-field tests pass 146. Cordis inspection and compaction
replays pass in both source and built modes (two cases per mode). Ten invalid
referenced-definition/unknown-header controls failed before their scoped
guards and pass after them.

Reservation validation: the actual generated developer root records tool as
optional primitive never. Widening it requires a bump, while traceId remains
additive and preserved; all 85 focused reader/classifier tests pass.

Final-parent validation: 205 tests across eight provider, type, catalog,
Session and native-format files pass. The preserved parent fixture verifies
Messages input-history replay; runtime/declaration and parent-owned-file
comparisons show no loss during the final rebase.

Co-authored-by: Tianyi Cui <53024+tianyicui@users.noreply.github.com>

# Conflicts:
#	docs/persistence-catalog.i18n.yaml
#	packages/api/session-controller/README.i18n.yaml
Released V3 accepts tool-result wrappers in ordinary user content. Leaving
those wrappers in a V4 artifact violates its declared content types and
reaches provider serializers that cannot represent them. Converting such
rows would require a new event, role, or source ownership decision.

Reject retired result tags only in known interpreted content slots and
raw Assistant stream block starts/ends. Reuse the native retired-syntax
admission path before recoverable suffix suppression and target artifact
restoration before successor publication. Preserve the existing system
and tool-result validators and the canonical V3 tool/result wrapper lift.

Known ignorable developer rows receive the same check during encoding;
unaware physical readers still defer their opaque developer payloads.
Tool arguments, replay state, schema parameters, nested metadata, and
unknown ignorable event payloads are not recursively scanned.

Real JSONL regressions cover V3 migration and native V4 read/write in
plain and Zstandard storage, including a corrupt native prefix. Refusals
preserve source bytes and the sole generation; opaque payloads survive.
No declared schema, frozen V0-V3 codec, or snapshot generation changes.

# Conflicts:
#	.agents/notes/implemented/architecture/2026-09-17-native-v4-read-validation.i18n.yaml
The retired-content guard runs on parsed physical JSON before normal event
admission so recovery cannot suppress a retired wrapper after another invalid
row. At that point type and sequence fields are still JSON values rather than
validated event fields. Implicit interpolation therefore violates the typed
lint rule on the final combined branch.

Use explicit String conversions only for the error subject. Keep the guard's
ordering and refusal checks unchanged; requiring an already-valid envelope
here would weaken the pre-recovery check. This does not change the stored
representation or add a migration rule.
V3 permits external event, source and content identities and additional
request-tool fields. Copying those values into V4 can give an old plugin
named tool a core source identity, turn old tool-addition content into a
registry instruction, or activate formerly ignored deferLoading and delivery
metadata. Rejecting such collisions would instead refuse valid source logs.

Keep historical extensions in the existing extensible JSON representation.
Prefix unknown content and ignorable event tags with plugin:, preserving
all other content fields and opaque event payloads. Prefix external source
identities with plugin:plugin: or plugin:source: so wrapper and direct kinds
cannot collide even when original names already contain a category prefix.
Known first-party mappings remain explicit and unchanged.

Prefix extra request-tool field names and block-start field names with
plugin:, retaining exact values and existing-prefix doubling. Core schema
parameters, arguments, replay state and nested plugin data are not traversed.
Keep future-generation delivery records inactive after header promotion.
Pin the V3 event vocabulary independently of the current writer and provide
an offline verifier against an explicitly selected historical source commit.

Attachment authorization and export inspect declared event content only.
Team checkpoints preserve all own keys of unknown JSON blocks while still
rejecting malformed known content and retired core tool-result blocks.
Exercise physical read/write/reopen, prefix injectivity, own __proto__ keys,
provider opacity and retained source-identity snapshots.

No generic extension block, metadata wrapper or new schema-tooling type is
introduced. V4 type declarations, persistence schema, checkpoint and frozen
V0-V3 codecs remain unchanged. Canonical tool-result lifting and incomplete
child-catalog policy remain separate concerns.

Adapted from the extension-preservation work in 7ba7e174dd1 by Tianyi Cui.
Co-authored-by: Tianyi Cui <53024+tianyicui@users.noreply.github.com>

# Conflicts:
#	.agents/notes/implemented/architecture/2026-09-17-persistence-attribution-policy.i18n.yaml
#	docs/session-format-status.i18n.yaml
#	packages/session/session-format-v3-to-v4/README.i18n.yaml
V3 and V4 declare the same block-start fields: type, index and blockType.
The migration's identifier policy can qualify an extension blockType, but
prefixing every additional field has no current V4 field collision to solve.
That rewrite changed captured keys to anticipate hypothetical future fields.

Retain every non-blockType field and value exactly, including already
prefixed names and own JSON keys. Keep the approved blockType identifier
prefix and the separate ToolSchema field policy unchanged. Update the local
conversion tests to verify both known and extension starts retain metadata
and to use the actual raw-record time field. Later field-specific conversion
belongs to a concrete source/target change instead of this edge.
A V3 delivery marker claiming generation 4 would become active when its
header advances to V4. The integration baseline already refused that case.
Renaming every higher-generation marker instead introduced a general
conversion policy for future formats that this V3-to-V4 edge does not own.

Restore the targeted generation 4 refusal. Keep other delivery event names,
payloads and coordinates unchanged, including generations 5 and 99, which
remain inactive in V4. Their future activation checks belong to a concrete
later format edge. Preserve the separate plugin-event identifier policy.

Cover read/write opens for both plain and zstd storage: target-generation
refusal preserves source bytes and publishes no successor, while later
markers survive V4 publication and native reopening unchanged. Update the
snapshot-normalizer cases and document the alpha converter coverage limit:
unsupported histories may fail without source mutation, and later converter
fixes may expand support without a new version when V4 output stays compatible.
…etation

The physical V3 codec admits an unclassified event envelope without deciding
whether its type is required. The V3-to-V4 stage then used shared message
walkers and target vocabulary, so an invalid required developer/message could
acquire the V4 developer role although historical V3 restoration rejects it.
Generic required unknown events were also left to the wrong version's reader.

After preserving explicitly ignorable extensions, require every remaining
source type to belong to the fixed V3 vocabulary before interpreting payloads.
This is a fail-loud check for invalid or nonconforming source data, not new
compatibility handling for third-party events, which carry ignorable:true.
The approved plugin identifier conversion and native V4 readers are unchanged.

Exercise a complete developer-event sequence through direct migration,
historical/current catalogs, ignorable conversion and native V4 restoration;
add a generic unknown type. Both negative controls accepted the invalid source
before this guard. Plain/zstd read/write regressions verify refusal preserves
the source generation and never publishes a partial V4 successor.
The preserved direct-source set is the predecessor migration's accepted set
without the plugin wrapper. It includes older coordinator and subagent-report
identities even though those names have no current producer declaration.
Describe that historical admission fact instead of implying every retained
identity has a live V4 owner. No mapping or runtime behavior changes.
Convert an unknown legacy plugin wrapper such as
{kind: 'plugin', plugin: 'acme'} into {kind: 'plugin:acme'}, retaining the
complete plugin name after one prefix. Keep the existing explicit
first-party producer renames and preserve all non-identity metadata.

A direct source already records its producer in kind. Reclassifying
unknown direct kinds into plugin:source:<name> needlessly changes that
attribution and forces plugin consumers to adopt new identifiers even
though V4 accepts their existing representation. Return every valid
direct source unchanged, including already-prefixed names. Remove the
obsolete direct-kind table and two-category identity helper rather than
adding collision classification or another mapping layer.

Update the source conversion examples, codec/restore regressions, and
native fixture producers together. Refresh only the owning Python
advanced scenario's unshipped V4 outputs; preserve historical V0-V3
recordings and their generation selection. Event names, content tags,
and ToolSchema migration remain separate concerns.

Refs deepseek-ai#2692
The V3 writer on master declares and emits only name, description and
parameters. deferLoading is a first-class V4 persistence addition whose
provider behavior is not implemented yet. Prefixing every historical extra
ToolSchema key changes ordinary plugin metadata without a format need.

Preserve all ordinary tool-definition keys and JSON values unchanged,
including vendor metadata, own __proto__ properties, already-prefixed keys
and nested parameters. Remove the general key-renaming mapper. Reject only
an own top-level deferLoading field in V3 input: there is no established V3
meaning to translate, and the alpha converter may refuse unsupported source
forms rather than invent compatibility semantics. Leave native V4's
optional-true declaration, admission and provider refusal behavior intact.

The regression tests fail on the old mapper for metadata preservation and
unsupported-field refusal. Physical plain/zstd read/write tests verify source
bytes and identity remain unchanged and no V4 successor appears on refusal;
controls preserve nested parameters.deferLoading and native V4 true values.
No Session format/schema change, frozen codec edit, or historical recording
version bump is needed.
A readable V3 child may have no own descriptor, an unknown descriptor version,
or multiple descriptors. Those states do not provide a unique discovery fact,
but they do not make the parent or the retained child history unreadable.
Rejecting the entire parent prevented otherwise valid V3 Sessions from opening.

Append a missing parent catalog row only for exactly one supported own child
descriptor. Keep existing parent entries and every child record when no fact is
available. Retain rejection of mismatched creation times, unambiguous mode/label
conflicts, malformed known fields, unreadable membership and revision drift.

Cover raw and compressed read/write publication, preserved source bytes, late
descriptor availability, inherited descriptors and Preview packing. Document
that unavailable discovery evidence can leave a child absent from its parent
catalog and does not trigger automatic backfill after the V4 successor exists.

Extracts the child-evidence behavior from 7ba7e174dd1 and the Preview regression
from 9c85eb60bd25, preserving the original migration authors' work while keeping
message representation and extension interpretation outside this commit.

Co-authored-by: Dudu-0223 <fsyo0223@gmail.com>
The ordinary V3 writer records a user-role message containing one result
wrapper. V4 writes the same tool output directly in a tool-role message.
Keep that native V4 representation instead of introducing a nested message
content type for hypothetical historical shapes.

Preserve extra historical fields when removing the wrapper. Prefix outer
message fields with plugin:message: and result fields with plugin:result:
so equal names retain their original ownership and cannot overwrite the
interpreted call ID or error flag. Include the complete old name, even
when it is already prefixed, and preserve every JSON value and own key.
Only the validated wrapper supplies the interpreted result fields.

Leave unimplemented nested-result conversion as an explicit refusal that
preserves its source. Describe this as the converter's current limit, not
an impossibility or a reason to extend the established V4 format.

Cover owner/name collisions, opaque special keys, absent error flags,
unchanged sources and strict native reopening through plain and compressed
read/write persistence. No current message type or schema is added.
The exported converter rejects malformed wrappers with SessionFormatError in addition to refusing unsupported nesting. Document both failure categories so callers can distinguish corruption from a converter support limit.

Remove the obsolete claim that recorded replay compares serialized field order: the native-writer parity assertion compares parsed objects. The emitted fields and their order remain unchanged.
# Conflicts:
#	.agents/notes/implemented/architecture/2026-08-31-released-session-format-migrations.i18n.yaml
#	.agents/notes/implemented/architecture/2026-09-01-parent-owned-subagent-catalog.i18n.yaml
#	.agents/notes/implemented/architecture/2026-09-16-desktop-policy-login-loading.i18n.yaml
#	.agents/notes/implemented/bug-fix/2026-09-19-v3-incomplete-child-catalog-evidence.i18n.yaml
#	.agents/notes/implemented/simplification/2026-09-08-web-subagent-catalog-projections.md
#	.agents/notes/implemented/simplification/2026-09-08-web-subagent-catalog-projections.zh.md
#	docs/subsystems/subagent.i18n.yaml
#	docs/tool-catalog.i18n.yaml
#	packages/client/ui-subagent/README.i18n.yaml
#	packages/client/ui-workspace/README.i18n.yaml
#	packages/session/session-persistence-jsonl/README.i18n.yaml
#	packages/subagent/subagent/README.i18n.yaml
#	packages/subagent/tool-subagent-control/README.i18n.yaml
# Conflicts:
#	.agents/notes/implemented/bug-fix/2026-09-19-session-local-subagent-migration.i18n.yaml
#	scripts/migrate-sessions-to-v4.spec.ts
#	scripts/no-unknown-casts.baseline.json
# Conflicts:
#	packages/api/session-controller/README.i18n.yaml
#	packages/session/session-format-v3-to-v4/README.i18n.yaml
# Conflicts:
#	.agents/notes/implemented/process/2026-09-11-persistence-type-history.i18n.yaml
#	docs/persistence-changes/README.i18n.yaml
# Conflicts:
#	.agents/notes/implemented/simplification/2026-09-17-plugin-inventory-phase-dot.i18n.yaml
#	.agents/notes/implemented/simplification/2026-09-17-unified-subagent-activations.md
#	.agents/notes/implemented/simplification/2026-09-17-unified-subagent-activations.zh.md
#	docs/persistence-catalog.i18n.yaml
#	docs/persistence-changes/2026-09-18-schedule-optional-title.i18n.yaml
#	docs/persistence-changes/2026-09-19-external-subagent-catalog.md
#	docs/persistence-changes/2026-09-19-external-subagent-catalog.zh.md
#	docs/subsystems/subagent.i18n.yaml
#	packages/client/ui-subagent/README.i18n.yaml
#	packages/session/session-format-v3-to-v4/README.i18n.yaml
#	packages/subagent/subagent/README.i18n.yaml
#	packages/subagent/subagent/tests/external-activation.spec.ts
# Conflicts:
#	.agents/notes/implemented/simplification/2026-09-17-plugin-inventory-phase-dot.i18n.yaml
#	packages/subagent/subagent/src/manager.ts
#	packages/test-support/client-runtime/README.i18n.yaml
#	snapshots/sdk/subagent-dsh-sdk-diagnostic/session.v4.jsonl
#	snapshots/sdk/subagent-dsh-sdk-dynamic-route/session.v4.jsonl
#	snapshots/session/product-subagent-result-diagnostic/session.v4.jsonl
#	snapshots/session/subagent-acp-diagnostic/session.v4.jsonl
…ai#4689)

* fix(session): repair interrupted turns during V3 migration

* docs(session-format-v3-to-v4): state reference remapping in the summary and code

The frontmatter summary still promised preserved references after the edge
began remapping audited same-artifact references around an inserted
interrupted turn/end. Two local comments record why the opaque branch moves
only `seq` and why the open-step flag needs no reset at a turn boundary.

* docs(session-format-catalog): describe the V3-to-V4 edge as a converting link

The catalog listed the edge as an identity conversion, which stopped being
true once it inserted an evidenced interrupted turn/end and remapped the
audited local references that follow.
Replays the upstream V4 commit series (v3-to-v4 codec, migration, catalog,
persistence, builders and consumers) and rescopes it to @mutantcat. Adds the
0.2-era packages the V4 consumers depend on (working-directory,
tool-working-directory, agent-preset, agent-preset-registry,
llm-deepseek-api-key). Not green yet: those consumers also need 0.2 core and
vendor APIs that this base predates.
Drops the 0.2-era packages the upstream V4 consumers pull in
(working-directory, tool-working-directory, agent-preset,
agent-preset-registry, llm-deepseek-api-key) and restores the fork's
subagent, session-controller and schedule surfaces from main, so the port
keeps the fork's own features. Ports scripts/migration-failure-summary.ts and
points the V4 migration CLI at the package default export.

Not green: the fork's consumers still carry the retired {kind:'plugin'}
message source and the pre-V4 consumer API, so host and client typechecks
report fallout that the upstream saga commits had converted elsewhere.
Session format v4 with the catalog payload vocabulary (v0 local, v1 adds
`unknown`, v2 external), its persistence record chain down to the
2026-10-21 successor, and the regenerated historical-format references.

Harness session import: a Host gateway that lists another harness's
sessions and adopts them one commit per session, plus the settings page
that drives it. Model settings gain the pi-ai reasoning-effort control
and route-wide input fallback; attachments classify drops the platform
delivers without an entry path.

Fixes found while auditing:

- agent-loop dropped a step interrupt that no provider chunk had
  delivered: the interrupt controller now joins the request signal, so
  `interruptModelStep()` cuts an in-flight wait instead of leaving the
  turn parked until the provider answers.
- llm-deepseek re-validated `defaultInput` against the modality list its
  own schema already enforces; the empty and duplicate guards stay.
- ModelRow handed one optional-value callback to both the id and the
  name input, so both editors carried an unreachable `?? ''` for the id;
  the row now distinguishes `onIdChange` from `onNameChange`.
- Persistence record anchors were duplicated in the catalog successor
  and its digest disagreed with the finalized chain.

Test reliability under a loaded host: native-filesystem waits and the
pi-ai idle-watchdog close observation had wall-clock bounds tighter than
a saturated runner; both now allow the delay they actually measure.
Copilot AI balanced review requested due to automatic review settings October 21, 2026 02:48
@tyza66 tyza66 added kind/feature Adds or intentionally changes behavior. area/session Session format, persistence, and recorded history. area/api Cross-interface protocols, remotes, and the session-controller surface. area/web Browser and Electron graphical interfaces. area/llm Model providers, adapters, and the agent loop. area/infra Build, CI, repository gates, generators, and developer tooling. labels Oct 21, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

…y generation

The Python SDK snapshot suite passes `native_writer_output` and a source
generation to the comparison helpers, but `scripts/smoke-python-runtime.py`
had neither: the keyless SDK lane failed with a TypeError instead of
comparing anything. The helpers now mirror the TypeScript comparison — each
side tokenizes the delivery markers it recorded at its own generation, the
default comparison keeps every generation exact, and a comparison whose
Session roles span generations refuses instead of comparing mixed
artifacts.
…igration

- AgentDefaultModelConfig: add static inject = ['settings'] so the settings
  section is installed before newSession reads currentSelection(), fixing the
  {{model}} prompt-variable race in ACP sessions.
- ToolRuntime scheduler symbol: use Symbol.for() so the built and source
  module copies share one key across the source/artifact plane split.
- Composer keymap: route plain-text pastes above 4000 characters to file
  attachment intake instead of inline editor insertion, preventing Lexical
  reconciler stalls on large pastes.
- Snapshots: migrate all scenarios to v4 session format, restore missing
  web-search-round and feedback-release fixtures, update borrower manifests
  to point at v4 owner sessions, and adjust corpus policy expectations.
- tsconfig.host.json: remove dead feedback-release.e2e.ts include.
- scripts/session-snapshot-corpus.corpus.ts: update retained scenario count.
@tyza66
tyza66 merged commit 544a87c into main Oct 21, 2026
24 of 30 checks passed
@tyza66
tyza66 deleted the feat/session-format-v4 branch October 21, 2026 06:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/api Cross-interface protocols, remotes, and the session-controller surface. area/infra Build, CI, repository gates, generators, and developer tooling. area/llm Model providers, adapters, and the agent loop. area/session Session format, persistence, and recorded history. area/web Browser and Electron graphical interfaces. kind/feature Adds or intentionally changes behavior.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants