dsh-ptc-plus 0.2.4 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.en.md +177 -0
  2. package/README.md +85 -69
  3. package/assets/ptc-plus-bindings-en.png +0 -0
  4. package/assets/ptc-plus-bindings-zh.png +0 -0
  5. package/assets/ptc-plus-repair-en.png +0 -0
  6. package/assets/ptc-plus-settings-en.png +0 -0
  7. package/assets/ptc-plus-settings-zh.png +0 -0
  8. package/client.js +1168 -144
  9. package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +1 -1
  10. package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +15 -3
  11. package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +7 -3
  12. package/docs/adr/0014-persist-cell-rewrite-policy.md +7 -5
  13. package/docs/adr/0019-plugin-settings-and-kill-switch.md +19 -4
  14. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
  15. package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +5 -3
  16. package/docs/adr/0022-stage-npm-releases-with-oidc.md +2 -2
  17. package/docs/architecture.md +23 -14
  18. package/docs/client-ui.md +29 -11
  19. package/docs/durability-design.md +42 -20
  20. package/docs/installation.md +11 -3
  21. package/docs/program-data-plane.md +6 -0
  22. package/docs/runtime-reference.md +27 -14
  23. package/docs/value-wire.md +1 -1
  24. package/index.js +38 -4
  25. package/internal/cell-analysis.js +89 -12
  26. package/internal/cell-rewriter.js +162 -4
  27. package/internal/config-spec.js +192 -117
  28. package/internal/direct-surface-owner.js +56 -5
  29. package/internal/edit-transport-owner.js +11 -2
  30. package/internal/failure-reporting.js +15 -0
  31. package/internal/kernel-worker.js +11 -0
  32. package/internal/rejected-cell-editor.js +21 -12
  33. package/internal/repl-convenience.js +94 -6
  34. package/internal/repl-memory-projection.js +277 -0
  35. package/internal/repl-rewrite-contract.js +15 -0
  36. package/internal/run-code-description.js +34 -0
  37. package/internal/runtime-bridge-owner.js +63 -15
  38. package/internal/runtime-config.js +3 -0
  39. package/internal/session-cell-executor.js +74 -14
  40. package/internal/session-events.js +7 -0
  41. package/internal/session-journal.js +331 -63
  42. package/internal/session-log-view.js +2 -1
  43. package/internal/session-runtime.js +110 -9
  44. package/internal/session-state.js +116 -4
  45. package/package.json +10 -7
  46. package/src/client-activity.js +501 -0
  47. package/src/client.js +456 -20
  48. package/README.zh.md +0 -161
  49. /package/assets/{ptc-plus-repair.png → ptc-plus-repair-zh.png} +0 -0
@@ -8,7 +8,7 @@ PTC Plus evaluates each cell as an async function body, where static `import` an
8
8
 
9
9
  [`internal/cell-rewriter.js`](../../internal/cell-rewriter.js) owns module-syntax adaptation. `@babel/parser` parses the original cell with its TypeScript plugin under script semantics while explicitly allowing top-level module declarations, `return`, and `await`; recognizing a module declaration therefore does not impose module strict mode on the surrounding async-function body. Before any edit or module preload record is produced, `@babel/traverse` builds the original Program scope. Its declaration and hoisting rules reject same-scope collisions between value imports and lexical, `var`, function, or other import bindings while retaining block shadowing and the separate TypeScript type namespace. The same scope validates local exports and drives imported-reference rewriting, supplemented by top-level TypeScript declaration names because Babel intentionally excludes the type namespace from runtime scope. The rewriter derives every accepted import, export, type-only declaration, specifier, source value, and edit range from that AST. One batch edit assembler validates non-overlapping ranges, produces generated source, and composes source mappings without rebuilding either artifact per edit. [`internal/cell-analysis.js`](../../internal/cell-analysis.js) then wraps the rewritten cell as an async function body and first parses it with Acorn. Already-valid JavaScript proceeds unchanged; only a cell that Acorn cannot parse is passed through Node's `stripTypeScriptTypes` and parsed again for binding and durability analysis.
10
10
 
11
- Each static import or remote re-export creates an ordered module preload record. A value-bearing record carries its source, import attributes, original position, generated temporary-global identifier, and the default or named exports required by its AST specifiers. The dedicated worker uses Node's synchronous module resolve hook to give REPL imports and static-link adapters one parent at the session cwd, falling back to the worker process cwd when the session has no recorded cwd. Node's ESM resolver therefore owns relative URLs, bare packages, conditional `exports`, URL schemes, import conditions, and platform path conversion; plugin and target-module dependency imports retain their natural parents. Each ephemeral data-URL adapter expresses the required members as static re-exports, so Node completes export linkage before evaluating the target module, then returns the namespace for a value-bearing preload. The worker resolves records before compiling the cell and exposes each namespace under its temporary identifier. One generated lexical prologue after the original directive prologue and before every executable cell statement captures each temporary identifier into a distinct persistent namespace identifier in source order; import declarations are erased at their original ranges, and the temporary globals are removed when the cell settles. Both identities are allocated outside current source identifiers, persistent REPL bindings, reserved program bindings, and every live private namespace. The prologue references the generated host identifier directly, so a lexical `globalThis` binding has ordinary JavaScript semantics and cannot redirect capture. A binding catalog stores only the persistent namespace identity and maps public import aliases to it and the export name; scope-aware rewriting routes unshadowed reads through the namespace, keeps bare, optional, and tagged calls detached from the namespace receiver with the language's comma-reference form, and lowers writes to a deterministic `TypeError`. Private namespace names remain reserved after a public alias is redeclared, so closures from earlier cells cannot be redirected by a later import. The catalog is rebuilt during journal replay. In loose REPL mode an explicit later declaration replaces the alias for future cells, while closures compiled before that declaration retain their namespace read. Destructuring, loop, nested, and delete writes that cannot preserve native evaluation semantics are rejected before execution. Direct `eval` and `WithStatement` are rejected on the original Program while value import aliases are active because generated namespace reads cannot reproduce their dynamic lexical resolution; this validation precedes module preload. Indirect or locally shadowed eval, non-strict `with` without value aliases, and cells containing only type imports retain ordinary JavaScript semantics. Side-effect, default, namespace, named, default-plus-named, and default-plus-namespace forms remain supported. Export modifiers are removed while declarations remain; default exports bind the fixed public name `__default`; anonymous default function and class lowering emits an explicit statement terminator, while default expressions retain their parsed continuation; remote re-exports become preload-only side-effect modules; local export lists are removed; and declaration-level or specifier-level type-only syntax is erased.
11
+ Each static import or remote re-export creates an ordered module preload record. A value-bearing record carries its source, import attributes, original position, generated temporary-global identifier, and the default or named exports required by its AST specifiers. The dedicated worker uses Node's synchronous module resolve hook to give REPL imports and static-link adapters one parent at the session cwd, falling back to the worker process cwd when the session has no recorded cwd. Node's ESM resolver therefore owns relative URLs, bare packages, conditional `exports`, URL schemes, import conditions, and platform path conversion; plugin and target-module dependency imports retain their natural parents. Each ephemeral data-URL adapter expresses the required members as static re-exports, so Node completes export linkage before evaluating the target module, then returns the namespace for a value-bearing preload. The worker resolves records before compiling the cell and exposes each namespace under its temporary identifier. One generated lexical prologue after the original directive prologue and before every executable cell statement captures each temporary identifier into a distinct persistent namespace identifier in source order; import declarations are erased at their original ranges, and the temporary globals are removed when the cell settles. Both identities are allocated outside current source identifiers, persistent REPL bindings, reserved program bindings, and every live private namespace. The prologue references the generated host identifier directly, so a lexical `globalThis` binding has ordinary JavaScript semantics and cannot redirect capture. A binding catalog stores only the persistent namespace identity and maps public import aliases to it and the export name; scope-aware rewriting routes unshadowed reads through the namespace, keeps bare, optional, and tagged calls detached from the namespace receiver with the language's comma-reference form, and lowers writes to a deterministic `TypeError`. Private namespace names remain reserved after a public alias is redeclared, so closures from earlier cells cannot be redirected by a later import. The catalog is rebuilt during journal replay. In loose REPL mode an explicit later declaration replaces the alias for future cells, while closures compiled before that declaration retain their namespace read. Destructuring, loop, nested, and delete writes that cannot preserve native evaluation semantics are rejected before execution. Direct `eval` and `WithStatement` are rejected on the original Program while value import aliases are active because generated namespace reads cannot reproduce their dynamic lexical resolution; this validation precedes module preload. Indirect or locally shadowed eval, non-strict `with` without active aliases, and cells containing only type imports retain ordinary JavaScript semantics. Side-effect, default, namespace, named, default-plus-named, and default-plus-namespace forms remain supported. Export modifiers are removed while declarations remain; default exports map the fixed public name `__default` through the same alias lowering to a generated persistent private slot. Later default exports update that slot at their source position independently of variable-redeclaration policy, and the binding catalog adopts the alias definition only after the worker reports the generated post-update commit signal. This keeps `__default` live and read-only while preventing an early return or failed class evaluation from publishing an update that did not execute. Anonymous default function and class lowering emits an explicit statement terminator, while default expressions retain their parsed continuation; remote re-exports become preload-only side-effect modules; local export lists are removed; and declaration-level or specifier-level type-only syntax is erased.
12
12
 
13
13
  The rewriter normalizes Babel syntax and local-export validation failures as original-cell positions before they cross the module boundary. Executable wrapper parsing retains Acorn's wrapper-relative positions, and only those positions receive the one-line wrapper subtraction during `PTC-C001` rendering. [`internal/cell-analysis.js`](../../internal/cell-analysis.js) then applies return, redeclaration, collision, and durability analysis to the successfully rewritten program. Return control-flow lowering addresses its per-cell worker-global signal through the top-level `this` receiver, which bypasses both lexical `globalThis` shadowing and `with` object-environment resolution; worker startup validates that this receiver is the evaluator's global context, and settlement removes the signal property. Lowered returns emit an explicit statement terminator so a return in a `switch` clause remains valid after replacement. Rewrite provenance remains parallel to the closed journal schema under `meta.dshPtcPlusRewrites` and does not alter replay equality. A successful rewrite remains private audit metadata; only a failed rewrite or missing valid journal may project its description as a bounded recovery context.
14
14
 
@@ -8,7 +8,11 @@ The session runtime coordinates two different lifecycles: transport ownership fo
8
8
 
9
9
  [`internal/worker-client.js`](../../internal/worker-client.js) exclusively owns worker creation, the readiness handshake, the private port, bounded stderr capture, reset, concurrent termination tracking, scratch allocation, and scratch cleanup. It reports private-channel messages and terminal failures through callbacks. [`internal/session-runtime.js`](../../internal/session-runtime.js) owns the active cell and decides how a reported message or failure settles the journal, durability state, replay transaction, or binding call. Messages reach the kernel only from the client's current worker, while each active semantic transaction retains its worker identity for lease and replay checks.
10
10
 
11
- Cross-process policy that must agree on both sides has one source. [`internal/module-policy.js`](../../internal/module-policy.js) defines durable imports, forbidden kernel-control imports, and ambient globals for both static analysis and the worker runtime. [`internal/failure-reporting.js`](../../internal/failure-reporting.js) defines hostile-error normalization, active-cell stack extraction, bounded diagnostic logs, and repeat-failure tracking for the host and worker. Session-log recovery belongs to [`internal/session-journal.js`](../../internal/session-journal.js), which owns journal shape, event association, and branch folding. Live durable nodes, current-call exclusion, no-op confirmations, recovery boundaries, and cold nodes all use persisted `tool/call.seq` identity. During live dispatch, the journal owner uses `callId` only to resolve the unique unpaired top-level call, then passes its sequence into semantic state. Sessionless direct runtime calls do not fabricate a persisted identity. Journal v3 stores confirmed no-ops as sequences; v2 journals with empty confirmations migrate without ambiguity, while a v2 call-id confirmation forms an unknown suffix. A failed durable replay records one log-only recovery boundary in the current settled `tool/result` metadata under `meta.dshPtcPlusRecoveryBoundaries`; its payload identifies the failed call event and its parent frontier without inventing a DSH event type. Every boundary is normalized as it enters recovery, before its event sequence participates in ordering; malformed boundary data therefore rejects reconstruction instead of disappearing from the fold. A completed record enters branch folding at its `tool/result` event position because that event confirms durability. Folding leaves immutable call/result events intact, removes the failed node and dependent descendants, and recomputes checkpoints from the remaining records. [`internal/session-runtime.js`](../../internal/session-runtime.js) resets the worker and retries the contracted frontier, recursively reaching the empty REPL only if every earlier candidate also fails. The exported migration helper converts old custom boundary events into this metadata before DSH restores a session and never mutates its input.
11
+ Cross-process policy that must agree on both sides has one source. [`internal/module-policy.js`](../../internal/module-policy.js) defines durable imports, forbidden kernel-control imports, and ambient globals for both static analysis and the worker runtime. [`internal/failure-reporting.js`](../../internal/failure-reporting.js) defines hostile-error normalization, active-cell stack extraction, bounded diagnostic logs, and repeat-failure tracking for the host and worker. Session-log recovery belongs to [`internal/session-journal.js`](../../internal/session-journal.js), which owns journal shape, event association, and branch folding. Live durable nodes, current-call exclusion, no-op confirmations, recovery boundaries, and cold nodes all use persisted `tool/call.seq` identity. During live dispatch, the journal owner uses `callId` only to resolve the unique unpaired top-level call, then passes its sequence into semantic state. Sessionless direct runtime calls do not fabricate a persisted identity. Journal v3 stores confirmed no-ops as sequences; v2 journals with empty confirmations migrate without ambiguity, while a v2 call-id confirmation forms an unknown boundary. A failed durable replay records one log-only recovery boundary in the current settled `tool/result` metadata under `meta.dshPtcPlusRecoveryBoundaries`; its payload identifies the failed call event and its parent frontier without inventing a DSH event type. Every boundary is normalized as it enters recovery before its event sequence participates in ordering. Malformed boundary or association data is rejected as evidence for old state, but it does not turn historical plugin metadata into an availability gate for the current valid call. A completed record enters branch folding at its `tool/result` event position because that event confirms durability. Folding leaves immutable call/result events intact, removes unproved nodes and dependent descendants, and recomputes checkpoints from the remaining records. [`internal/session-runtime.js`](../../internal/session-runtime.js) resets the worker and retries the greatest verified frontier, recursively reaching the empty REPL if every non-empty candidate fails, then executes the current cell against that actual state. The contraction is recorded on the current settled result and reported once so later recovery does not repeatedly encounter the same boundary. The exported migration helper converts old custom boundary events into this metadata before DSH restores a session and never mutates its input.
12
+
13
+ Recovery is conservative about state and best-effort about availability. A post-boundary cell or binding may be retained only when owned evidence mechanically proves that its ancestry or independence does not cross the unknown boundary. The current journal does not contain a complete per-binding dependency graph, so recovery treats a cell as atomic and discards its affected suffix when it cannot prove a finer partition. This can lose bindings, including all bindings in the empty-frontier case, but never claims that an unverified heap or historical effect was restored. DSH policy and failures of the current request remain outside this fallback.
14
+
15
+ The selected frontier must also agree with the state the model can know when it produces the current call. DSH's append-only session log owns durable evidence, while its ordered surface and derived request history own model visibility; raw events shadowed from that surface do not become model knowledge merely because replay can still read them. UI-only `dshPtcPlusBindings` metadata also does not qualify. A cell remains eligible when a result-only replacement leaves the assistant tool call carrying its exact source on the model-visible surface. When compaction shadows that provenance, the live or cold runtime contracts the affected cell and descendants unless an explicitly selected bounded model-visible structure publishes the exact surviving state. Such a projection is not injected by default merely to retain hidden bindings. Recovery never parses a natural-language summary into binding proof. Surface replacement generation is checked before a later cell uses an already-live worker, so this rule is not cold-start-only.
12
16
 
13
17
  ## Alternatives considered
14
18
 
@@ -32,8 +36,16 @@ Cross-process policy that must agree on both sides has one source. [`internal/mo
32
36
 
33
37
  **Always recover from an empty REPL.** This guarantees a clean worker but discards verified bindings and checkpoints unnecessarily. Parent-by-parent contraction retains the largest frontier whose replay actually succeeds.
34
38
 
35
- **Compare raw boundary sequences before decoding boundary events.** This appears to avoid work for boundaries after the current record, but an absent or nonnumeric sequence is not orderable and can evade the decoder entirely. Normalizing at session-log ingestion gives the fold one trusted boundary shape and makes damaged history fail closed.
39
+ **Reject every future cell until malformed history is repaired.** This preserves a simple reconstruction invariant but lets plugin-owned optimization metadata permanently disable `run_code` in an otherwise usable DSH conversation. Rejecting an unproved binding is required; refusing the current valid call is not. The runtime instead contracts to verified state or an empty REPL and lets the current cell establish a new branch.
40
+
41
+ **Recover arbitrary bindings after an unknown boundary.** This retains more names in favorable cases, but source text and completion equality do not prove the values, mutations, closures, or transitive reads that produced those bindings. Fine-grained retention requires explicit owned dependency evidence; absent that evidence, the whole affected cell or suffix is discarded.
42
+
43
+ **Treat every raw-log durable node as model-knowable.** This maximizes continuity after compaction, but can expose names and values whose declaration is absent from the request history that produced the current call. Durability proves reconstruction, not awareness. The runtime uses the intersection of durable and model-visible provenance instead.
44
+
45
+ **Parse the compaction summary for binding names.** A natural-language summary can omit, rename, or ambiguously describe bindings and does not carry an owner-proven relation to journal nodes. Only an explicit structured model-visible projection can preserve state after exact source provenance leaves the surface.
46
+
47
+ **Compare raw boundary sequences before decoding boundary events.** This appears to avoid work for boundaries after the current record, but an absent or nonnumeric sequence is not orderable and can evade the decoder entirely. Normalizing at session-log ingestion lets the fold reject the damaged record as state evidence and place a conservative contraction boundary without silently accepting it.
36
48
 
37
49
  ## Consequences
38
50
 
39
- Worker resource cleanup and startup failures are independently testable, while the kernel retains one owner for journal and replay meaning. The client callback is deliberately narrow: it carries transport facts, not journal decisions. Recovery metadata stays private to the settled `tool/result`; it does not change tool schemas, model messages, truthful top-level tool identity, `edit_run_code`, or recorded-value replay. Adding a private protocol message still requires a kernel handler and contract tests because the client does not interpret semantic payloads. Shared policy modules become load-bearing single sources and must remain usable in both the main thread and worker without importing session state.
51
+ Worker resource cleanup and startup failures are independently testable, while the kernel retains one owner for journal and replay meaning. The client callback is deliberately narrow: it carries transport facts, not journal decisions. Recovery metadata stays private to the settled `tool/result`; it does not change tool schemas, truthful top-level tool identity, `edit_run_code`, or recorded-value replay. The runtime consumes DSH's public surface contract without copying its projection algorithm or adding a DSH version whitelist. A contraction caused by damaged or no-longer-model-visible history can remove previously available bindings, so the first continued cell receives one bounded diagnostic and may need to redeclare them; historical program-binding effects are neither replayed nor described as undone. Adding a private protocol message still requires a kernel handler and contract tests because the client does not interpret semantic payloads. Shared policy modules become load-bearing single sources and must remain usable in both the main thread and worker without importing session state.
@@ -2,11 +2,15 @@
2
2
 
3
3
  ## Problem
4
4
 
5
- The persistent REPL uses one worker lexical environment so later cells can refer to earlier top-level bindings. That convenience is not JavaScript's module or function semantics, and mixed old/new destructuring requires a policy for combining declaration and assignment. Keeping that policy inside `internal/cell-analysis.js` makes core parsing and durability analysis depend on one replaceable persistence strategy.
5
+ The persistent REPL uses one worker lexical environment so later cells can refer to earlier top-level bindings. That convenience is not JavaScript's module or function semantics. Mixed old/new destructuring needs a policy for combining declaration and assignment, while ordinary iterative work also needs an explicit policy for replacing an earlier function or class declaration. Keeping either policy inside `internal/cell-analysis.js` makes core parsing and durability analysis depend on one replaceable persistence strategy.
6
6
 
7
7
  ## Decision
8
8
 
9
- `internal/cell-analysis.js` owns cell parsing, binding inventory, durability classification, return rewriting, and execution preparation. Shared binding-pattern traversal lives in `internal/binding-pattern.js`. `internal/repl-convenience.js` owns the optional loose-redeclaration policy, including all-existing replacement and mixed destructuring lowering; it returns executable source, collisions, redeclaration metadata, and rewrite provenance through one narrow function. The current persistent REPL continues to enable this policy, so the user-facing convenience and journal behavior remain unchanged. A future state/frame persistence implementation can replace this policy without changing the cell language analyzer.
9
+ `internal/cell-analysis.js` owns cell parsing, binding inventory, durability classification, return rewriting, and execution preparation. Shared binding-pattern traversal lives in `internal/binding-pattern.js`. `internal/repl-convenience.js` owns two independent, optional cross-cell policies: variable replacement (including mixed destructuring lowering) and named function/class replacement. It returns executable source, collisions, redeclaration metadata, and rewrite provenance through one narrow function. The variable policy remains enabled by default; function/class replacement is disabled by default and must be enabled with `looseTopLevelFunctionClassRedeclarations`.
10
+
11
+ Only a top-level named `FunctionDeclaration` or `ClassDeclaration` whose name existed before preparation is eligible. Its catalog provenance must prove that the existing binding is writable: `let`, `var`, function, and class are writable; a source `const` is writable only when that cell's variable policy lowered it to `let`; imports and reserved/private bindings are not writable. Unknown provenance fails closed. A function is lowered at its declaration position to assignment of an anonymous function expression, so recursive reads resolve through the replaceable outer binding. A class is lowered to assignment of a named class expression, preserving its inner self-reference and ensuring a throw during class evaluation leaves the old outer value unchanged. Both forms deliberately take effect at the declaration position rather than acquiring native declaration hoisting. A terminated `void 0` statement prevents the assignment value from becoming the cell completion.
12
+
13
+ The original Program is validated before lowering. Same-cell duplicates, nested or block declarations, parsing, TypeScript scope rules, and source positions therefore remain owned by the language pipeline. A future state/frame persistence implementation can replace these policies without changing the cell language analyzer.
10
14
 
11
15
  ## Alternatives considered
12
16
 
@@ -18,4 +22,4 @@ The persistent REPL uses one worker lexical environment so later cells can refer
18
22
 
19
23
  ## Consequences
20
24
 
21
- The core analyzer has no mixed-redeclaration lowering helpers and can be tested independently of the REPL convenience policy. The compatibility module remains responsible for its custom hybrid semantics and its focused tests; it must not be described as native JavaScript semantics. Future persistence work can introduce fresh cell lexical frames while retaining or replacing the convenience adapter at one explicit boundary.
25
+ The core analyzer has no redeclaration lowering helpers and can be tested independently of the REPL convenience policy. The compatibility module remains responsible for its custom replacement semantics and focused tests; neither policy is native JavaScript semantics. Binding assignability is reconstructed from prepared source and each cell's persisted policy rather than runtime-value inspection. Future persistence work can introduce fresh cell lexical frames while retaining or replacing the convenience adapter at one explicit boundary.
@@ -2,13 +2,13 @@
2
2
 
3
3
  ## Problem
4
4
 
5
- Durable replay prepares the original cell source again. AST module and REPL convenience rewrites are configuration-controlled, so using the current profile during replay can reject a cell that was durable when it was recorded. Replay would then discard recoverable bindings even though the journal still proves the cell's calls and completion.
5
+ Durable replay prepares the original cell source again. AST module rewrites and both REPL redeclaration conveniences are configuration-controlled, so using the current profile during replay can reject a cell that was durable when it was recorded or infer the wrong binding assignability. Replay would then discard recoverable bindings even though the journal still proves the cell's calls and completion.
6
6
 
7
7
  ## Decision
8
8
 
9
- Journal version 3 records `rewritePolicy` beside `bindingMode` with the three boolean rewrite settings used to prepare the cell. Live journals capture the active profile; replay passes each node's recorded policy to `prepareProgram`. The policy is part of journal normalization and equality, is frozen with the journal, and is required by the closed schema. A profile change therefore affects only new cells; existing durable nodes retain the language policy under which they were evaluated.
9
+ Journal version 4 replaces `bindingMode` with the closed `bindingPolicy` object containing the exact `variableRedeclarations` and `functionClassRedeclarations` booleans used to prepare the cell. It retains `rewritePolicy` with the three module and mixed-declaration rewrite booleans, and adds the closed `moduleSemantics.defaultExportBinding` value. Current cells record `live-readonly`; this is a source-lowering generation, not a user setting. Live journals capture both active policies and the fixed module semantics; replay passes every recorded value to `prepareProgram`. These objects participate in normalization and semantic equality, are frozen with the journal, and are required by the closed schema. A profile change therefore affects only new cells; existing durable nodes retain the language policy, module lowering, and writable-binding provenance under which they were evaluated.
10
10
 
11
- The journal decoder also owns the predecessor adapters. Version 1 has its original closed field set and migrates with all three later rewrite switches disabled, matching the language pipeline that emitted it; `bindingMode` continues to preserve its top-level redeclaration policy. Its string call-ID confirmations become version 3 event sequences only when the enclosing session log contains exactly one earlier unjournaled `run_code` call with that identity. Missing or repeated candidates are ambiguous and fail closed. Version 2 already contains `rewritePolicy` and normalizes to version 3 only when its confirmed no-op list is absent or empty; version 2 call-ID confirmations remain unsupported.
11
+ The journal decoder also owns the predecessor adapters. Versions 1 through 3 retain their original closed field sets as inputs and normalize to version 4. Their `bindingMode` maps exactly to `variableRedeclarations`; `functionClassRedeclarations` is always `false` because those language pipelines did not implement the feature. They receive `legacy-variable` default-export semantics so loose journals preserve the historically writable generated binding and strict journals preserve its const behavior. Version 1 receives all three later rewrite switches as disabled and its string call-ID confirmations become event sequences only when the enclosing session log contains exactly one earlier unjournaled `run_code` call with that identity. Missing or repeated candidates are ambiguous, so they cannot prove old state and form an unknown recovery boundary. Versions 2 and 3 retain their recorded `rewritePolicy`; unsupported non-numeric predecessor confirmations receive the same conservative contraction. Per [ADR 0007](0007-separate-worker-transport-from-session-semantics.md), rejecting predecessor state evidence does not make historical metadata an availability gate for the current valid call.
12
12
 
13
13
  ## Alternatives considered
14
14
 
@@ -18,8 +18,10 @@ The journal decoder also owns the predecessor adapters. Version 1 has its origin
18
18
 
19
19
  **Read the current profile during replay.** This keeps the journal small, but makes durable recovery depend on mutable deployment configuration and causes valid historical cells to become unrecoverable after a configuration change.
20
20
 
21
- **Accept every version 2 confirmation by call ID.** Preserving those values would require replay to guess which event a reused provider call ID denotes. Only the empty-confirmation subset has an identity-independent meaning, so broader migration would weaken the version 3 event-sequence contract owned by [ADR 0007](0007-separate-worker-transport-from-session-semantics.md).
21
+ **Infer the default-export lowering from source or journal version during replay.** Source does not identify which implementation generation evaluated it, while version-derived behavior becomes implicit protocol state. Persisting the closed semantic value makes migration explicit and keeps replay deterministic.
22
+
23
+ **Accept every predecessor confirmation by call ID.** Preserving those values would require replay to guess which event a reused provider call ID denotes. Only the version 1 adapter with a unique enclosing session event has sufficient evidence, so broader migration would weaken the event-sequence contract owned by [ADR 0007](0007-separate-worker-transport-from-session-semantics.md).
22
24
 
23
25
  ## Consequences
24
26
 
25
- The current journal schema is version 3. Replay remains source-based and deterministic while configuration changes no longer invalidate durable cells. Version 1 histories retain the predecessor language behavior and migrate only uniquely identified confirmations; version 2 histories without call-ID confirmations retain their recorded rewrite policy. Ambiguous identity remains unsupported recovery input rather than receiving guessed defaults. New profile settings apply at the next live cell boundary.
27
+ The current journal schema is version 4. Replay remains source-based and deterministic while configuration changes no longer invalidate durable cells or alter reconstructed binding assignability. Version 1 histories retain the predecessor rewrite behavior and migrate only uniquely identified confirmations; versions 2 and 3 retain their recorded rewrite policy. Every predecessor fixes function/class replacement to disabled and default-export lowering to `legacy-variable` instead of guessing from current behavior. Ambiguous identity remains unsupported recovery input rather than receiving guessed defaults. New profile settings apply at the next live cell boundary.
@@ -8,26 +8,41 @@ Accepted
8
8
 
9
9
  ## Context
10
10
 
11
- PTC Plus 0.1.0 deliberately had no Client UI. The runtime surface was the DSH PTC mode `run_code`/`edit_run_code` transport, and adding a settings bundle was judged not to improve the core REPL path. The plugin also lacked a user-facing way to disable its runtime without removing the Cordis entry, and enabling/disabling state was invisible outside the session log. The operator now wants a plugin settings UI containing every Host Config field, a kill switch, and an explicit enabled marker. `dsh-element-inspector` demonstrates the supported DSH pattern: Host registers a settings namespace and the browser half contributes a card into `settings.plugin.item`. `deepseek-harness-rc2` defines the settings service, client `settingsScope`, and the slot contract.
11
+ PTC Plus 0.1.0 deliberately had no Client UI. The runtime surface was the DSH PTC mode `run_code`/`edit_run_code` transport, and adding a settings bundle was judged not to improve the core REPL path. The plugin also lacked a user-facing way to disable its runtime without removing the Cordis entry, and enabling/disabling state was invisible outside the session log. PTC Plus requires a plugin settings UI containing every Host Config field, a kill switch, and an explicit enabled marker. DSH's public settings service registers a namespace, the browser half contributes a card through `settings.plugin.item`, and the client receives the scoped settings service through `settingsScope`.
12
12
 
13
13
  ## Decision
14
14
 
15
15
  PTC Plus registers one DSH settings namespace `ptc-plus` with the same field list, defaults and validation as the Host Config schema. The field definitions live once in `internal/config-spec.js`; `index.js` builds the Cordis Config schema from it, and the esbuild client bundle imports the same module so the UI does not copy defaults or validation.
16
16
 
17
+ The settings card separates four groups: common and compatibility controls, optional capabilities, advanced behavior, and resource limits. High-attention decisions appear before policies that most users can leave at their defaults. `autoDescribeRunCode` defaults to true and controls only a request-bound local execution policy. When enabled, a call that omits the outer `description` uses derived arguments for local DSH validation, and the fallback summary enters presentation metadata only; when disabled, DSH validates the original arguments. Both states produce byte-stable model-visible tool schemas, order, and system sections with the required `description` declaration. Original call arguments, explicit summaries, cell source, and nested native-tool arguments remain unchanged.
18
+
17
19
  The configurable set is the Host runtime configuration plus `enabled: boolean` (default true). `enabled` is the kill switch: when false the Host registers only the settings namespace and keeps the client card available; it does not install the runtime bridge, edit transport, direct-surface owner, system prompt section, tool registrations or session hooks. When true the same runtime as pre-settings PTC Plus is installed. Every field is live when its owner can reconcile it: the Host observes the settings scope and installs, uninstalls, or reconfigures owners without replacing existing session-bound bindings. Node fixes a worker's V8 `maxOldGenerationSizeMb` limit at construction, so changing that field while a session worker is active is rejected and the settings provider is restored to the previous value; other reconfiguration failures use the same compensation path. The TypeScript language check occurs at activation, allowing disabled settings to load on a host using another code runtime while rejecting and rolling back an enable attempt. If the settings service is absent, the Host falls back to composition config and enables the runtime.
18
20
 
19
- Every settings-card string — field labels and hints, header description, enabled state (“已启用/已停用”, or “Enabled/Disabled” in English), expand/collapse name, sync and result messages, and the session indicator tooltip — is registered into the DSH client locale namespace `settings.ptcPlus` and follows the active DSH client locale. Field copy exists in both locales from the single `internal/config-spec.js` source (`label`/`labelEn`, `description`/`descriptionEn`); card chrome copy is owned by the Client half. The stable REPL guidance remains plugin-owned protocol text without UI branding. The tool names remain `run_code`/`edit_run_code`; we do not rename DSH-owned transport names.
21
+ Every settings-card and PTC Plus tool-row string is registered into the DSH client locale namespace `settings.ptcPlus` and follows the active DSH client locale. Field copy exists in both locales from the single `internal/config-spec.js` source (`label`/`labelEn`, `description`/`descriptionEn`); display chrome copy is owned by the Client half. The stable REPL guidance remains plugin-owned protocol text without UI branding. The transport names remain `run_code`/`edit_run_code`; DSH owns their identity.
22
+
23
+ The existing `conversation.session.header.actions` entry with id `ptc-plus-active` remains the lightweight enabled indicator. Hovering it, or focusing it from the keyboard, opens a compact reusable-binding card. The card lists the top-level bindings the current runtime owner has proved reusable by the agent, classified as variable, function, class, or import. Each entry includes its original declaration line and column, a one-line preview, and an accessible inline action that expands the declaration source. Preparation owns declaration spans before execution and `BindingCatalog` retains that provenance as bindings are replaced or replayed; the presentation path never reads, serializes, previews, or invokes the bound runtime values or getters. The runtime emits a complete, value-independent `meta.dshPtcPlusBindings` presentation envelope after each settled cell. The enclosed inventory is bounded to 128 entries, 1024 UTF-16 code units per declaration source, and 16384 code units of declaration source in aggregate, and reports its exact valid total and omitted count. Volatile is a cold-recovery classification, not a live-availability classification, so a live volatile worker continues to publish retained bindings while their exact provenance remains in the model-visible state frontier. A restore-bearing or discarded settlement, or a DSH surface contraction that hides binding provenance, contracts the worker and reports the binding surface as unconfirmed until a later cell materializes and proves the effective state.
24
+
25
+ The inventory preserves `BindingCatalog`'s last-declared-first stack order instead of sorting by name. Declarations later in one cell appear above earlier declarations, and redeclaring an existing binding moves it back to the top. The Client aligns every entry through a fixed-width type column followed by its name and definition preview.
26
+
27
+ The Host optionally registers `ptcPlusRepl` through DSH's public `sessionProjections` seam. Each enabled runtime activation owns an opaque presentation generation shared by its metadata producer and projection definition. The projection's pure fold pairs public `run_code` and `edit_run_code` events through the formal `tool/call.data.callId` and `tool/result.data.message.source.callId` identity; optional `sourceEventSeqs` provenance is not required. It accepts only complete snapshots from its own generation, clears live proof at `session/end-seed`, and serves only the inventory to the Client. A paired result with absent, malformed, or foreign-generation metadata cannot overwrite an already proved inventory. Unrelated tool results and result-only surface replacements that leave binding provenance model-visible are also no-ops for this UI fold; a replacement that contracts the runtime's model-visible state frontier invalidates the corresponding inventory through the same owner decision, so presentation cannot claim a binding the next cell will not own. The bounded call-correlation state is checkpointed separately from the wire value, carries the generation, and has its own invalidation version. A cached state from another generation normalizes to an unconfirmed inventory without replaying stale live claims. DSH deployments without the service, hosts exposing an incompatible registration contract, and unsupported versioned envelopes retain the indicator without binding data while the core runtime stays enabled. Existing logs require no migration. The DSH header-action kit exposes no public navigation command for opening a tool call or source location, so the card uses inline inspection and does not reach through private stores or query Host DOM. Source navigation requires a public Host capability. The projection generation and private metadata are presentation-only: neither enters model messages nor changes the closed journal schema, canonical result value, runtime context, tool schema, replay reader, authority, or compatibility rules. In particular, the UI inventory does not prove model awareness or keep a binding alive after the runtime's model-visible frontier contracts.
28
+
29
+ While the setting is ready and enabled, the Client registers `run_code` and `edit_run_code` in the public keyed `tool.call.toolview` slot; disabling disposes both entries and restores DSH's owner fallback. Each replacement row preserves expandable source, result, visible lifecycle state, and Inspect access, then derives optional feature notices from existing PTC Plus presentation metadata. A non-PTC session uses a neutral Code title unless a settled result carries a valid PTC journal. The view never folds a conversation window or displays cumulative counts. Unknown or malformed metadata is ignored as feature evidence without hiding the base row; v1 journals with non-empty confirms receive the same treatment because the Client deliberately does not load the session identity needed to resolve them.
30
+
31
+ Feature notices name actual per-result events rather than configuration defaults: validated import/export/mixed-redeclaration provenance, a complete safe-edit target/derived-run/non-noop/optional-recovery-boundary relation consistent with any target precondition in the outer call, successful `code.run` calls, a completed durable recovery proven by the stable `PTC-R002` warning/recover/rolled-back tuple, and `repl.state` operations. Volatile-retained and discarded state remain recovery classifications, not user-facing feature notices. Recovery boundaries are validated when present but deliberately not displayed. Features without an existing Client-only fact, including ordinary loose redeclarations, canonicalizer provenance, official ownership of a `cordis_*` member, and whether a model recovery tip was selected, are not guessed from code shapes, name prefixes, or settings. The body feature row remains read-only and adds no Host owner. The REPL-memory card adds only the public session projection and parallel presentation snapshot described above; it adds no tool, prompt section, runtime context, session event type, journal field, value inspection, or migration behavior.
20
32
 
21
33
  Runtime activation is transactional. If a live enable cannot complete, every owner acquired before the failing setup step is disposed, the failure is logged, and the settings provider is asked to persist `enabled: false`; a failed compensating write remains an explicit activation diagnostic rather than an unreported state mismatch.
22
34
 
23
35
  ## Alternatives Considered
24
36
 
25
- 1. Keep no Client UI. Rejected: the operator explicitly needs a settings surface and a kill switch. Away from settings, users cannot tell whether PTC Plus is actively installed because the only marker is application behavior.
37
+ 1. Keep no Client UI. Rejected: PTC Plus requires a settings surface and a kill switch. Away from settings, users cannot tell whether PTC Plus is actively installed because the only marker is application behavior.
26
38
  2. Rely on DSH generic schema rendering without a `settings.plugin.item` card. Rejected: the generic tab renders only namespace cards supplied through the slot; shipping no card would leave the namespace invisible even though the schema is registered.
27
39
  3. Rename the `run_code` tool or use a schema/name-level UI alias. Rejected: PTC Plus must preserve the DSH host tool names and their public pipeline semantics; presentation labels belong in Client-owned UI, not in transport identity or model-visible stable text.
28
40
  4. Keep non-`enabled` fields restart-applied. Rejected: a settings UI that persists values without changing the active runtime is misleading. Runtime owners now accept live configuration updates; session kernels keep their bindings while applying new limits and policies to subsequent cells.
29
41
  5. Keep the card copy Chinese-only, or let the card branch on the browser locale itself. Rejected: the plugin's own copy must follow the same Host-owned locale preference every other settings surface follows, so it registers both dictionaries under `settings.ptcPlus` and uses the `t` seat composed by the DSH slot renderer; duplicating per-locale text outside `internal/config-spec.js` would break the single-source rule for field copy.
42
+ 6. Restyle rendered tool calls by querying Host DOM. Rejected: DOM structure is not a DSH extension contract and would silently break across Client versions. The public keyed toolview slot provides the presentation-only boundary.
43
+ 7. Query the live worker or serialize binding values when the card opens. Rejected: hover must not execute code, invoke getters, cross the Value Graph boundary, or make UI availability affect runtime state. Bounded declaration provenance from the already submitted source communicates useful continuity without those side effects.
44
+ 8. Implement “jump to definition” by locating Host conversation DOM. Rejected: the header-action kit has no public navigation capability in the supported DSH generations, and DOM shape is not a compatibility contract. Inline definition inspection is available; actual navigation waits for a public Host surface.
30
45
 
31
46
  ## Consequences
32
47
 
33
- The package gains a browser bundle (`src/client.js` → `client.js`), `dsh.client` metadata, a `./client` export and the `@deepseek-ai/dsh-settings` dependency. Host tests must cover the settings-disabled path and the live toggle path. Bundle loading follows DSH's public injected module and slot contracts; it does not advertise a literal DSH compatibility floor. Missing settings service degrades to the previous behavior instead of failing the plugin.
48
+ The package gains a browser bundle (`src/client.js` → `client.js`), `dsh.client` metadata, a `./client` export and the `@deepseek-ai/dsh-settings` dependency. Host tests must cover the settings-disabled path, live toggle path, value-independent binding snapshot, definition-source budgets, and optional projection registration; Client tests cover both running and settled tool blocks, the `arguments` and `argsRaw` fields, source/result fallback, exact feature derivation, malformed metadata, and the hover/focus card. Bundle loading follows DSH's public injected module and slot contracts; it does not advertise a literal DSH compatibility floor. Missing settings or session-projection services degrade the affected UI while the core runtime stays enabled instead of failing the plugin. Client presentation cannot affect model requests, canonical values, session persistence, or recovery.
@@ -22,11 +22,11 @@ The official plugin also registers Host inspect providers in the process-global
22
22
 
23
23
  Cordis tools remain native program bindings under `tools.*`. The PTC code-only direct-tool projection remains exactly `[run_code, edit_run_code]`; native agents and unrelated scopes do not inherit this option.
24
24
 
25
- Cordis definitions and Runs are process-local, while PTC journals replay every settled program binding as a recorded value without redispatch. On the first prompt for an agent or a new Cordis enable generation, PTC Plus derives whether normalized journals contain a Cordis transcript. If they do, it emits the fixed `tools:ptc-plus-cordis-recovery` runtime context: recorded values remain historical data, but the model must perform a live read-only Cordis Inspect before relying on prior IDs, state, approvals, or capability observations. The context remains until a newly settled successful `cordis_inspect*` call appears in the session log. It neither asserts that live state was lost nor reruns a mutating call.
25
+ Cordis definitions and Runs are process-local, while PTC journals replay every settled program binding as a recorded value without redispatch. On the first prompt for an agent or a new Cordis enable generation, PTC Plus derives whether normalized journals contain a Cordis transcript. If they do, it emits the fixed `tools:ptc-plus-cordis-recovery` runtime context: recorded values remain journal data, but the model must perform a live read-only Cordis Inspect before relying on prior IDs, state, approvals, or capability observations. The context remains until a newly settled successful `cordis_inspect*` call appears in the session log. It neither asserts that live state was lost nor reruns a mutating call.
26
26
 
27
27
  ## Alternatives Considered
28
28
 
29
- 1. Add the Cordis row to the PTC preset. Rejected: PTC Plus is installed as a host plugin and does not own or fork DSH's shipped preset files; the operator asked for a plugin setting.
29
+ 1. Add the Cordis row to the PTC preset. Rejected: PTC Plus is installed as a host plugin and does not own or fork DSH's shipped preset files; a plugin setting keeps the optional mount within the plugin's ownership boundary.
30
30
  2. Register Cordis tools during `system-prompt/assemble` and continue with the existing assembly. Rejected: provider and schema collection has already happened, so waiting alone would still omit the tools. The selected barrier instead discards that stale snapshot and asks DSH's public assembler to recollect after the owner-controlled mount is ready.
31
31
  3. Copy the tool definitions or their current names. Rejected: DSH owns their schemas, guidance, lifecycle, authority, and compatibility. Even a names-only completeness list would drift when DSH evolves and create a second contract.
32
32
  4. Enable the tools by default. Rejected: the tools can execute model-written plugins against the live runtime and carry a fixed schema cost. That trust and context expansion must be explicit.
@@ -58,11 +58,13 @@ assembly pattern remains available for future UI or host features. A plugin
58
58
  update alone cannot repair an already-running DSH process that has failed while
59
59
  loading legacy artifacts; the process must be stopped and the artifacts
60
60
  migrated before restore. Existing valid sessions are unaffected by the
61
- migration, while malformed legacy artifacts must fail closed and remain
62
- diagnosable.
61
+ migration, while malformed legacy artifacts must remain unchanged and
62
+ diagnosable. This migration rule rejects an unsafe overwrite of the source
63
+ artifact; it does not authorize the session runtime to reject later valid cells
64
+ once DSH has supplied a loadable session. Runtime contraction follows
65
+ [ADR 0007](0007-separate-worker-transport-from-session-semantics.md).
63
66
 
64
67
  The long-term upstream requirement is one of: a restore-preflight migration
65
68
  hook, an atomic raw-artifact rewrite API, or a contract that isolates one bad
66
69
  session from workspace registry initialization. None of these is emulated by
67
70
  PTC Plus in the absence of a public DSH extension point.
68
-
@@ -8,7 +8,7 @@ The npm registry is retiring long-lived bypass-2FA credentials as a direct publi
8
8
 
9
9
  The `Release` GitHub Actions workflow is the only automated npm publication path. A maintainer explicitly dispatches it from an annotated `v<package.version>` tag ref; there is no independent tag input. Before obtaining npm authority, the workflow proves that its `GITHUB_REF`, provenance-bearing `GITHUB_SHA`, annotated tag target, checked-out commit, manifest version, and the successful `main` push run of `.github/workflows/ci.yml` all identify the same commit. Normal CI does not run for tag pushes.
10
10
 
11
- The npm Trusted Publisher is bound to the exact repository, `release.yml` workflow filename, and `npm-release` GitHub environment. Its allowed action is only `npm stage publish`; direct `npm publish` and reusable npm write tokens are absent from the workflow. The package uses npm's most restrictive recommended publishing-access policy even when npm documentation and the live UI use different labels for it. After the OIDC staging path is proven, maintainers revoke every remaining reusable token capable of publishing or staging this package rather than treating the selected UI label as evidence that those credentials no longer exist. The release job receives only `contents: read` and `id-token: write`, uses a GitHub-hosted runner without a package-manager cache, revalidates the dispatch ref and SHA after the environment boundary, builds and smoke-tests one tarball, and sends that same file to npm staging with provenance.
11
+ The npm Trusted Publisher is bound to the exact repository, `release.yml` workflow filename, and `npm-release` GitHub environment. Its allowed action is only `npm stage publish`; direct `npm publish` and reusable npm write tokens are absent from the workflow. The package uses npm's most restrictive recommended publishing-access policy even when npm documentation and the live UI use different labels for it. After the OIDC staging path is proven, maintainers revoke every remaining reusable token capable of publishing or staging this package rather than treating the selected UI label as evidence that no such credentials remain. The release job receives only `contents: read` and `id-token: write`, uses a GitHub-hosted runner without a package-manager cache, revalidates the dispatch ref and SHA after the environment boundary, builds and smoke-tests one tarball, and sends that same file to npm staging with provenance.
12
12
 
13
13
  Promotion is separate from staging. A maintainer reviews the staged metadata and downloadable tarball, then approves or rejects it through npm with interactive 2FA. A successful approval is not treated as immediate availability because npm publish-time scanning may delay or block registry visibility. GitHub Release publication follows observed availability of the exact npm version.
14
14
 
@@ -28,4 +28,4 @@ npm 12 install-time restrictions remain enabled for the release job. The manifes
28
28
 
29
29
  ## Consequences
30
30
 
31
- An npm release now requires a successful immutable CI identity, an explicit workflow dispatch, a narrowly scoped OIDC exchange, and a separate npm 2FA promotion. Once the proven migration removes legacy package-capable tokens, no npm write secret needs rotation, and a compromised release job can at most create a staged candidate under the configured package and workflow relationship. The trade-off is an intentional manual promotion step and a possible registry scanning delay. Maintainers must configure the npm Trusted Publisher, package publishing access, GitHub environment, tag protection, and post-verification token revocation outside the repository; those deployment facts cannot be inferred or silently created by source code.
31
+ An npm release requires a successful immutable CI identity, an explicit workflow dispatch, a narrowly scoped OIDC exchange, and a separate npm 2FA promotion. With package-capable reusable tokens revoked, no npm write secret needs rotation, and a compromised release job can at most create a staged candidate under the configured package and workflow relationship. The trade-off is an intentional manual promotion step and a possible registry scanning delay. Maintainers must configure the npm Trusted Publisher, package publishing access, GitHub environment, tag protection, and post-verification token revocation outside the repository; those deployment facts cannot be inferred or silently created by source code.
@@ -15,8 +15,8 @@ PTC Plus 把 DSH PTC 模式的顶层 `run_code` 变成与 session 绑定的连
15
15
  主入口接管模型直接发起的顶层 `run_code`,并通过 DSH tool registry 真实注册 `edit_run_code`。
16
16
  使用 code-only direct-tool projection 的 PTC request 始终按固定顺序暴露 `[run_code, edit_run_code]`;插件用 agent scope 的
17
17
  `tools.register()` 真实注册 edit transport,并用同一 scope 的 `tools.presentAs('both')` 放行这两个 provider tool call,再把其他 native tool 保留在 program SDK。首次可判定的 prompt assembly 在安装这项 presentation effect 前建立 agent composition owner;注册与 mode 记录都在所属 session、agent 或插件释放时撤销,native-mode 与无关 agent scope 不继承 `edit_run_code`。
18
- DSH 为一个 agent composition 固定选择 `native`、`ptc` 或 `both`。首次 assembly 的非空 `tools:ptc-only` section text 证明 `ptc`,同名空 section 证明 `both`;旧 Host 的 `tools:code-only` 仅在当前 section 缺失时作为边界别名,两者同时存在时当前 section 优先。只有缺少 owner signal 时才能从 tool shape 推断。后续 assembly 不重新解释插件自己的 `tools.presentAs('both')` 效果,而是从 agent composition owner 派生 presentation。`ptc` 独占 direct-tool collapse、顶层 native-call normalization 和 native dispatch rejection;`both` 保留 native direct tools。`ptc` 与 `both` 都适配 PTC `run_code` schema、program SDK 和 session runtime contexts,只有 `ptc` 安装并直接执行 `edit_run_code`。model stream 将完整 call 的 session 与 call ID 绑定到由 composition mode 派生的 request policy,dispatch wrapper 替换 cancellation signal 不改变执行判定;result settlement 和 session、agent 或插件释放分别撤销单次 call 与整个 lifecycle。
19
- 若模型误发 live schema 可证明的顶层 native call,stream canonicalizer 会把这个 out-of-surface 调用规范为与模型本应生成的同一 member `run_code`;未知、畸形或不一致输入原样透传。派生 cell 不携带 provenance、纠错提示或其他模型可见标记;普通参数保留原始 JSON,含 own `__proto__` 的参数使用安全字面量,超过 TypeScript parser 安全深度的参数才使用浅层 `JSON.parse` 表达式。该历史修改只修复无效 direct call。已声明的 `run_code` 与 `edit_run_code` 原样通过,因此 session history、UI 与下一轮 model context 保留合法调用的实际 tool name 和参数。
18
+ DSH 为一个 agent composition 固定选择 `native`、`ptc` 或 `both`。首次 assembly 的非空 `tools:ptc-only` section text 证明 `ptc`,同名空 section 证明 `both`;`tools:code-only` 仅在 `tools:ptc-only` 缺失时作为兼容别名,两者同时存在时以 `tools:ptc-only` 为准。只有缺少 owner signal 时才能从 tool shape 推断。后续 assembly 不重新解释插件自己的 `tools.presentAs('both')` 效果,而是从 agent composition owner 派生 presentation。`ptc` 独占 direct-tool collapse、顶层 native-call normalization 和 native dispatch rejection;`both` 保留 native direct tools。`ptc` 与 `both` 都适配 PTC `run_code` schema、program SDK 和 session runtime contexts,只有 `ptc` 安装并直接执行 `edit_run_code`。model stream 将完整 call 的 session 与 call ID 绑定到由 composition mode 派生的 request policy,dispatch wrapper 替换 cancellation signal 不改变执行判定;result settlement 和 session、agent 或插件释放分别撤销单次 call 与整个 lifecycle。
19
+ 若模型误发 live schema 可证明的顶层 native call,stream canonicalizer 会把这个 out-of-surface 调用规范为与模型本应生成的同一 member `run_code`;未知、畸形或不一致输入原样透传。派生 cell 不携带 provenance、纠错提示或其他模型可见标记;普通参数保留原始 JSON,含 own `__proto__` 的参数使用安全字面量,超过 TypeScript parser 安全深度的参数才使用浅层 `JSON.parse` 表达式。该规范化只修复无效 direct call。已声明的 `run_code` 与 `edit_run_code` 原样通过,因此 session history、UI 与下一轮 model context 保留合法调用的实际 tool name 和参数。
20
20
 
21
21
  `edit_run_code` 对其持久化 `tool/call` 事件出现时当前 open turn 最近的可编辑 cell 生效,无论该 cell 被拒绝、运行失败,还是成功执行但结果需要微调。执行通过 call event sequence 读取该快照,后续 settlement 或 handler 调度不改变目标;派生 cell 则按外层 `tool/result` event sequence 进入恢复历史,使 cold replay 保持 live kernel 的实际结算顺序。
22
22
  调查 tool 不擦除目标;edit 发起后的其他 settlement 不追溯改变其目标;一次成功 edit 生成的新 cell 成为下一次 edit 的目标。调用方必须且只能提交 `edits` 或
@@ -40,20 +40,21 @@ return 或打印需要展示的值。失败恢复先按状态分类:解析或
40
40
  `run_code` 并复用已有 binding,完整重写只保留给结构性改动或超出编辑预算的修正。能力和命令执行依赖当前 request
41
41
  与 execution world,模型必须先探查 live binding、实际 executable 和路径语义,不假设某个平台、shell 或 package runner。
42
42
 
43
- live `PTC-C001` 若精确落在源码 EOF,独立的 bounded analyzer 只把追加单个 `}`、`)`、`]` 的三个源码分别送入该 cell 提交时的同一 preparation context。仅当唯一候选通过、无 binding collision,现有 exact editor 能从不超过固定预算的唯一尾部物化同一源码,并且 runtime 已取得该 rejected `run_code` 的持久 call sequence 时,诊断才输出一行可直接调用的 `edit_run_code({ edits: [...], expected_target_call_seq })`。edit transport 把该 sequence 与其 call event 捕获的目标比较,目标已变化时不编辑也不派生执行。这项 validated repair 优先于长度阈值,但只证明语法/preflight 接受;插件不自动 dispatch,也不改写原始 `run_code`。非 EOF、多候选、多 token、超预算、缺少目标身份或后续 preparation 拒绝均回到原有 length-adaptive help。
43
+ live `PTC-C001` 若精确落在源码 EOF,独立的 bounded analyzer 只把追加单个 `}`、`)`、`]` 的三个源码分别送入该 cell 提交时的同一 preparation context。仅当唯一候选通过、无 binding collision,现有 exact editor 能从不超过固定预算的唯一尾部物化同一源码,并且 runtime 已取得该 rejected `run_code` 的持久 call sequence 时,诊断才输出一行可直接调用的 `edit_run_code({ edits: [...], expected_target_call_seq })`。edit transport 把该 sequence 与其 call event 捕获的目标比较,目标已变化时不编辑也不派生执行。这项 validated repair 优先于长度阈值,但只证明语法/preflight 接受;插件不自动 dispatch,也不改写原始 `run_code`。非 EOF、多候选、多 token、超预算、缺少目标身份或后续 preparation 拒绝均使用 length-adaptive help。
44
44
 
45
45
  稳定指引保留这些跨任务不变量和失败恢复的优先动作。长 cell 失败的短提示直接作为当前 `PTC-X001` 的结构化 `help` 输出,避免一次性建议触发完整 runtime-context 快照;重复绑定失败和当前 execution world 中由诊断确认的 executable、shell 或 path 错误仍由当前 session log 派生为 `tools:ptc-plus-tip/<trigger>/<ordinal>` runtime context,分别提示能力探查或重新确认环境。投影只接受 DSH system-prompt owner 的规范快照,并按命名 section 的有效状态变化重建提示;聚合快照重复同一 section 不增加次数,正文不参与身份判断。
46
46
  相同提示受 `tipCooldownMessages` 间隔约束,连续未解决时才升级为详细版本,成功 cell 会重置未解决计数;提示不会改变
47
47
  system sections、tool schema 或 tool order,也不假设 Windows、WSL、POSIX、shell 或 package runner。
48
48
 
49
- 当前策略位于独立的 `internal/recovery-tips.js` local provider,核心只消费其有界的 named context。另一个固定名称 `tools:ptc-plus-cordis-recovery` 只在新 agent 或 Cordis 重新启用代际观察到历史 Cordis transcript 时出现,将 replay value 限定为历史数据,并保持到 session log 出现新的成功 `cordis_inspect*` settlement;它不受疲劳阈值控制、不重放调用,也不推断 live process 是否实际丢失。未来外部决策插件的接入必须等待稳定的 facts/decision contract;在此之前不猜测跨插件 API,local provider 继续作为 fallback。
49
+ 当前策略位于独立的 `internal/recovery-tips.js` local provider,核心只消费其有界的 named context。另一个固定名称 `tools:ptc-plus-cordis-recovery` 只在新 agent 或 Cordis 重新启用代际观察到历史 Cordis transcript 时出现,将 replay value 限定为历史数据,并保持到 session log 出现新的成功 `cordis_inspect*` settlement;它不受疲劳阈值控制、不重放调用,也不推断 live process 是否实际丢失。外部决策插件必须提供稳定的 facts/decision contract 才能接入;缺少该契约时,核心不猜测跨插件 API,并继续使用 local provider。
50
50
 
51
51
  ## 设置与启用开关
52
52
 
53
- Host half 通过 DSH 公共 settings 服务注册 `ptc-plus` 命名空间,优先使用 provider-owned `installSection()`,并在旧 Host 上适配根模块的 `installSettingsSection()`;两条路径共享同一 fallback、watch、rollback 与 disposal 语义。字段清单、默认值与校验来自 `internal/config-spec.js`。
54
- Client half 通过 `settings.plugin.item` 卡片呈现全部配置。`enabled` 是 kill switch:关闭时只保留 settings 注册和设置卡片,
55
- 不注册 runtime、hook、tool surface 或 system prompt section;设置卡片显示“已启用/已停用”,当前 `ptc` 与旧 Host `code` preset 会话在头部单独显示 `PTC Plus` 指示器。稳定指引不包含 UI 品牌名。
56
- 所有字段都由 settings watch 即时交给各自 owner,且不替换已有 session-bound binding。每个已提交 cell 固定其提交时的配置代际,timer、worker 消息、program binding bridge、结果校验和诊断共同消费该快照;重配置更新随后提交 cell 的默认值。`maxOldGenerationSizeMb` 在活动 worker 存在时因 Node 的创建期限制而拒绝并回滚。settings 服务缺失时 Host 回退到 composition config,原有行为不变。
53
+ Host half 通过 DSH 公共 settings 服务注册 `ptc-plus` 命名空间,优先使用 provider-owned `installSection()`,并兼容根模块的 `installSettingsSection()`;两条路径共享同一 fallback、watch、rollback 与 disposal 语义。字段清单、默认值与校验来自 `internal/config-spec.js`。
54
+ Client half 通过 `settings.plugin.item` 卡片呈现全部配置。`enabled` 是 kill switch:关闭时只保留 settings 注册和设置卡片;`enhancedToolView` 是默认开启的正文展示开关,关闭时只交还 DSH 原生 `run_code`/`edit_run_code` generic row,
55
+ 不注册 runtime、hook、tool surface 或 system prompt section;`ptc` 与兼容 `code` preset 会话在头部单独显示 `PTC Plus` 指示器。设置卡片显示“已启用/已停用”,稳定指引不包含 UI 品牌名。
56
+ `autoDescribeRunCode` 默认开启且只控制请求绑定的本地执行策略。开启时,缺少外层 `run_code.description` 的调用使用派生参数通过本地 DSH 校验,备用摘要仅进入 presentation metadata;关闭时由 DSH 校验原始参数。两种状态的模型可见 tool schema、tool order 和 system sections 保持字节稳定并包含 required `description`;原始调用参数、cell 与嵌套 native 参数保持不变。
57
+ 所有字段都由 settings watch 即时交给各自 owner,且不替换已有 session-bound binding。每个已提交 cell 固定其提交时的配置代际,timer、worker 消息、program binding bridge、结果校验和诊断共同消费该快照;重配置更新随后提交 cell 的默认值。`maxOldGenerationSizeMb` 在活动 worker 存在时因 Node 的创建期限制而拒绝并回滚。settings 服务缺失时 Host 回退到 composition config,并保持相同的运行时语义。
57
58
 
58
59
  `cordisToolsEnabled` 默认关闭且即时生效。Host 只在可见 `run_code` 的 agent scope 中挂载官方 `@deepseek-ai/dsh-tool-cordis`,并通过公共 `agentPresets` service 定位 shipped `cordis` preset,再用维护中的 Skill filesystem plugin 把其 companion Skill 目录发布到同一 scope。两个 child fiber、tool guidance 与 `cordis-plugin-development` Skill 是一个 mount;首轮 request 等待完整发布和 scoped Skill load 验证,关闭、agent/runtime 释放或任一激活失败时逆序卸载。工具名、数量、schema 和 guidance 直接来自 official tool fiber,Skill 内容直接来自 shipped preset,Host 均不复制。官方插件同时向 process-global `cordisInspect` 注册 Host provider;owner 将 manifest 相同的 per-agent 注册合并为引用计数 lease,查询委托给当前仍存活的官方 registration,最后一份 lease 释放后才注销 provider。manifest 不一致时启用失败。该开关不切换 preset,也不改变 code-only direct-tool projection。
59
60
 
@@ -86,9 +87,9 @@ CodeRuntime request 已携带的 owner-provided program namespace 会被原样
86
87
 
87
88
  ## REPL 生命周期
88
89
 
89
- 每个顶层 `run_code` 是同一 session kernel 的下一格。顶层 binding 跨 cell 保留;默认宽松模式允许一个完整 declarator 全部替换已有变量,严格模式拒绝重声明,关闭 `autoSplitRedeclarations` 时混合新旧名称的解构也在执行前拒绝。cell 始终作为 async function body 求值,支持 block scope、top-level `await` 和普通控制流 return;return lowering 通过顶层 `this` receiver 访问每个 cell 独立生成并在 settlement 清理的 worker-global signal property,因此 lexical `globalThis` 与 `with` object environment 都不能重定向控制流。worker 启动时验证该 receiver 指向 evaluator 的 global context。跨 cell 重声明是 REPL 便利策略,由 `internal/repl-convenience.js` 独立实现;它不是 cell 语言或原生 JavaScript 语义的组成部分。
90
+ 每个顶层 `run_code` 是同一 session kernel 的下一格。顶层 binding 跨 cell 保留;默认开启的变量策略允许一个完整 declarator 全部替换已有变量,关闭时拒绝变量重声明,关闭 `autoSplitRedeclarations` 时混合新旧名称的解构也在执行前拒绝。独立的 `looseTopLevelFunctionClassRedeclarations` 默认关闭;开启后,重复的 named function/class 声明只在已有 binding 经源码 provenance 证明可写时,于声明位置 lowering 为赋值。函数使用匿名 function expression,使递归读取可替换的 outer binding;class 使用 named class expression,保留内部 self-reference,且 class 求值失败不会覆盖旧值。该 lowering 不保留函数声明 hoisting;不可写 const/import、保留名称与来源不明目标在执行前拒绝。cell 始终作为 async function body 求值,支持 block scope、top-level `await` 和普通控制流 return;return lowering 通过顶层 `this` receiver 访问每个 cell 独立生成并在 settlement 清理的 worker-global signal property,因此 lexical `globalThis` 与 `with` object environment 都不能重定向控制流。worker 启动时验证该 receiver 指向 evaluator 的 global context。跨 cell 重声明是 REPL 便利策略,由 `internal/repl-convenience.js` 独立实现;它不是 cell 语言或原生 JavaScript 语义的组成部分。
90
91
 
91
- 包裹前的 AST 重写适配模块语法(cell 是 async function body,模块声明在函数体内非法):全部默认开启且可用 config 关闭(`autoRewriteImports` / `autoStripExports` / `autoSplitRedeclarations`)。原始 Babel Program 在任何 lowering 或 preload 前完成 scope validation;同一 cell 的 value import 与 lexical、hoisted `var`、function 或其他 import 重名会按模块 binding 规则拒绝,block shadow 和 type-only 名称仍合法。静态 `import` 声明产生有序 module preload record;worker 通过短生命周期 ESM adapter 让 Node 从 session cwd 完成解析和 export linkage,再在 cell 编译前通过生成的临时 global identifier 把 namespace 交给另一个持久 lexical identifier。两者都避开当前源码、持久 binding、program binding 与活跃 private namespace,生成的 prologue 直接引用临时 identifier,因此 lexical `globalThis` shadowing 不参与 host capture;临时 global 在 cell settlement 清理。session 未记录 cwd 时,解析基准回退到 worker process cwd;目标模块的依赖保持各自的自然 parent。scope-aware alias lowering 保留 named/default binding 的 live read、只读、shadowing 和跨 cell 重放语义。value import alias 活跃时,原始 Program 上的 direct `eval` 与 `WithStatement` 在任何 preload record 生效前拒绝,因为生成的 namespace property read 无法复现它们的动态 lexical resolution;没有 value alias 的非严格 `with`、type-only import 和局部 shadow 的 `eval` 仍使用普通 JavaScript 语义。顶层 `export` 修饰符剥离(声明保留、`export default` 转为 `__default` 绑定、re-export 转为预加载的副作用模块、type-only export 擦除);REPL 便利层在 `autoSplitRedeclarations` 开启时按插件定义的兼容规则 lowering 混合新旧名称的顶层解构,关闭时保持原文走既有解析失败路径。无法精确适配的形态在执行前拒绝。改写来源以 `meta.dshPtcPlusRewrites` 平行记录(journal schema 封闭)。成功改写不改变下一步决策,因此不产生 runtime context;改写后失败或缺少有效 journal 时,`tools:ptc-plus-rewrite-info` 才在下一轮说明恢复边界。
92
+ 包裹前的 AST 重写适配模块语法(cell 是 async function body,模块声明在函数体内非法):全部默认开启且可用 config 关闭(`autoRewriteImports` / `autoStripExports` / `autoSplitRedeclarations`)。原始 Babel Program 在任何 lowering 或 preload 前完成 scope validation;同一 cell 的 value import 与 lexical、hoisted `var`、function 或其他 import 重名会按模块 binding 规则拒绝,block shadow 和 type-only 名称仍合法。静态 `import` 声明产生有序 module preload record;worker 通过短生命周期 ESM adapter 让 Node 从 session cwd 完成解析和 export linkage,再在 cell 编译前通过生成的临时 global identifier 把 namespace 交给另一个持久 lexical identifier。两者都避开当前源码、持久 binding、program binding 与活跃 private namespace,生成的 prologue 直接引用临时 identifier,因此 lexical `globalThis` shadowing 不参与 host capture;临时 global 在 cell settlement 清理。session 未记录 cwd 时,解析基准回退到 worker process cwd;目标模块的依赖保持各自的自然 parent。scope-aware alias lowering 保留 named/default binding 的 live read、只读、shadowing 和跨 cell 重放语义。`export default` 的固定公开名 `__default` 通过同一 alias lowering 指向持久 private slot;只有既有 binding 明确带有 synthetic-default provenance 时才可复用该 slot,普通 binding、import alias 或 reserved name 冲突都在执行前拒绝。后续合法 default export 在原声明位置更新该 slot,不依赖变量重声明开关,并且只有 worker 在更新后发出 commit signal,BindingCatalog 才更新公开定义来源。function/class replacement 的 commit identity 进一步绑定到每个 declaration occurrence,同一 cell 的同名后续声明若因 return 或异常未执行,不会覆盖 inventory 中最后实际提交的定义。提前 return 或 class 求值失败因此不会发布未执行的 alias 或 replacement 更新。value alias 活跃时,原始 Program 上的 direct `eval` 与 `WithStatement` 在任何 preload record 生效前拒绝,因为生成的 namespace property read 无法复现它们的动态 lexical resolution;没有 value alias 的非严格 `with`、type-only import 和局部 shadow 的 `eval` 仍使用普通 JavaScript 语义。顶层 `export` 修饰符剥离(声明保留、`export default` 转为 `__default` 绑定、re-export 转预加载的副作用模块、type-only 擦除);REPL 便利层在 `autoSplitRedeclarations` 开启时按插件定义的兼容规则 lowering 混合新旧名称的顶层解构,关闭时保持原文走既有解析失败路径。无法精确适配的形态在执行前拒绝。改写来源以 `meta.dshPtcPlusRewrites` 平行记录(journal schema 封闭)。成功改写不改变下一步决策,因此不产生 runtime context;改写后失败或缺少有效 journal 时,`tools:ptc-plus-rewrite-info` 才在下一轮说明恢复边界。
92
93
 
93
94
  可确定的计算和 recorded-value capability call 可以推进 durable head。未进入 journal 的 Node/OS 能力、环境输入、时钟、随机数和 timer 进入 sticky `volatile`;live worker 继续可用,cold replay 回到最后 durable frontier。`require(...)` 与动态导入按同一白名单分类:白名单内置模块(assert/buffer/querystring/string_decoder/stream/util/url/zlib)保持 durable,其余 volatile,`worker_threads`/`cluster` 等内核控制模块在执行前以 PTC-C002 拒绝。durable 只描述冷恢复对已结算历史的重放能力;失败 cell 仍可能已经修改 binding 或产生外部 effect,不能由 durability 推导出安全重试。worker thread 是生命周期隔离,不是安全沙箱。
94
95
 
@@ -100,9 +101,13 @@ CodeRuntime request 已携带的 owner-provided program namespace 会被原样
100
101
 
101
102
  ```ts
102
103
  {
103
- version: 3,
104
- bindingMode: "loose" | "strict",
104
+ version: 4,
105
+ bindingPolicy: {
106
+ variableRedeclarations: boolean,
107
+ functionClassRedeclarations: boolean
108
+ },
105
109
  rewritePolicy: { autoRewriteImports, autoStripExports, autoSplitRedeclarations },
110
+ moduleSemantics: { defaultExportBinding: "legacy-variable" | "live-readonly" },
106
111
  status: "durable" | "volatile" | "discarded" | "noop",
107
112
  calls: CallTranscript[],
108
113
  operations: StateOperation[],
@@ -113,11 +118,15 @@ CodeRuntime request 已携带的 owner-provided program namespace 会被原样
113
118
  }
114
119
  ```
115
120
 
116
- `calls` 只保存 global、member、PTC Value Graph 编码的 args/result 或 error,以及 settlement 序号。cold replay 校验调用名称、参数、数量和提交顺序,并按 recorded settlement order 释放 recorded result;不会重新 dispatch program binding 或重做外部 effect。该规则同样适用于 native tools、owner-provided namespace 和 `code.run`,不按名称分支。Cordis 的进程内对象不会因此被宣称已恢复;presentation 可以从已验证 transcript 派生重新检查要求,但不能改变 replay。若基础设施终止时仍有未结算 binding,heap 回滚到 durable frontier,discarded journal 以最先观察到的 `global.member` 保留 possible-effect boundary。effect、completeness 和 source metadata 属于 capability explorer,不伪装成 journal 字段。
121
+ `bindingPolicy` 与 `rewritePolicy` 固化可配置的 cell 语言行为;`moduleSemantics` 固化不可配置的 lowering 代际。当前 journal 写入 `live-readonly` default-export alias,v1-v3 迁移为 `legacy-variable`,因此 cold replay 不从当前实现或源码猜测历史 `__default` 的可写性。`calls` 只保存 global、member、PTC Value Graph 编码的 args/result 或 error,以及 settlement 序号。cold replay 校验调用名称、参数、数量和提交顺序,并按 recorded settlement order 释放 recorded result;不会重新 dispatch program binding 或重做外部 effect。该规则同样适用于 native tools、owner-provided namespace 和 `code.run`,不按名称分支。Cordis 的进程内对象不会因此被宣称已恢复;presentation 可以从已验证 transcript 派生重新检查要求,但不能改变 replay。若基础设施终止时仍有未结算 binding,heap 回滚到 durable frontier,discarded journal 以最先观察到的 `global.member` 保留 possible-effect boundary。effect、completeness 和 source metadata 属于 capability explorer,不伪装成 journal 字段。
117
122
 
118
123
  journal 通过 `run_code.output.presentationMeta` 附着到最终 result,再由 `tools/result` 做两阶段确认。缺失、损坏或被替换的 journal 形成 unknown/volatile 边界;未进入 runtime 的 call 由后续 `confirms` 以对应 `tool/call.seq` 证明为 no-op。volatile 源码保留在原 session log,但不参与 cold replay。
119
124
 
120
- durable replay 无法验证某个 node 时,当前已结算 `tool/result` 的私有 `meta.dshPtcPlusRecoveryBoundaries` 记录失败 call 与其 parent frontier,不修改冻结的历史 result。boundary 在进入排序前必须完成 schema 与 event-sequence validation;损坏 metadata 使恢复 fail closed,不能从 fold 中静默消失。折叠器剪除该 node 及依赖后代、重算可重建 checkpoints;kernel 重置 worker 并逐级验证更早 frontier,随后执行当前 live cell。新 durable node 的 parent 始终是 worker 实际拥有的 frontier。旧版自定义 boundary event 不由运行时生成,迁移工具必须在 DSH restore 前显式转换它。
125
+ 恢复从当前请求对应的 head 沿 parent 关系向更早状态收缩,而不是越过损坏记录继续假定原 heap 存在。候选 frontier 同时受两种证据约束:append-only session log 中的 journal 证明状态可重建;生成当前调用的 DSH ordered surface/derived request history 证明模型可以知道其精确 binding provenance。raw event 未删除不等于模型仍可见,Client 的 `dshPtcPlusBindings` inventory 也只是 UI presentation。只裁剪 tool result、仍把携带源码的 assistant call 留在模型 surface 中时不必删除对应状态;assistant provenance 被遮蔽且没有显式、有界、模型可见的结构化状态投影时,live worker 与 cold replay 都必须收缩。插件不从自然语言 compaction summary 推断 binding。
126
+
127
+ durable replay 无法验证某个 node 时,当前已结算 `tool/result` 的私有 `meta.dshPtcPlusRecoveryBoundaries` 记录失败 call 与其 parent frontier,不修改冻结的历史 result。boundary 在进入排序前必须完成 schema 与 event-sequence validation;损坏 metadata 不能从 fold 中静默消失,也不能证明任何旧状态。折叠器剪除结构不可证明或模型不可知的 node、binding 与依赖后代,重算可重建 checkpoints;只有 ancestry、独立性和模型可知 provenance 都有 owner evidence 的部分可以保留。现有 journal 没有完整的逐 binding 依赖图,因此不能证明细粒度剪枝时按 cell 与受影响后缀收缩。已经存在的 live kernel 在执行下一 cell 前检查 surface replacement generation;若模型可见历史已经收缩,先重置并物化对齐后的 frontier。
128
+
129
+ kernel 重置 worker 并逐级验证更早 frontier;最大非空 frontier 仍不可证明时使用空 REPL。这个最差情况会丢失全部旧 binding,但历史 PTC Plus metadata 损坏本身不得阻止当前合法 cell 执行。当前 cell 在实际重建的 frontier 上建立新分支,并通过一次 `PTC-R002` 说明哪些历史状态未恢复;收缩不重新派发或撤销历史 program binding effect,也不把未知状态说成已恢复。DSH 对当前请求的 validation、policy、authority、approval 与 cancellation 错误不属于该可用性降级。新 durable node 的 parent 始终是 worker 实际拥有的 frontier。自定义 boundary event 不由运行时生成,迁移工具必须在 DSH restore 前显式转换它。
121
130
 
122
131
  诊断由封闭结构确定性渲染,包括语法、preflight、绑定冲突、输出、运行异常和恢复边界。普通成功与首次进入 volatile 不投影 warning/note;恢复分类保留在 journal 和 `repl.state(list)` 中。
123
132
 
package/docs/client-ui.md CHANGED
@@ -1,33 +1,51 @@
1
1
  # Client UI
2
2
 
3
- PTC Plus 现在提供 DSH Web/Desktop 的**插件设置卡片**,位置在 Settings → Plugin configuration。
3
+ PTC Plus 在 DSH Web/Desktop 的 Settings → Plugin configuration 中提供**插件设置卡片**。
4
4
 
5
5
  ## 设置命名空间
6
6
 
7
7
  Host half 通过 DSH 公共 `settings` 服务注册命名空间 `ptc-plus`。字段清单、默认值和校验来自 `internal/config-spec.js`,
8
- 由 `index.js` 的 `Config` schema 与设置注册共用;client half 构建时把同一份字段清单打进 bundle,不在 UI 中复制默认值。
8
+ 由 `index.js` 的 `Config` schema 与设置注册共用;client half 构建时把同一份字段清单打进 bundle,不在 UI 中复制默认值。卡片按“常用与兼容性”“可选能力”“高级行为”“资源限制”分区显示,用户需要主动决策的选项位于前面。
9
9
 
10
10
  ## 可用设置
11
11
 
12
12
  | 分组 | 字段 | 说明 |
13
13
  | --- | --- | --- |
14
14
  | 开关 | `enabled` | 关闭后不注册 `run_code`/`edit_run_code`、不修改 system prompt、不创建 session runtime;只保留设置 UI。 |
15
- | Cordis | `cordisToolsEnabled` | 默认关闭;开启后为 PTC agent 加入官方 Cordis 工具、指引与精确的 `cordis-plugin-development` companion Skill,不发布同目录 sibling。 |
16
- | 计算 | `computeMs` / `maxWallMs` | 单 cell CPU 与墙钟预算。 |
17
- | 输出 | `maxOutputBytes` / `maxValueNodes` / `maxValueEdges` / `maxValueArrayLength` / `maxValueBigIntDigits` | Value Graph 与输出字节预算。 |
15
+ | 展示 | `enhancedToolView` | 默认开启;关闭后注销 PTC Plus 的两个 keyed tool view,恢复 DSH 原生 generic row。 |
16
+ | 常用与兼容性 | `autoDescribeRunCode` / `canonicalizeToolCalls` | 默认开启;允许缺少外层摘要的 `run_code` 执行,并修复 schema 可唯一确认的顶层 native 工具误调用。 |
17
+ | 可选能力 | `cordisToolsEnabled` | 默认关闭;开启后为 PTC agent 加入官方 Cordis 工具、指引与精确的 `cordis-plugin-development` companion Skill,不发布同目录 sibling。 |
18
+ | 高级行为 | `looseTopLevelRedeclarations` / `looseTopLevelFunctionClassRedeclarations` / `autoRewriteImports` / `autoStripExports` / `autoSplitRedeclarations` / `durableReplay` / `tipsEnabled` | function/class 重声明默认关闭,其余默认开启;分别控制变量重声明、可写 function/class binding 的声明位置替换、模块语法适配、worker 重启后的状态恢复与失败提示。 |
19
+ | 计算 | `computeMs` / `maxWallMs` | 单 cell CPU 与总耗时预算。 |
18
20
  | Worker | `maxOldGenerationSizeMb` / `maxNestedRunCodeDepth` | kernel worker 内存与嵌套执行深度。 |
19
- | 行为 | `canonicalizeToolCalls` / `looseTopLevelRedeclarations` / `durableReplay` | REPL 与 transport 策略。 |
20
- | 模块 | `autoRewriteImports` / `autoStripExports` / `autoSplitRedeclarations` | AST 重写开关。 |
21
- | 提示 | `tipsEnabled` / `tipCooldownMessages` / `tipEscalationFailures` | 恢复提示策略。 |
21
+ | 输出 | `maxOutputBytes` | 单 cell 日志与返回结果的合计字节上限。 |
22
+ | 返回值 | `maxValueNodes` / `maxValueEdges` / `maxValueArrayLength` / `maxValueBigIntDigits` | Value Graph 与大数组、BigInt 的返回上限。 |
23
+ | 提示阈值 | `tipCooldownMessages` / `tipEscalationFailures` | 同类提示间隔与详细提示阈值。 |
22
24
 
23
- ## enabled 开关与启用标识
25
+ ## enabled 开关与正文功能标记
24
26
 
25
27
  所有字段都是实时设置:Host 在 settings watch 中安装、卸载或重配置 runtime。已提交的 cell 固定使用提交时的配置快照,运行中的更新从随后提交的 cell 开始使用;这不会替换 session-bound REPL 或已有 binding。`maxOldGenerationSizeMb` 在活动 worker 存在时因 Node 的创建期限制而拒绝并回滚,其他可重配置字段照常交给 owner。关闭后唯一的宿主副作用是注册 settings 命名空间,
26
28
  保证设置卡片仍然可读;此时只有 `enabled` 可写,其他控件被禁用。运行时重配置失败时回滚到上一次已应用值。
27
29
 
28
30
  `cordisToolsEnabled` 默认关闭并即时生效。它不切换 preset,而是把官方 Cordis 工具、owner guidance 与 `cordis-plugin-development` Skill 作为一个 agent-scoped mount 加入或移出 PTC agent;顶层仍为 `run_code` / `edit_run_code`,普通 agent 不继承。Host 缺少 official preset、Skill/Cordis service 或任一 contribution 加载失败时,启用会完整回滚。完整运行要求见 [运行时参考](runtime-reference.md)。
29
31
 
30
- 设置卡片的全部文案(字段名称与说明、折叠头部副标题、启用状态、展开/收起名称、同步与错误提示)都注册到 DSH client locale 的 `settings.ptcPlus` 命名空间,随当前界面语言在中文与 English 之间切换;字段名称与说明的两种语言文本同样来自 `internal/config-spec.js`(`label`/`labelEn`、`description`/`descriptionEn`,某一字段的说明要么两种语言都有,要么都没有),卡片 chrome 文案由 client half 拥有。稳定 REPL 指引不承载 UI 品牌名。启用且会话选择当前 `ptc` 或旧 Host `code` preset 时,`conversation.session.header.actions` 额外显示 `PTC Plus` 指示器,其 tooltip 也跟随界面语言;当前 session projection 与旧顶层字段冲突时以前者为准。关闭时不注入任何 PTC 指引或工具 surface。
32
+ `enhancedToolView` 默认开启并即时生效。开启时 PTC Plus 通过公共 keyed tool-view surface 为 `run_code` 与 `edit_run_code` 提供增强行,并在可用时使用 DSH 公共 `DisclosureRow`/`CodeBlock` primitive,缺少某项 capability 时使用插件自有的等价降级;关闭时立即注销这两个 keyed view,由 DSH 原生 generic row 负责布局、状态、代码高亮和输入/输出卡片。该开关只影响 Client 展示,不改变工具、prompt、runtime 或 session 语义。
33
+
34
+ `autoDescribeRunCode` 的设置名称是“允许执行缺少摘要的 run_code”,默认开启并即时生效。缺少外层 `run_code.description` 时,调用使用派生参数通过本地 DSH 校验,备用摘要仅进入 presentation metadata;关闭时由 DSH 校验原始参数。两种状态的模型请求保持字节稳定并包含 required `description`,原始调用参数、已有摘要、cell 源码和嵌套 native 工具参数保持不变。
35
+
36
+ 设置卡片、正文 tool view 与 REPL 可复用 binding 卡片的全部文案都注册到 DSH client locale 的 `settings.ptcPlus` 命名空间,随当前界面语言在中文与 English 之间切换;字段名称与说明的两种语言文本同样来自 `internal/config-spec.js`(`label`/`labelEn`、`description`/`descriptionEn`,某一字段的说明要么两种语言都有,要么都没有),展示 chrome 文案由 client half 拥有。稳定 REPL 指引不承载 UI 品牌名。启用且会话选择 `ptc` 或兼容的 `code` preset 时,`conversation.session.header.actions` 以稳定 id `ptc-plus-active` 显示简洁的 `PTC Plus` 标识;当前 session projection 与兼容顶层字段冲突时以前者为准。关闭时不注入任何 PTC 指引或工具 surface。
37
+
38
+ 绿色 `PTC Plus` 活动标识支持鼠标悬浮、键盘聚焦和点击。卡片通过浏览器 Popover top layer 脱离普通 stacking context,按触发器和当前视口的可用空间在上方或下方定位,因此不受会话侧栏覆盖;长列表只在卡片内部滚动。变量、函数、类和导入使用可区分的类型色彩。卡片读取公共 `sessionProjections` 携带的 `projectionValues.ptcPlusRepl`,展示当前 runtime owner 已证明可供该 agent 后续 cell 复用的 binding 名称、类别、单行定义预览以及原始行列;点击整行即可在卡片内展开有界 TypeScript 声明源码,并以抽屉式过渡动画显示。声明来源由 AST preparation 从已经提交的 cell 文本确定,BindingCatalog 在替换和 replay 时更新,不读取值、不执行代码、不触发 getter。Host 在每个结算结果的并行私有 `meta.dshPtcPlusBindings` envelope 中写入 runtime generation 和完整的 value-independent inventory;最多携带 128 项、名称最长 128 字符、每段声明源码至多 1024 个 UTF-16 code unit、声明源码合计至多 16384 个 code unit,同时保留精确有效总数和省略数。volatile cell 的 live binding 只在其精确 provenance 仍属于模型可见 state frontier 时继续显示;restore、discarded settlement 或 model-visible surface contraction 会收缩 worker,在后续 cell 重新物化有效 binding surface 前显示不可确认。
39
+
40
+ 清单按最近声明或重声明优先的栈顺序排列,同一 cell 内靠后的声明位于靠前声明之上。BindingCatalog 保留这个顺序,presentation snapshot 和 projection 原样传递,不根据名称重新排序。
41
+
42
+ 投影通过 DSH 正式的 `tool/call.data.callId` / `tool/result.data.message.source.callId` 身份配对 `run_code` 与 `edit_run_code`,不依赖可选 `sourceEventSeqs`。它只接受当前 runtime generation 的 envelope,并在 `session/end-seed` 清除旧 lifecycle 的 live 证明;缺少、损坏、其他 generation 的 metadata、无关 tool result 与不影响 binding provenance 的 result-only replacement 都不能覆盖仍有效的最后已证明清单。若 surface replacement 遮蔽了声明 provenance,runtime 与 projection 共用的 model-visible frontier 判定会让相关 inventory 失效。缺少有效 metadata、projection service 或兼容 registration contract 时只显示不可确认,不主动读取历史、不迁移日志,也不影响继续会话。来源跳转要求 Host 提供打开 tool call 或 source location 的公共 capability;缺少该 capability 时,卡片只提供内联检查,不访问私有 store 或宿主 DOM。
43
+
44
+ `enhancedToolView` 开启且插件启用时,Client 通过公共 keyed `tool.call.toolview` 注册 `run_code` 与 `edit_run_code`;任一开关关闭时实时 dispose 两个注册,让 owner 恢复 DSH 原生 fallback。这个正文 slot 只依赖 Host 提供的 key、`toolName`、`sessionId`、`useSessions`、运行中/已结算 `block` 与 `inspect` 公共契约。增强行保留 Code / Code edit 标题、可展开的原始源码(`arguments` 与 `argsRaw` 任一公共形态均可读)、结果、可见且可访问的运行/失败/中断状态和 Inspect 入口;在提供时优先使用公共 `DisclosureRow` 与 `CodeBlock`,缺少 `DisclosureRow` 时回退到插件自有行,缺少 `CodeBlock` 时回退为纯文本源码。`DisclosureRow` 路径把运行/失败/中断状态、摘要和已证明的功能标记放进同一行始终可见的单行预览;完成态由标题与摘要表达,运行/失败/中断状态由文字 chip 承担并保持可访问。预览间距由插件自有类对称提供,并按单行内容布局,不依赖 Host primitive 的行内几何;源码、结果和 Inspect 只出现在展开主体。结果内容与原生 ioCard 采用相同的灰色圆角背景(`--dsw-alias-markdown-code-block` 加 12px 圆角与边框),失败态使用错误色文字;代码块的语言/复制栏保持原生 CodeBlock 的常亮可见;`检查调用` 入口采用原生 inspectButton 同款胶囊样式,默认 opacity 0,仅在卡片 `:hover` 或 `:focus-visible` 时淡入(opacity .1s),不触发任何布局位移。窄屏下回退路径的摘要允许换行。
45
+
46
+ 功能标记不是计数,也不把“插件已启用”冒充成一次功能收益。Client 只在完整 v1-v4 journal 和对应附属 metadata 能证明时显示:自动改写 import(附模块名)、自动剥离 export、自动拆分混合重声明(附已有 binding)、带完整 target/derived-run/non-noop/可选恢复边界关系且与调用目标前置条件一致的安全编辑执行、成功的 `code.run`、由 `PTC-R002` 的 `warning/recover/rolled-back` 语义元组证明本次确实发生的持久重放,以及 `repl.state` 操作。Client 对 v4 同时封闭校验 `bindingPolicy`、`rewritePolicy` 与 `moduleSemantics`;任一缺失或畸形都不产生功能标记。进程保留或 discarded 状态不作为正文功能标记。普通变量或 function/class 重声明、顶层 native 调用是否来自 canonicalizer、`cordis_*` 成员是否由官方可选 Cordis mount 提供,以及是否实际选择了 prompt 恢复提示没有独立的 Client-only 事实,因此不根据可复制的代码形状、工具名前缀或设置默认值推测;恢复边界本身不展示。
47
+
48
+ 这条路径不检查或改写宿主 DOM,不调用 Host、不主动加载历史。binding 卡片只消费随结果持久化的 value-independent presentation metadata,其中源码只是已提交 cell 的有界声明片段,不是运行时值;它不改变 canonical result、tool schema、system prompt、runtime context、journal schema 或迁移器,也不向模型公开状态。未知 journal 版本、未知 metadata 字段和损坏 metadata 只会让对应功能标记或 binding 列表缺席,不会隐藏源码或结果;无法脱离 session call identity 解析非空 v1 `confirms` 时同样只省略标记,不触发历史读取或迁移。因此 UI 是否安装、能否渲染以及用户是否悬浮、聚焦或展开正文行,都不会改变模型请求或会话继续语义,也不能作为在模型 surface 收缩后继续保留 runtime binding 的证据。
31
49
 
32
50
  live 配置若因宿主能力缺失或 runtime 安装/重配置失败,会先回滚所有已创建或更新的 owner,再把持久设置回写为上一次已应用值;回滚写入失败时 Host 记录 activation diagnostic,避免静默把配置显示成不存在的 runtime。
33
51
 
@@ -44,4 +62,4 @@ npm run build
44
62
  ## 回退
45
63
 
46
64
  如果 DSH settings service 不可用,Host half 不会因缺少设置服务而加载失败:设置卡片不可用时直接回退为 composition `config`,
47
- `enabled` 默认开启,原有运行时行为保持不变。
65
+ `enabled` 默认开启,运行时按 composition config 工作。