dsh-ptc-plus 0.4.10 → 0.5.0
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.
- package/README.en.md +60 -130
- package/README.md +60 -130
- package/assets/dsh-ptc-plus-banner-en.webp +0 -0
- package/assets/dsh-ptc-plus-banner-zh.webp +0 -0
- package/assets/ptc-plus-repl-workspace-en.png +0 -0
- package/assets/ptc-plus-repl-workspace-zh.png +0 -0
- package/assets/ptc-plus-settings-en.png +0 -0
- package/assets/ptc-plus-settings-zh.png +0 -0
- package/client.cjs +42336 -0
- package/cordis.patch.yml +1 -0
- package/docs/README.md +6 -2
- package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +31 -198
- package/docs/adr/0008-define-agent-operating-contract.md +5 -1
- package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -2
- package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
- package/docs/adr/0017-track-the-latest-dsh-public-surface.md +2 -2
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +1 -1
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +1 -1
- package/docs/adr/0022-stage-npm-releases-with-oidc.md +4 -2
- package/docs/adr/0023-global-user-bindings.md +2 -0
- package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +2 -0
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +25 -23
- package/docs/adr/0031-close-semantic-obligations-by-owner.md +1 -1
- package/docs/adr/0034-keep-program-values-separate-from-output-projections.md +29 -0
- package/docs/adr/0035-run-coverage-only-before-a-new-release.md +27 -0
- package/docs/adr/0036-separate-focused-development-from-delivery-verification.md +29 -0
- package/docs/adr/0037-keep-tool-hover-previews-passive.md +52 -0
- package/docs/adr/0038-freeze-session-capability-plans.md +71 -0
- package/docs/adr/0039-verify-documentation-without-runtime-suites.md +29 -0
- package/docs/adr/0040-separate-capability-configurations-from-session-observations.md +39 -0
- package/docs/adr/0041-record-cell-execution-directories.md +65 -0
- package/docs/adr/0042-persist-provider-availability-and-handback.md +41 -0
- package/docs/adr/0043-compose-capability-and-context-policies.md +37 -0
- package/docs/adr/README.md +6 -0
- package/docs/architecture.md +23 -25
- package/docs/capability-planning-components.md +93 -0
- package/docs/capability-projection.md +7 -0
- package/docs/client-ui.md +70 -23
- package/docs/client-visual-contract.md +26 -9
- package/docs/context-management.md +22 -0
- package/docs/context-optimization-plan.md +353 -0
- package/docs/context-public-interface-coverage.md +25 -0
- package/docs/durability-design.md +20 -15
- package/docs/evaluation.md +41 -6
- package/docs/evaluations/command-goat-deepseek-v4.1-flash-20261006.md +126 -0
- package/docs/evaluations/command-goat-deepseek-v4.1-flash.md +281 -0
- package/docs/installation.md +31 -10
- package/docs/program-data-plane.md +5 -3
- package/docs/publishing.md +5 -5
- package/docs/release-coverage.md +73 -0
- package/docs/release-notes.md +1 -1
- package/docs/review-procedure.md +56 -0
- package/docs/runtime-reference.md +48 -16
- package/docs/semantic-validation.md +14 -2
- package/docs/stateful-binding-updates-proposal.md +2 -2
- package/docs/user-bindings.md +1 -1
- package/docs/value-wire.md +11 -1
- package/docs/verification-optimization-work.md +26 -15
- package/docs/verification.md +30 -88
- package/index.js +123 -20
- package/internal/binding-toggle-receipt.js +24 -0
- package/internal/capability-command-receipt.js +29 -0
- package/internal/capability-environment.js +1247 -0
- package/internal/capability-plan.js +285 -0
- package/internal/capability-policy.js +96 -0
- package/internal/capability-statistics-store.js +180 -0
- package/internal/capability-statistics.js +210 -0
- package/internal/config-spec.js +79 -9
- package/internal/direct-surface-owner.js +27 -29
- package/internal/dynamic-environment-runtime.js +9 -9
- package/internal/edit-transport-owner.js +180 -139
- package/internal/execution-provider-owner.js +15 -12
- package/internal/execution-seam-compat.js +8 -8
- package/internal/kernel-worker.js +104 -32
- package/internal/native-root-dynamic.js +12 -13
- package/internal/program-bindings.js +190 -0
- package/internal/recovery-tips.js +3 -2
- package/internal/rejected-cell-editor.js +8 -8
- package/internal/repl-observation-interest.js +11 -3
- package/internal/rpc-contract.js +1 -0
- package/internal/runtime-bridge-owner.js +272 -34
- package/internal/runtime-config.js +58 -0
- package/internal/runtime-contexts.js +74 -5
- package/internal/runtime-intrinsics.js +1 -0
- package/internal/runtime-messages.js +41 -0
- package/internal/sdk-projection.js +10 -82
- package/internal/session-cell-executor.js +42 -19
- package/internal/session-events.js +34 -0
- package/internal/session-journal-recovery.js +234 -11
- package/internal/session-journal-schema.js +6 -3
- package/internal/session-journal.js +31 -2
- package/internal/session-log-view.js +1 -1
- package/internal/session-runtime.js +119 -15
- package/internal/settings-compat.js +12 -5
- package/internal/stateful-module-compiler.js +3 -2
- package/internal/stateful-module-runtime.js +7 -3
- package/internal/stateful-root-runtime.js +9 -4
- package/internal/user-binding-authoring.js +2 -7
- package/internal/user-bindings-owner.js +41 -2
- package/internal/validated-parse-repair.js +1 -1
- package/internal/value-wire.js +68 -21
- package/internal/worker-cwd-virtualization.js +20 -18
- package/internal/worker-realm-surfaces.js +3 -3
- package/package.json +47 -29
- package/src/client-authoring-view.js +67 -30
- package/src/client-binding-review.js +2 -1
- package/src/client-binding-toggle.js +18 -0
- package/src/client-capabilities-view.js +1341 -0
- package/src/client-capability-sort.js +29 -0
- package/src/client-copy.js +871 -8
- package/src/client-feature-gates.js +3 -0
- package/src/client-indicator-view.js +4 -6
- package/src/client-primitives.js +33 -2
- package/src/client-repl-view.js +20 -13
- package/src/client-settings-view.js +411 -41
- package/src/client-styles.js +108 -15
- package/src/client-tool-hover.js +185 -0
- package/src/client-tool-view.js +37 -8
- package/src/client-transport.js +44 -9
- package/src/client-workbench.js +6 -7
- package/src/client.js +76 -22
- package/client.js +0 -32479
|
@@ -2,19 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
`npm run coverage:focus` provides a bounded diagnostic loop for
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
`npm run coverage:focus` provides a bounded diagnostic loop for coverage
|
|
6
|
+
failures while preparing a new version for release. It checks selected
|
|
7
|
+
gate-owned sources with the smallest test set that can distinguish a correction
|
|
8
|
+
before the full release coverage gate runs again.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
Coverage is not part of ordinary development, review, commit preparation,
|
|
11
|
+
`npm run verify`, or `npm run check`; coverage-specific suites and instrumented cases are also excluded from those daily entries. The focused command does not replace the
|
|
12
|
+
full release coverage gate and does not write a verified-tree proof.
|
|
13
|
+
[Release coverage](release-coverage.md) owns coverage command syntax, thresholds,
|
|
14
|
+
generated evidence, and the final gates; [ADR 0035](adr/0035-run-coverage-only-before-a-new-release.md)
|
|
15
|
+
owns coverage scheduling.
|
|
12
16
|
|
|
13
17
|
## Contract
|
|
14
18
|
|
|
15
19
|
- Every selected source and test must exist inside the checkout.
|
|
16
20
|
- Selected sources must belong to the full coverage gate, and selected tests
|
|
17
|
-
must be
|
|
21
|
+
must be `.test.js` files under `test/`.
|
|
18
22
|
- Selected but unexecuted sources remain in the report as uncovered.
|
|
19
23
|
- The report applies the full gate's 100% line/function and 95% branch
|
|
20
24
|
thresholds.
|
|
@@ -52,15 +56,22 @@ preload does not independently reinterpret a caller's entry.
|
|
|
52
56
|
|
|
53
57
|
## Workflow
|
|
54
58
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
During ordinary development, use the selection-required behavioral entries in
|
|
60
|
+
[verification](verification.md) without coverage. Executable changes alone do not
|
|
61
|
+
trigger a complete gate; [ADR 0036](adr/0036-separate-focused-development-from-delivery-verification.md)
|
|
62
|
+
owns the development versus delivery boundary.
|
|
63
|
+
During release preparation, use focused coverage after the full release coverage
|
|
64
|
+
gate identifies a narrow deficit. Select every changed source whose threshold
|
|
65
|
+
matters and the tests that exercise its distinct paths. A passing focused run
|
|
66
|
+
establishes only that selected relation; rerun the full coverage gate before
|
|
67
|
+
packing and staging.
|
|
59
68
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
When complete validation is requested for delivery, timing or an identified
|
|
70
|
+
cross-suite risk, run one final command on the stable candidate: `npm run verify`
|
|
71
|
+
with unresolved findings, otherwise `npm run check -- --full`. Terminal findings alone do
|
|
72
|
+
not trigger another gate. A still-matching proof is reused for delivery; focused
|
|
73
|
+
results never replace it. Do not run `verify` immediately before `check` on the
|
|
74
|
+
same tree because the full check already invokes it.
|
|
64
75
|
|
|
65
76
|
Temporary concurrency overrides change scheduling only. Use one only when
|
|
66
77
|
current evidence identifies scheduling contention, and do not carry it into a
|
package/docs/verification.md
CHANGED
|
@@ -1,114 +1,56 @@
|
|
|
1
1
|
# Deterministic verification
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Development and independent review run focused tests for the changed semantic owner and its consumers. An executable edit does not by itself require the complete suite. Complete behavioral validation runs on a stable executable candidate for delivery proof, an explicit full-validation/timing request, or a cross-suite risk that focused evidence cannot settle. [ADR 0036](adr/0036-separate-focused-development-from-delivery-verification.md) owns this scheduling decision.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
npm run review:plan:new
|
|
13
|
-
npm run review:plan -- --base <base-commit>
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
The plan uses `dsh-review-plan/v1`, and the ignored root `REVIEW_PLAN.json` is its only active source. All five standard obligations from the template must remain present. Each standard or change-specific obligation is assigned to a lane or excluded with a concrete reason. A lane declares semantic scope, exact file paths or directory prefixes, upstream lanes, owners, consumers, and counterexamples. `.` selects the complete checkout. Paths only determine content inputs and changed-path coverage; they do not replace the semantic declarations. A lane fingerprint covers that declaration, its assigned obligations, selected raw Git entry identities, persistent invalidation generation, and upstream fingerprints. Dependencies must form a DAG. The plan command refuses missing standard obligations, placeholders, unknown obligations, dependency cycles, a non-ancestor base, and changed paths outside every lane.
|
|
17
|
-
|
|
18
|
-
The plan command prints one `review-lane <id> <head> <fingerprint>` line per lane. Lanes may be reviewed concurrently. Record each clean external report with the identity captured before that review:
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
npm run review:lane -- \
|
|
22
|
-
--lane <id> \
|
|
23
|
-
--evidence <report-path> \
|
|
24
|
-
--expect-head <head> \
|
|
25
|
-
--expect-fingerprint <lane-fingerprint>
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
The report's last non-empty line must be `VERDICT: NO FINDINGS`. A readable findings or incomplete report retires the named lane and every declared dependent from the recorded plan before checking whether the current candidate drifted; restoring an earlier tree therefore cannot revive evidence that the report rejected. An unreadable or missing report path is a command-input error and does not retire valid evidence. Unrelated lanes remain effective. A clean report retains the captured `HEAD` as provenance; an unrelated concurrent edit or `HEAD` advance does not prevent recording. Direct input, upstream fingerprint, scope, obligation, owner, consumer, counterexample, or dependency changes do. Put findings in `REVIEW_FINDINGS.md`, fix them, and rerun only the incomplete lanes shown by `npm run review:status`.
|
|
29
|
-
|
|
30
|
-
Review-state mutations and authorization decisions use one atomic lock under checkout-local Git metadata. Plan recording, clean or negative lane recording, finalization, clearing, and pre-commit therefore have one observable order. A negative report queued behind an older clean recorder advances the reported lane's persistent invalidation `{epoch, revision}`, including while that lane is temporarily absent from the current plan, before retiring its current dependent closure. Advancement increments the revision normally and rotates to a new random epoch with revision zero at `Number.MAX_SAFE_INTEGER`, so a valid tuple never becomes unretirable. A temporarily absent lane must already have a retained tuple; unknown or mistyped lane IDs are rejected rather than creating state. Every active lane must have an explicit tuple. The tuple participates in the lane fingerprint and propagates through dependency fingerprints, so an older clean report cannot be submitted after re-addition. It survives normal `review:clear` and temporary lane removal; only a review captured after retirement can authorize the repaired fingerprint. Missing, partial, or malformed generation state blocks status and authorization. Explicit clear preserves valid generations but removes damaged generation state; plan recording then creates a new random epoch, so old reports still cannot match. The lock records a PID and unique release token and bounds acquisition, but never reclaims another owner automatically. If a command crashed, confirm that no review-state command is active, obtain the exact checkout-local path with `git rev-parse --git-path review-findings/state.lock`, remove only that directory, and retry. `review:status` remains a diagnostic snapshot and does not grant authorization. Finalization and pre-commit also re-read the active plan, candidate, proof, lane records, and evidence before they persist or return a decision, so edits made outside the review commands fail closed.
|
|
5
|
+
| Scope | Entry and required selection |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| Backend | `npm run test:backend:focus -- --file test/<owner>.test.js --test-name-pattern="<case>"` |
|
|
8
|
+
| Client | `npm run test:client:focus -- "<test-name pattern>"` |
|
|
9
|
+
| Layout | `npm run test:client:layout:focus -- --case <id>` |
|
|
10
|
+
| Fallback/native fixture | `npm run test:client:fallback:focus -- --case <id>` |
|
|
31
11
|
|
|
32
|
-
|
|
12
|
+
Backend and browser entries accept `--list`, and selections can repeat. Omitting selection from a focused execution fails before starting the suite. `test:semantics` selects six files and is not a quick diagnostic; choose its relevant file/case through `test:backend:focus`. An independent layout invocation exports fresh HTML through `test:client:fixtures`, which selects only the two tests that own all 26 states. Its browser assertions remain unchanged. The complete visual gate still runs the full Client suite once and reuses its exports. Fixtures supplied through `--fixtures-dir` must come from the current candidate.
|
|
33
13
|
|
|
34
|
-
|
|
14
|
+
`npm run check` classifies the complete source candidate against HEAD, runs the appropriate checks below, checks source/ledger stability, and records the verified tree and verification scope for the commit hook. Run `check` alone when the ledger is absent or resolved; while findings remain unresolved, use `verify`. Do not run complete component suites around the same final command: that duplicates its work. Focused evidence is not a source-bound commit proof. Inspect a failed gate's stage/case, reproduce it with a focused entry, repair it, and rerun the final command only after the candidate is stable.
|
|
35
15
|
|
|
36
|
-
|
|
37
|
-
npm run check
|
|
38
|
-
npm run review:finalize
|
|
39
|
-
git diff --check HEAD --
|
|
40
|
-
```
|
|
16
|
+
Stage runners print inventory, concurrency and elapsed times. Start a permitted long gate with a short tool yield and retain its process/session ID. Tell the user the command, start time and expected range before waiting. Keep verbose diagnostics in ignored logs and wait on the same process in intervals no longer than 60 seconds. Report completed stages, failures or changed timing expectations. Inspect process activity and stage output before treating a slow run as stalled; elapsed time alone is insufficient. Stop only the owned process on cancellation, and never report an interrupted run as verified. Do not start another copy while waiting.
|
|
41
17
|
|
|
42
|
-
|
|
18
|
+
## Change classification
|
|
43
19
|
|
|
44
|
-
`npm run verify`
|
|
20
|
+
`npm run verify:docs` checks a documentation-only candidate without starting builds, runtime tests, browsers or models. It rejects a mixed candidate instead of expanding into the full gate. `npm run check` chooses this same path automatically when every change from HEAD is a regular non-executable Markdown file in `docs/`, `AGENTS.md`, `CONTEXT.md`, `README.md` or `README.en.md`. Additions and deletions count, as do unstaged and untracked source files. Markdown fixtures, templates, symlinks, executable modes, configuration, tests and implementation changes select full verification. An explicit complete-validation request uses `npm run check -- --full` for a source proof or `npm run verify` for behavioral validation alone, regardless of changed paths. An unchanged tree also uses the full path when a new check is explicitly requested; reuse an existing matching proof instead.
|
|
45
21
|
|
|
46
|
-
|
|
22
|
+
The documentation path checks diff whitespace, local link target existence and literal `npm run` commands in changed documents and documents linking to them. It does not execute code blocks, visit remote links, check heading anchors or establish semantic accuracy. Independent review checks anchors, contracts, examples and instruction behavior against their owners. A deleted document is checked through its remaining referrers. Source, `HEAD` and findings stay unchanged through verification; after that stability check, a resolved local findings ledger may be archived. The proof records `documentation` or `full`. `check` does not enforce the review plan, lane evidence or prospective-index identity: `review:finalize` enforces the first two after the proof, and pre-commit enforces the last against the staged tree. [ADR 0039](adr/0039-verify-documentation-without-runtime-suites.md) owns this distinction.
|
|
47
23
|
|
|
48
|
-
|
|
24
|
+
For test-description or comment changes, inspect the diff and verify that test registration, selectors, executable fixtures and assertions retain their meaning. Select changed cases when execution can distinguish a possible behavior change; cosmetic wording does not justify unrelated runtime or browser matrices. Runtime or verification-owner changes use their focused regressions. These development observations do not replace the candidate classification used for a later commit.
|
|
49
25
|
|
|
50
|
-
|
|
26
|
+
## Reading test output
|
|
51
27
|
|
|
52
|
-
|
|
53
|
-
npm run coverage:focus -- \
|
|
54
|
-
--source internal/source-position-map.js \
|
|
55
|
-
--test test/source-position-map.test.js \
|
|
56
|
-
--test test/compiler-memory-facts.test.js \
|
|
57
|
-
--test test/stateful-source-line-mapping.test.js
|
|
58
|
-
```
|
|
28
|
+
Start with the selected inventory and a case pattern. Keep lengthy complete-run output in an ignored log, report the command, exit status, elapsed time and failures, then read the failing case's local context. Do not replay successful-case inventories or entire prior logs into progress messages and handoffs. Preserve exact failure text and its log location; summaries are not substitutes for evidence when diagnosing a failure. Test descriptions follow [the semantic naming guidance](semantic-validation.md#test-descriptions).
|
|
59
29
|
|
|
60
|
-
|
|
30
|
+
## Findings ledger
|
|
61
31
|
|
|
62
|
-
|
|
32
|
+
Before creating or updating findings, read [Findings ledger](review-procedure.md#findings-ledger).
|
|
63
33
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
Set `DSH_PTC_TEST_CONCURRENCY` to a positive integer to override the backend process count. Use `1` for serial diagnosis or when CPU/memory contention affects timing-sensitive tests. This changes scheduling, not runtime compute budgets, test selection, or coverage thresholds. For example:
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
DSH_PTC_TEST_CONCURRENCY=1 npm run verify
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
```powershell
|
|
73
|
-
$env:DSH_PTC_TEST_CONCURRENCY = '1'
|
|
74
|
-
npm run verify
|
|
75
|
-
Remove-Item Env:DSH_PTC_TEST_CONCURRENCY
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Coverage runs additionally set `DSH_PTC_TEST_WORKER_COVERAGE=1` and load `scripts/instrument-worker-coverage.mjs` through Node's `--import`; that test-only seam adds the coverage variable to the helper's inner-Worker launch environment so real kernel, console, and candidate worker branches are measured. Production never sets the marker and continues to project `NODE_V8_COVERAGE` out of the user Worker environment. Because some transport tests install module mocks before importing the real transport, the runner executes two backend groups into the same coverage directory: the mock-dependent files run without the preload, and the remaining files run with it. Each group writes its own `run-*-mock.tap` or `run-*-instrumented.tap` diagnostic, both managed by the same retention and cleanup lifecycle. Test arguments such as `--test-name-pattern` are placed before the selected file list so Node applies them to test selection rather than treating them as file inputs.
|
|
79
|
-
|
|
80
|
-
`npm run coverage` and `npm run coverage:focus` print their current stage and total elapsed time. Each invocation collects worker coverage in its own temporary directory under `coverage/`, removed after c8 finishes reporting, so simultaneous runs cannot erase or combine each other's evidence. The default report is printed to the terminal. A separate `coverage/run-*.tap` log retains Node's test diagnostics, including process exit codes and signals that the concise terminal reporter can omit. An explicit Node test reporter option replaces these default reporters. Source changes during verification still invalidate a `check` proof; separate coverage directories do not make concurrent source edits safe.
|
|
81
|
-
|
|
82
|
-
Windows Node 24 and 26 reproductions and stacks identify a cppgc finalizer (`node::contextify::ContextifyScript`, or `ContextifyContext` when coverage was enabled) running after the environment that owned its private realm was gone, which wrote through a dangling `node::Realm` during isolate teardown. The reproduction ended the whole DSH process when an in-host session worker was terminated; macOS runners also observed whole-file aborts under coverage, but the available evidence does not establish that those aborts came from the same Windows stack or that the same cause applies across platforms. The session kernel now runs in a plugin-owned helper process (`internal/kernel-child.js`); `IsolatedWorker` sends a cooperative shutdown and, if the helper does not leave within its bound, kills only that helper. DSH is no longer the worker-teardown target on any supported host, so a wedged kernel is contained as a helper failure rather than a host-native crash. The former file-level retry wrapper is gone, and any failing CI smoke or local exhaustive test fails its run. This boundary removes the plugin's in-host kill exposure; it neither repairs the upstream Node path nor proves the macOS abort shared the Windows cause.
|
|
83
|
-
|
|
84
|
-
Native oracle workers and external Windows command fixtures explicitly disable inherited `NODE_V8_COVERAGE`; they execute no code included by the plugin coverage gate. Subprocesses testing plugin implementations still collect coverage. PowerShell semantic-version observations share one invocation, and Windows registry fixtures read the three effective registry values with one real npm configuration query. Each retains its original inputs and assertions.
|
|
34
|
+
## Incremental independent review
|
|
85
35
|
|
|
86
|
-
|
|
36
|
+
Before independent review or a review-state command, read [Incremental independent review](review-procedure.md#incremental-independent-review). Keep reviewer reports outside the checkout or in an ignored path so they cannot become candidate source after the plan is frozen. Both documentation and executable changes retain source-bound proof, independent review and prospective-index checks.
|
|
87
37
|
|
|
88
|
-
|
|
38
|
+
## Complete behavioral validation
|
|
89
39
|
|
|
90
|
-
|
|
40
|
+
The full local and CI behavioral gates run `test:client:visual`: one Client suite exports fresh renderer fixtures, followed by deterministic browser layout and visual assertions. Install its locked browser first with `npx playwright install --with-deps chromium`; missing browsers fail verification. The [Client visual contract](client-visual-contract.md) owns the bounded control/state inventory, reference palettes, fault counterexamples and official-Host evidence limits. Use the selection-required browser entries for focused diagnostics; `test:client:visual` is the complete gate and is not a development feedback loop.
|
|
91
41
|
|
|
92
|
-
|
|
42
|
+
`npm run verify` and the full branch of `npm run check` validate the ledger, compare generated bundles, check syntax, run Client tests, and run all ordinary backend tests through `npm run test:backend` without coverage instrumentation. The ordinary runner selects the sorted root `test/*.test.js` inventory. Coverage-only runner/report tests and collection probes live under `test/coverage/` and are excluded from daily execution. The release coverage runner includes both inventories and sets `DSH_PTC_TEST_COVERAGE_SUITE=1` for mixed instrumented cases; the ordinary runner clears that marker. Both runners share the concurrency calculation. Backend test files run in separate Node processes. Compiler boundary protocols are divided into disjoint storage, Promise and reflection files so the bounded file pool can execute them alongside the parameter/namespace/error cases without sharing their native or PTC realms. The default process count is half the available CPUs, rounded down and bounded between one and eight; each process can also own runtime Workers. Tests within each file retain their existing ordering. The ordinary runner enables module mocks without preloading coverage hooks and removes inherited coverage and temporary bytecode settings from its test-process environment.
|
|
93
43
|
|
|
94
|
-
|
|
44
|
+
After the shared ledger, baseline, semantic, build and syntax prerequisites pass, `scripts/deterministic-test-groups.mjs` runs the complete Client visual and backend groups concurrently on machines with at least 12 available CPUs. Smaller machines retain serial phases; CI keeps its focused runtime group. Both concurrent groups must settle, and any nonzero exit, signal or startup failure rejects verification. Each browser pool retains its own CPU bound and process isolation.
|
|
95
45
|
|
|
96
|
-
|
|
46
|
+
The full branch of the local pre-commit `npm run check` runs the complete ordinary backend suite. Both verification scopes produce a review-bound proof. Coverage runs only before a new version is released, as specified by [ADR 0035](adr/0035-run-coverage-only-before-a-new-release.md). CI starts from a clean lockfile install and runs `verify:ci` on every supported Node and operating-system entry: frozen-baseline validation, semantic ownership, generated builds, Client tests, and focused runtime contracts covering compiler startup, helper-process lifecycle, plugin takeover, native-call transport, and durable argument diagnostics. Ubuntu Node 24 additionally exercises the packed plugin against both frozen official `latest` and `next` DSH graphs. Normal CI therefore proves portable installation and runtime integration without running coverage-specific suites, collecting coverage, or running the exhaustive semantic suite on hosted runners.
|
|
97
47
|
|
|
98
|
-
|
|
48
|
+
`host:baseline:verify`, which is part of deterministic verification, is offline. It requires the installed root DSH `next` cohort to match `package-lock.json` and validates the tracked `compat/latest` declaration and lock without installing it. CI separately runs `test:host-contract`: the root graph exercises `next`, while a temporary consumer created from `compat/latest/package-lock.json` exercises `latest` against the packed plugin. The tracked `compat/current` graph additionally captures the newest published official release, even when its distribution is on another npm channel; its isolated packed consumer is part of `test:host-contract`. Registry and official release lookup and lock regeneration belong only to `host:baseline:update` and the scheduled baseline-refresh workflow. That workflow publishes a changed frozen candidate as a pull request and explicitly dispatches this CI workflow once because its `GITHUB_TOKEN`-authored events cannot start another run; ordinary gates never install a floating channel.
|
|
99
49
|
|
|
100
|
-
|
|
50
|
+
## Release coverage
|
|
101
51
|
|
|
102
|
-
|
|
52
|
+
Coverage runs only when preparing a new version. Read [Release coverage](release-coverage.md) before its commands; ordinary development, review and commit preparation do not run it.
|
|
103
53
|
|
|
104
|
-
|
|
105
|
-
temporary object per unit of work. The namespace walk reads a node's own keys
|
|
106
|
-
once and indexes node arrays directly instead of materializing an entry pair
|
|
107
|
-
per property; the source-map builder validates and writes its four offsets
|
|
108
|
-
positionally, and replacement runs reach that builder through a direct emitter
|
|
109
|
-
instead of a generator and a per-run segment object. Rehomed compiler messages
|
|
110
|
-
write their owned data descriptor directly rather than copying a generic
|
|
111
|
-
descriptor first. Every one of these paths produced identical output before and
|
|
112
|
-
after the change; only the immediately discarded allocations are gone.
|
|
54
|
+
Browser scripts accept `--list` without launching a browser or generating fixtures, repeated exact `--case <id>` selections, and `--concurrency <positive integer>`. Default invocations execute every case. Focused layout measurements use `measurements-focused.json`, separate from complete layout evidence. Ordinary backend diagnostics accept repeated `--file <test/path.test.js>` and `--list`; unknown files and coverage-only paths are rejected before execution, while supported Node name/skip patterns, reporters and destinations, timeout, concurrency, only and force-exit options remain available. Additional positional paths, unknown options and coverage flags are rejected by the same parser for both CLI and library execution. For example, `npm run test:backend -- --file test/semantic-boundary-promises.test.js --test-name-pattern="promise catch"` runs only that responsibility through the ordinary environment owner. Focused runs cannot establish the complete verification proof.
|
|
113
55
|
|
|
114
|
-
For
|
|
56
|
+
For development diagnostics, use the selection-required entries above. `test:semantics`, `test:client:visual` and the default backend entry are complete inventories for their respective scopes, so they are not short feedback loops. Focused execution does not replace a requested delivery gate. Model-backed evaluation remains separate and opt-in; see [Evaluation](evaluation.md).
|
package/index.js
CHANGED
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
import Schema from '@deepseek-ai/schemastery'
|
|
10
10
|
import { randomUUID } from 'node:crypto'
|
|
11
11
|
import { createDirectSurfaceOwner } from './internal/direct-surface-owner.js'
|
|
12
|
-
import {
|
|
12
|
+
import { CapabilityStatisticsStore } from './internal/capability-statistics-store.js'
|
|
13
|
+
import { createCapabilityEnvironment } from './internal/capability-environment.js'
|
|
14
|
+
import { capabilityCommandReceipt } from './internal/capability-command-receipt.js'
|
|
15
|
+
import { toolSdkSchemas } from './internal/program-bindings.js'
|
|
16
|
+
import { createRuntimeMessageOwner, handBackRuntimeSessions } from './internal/runtime-contexts.js'
|
|
13
17
|
import { createSessionLogOwner } from './internal/session-log-view.js'
|
|
14
18
|
import { PTC_DELIVERY_CONTEXT } from './internal/runtime-messages.js'
|
|
15
19
|
import { createCordisToolsOwner } from './internal/cordis-tools-owner.js'
|
|
@@ -27,6 +31,7 @@ import { createReplMemoryProjection } from './internal/repl-memory-projection.js
|
|
|
27
31
|
import { createUserBindingDraftProjection } from './internal/user-binding-draft-projection.js'
|
|
28
32
|
import { createUserBindingsOwner } from './internal/user-bindings-owner.js'
|
|
29
33
|
import { createHostRpc } from './internal/host-rpc.js'
|
|
34
|
+
import { RPC_CONTRACTS } from './internal/rpc-contract.js'
|
|
30
35
|
import { createReplObservationInterest } from './internal/repl-observation-interest.js'
|
|
31
36
|
import { guidancePolicies } from './internal/binding-update-policy.js'
|
|
32
37
|
import * as dshSettings from '@deepseek-ai/dsh-settings'
|
|
@@ -77,8 +82,8 @@ function replGuidance({ bindingPolicy, rewritesEnabled, languageSemantics, durab
|
|
|
77
82
|
: 'Mixed new/existing top-level destructuring remains unsupported; separate the declaration from the assignment.'
|
|
78
83
|
const cellConventions = languageSemantics === 'legacy-v1'
|
|
79
84
|
? `Cells are async function bodies; ${moduleSyntax} Use dynamic import or require explicitly when static module syntax is unsupported. ${variableRedeclaration} ${functionClassRedeclaration} ${splitSyntax}`
|
|
80
|
-
|
|
81
|
-
? 'Declarations and assignments update the same logical binding within its scope, including const, functions, classes and imports. A bare declaration preserves an existing value. A declaration publishes its bindings after its whole pattern initializes;
|
|
85
|
+
: `Cells support TypeScript, top-level await, return, static import and top-level export. ${languageSemantics === 'stateful-v1'
|
|
86
|
+
? 'Declarations and assignments update the same logical binding within its scope, including const, functions, classes and imports. A bare declaration preserves an existing value. A declaration publishes its bindings after its whole pattern initializes; assignments preserve partial writes. Imports follow their module until locally overwritten; importing again changes that same binding source. Existing closures observe later updates, while saved values retain their identity.'
|
|
82
87
|
: 'Redeclaration protection is enabled: existing session names cannot be redeclared, and newly created const and import bindings reject assignment. Other declarations follow native scope rules. Previously writable bindings stay writable.'}`
|
|
83
88
|
const recovery = durableReplay
|
|
84
89
|
? 'Direct Node/OS access remains live but is not replayed after a kernel restart.'
|
|
@@ -86,22 +91,22 @@ function replGuidance({ bindingPolicy, rewritesEnabled, languageSemantics, durab
|
|
|
86
91
|
const cordisRecovery = cordisToolsEnabled
|
|
87
92
|
? 'Keep large Cordis plugin source in a binding before the tool call so it can be reused after an error. A Cordis parse or validation failure can follow external effects; repeat the call only when its retry rules and current results establish that it is safe.'
|
|
88
93
|
: ''
|
|
89
|
-
return `\`run_code\` continues one persistent PTC REPL. Ordinary top-level bindings remain available to later cells, so reuse them instead of resending setup code. Choose the smallest cell that answers the request and return only the value the next step needs.
|
|
94
|
+
return `\`run_code\` continues one persistent PTC REPL. Use Node.js modules in the cell; use external processes when their environment is needed. Ordinary top-level bindings remain available to later cells, so reuse them instead of resending setup code. Choose the smallest cell that answers the request and return only the value the next step needs.
|
|
90
95
|
|
|
91
|
-
After an execution error, earlier statements may have taken effect. Inspect relevant values in a short cell
|
|
96
|
+
After an execution error, earlier statements may have taken effect. Inspect relevant values in a short cell, then continue from them; repeat external operations only when their retry rules and current results establish safety. \`edit_run_code\` edits and reruns a complete cell.
|
|
92
97
|
|
|
93
98
|
## Cell conventions
|
|
94
|
-
Use \`return\` for an explicit result and \`console\` for logs. Keep large inspection results in bindings or reduce them to targeted excerpts: \`tools.read\` is bounded inspection, not a lossless whole-file reader. ${cellConventions}
|
|
99
|
+
Use \`return\` for an explicit result and \`console\` for logs. Return supported values directly, including undefined, NaN, infinities, -0 and BigInt; JSON.stringify loses distinctions or rejects BigInt. Do not mix BigInt with Number arithmetic; use a BigInt literal or convert explicitly. Keep functions, Date, Map and other unsupported values in bindings; return needed data or type observations. Native \`tools.*\` arguments must satisfy their own JSON schemas; PTC value transport does not widen those contracts. Keep large inspection results in bindings or reduce them to targeted excerpts: \`tools.read\` is bounded inspection, not a lossless whole-file reader. Relative filesystem, path and default child-process operations use the current working directory at the time they run; a successful in-cell \`tools.working_directory({ cd })\` applies to the rest of that cell, while cell imports and \`import.meta\` keep the directory the cell started in, and \`process.chdir\` is unavailable. ${cellConventions}
|
|
95
100
|
|
|
96
101
|
## Available capabilities
|
|
97
|
-
Use \`capabilities.tree()\`, \`capabilities.find()\`, and \`capabilities.inspect()\` for unfamiliar program APIs
|
|
98
|
-
Program API references expire when their cell ends. Reusable helpers must read the current namespace when called, for example \`async function readNow(args) { return tools.read(args) }\`,
|
|
102
|
+
Use \`capabilities.tree()\`, \`capabilities.find()\`, and \`capabilities.inspect()\` for unfamiliar program APIs; Reserve \`code.run\` for isolated execution of source held as data.
|
|
103
|
+
Program API references expire when their cell ends. Reusable helpers must read the current namespace when called, for example \`async function readNow(args) { return tools.read(args) }\`, instead of retaining an earlier \`tools\` object or method. Completed-cell callbacks cannot call program APIs.
|
|
99
104
|
|
|
100
|
-
Native tool availability
|
|
105
|
+
Native tool availability depends on the current DSH profile and execution world; inspect executables, shells, and paths instead of assuming Windows, WSL, POSIX, or a particular shell. ${recovery}${cordisToolsEnabled ? ` ${cordisRecovery}` : ''}`
|
|
101
106
|
}
|
|
102
107
|
|
|
103
108
|
/** Register the session-bound REPL runtime. */
|
|
104
|
-
function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessionId, sessionLog) {
|
|
109
|
+
function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, toolSdkSchemasForAgent, sessionId, sessionLog, settingsForCommand) {
|
|
105
110
|
const presentationGeneration = randomUUID()
|
|
106
111
|
const replMemoryProjection = createReplMemoryProjection(presentationGeneration)
|
|
107
112
|
const userBindingDraftProjection = createUserBindingDraftProjection(presentationGeneration)
|
|
@@ -112,6 +117,7 @@ function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessi
|
|
|
112
117
|
let runtimeBridge
|
|
113
118
|
let editTransport
|
|
114
119
|
let directSurface
|
|
120
|
+
let capabilityEnvironment
|
|
115
121
|
let userBindings
|
|
116
122
|
let observationInterest
|
|
117
123
|
let ready
|
|
@@ -340,6 +346,9 @@ function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessi
|
|
|
340
346
|
maxOldGenerationSizeMb: activeConfig.maxOldGenerationSizeMb,
|
|
341
347
|
valueLimits: valueLimitsFromConfig(activeConfig),
|
|
342
348
|
})
|
|
349
|
+
capabilityEnvironment = createCapabilityEnvironment({ ctx, config: activeConfig,
|
|
350
|
+
toolSchemasForAgent: toolSdkSchemasForAgent, modelSchemasForAgent: toolSchemasForAgent,
|
|
351
|
+
store: new CapabilityStatisticsStore({ ctx }) })
|
|
343
352
|
runtimeBridge = createRuntimeBridgeOwner({
|
|
344
353
|
ctx,
|
|
345
354
|
seam,
|
|
@@ -350,15 +359,18 @@ function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessi
|
|
|
350
359
|
maxNestedRunCodeDepth: activeConfig.maxNestedRunCodeDepth,
|
|
351
360
|
presentationGeneration,
|
|
352
361
|
sessionId,
|
|
353
|
-
toolSchemasForAgent,
|
|
362
|
+
toolSchemasForAgent: toolSdkSchemasForAgent,
|
|
363
|
+
capabilityEnvironment,
|
|
354
364
|
userBindingDraftForAgent: agent => userBindings.draftCapabilityForAgent(agent),
|
|
355
365
|
bindingSubmissionForAgent: (agent, ensureLease, onAccepted) => userBindings.submissionForAgent(agent, ensureLease, onAccepted),
|
|
356
366
|
})
|
|
357
367
|
editTransport = createEditTransportOwner(ctx, {
|
|
358
|
-
readSessionLog: runtimeBridge.
|
|
368
|
+
readSessionLog: runtimeBridge.readNestedDispatchLog,
|
|
359
369
|
sessionLogView: sessionLog.project,
|
|
360
370
|
durableReplay: activeConfig.durableReplay,
|
|
361
371
|
executeTentative: runtimeBridge.executeTentative,
|
|
372
|
+
canScheduleEdit: runtimeBridge.canScheduleEdit,
|
|
373
|
+
scheduleEdit: runtimeBridge.scheduleEdit,
|
|
362
374
|
presentationGeneration,
|
|
363
375
|
sessionId,
|
|
364
376
|
toolSchemasForAgent,
|
|
@@ -370,6 +382,7 @@ function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessi
|
|
|
370
382
|
canonicalizeToolCalls: activeConfig.canonicalizeToolCalls,
|
|
371
383
|
sessionId,
|
|
372
384
|
toolSchemasForAgent,
|
|
385
|
+
capabilityEnvironment,
|
|
373
386
|
userBindingsForAgent: () => userBindings.snapshot(),
|
|
374
387
|
setAgentPresentation: (agent, presentation) => (
|
|
375
388
|
userBindings.setAgentPresentation(agent, presentation)
|
|
@@ -383,9 +396,89 @@ function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessi
|
|
|
383
396
|
return replGuidance(guidancePolicies(activeConfig))
|
|
384
397
|
},
|
|
385
398
|
}))
|
|
386
|
-
disposers.push(ctx.on('system-prompt/assemble', (assembly, context, next) =>
|
|
387
|
-
directSurface.assemble(assembly, context, next)
|
|
388
|
-
|
|
399
|
+
disposers.push(ctx.on('system-prompt/assemble', async (assembly, context, next) => {
|
|
400
|
+
const projected = await directSurface.assemble(assembly, context, next)
|
|
401
|
+
if (ctx.tools.get(RUN_CODE, context?.agent) !== undefined) runtimeBridge.prepareAgent(context?.agent)
|
|
402
|
+
return projected
|
|
403
|
+
}))
|
|
404
|
+
disposers.push(() => capabilityEnvironment.dispose())
|
|
405
|
+
disposers.push(ctx.on('agent/pre-step', (payload, next) => capabilityEnvironment.preStep(payload, next), { prepend: true }))
|
|
406
|
+
disposers.push(ctx.on('agent/created', ({ agent }) => {
|
|
407
|
+
if (ctx.tools.get(RUN_CODE, agent) !== undefined) {
|
|
408
|
+
runtimeBridge.prepareAgent(agent)
|
|
409
|
+
editTransport.ensureInstalled(agent)
|
|
410
|
+
}
|
|
411
|
+
}))
|
|
412
|
+
for (const agent of ctx.agents?.list?.() ?? []) {
|
|
413
|
+
if (ctx.tools.get(RUN_CODE, agent) !== undefined) {
|
|
414
|
+
runtimeBridge.prepareAgent(agent)
|
|
415
|
+
editTransport.ensureInstalled(agent)
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
disposers.push(ctx.on('agent/pre-step', (payload, next) => userBindings.preStep(payload, next)))
|
|
419
|
+
disposers.push(ctx.on('session/event', (session, event) => userBindings.committedEvent(session, event)))
|
|
420
|
+
disposers.push(ctx.on('tools/post-execute', (exec, result, next) => capabilityEnvironment.postExecute(exec, result, next), { prepend: true }))
|
|
421
|
+
disposers.push(ctx.on('tools/ptc-dispatch-log', (dispatch, next) => capabilityEnvironment.dispatchLog(dispatch, next), { prepend: true }))
|
|
422
|
+
disposers.push(ctx.on('agent/status', payload => { if (payload?.agent) capabilityEnvironment.status(payload.agent, payload.status) }))
|
|
423
|
+
disposers.push(ctx.on('agent/created', ({ agent }) => capabilityEnvironment.status(agent, agent.status)))
|
|
424
|
+
if (typeof ctx.inject === 'function') {
|
|
425
|
+
const capabilitiesRpc = ctx.inject(['ptcPlusRpc'], scope => {
|
|
426
|
+
scope.effect(() => scope.ptcPlusRpc.register(RPC_CONTRACTS.capabilities, async (operation, payload, signal) => {
|
|
427
|
+
signal.throwIfAborted()
|
|
428
|
+
if (operation === 'catalog') return { ok: true, value: await capabilityEnvironment.describeCatalog(signal) }
|
|
429
|
+
if (operation === 'targets') return { ok: true, value: capabilityEnvironment.describeTargets() }
|
|
430
|
+
if (operation === 'session-catalog') return { ok: true, value: await capabilityEnvironment.describeSessionCatalog(payload, signal) }
|
|
431
|
+
if (operation === 'allocation') return { ok: true, value: await capabilityEnvironment.observeAllocation(payload, signal) }
|
|
432
|
+
if (operation !== 'inspect') return { ok: false, error: { code: 'UNKNOWN_OPERATION', message: 'Unknown capability plan operation', details: null } }
|
|
433
|
+
return { ok: true, value: capabilityEnvironment.describeSessions() }
|
|
434
|
+
}), 'ptc-plus: capability plan observation')
|
|
435
|
+
})
|
|
436
|
+
if (typeof capabilitiesRpc === 'function') disposers.push(capabilitiesRpc)
|
|
437
|
+
else if (typeof capabilitiesRpc?.dispose === 'function') disposers.push(() => capabilitiesRpc.dispose())
|
|
438
|
+
const commands = ctx.inject(['commands'], scope => {
|
|
439
|
+
scope.effect(() => scope.commands.register({ name: 'capabilities',
|
|
440
|
+
description: 'Inspect, search or change session capabilities; global choices also save settings.',
|
|
441
|
+
input: { hint: 'refresh | list [all|skills|tools|mcp|mcp_tools] [query] | set session|global <id> [availability|disclosure] <choice>' },
|
|
442
|
+
handler: async ({ commandId, agent, rawInput, signal }) => {
|
|
443
|
+
const input = rawInput.trim()
|
|
444
|
+
const receipt = (state, error) => capabilityCommandReceipt(state, String(agent.session.id), error,
|
|
445
|
+
state === 'pending' && capabilityEnvironment.pendingChoiceMessage(agent)
|
|
446
|
+
? { commandId, rawInput, message: capabilityEnvironment.pendingChoiceMessage(agent) } : undefined)
|
|
447
|
+
const selection = /^set (session|global) (\S+) (?:(availability|disclosure) )?(full|on-demand|closed|automatic|inherit|enabled|host|adaptive)$/.exec(input)
|
|
448
|
+
if (selection) {
|
|
449
|
+
if (!capabilityEnvironment.canRefresh(agent)) return { kind: 'error', text: receipt('unavailable') }
|
|
450
|
+
const changed = await capabilityEnvironment.change(agent, {
|
|
451
|
+
scope: selection[1], id: decodeURIComponent(selection[2]), dimension: selection[3], selection: selection[4], commandId,
|
|
452
|
+
}, signal, settingsForCommand?.())
|
|
453
|
+
return { kind: changed.sessionApplied ? 'success' : 'error',
|
|
454
|
+
text: receipt(changed.sessionApplied ? changed.publication === 'pending' ? 'pending' : 'recorded' : 'saved-only', changed.error) }
|
|
455
|
+
}
|
|
456
|
+
const listing = /^list(?: (all|skills|tools|mcp|mcp_tools))?(?: (.*))?$/.exec(input)
|
|
457
|
+
if (listing) {
|
|
458
|
+
const entries = await capabilityEnvironment.catalogFor(agent, signal)
|
|
459
|
+
const query = (listing[2] ?? '').toLocaleLowerCase()
|
|
460
|
+
const kind = listing[1] ?? 'all'
|
|
461
|
+
return { kind: 'success', text: JSON.stringify({ schema: 'ptc-capability-catalog/v1', entries: entries.filter(entry => (
|
|
462
|
+
kind === 'all' || kind === 'skills' && entry.category === 'skill'
|
|
463
|
+
|| kind === 'tools' && entry.category === 'tool' && entry.source?.kind !== 'mcp'
|
|
464
|
+
|| kind === 'mcp' && ['server', 'resource'].includes(entry.category)
|
|
465
|
+
|| kind === 'mcp_tools' && entry.category === 'tool' && entry.source?.kind === 'mcp'
|
|
466
|
+
) && (!query || `${entry.id} ${entry.description}`.toLocaleLowerCase().includes(query))) }) }
|
|
467
|
+
}
|
|
468
|
+
if (input !== '' && input !== 'refresh') return { kind: 'error', text: receipt('usage') }
|
|
469
|
+
if (input === 'refresh') {
|
|
470
|
+
if (!capabilityEnvironment.canRefresh(agent)) {
|
|
471
|
+
return { kind: 'error', text: receipt('unavailable') }
|
|
472
|
+
}
|
|
473
|
+
await capabilityEnvironment.refresh(agent, undefined, signal, commandId)
|
|
474
|
+
return { kind: 'success', text: receipt(capabilityEnvironment.describe(agent).publication === 'pending' ? 'pending' : 'recorded') }
|
|
475
|
+
}
|
|
476
|
+
return { kind: 'success', text: JSON.stringify(capabilityEnvironment.describe(agent), null, 2) }
|
|
477
|
+
} }), 'ptc-plus capability command')
|
|
478
|
+
})
|
|
479
|
+
if (typeof commands === 'function') disposers.push(commands)
|
|
480
|
+
else if (typeof commands?.dispose === 'function') disposers.push(() => commands.dispose())
|
|
481
|
+
}
|
|
389
482
|
disposers.push(ctx.on('llm/stream', (options, next) => directSurface.stream(options, next), { global: true }))
|
|
390
483
|
disposers.push(ctx.on('tools/execute', (exec, next) => {
|
|
391
484
|
const rejected = directSurface.executionRejection(exec)
|
|
@@ -402,9 +495,12 @@ function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessi
|
|
|
402
495
|
.then(result => directSurface.argumentDiagnostic(exec, result))
|
|
403
496
|
}
|
|
404
497
|
if (exec.name === EDIT_RUN_CODE) return runtimeBridge.withSubmission(exec, next)
|
|
498
|
+
capabilityEnvironment.validateSupply(exec.agent, exec.name, exec.arguments)
|
|
499
|
+
capabilityEnvironment.admitted(exec)
|
|
405
500
|
return next()
|
|
406
501
|
}))
|
|
407
502
|
disposers.push(ctx.on('tools/result', (exec, result) => {
|
|
503
|
+
capabilityEnvironment.dispatched(exec, result)
|
|
408
504
|
directSurface.handleResult(exec)
|
|
409
505
|
if (exec.name === EDIT_RUN_CODE) return editTransport.handleResult(exec, result)
|
|
410
506
|
if (exec.name === RUN_CODE) return runtimeBridge.handleResult(exec, result)
|
|
@@ -423,6 +519,7 @@ function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessi
|
|
|
423
519
|
() => editTransport.disposeAgent(agent),
|
|
424
520
|
() => runtimeBridge.disposeAgent(agent),
|
|
425
521
|
() => userBindings.clearAgentPresentation(agent),
|
|
522
|
+
() => capabilityEnvironment.disposeAgent(agent),
|
|
426
523
|
], 'ptc-plus: agent disposal failed')))
|
|
427
524
|
disposers.push(ctx.on('session/disposed', session => disposeLifecycleOwners([
|
|
428
525
|
() => directSurface.disposeSession(session),
|
|
@@ -464,8 +561,6 @@ function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessi
|
|
|
464
561
|
try {
|
|
465
562
|
runtimeBridge.reconfigure(nextConfig)
|
|
466
563
|
rollbacks.push(() => runtimeBridge.reconfigure(previousConfig))
|
|
467
|
-
editTransport.reconfigure(nextConfig)
|
|
468
|
-
rollbacks.push(() => editTransport.reconfigure(previousConfig))
|
|
469
564
|
directSurface.reconfigure(nextConfig)
|
|
470
565
|
rollbacks.push(() => directSurface.reconfigure(previousConfig))
|
|
471
566
|
await userBindings.reconfigure(nextConfig)
|
|
@@ -495,6 +590,7 @@ function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessi
|
|
|
495
590
|
}
|
|
496
591
|
}
|
|
497
592
|
activeConfig = nextConfig
|
|
593
|
+
capabilityEnvironment.reconfigure(nextConfig)
|
|
498
594
|
observationInterest.reconfigure(nextConfig.replViewEnabled)
|
|
499
595
|
} catch (error) {
|
|
500
596
|
const rollbackFailures = []
|
|
@@ -534,10 +630,13 @@ export const apply = (ctx, config = {}) => {
|
|
|
534
630
|
|
|
535
631
|
/** Install the plugin into the scope of the seam the host registered. */
|
|
536
632
|
function applyWithExecutionSeam(ctx, resolvedConfig, seam, volatileHost, volatileConfig) {
|
|
633
|
+
let settingsWriter
|
|
537
634
|
const sessionLog = createSessionLogOwner(ctx)
|
|
538
635
|
const toolSchemasForAgent = agent => typeof ctx.tools.schemas === 'function'
|
|
539
636
|
? ctx.tools.schemas(agent)
|
|
540
637
|
: []
|
|
638
|
+
// SDK contracts include output schemas; model-facing schemas remain unchanged.
|
|
639
|
+
const toolSdkSchemasForAgent = agent => toolSdkSchemas(ctx.tools, agent)
|
|
541
640
|
const sessionId = agent => {
|
|
542
641
|
const id = agent?.session?.id ?? agent?.id
|
|
543
642
|
return id === undefined ? undefined : String(id)
|
|
@@ -621,7 +720,7 @@ function applyWithExecutionSeam(ctx, resolvedConfig, seam, volatileHost, volatil
|
|
|
621
720
|
}
|
|
622
721
|
let candidate
|
|
623
722
|
try {
|
|
624
|
-
candidate = installPtCRuntime(ctx, record.config, seam, toolSchemasForAgent, sessionId, sessionLog)
|
|
723
|
+
candidate = installPtCRuntime(ctx, record.config, seam, toolSchemasForAgent, toolSdkSchemasForAgent, sessionId, sessionLog, () => settingsWriter)
|
|
625
724
|
record.runtime = candidate
|
|
626
725
|
} catch (error) {
|
|
627
726
|
const failedRuntime = error?.[INSTALL_CLEANUP]
|
|
@@ -763,9 +862,10 @@ function applyWithExecutionSeam(ctx, resolvedConfig, seam, volatileHost, volatil
|
|
|
763
862
|
}
|
|
764
863
|
ctx.effect(() => async () => controller.dispose(), 'ptc-plus runtime lifecycle')
|
|
765
864
|
|
|
865
|
+
const handbackOwners = Object.fromEntries(['sessions', 'sessionPersistence', 'sessionQuery'].map(name => [name, ctx.get(name)]))
|
|
766
866
|
const messageOwner = createRuntimeMessageOwner(context => (
|
|
767
867
|
committed?.retiring ? [] : committed?.runtime.contextsForRequest(context) ?? []
|
|
768
|
-
), sessionLog.project)
|
|
868
|
+
), sessionLog.project, () => committed !== undefined && committed.retiring !== true, handbackOwners.sessionQuery)
|
|
769
869
|
ctx.effect(() => ctx.systemPrompt.context({
|
|
770
870
|
name: PTC_DELIVERY_CONTEXT, order: 98, text: '',
|
|
771
871
|
}), 'ptc-plus dynamic message delivery witness')
|
|
@@ -777,10 +877,13 @@ function applyWithExecutionSeam(ctx, resolvedConfig, seam, volatileHost, volatil
|
|
|
777
877
|
)), 'ptc-plus accepted messages')
|
|
778
878
|
ctx.effect(() => ctx.on('agent/disposed', ({ agent }) => messageOwner.disposeAgent(agent)), 'ptc-plus message agent disposal')
|
|
779
879
|
ctx.effect(() => () => messageOwner.dispose(), 'ptc-plus dynamic message lifecycle')
|
|
880
|
+
ctx.effect(() => async () => {
|
|
881
|
+
await controller.dispose()
|
|
882
|
+
await handBackRuntimeSessions(ctx, handbackOwners)
|
|
883
|
+
}, 'ptc-plus provider handback')
|
|
780
884
|
|
|
781
885
|
let configSource = () => resolveConfig(volatileConfig ?? resolvedConfig)
|
|
782
886
|
let configurationGeneration = 0
|
|
783
|
-
let settingsWriter
|
|
784
887
|
let configurationRollback = false
|
|
785
888
|
const reportActivationFailure = (error) => {
|
|
786
889
|
ctx.logger?.warn?.('ptc-plus: runtime activation failed', error)
|