dsh-ptc-plus 0.4.10 → 0.5.1

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 (125) hide show
  1. package/README.en.md +65 -127
  2. package/README.md +65 -127
  3. package/assets/dsh-ptc-plus-banner-en.webp +0 -0
  4. package/assets/dsh-ptc-plus-banner-zh.webp +0 -0
  5. package/assets/ptc-plus-repl-workspace-en.png +0 -0
  6. package/assets/ptc-plus-repl-workspace-zh.png +0 -0
  7. package/assets/ptc-plus-settings-en.png +0 -0
  8. package/assets/ptc-plus-settings-zh.png +0 -0
  9. package/client.cjs +42702 -0
  10. package/cordis.patch.yml +1 -0
  11. package/docs/README.md +6 -2
  12. package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +31 -198
  13. package/docs/adr/0008-define-agent-operating-contract.md +5 -1
  14. package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -2
  15. package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
  16. package/docs/adr/0017-track-the-latest-dsh-public-surface.md +4 -4
  17. package/docs/adr/0019-plugin-settings-and-kill-switch.md +1 -1
  18. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +1 -1
  19. package/docs/adr/0022-stage-npm-releases-with-oidc.md +4 -2
  20. package/docs/adr/0023-global-user-bindings.md +2 -0
  21. package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +2 -0
  22. package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +25 -23
  23. package/docs/adr/0031-close-semantic-obligations-by-owner.md +1 -1
  24. package/docs/adr/0034-keep-program-values-separate-from-output-projections.md +29 -0
  25. package/docs/adr/0035-run-coverage-only-before-a-new-release.md +27 -0
  26. package/docs/adr/0036-separate-focused-development-from-delivery-verification.md +29 -0
  27. package/docs/adr/0037-keep-tool-hover-previews-passive.md +52 -0
  28. package/docs/adr/0038-freeze-session-capability-plans.md +71 -0
  29. package/docs/adr/0039-verify-documentation-without-runtime-suites.md +29 -0
  30. package/docs/adr/0040-separate-capability-configurations-from-session-observations.md +39 -0
  31. package/docs/adr/0041-record-cell-execution-directories.md +65 -0
  32. package/docs/adr/0042-persist-provider-availability-and-handback.md +41 -0
  33. package/docs/adr/0043-compose-capability-and-context-policies.md +37 -0
  34. package/docs/adr/0044-coexist-with-the-official-persistent-repl.md +35 -0
  35. package/docs/adr/README.md +8 -0
  36. package/docs/architecture.md +23 -25
  37. package/docs/capability-planning-components.md +93 -0
  38. package/docs/capability-projection.md +7 -0
  39. package/docs/client-ui.md +70 -23
  40. package/docs/client-visual-contract.md +26 -9
  41. package/docs/context-management.md +22 -0
  42. package/docs/context-optimization-plan.md +353 -0
  43. package/docs/context-public-interface-coverage.md +25 -0
  44. package/docs/durability-design.md +20 -15
  45. package/docs/evaluation.md +41 -6
  46. package/docs/evaluations/command-goat-deepseek-v4.1-flash-20261006.md +126 -0
  47. package/docs/evaluations/command-goat-deepseek-v4.1-flash.md +281 -0
  48. package/docs/installation.md +33 -10
  49. package/docs/program-data-plane.md +5 -3
  50. package/docs/publishing.md +7 -7
  51. package/docs/release-coverage.md +73 -0
  52. package/docs/release-notes.md +1 -1
  53. package/docs/review-procedure.md +56 -0
  54. package/docs/runtime-reference.md +53 -16
  55. package/docs/semantic-validation.md +14 -2
  56. package/docs/stateful-binding-updates-proposal.md +2 -2
  57. package/docs/user-bindings.md +1 -1
  58. package/docs/value-wire.md +11 -1
  59. package/docs/verification-optimization-work.md +26 -15
  60. package/docs/verification.md +30 -88
  61. package/index.js +146 -20
  62. package/internal/binding-toggle-receipt.js +24 -0
  63. package/internal/capability-command-receipt.js +31 -0
  64. package/internal/capability-environment.js +1253 -0
  65. package/internal/capability-plan.js +285 -0
  66. package/internal/capability-policy.js +96 -0
  67. package/internal/capability-statistics-store.js +180 -0
  68. package/internal/capability-statistics.js +214 -0
  69. package/internal/config-spec.js +89 -10
  70. package/internal/direct-surface-owner.js +27 -29
  71. package/internal/dynamic-environment-runtime.js +9 -9
  72. package/internal/edit-transport-owner.js +195 -140
  73. package/internal/execution-provider-owner.js +40 -12
  74. package/internal/execution-seam-compat.js +26 -8
  75. package/internal/kernel-worker.js +104 -32
  76. package/internal/native-root-dynamic.js +12 -13
  77. package/internal/official-repl-priority.js +51 -0
  78. package/internal/program-bindings.js +190 -0
  79. package/internal/recovery-tips.js +3 -2
  80. package/internal/rejected-cell-editor.js +8 -8
  81. package/internal/repl-observation-interest.js +11 -3
  82. package/internal/rpc-contract.js +1 -0
  83. package/internal/runtime-bridge-owner.js +275 -34
  84. package/internal/runtime-config.js +58 -0
  85. package/internal/runtime-contexts.js +74 -5
  86. package/internal/runtime-intrinsics.js +1 -0
  87. package/internal/runtime-messages.js +41 -0
  88. package/internal/sdk-projection.js +10 -82
  89. package/internal/session-cell-executor.js +42 -19
  90. package/internal/session-events.js +34 -0
  91. package/internal/session-journal-recovery.js +234 -11
  92. package/internal/session-journal-schema.js +6 -3
  93. package/internal/session-journal.js +31 -2
  94. package/internal/session-log-view.js +1 -1
  95. package/internal/session-runtime.js +119 -15
  96. package/internal/settings-compat.js +12 -5
  97. package/internal/stateful-module-compiler.js +3 -2
  98. package/internal/stateful-module-runtime.js +7 -3
  99. package/internal/stateful-root-runtime.js +9 -4
  100. package/internal/user-binding-authoring.js +2 -7
  101. package/internal/user-bindings-owner.js +19 -2
  102. package/internal/validated-parse-repair.js +1 -1
  103. package/internal/value-wire.js +68 -21
  104. package/internal/worker-cwd-virtualization.js +20 -18
  105. package/internal/worker-realm-surfaces.js +3 -3
  106. package/package.json +49 -30
  107. package/src/client-activity.js +3 -2
  108. package/src/client-authoring-view.js +67 -30
  109. package/src/client-binding-review.js +2 -1
  110. package/src/client-binding-toggle.js +18 -0
  111. package/src/client-capabilities-view.js +1555 -0
  112. package/src/client-capability-sort.js +29 -0
  113. package/src/client-copy.js +923 -8
  114. package/src/client-feature-gates.js +3 -0
  115. package/src/client-indicator-view.js +4 -6
  116. package/src/client-primitives.js +33 -2
  117. package/src/client-repl-view.js +20 -13
  118. package/src/client-settings-view.js +411 -41
  119. package/src/client-styles.js +112 -15
  120. package/src/client-tool-hover.js +185 -0
  121. package/src/client-tool-view.js +45 -9
  122. package/src/client-transport.js +44 -9
  123. package/src/client-workbench.js +6 -7
  124. package/src/client.js +76 -22
  125. package/client.js +0 -32479
@@ -7,7 +7,7 @@ import {
7
7
  installProjectedProcessCwd,
8
8
  } from './worker-realm-surfaces.js'
9
9
 
10
- const { Reflect: privateReflect, appendArray, bufferConcat, bufferIsBuffer, bufferToString,
10
+ const { Reflect: privateReflect, appendArray, bufferConcat, bufferFrom, bufferIsBuffer, bufferToString,
11
11
  copyArray, isArray, regexpTest } = internal
12
12
  const TRAILING_SEPARATOR = /[\\/]$/
13
13
 
@@ -108,29 +108,32 @@ export function projectChildProcessArguments(name, args, sessionCwd) {
108
108
  : withDefaultOptions(2)
109
109
  }
110
110
 
111
- /** Install one session's cwd projection into Node builtins loaded by the worker. */
112
- export function installWorkerCwdVirtualization(sessionCwd, originalRequire, markVolatile) {
111
+ /** Install stable Node wrappers; the execution owner supplies each cell's cwd. */
112
+ export function installWorkerCwdVirtualization(sessionCwd, originalRequire, markVolatile, readCwd = () => sessionCwd) {
113
+ const nativeCwd = process.cwd
114
+ const currentCwd = () => readCwd() ?? privateReflect.apply(nativeCwd, process, [])
113
115
  if (sessionCwd === undefined) {
114
- const nativeCwd = process.cwd
115
116
  assignWorkerRealmProperty(WORKER_REALM_MUTATION.STABLE, 'process.cwd', process, 'cwd', () => {
116
- markVolatile('process.cwd')
117
- return privateReflect.apply(nativeCwd, process, [])
117
+ if (readCwd() === undefined) markVolatile('process.cwd')
118
+ return currentCwd()
118
119
  })
119
- return
120
- }
120
+ } else installProjectedProcessCwd(process, currentCwd)
121
121
  const nativeResolve = resolve
122
- const bufferPrefix = Buffer.from(sessionCwd + (regexpTest(TRAILING_SEPARATOR, sessionCwd) ? '' : sep))
122
+ const directoryPrefix = () => {
123
+ const cwd = currentCwd()
124
+ return cwd + (regexpTest(TRAILING_SEPARATOR, cwd) ? '' : sep)
125
+ }
123
126
  const sessionPath = (value) => {
124
- if (typeof value === 'string') return isAbsolute(value) ? value : nativeResolve(sessionCwd, value)
127
+ if (typeof value === 'string') return isAbsolute(value) ? value : nativeResolve(currentCwd(), value)
125
128
  if (bufferIsBuffer(value)) {
126
129
  const absolute = isAbsolute(bufferToString(value, 'utf8', 0, 3))
127
- return absolute ? value : bufferConcat([bufferPrefix, value])
130
+ return absolute ? value : bufferConcat([bufferFrom(directoryPrefix()), value])
128
131
  }
129
132
  return value
130
133
  }
131
134
  const sessionPathPrefix = (value) => {
132
135
  if (typeof value !== 'string' || isAbsolute(value) || parse(value).root !== '') return sessionPath(value)
133
- return sessionCwd + (regexpTest(TRAILING_SEPARATOR, sessionCwd) ? '' : sep) + value
136
+ return directoryPrefix() + value
134
137
  }
135
138
  const projectPaths = (args, indices) => {
136
139
  const next = copyArray(args)
@@ -148,18 +151,17 @@ export function installWorkerCwdVirtualization(sessionCwd, originalRequire, mark
148
151
  const sessionGlobOptions = (args) => {
149
152
  if (typeof args[1] === 'function') {
150
153
  const next = copyArray(args, 0, 1)
151
- appendArray(next, { cwd: sessionCwd })
154
+ appendArray(next, { cwd: currentCwd() })
152
155
  return copyArray(args, 1, args.length, next)
153
156
  }
154
157
  const next = copyArray(args)
155
- if (next[1] === undefined) next[1] = { cwd: sessionCwd }
158
+ if (next[1] === undefined) next[1] = { cwd: currentCwd() }
156
159
  else if (next[1] !== null && typeof next[1] === 'object' && next[1].cwd === undefined) {
157
- next[1] = { ...next[1], cwd: sessionCwd }
160
+ next[1] = { ...next[1], cwd: currentCwd() }
158
161
  }
159
162
  return next
160
163
  }
161
164
 
162
- installProjectedProcessCwd(process, sessionCwd)
163
165
  const fs = originalRequire('node:fs')
164
166
  const path = originalRequire('node:path')
165
167
  for (const [name, indices] of Object.entries(FILE_SYSTEM_PATH_ARGUMENTS)) {
@@ -174,10 +176,10 @@ export function installWorkerCwdVirtualization(sessionCwd, originalRequire, mark
174
176
  wrapBuiltin(fs, name, sessionGlobOptions)
175
177
  wrapBuiltin(fs.promises, name, sessionGlobOptions)
176
178
  }
177
- wrapBuiltin(path, 'resolve', args => copyArray(args, 0, args.length, [sessionCwd]))
179
+ wrapBuiltin(path, 'resolve', args => copyArray(args, 0, args.length, [currentCwd()]))
178
180
  const childProcess = originalRequire('node:child_process')
179
181
  for (const name of ['exec', 'execFile', 'execFileSync', 'execSync', 'fork', 'spawn', 'spawnSync']) {
180
182
  wrapBuiltin(childProcess, name,
181
- args => projectChildProcessArguments(name, args, sessionCwd), CHILD_PROCESS_PROJECTED_PROPERTIES[name])
183
+ args => projectChildProcessArguments(name, args, currentCwd()), CHILD_PROCESS_PROJECTED_PROPERTIES[name])
182
184
  }
183
185
  }
@@ -41,7 +41,7 @@ export const WORKER_REALM_SURFACE_CONTRACT = objectFreeze([
41
41
  surface: 'process.cwd',
42
42
  owner: 'internal/worker-cwd-virtualization.js',
43
43
  classification: WORKER_REALM_MUTATION.STABLE,
44
- contract: 'recorded cwd; immutable and non-configurable when a session cwd exists',
44
+ contract: 'current recorded cell cwd; callable immutable and non-configurable when a session cwd exists',
45
45
  reference: 'plain Node worker process descriptor',
46
46
  verification: 'cwd-descriptor',
47
47
  }),
@@ -49,7 +49,7 @@ export const WORKER_REALM_SURFACE_CONTRACT = objectFreeze([
49
49
  surface: 'cwd-projected Node builtins',
50
50
  owner: 'internal/worker-cwd-virtualization.js',
51
51
  classification: WORKER_REALM_MUTATION.STABLE,
52
- contract: 'relative fs, path, and child-process calls use the recorded cwd while retaining callable descriptors',
52
+ contract: 'relative fs, path, and new child-process calls use the current recorded cell cwd while retaining callable descriptors',
53
53
  reference: 'plain Node worker fs, fs/promises, path, and child_process callable descriptors',
54
54
  verification: 'cwd-projected-builtins',
55
55
  }),
@@ -148,7 +148,7 @@ export function installProjectedProcessCwd(processObject, cwd) {
148
148
  configurable: false,
149
149
  enumerable: true,
150
150
  writable: false,
151
- value: () => cwd,
151
+ value: typeof cwd === 'function' ? cwd : () => cwd,
152
152
  })
153
153
  }
154
154
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ptc-plus",
3
- "version": "0.4.10",
3
+ "version": "0.5.1",
4
4
  "icon": "assets/ptc-plus-icon.svg",
5
5
  "description": "A session-bound agent-native REPL for DeepSeek Harness PTC mode.",
6
6
  "type": "module",
@@ -8,13 +8,13 @@
8
8
  "exports": {
9
9
  ".": "./index.js",
10
10
  "./execution-provider": "./internal/execution-provider-owner.js",
11
- "./client": "./client.js",
11
+ "./client": "./client.cjs",
12
12
  "./package.json": "./package.json",
13
13
  "./locale/*.json": "./locale/*.json"
14
14
  },
15
15
  "files": [
16
16
  "index.js",
17
- "client.js",
17
+ "client.cjs",
18
18
  "compiler-core.cjs",
19
19
  "compiler-core.cjs.map",
20
20
  "compiler-platform.cjs",
@@ -42,45 +42,32 @@
42
42
  "LICENSE"
43
43
  ],
44
44
  "engines": {
45
- "node": "^22.19.0 || >=24.0.0"
45
+ "node": "^22.19.0 || >=24.0.0",
46
+ "dsh": "*"
46
47
  },
47
48
  "allowScripts": {
48
49
  "esbuild": false
49
50
  },
50
51
  "dependencies": {
51
52
  "@babel/code-frame": "^7.29.7",
52
- "@babel/core": "^7.29.7",
53
- "@babel/generator": "^7.29.8",
54
- "@babel/helper-create-class-features-plugin": "^7.29.7",
55
53
  "@babel/parser": "^7.29.0",
56
- "@babel/plugin-proposal-decorators": "^7.29.7",
57
- "@babel/plugin-transform-explicit-resource-management": "^7.29.7",
58
- "@babel/plugin-transform-optional-chaining": "^7.29.7",
59
- "@babel/plugin-transform-typescript": "^7.29.7",
60
- "@babel/traverse": "^7.29.0",
61
- "@codemirror/lang-javascript": "^6.2.5",
62
- "@codemirror/language": "^6.12.4",
63
- "@codemirror/state": "^6.7.4",
64
- "@codemirror/view": "^6.43.11",
65
- "@deepseek-ai/schemastery": "^3.18.3",
66
54
  "@deepseek-ai/dsh-util-values": "^0.2.0-rc.2 || ^0.2.1-alpha.1",
55
+ "@deepseek-ai/schemastery": "^3.18.3",
67
56
  "@jridgewell/trace-mapping": "^0.3.31",
68
- "@lezer/highlight": "^1.2.3",
69
- "acorn": "^8.15.0",
70
- "amaro": "1.1.11",
71
- "codemirror": "^6.0.2",
72
- "fflate": "^0.8.3",
73
- "pathe": "^2.0.3",
74
- "react-is": "^19.3.0",
75
- "tslib": "^2.8.1",
76
- "typescript": "^6.0.3"
57
+ "acorn": "^8.15.0"
77
58
  },
78
59
  "peerDependencies": {
60
+ "@deepseek-ai/cordis": "*",
79
61
  "@deepseek-ai/dsh-atomic-write": "*",
80
62
  "@deepseek-ai/dsh-home-paths": "*",
81
63
  "@deepseek-ai/dsh-llm": "*",
64
+ "@deepseek-ai/dsh-scope": "*",
65
+ "@deepseek-ai/dsh-session": "*",
66
+ "@deepseek-ai/dsh-session-format-catalog": "*",
82
67
  "@deepseek-ai/dsh-settings": "*",
68
+ "@deepseek-ai/dsh-skill": "*",
83
69
  "@deepseek-ai/dsh-skill-filesystem": "*",
70
+ "@deepseek-ai/dsh-spill-policy": "*",
84
71
  "@deepseek-ai/dsh-tool-cordis": "*",
85
72
  "@deepseek-ai/dsh-tools": "*",
86
73
  "@deepseek-ai/dsh-typert-protocol": "*"
@@ -89,12 +76,18 @@
89
76
  "build": "node scripts/client-bundle.mjs build && node scripts/compiler-bundle.mjs build",
90
77
  "build:check": "node scripts/client-bundle.mjs check && node scripts/compiler-bundle.mjs check",
91
78
  "test:client": "vitest run --config scripts/client-test.config.mjs",
79
+ "test:client:focus": "node scripts/client-focused-tests.mjs",
80
+ "test:client:fixtures": "vitest run --config scripts/client-test.config.mjs --testNamePattern=\"^(REPL observations and shared workbench remain separate from session execution|saved cards retain exact source across remount and locale changes; catalog conflicts keep drafts actionable)$\"",
92
81
  "test:client:visual": "node scripts/client-visual-check.mjs",
93
- "test:semantics": "node --test --test-concurrency=1 test/semantic-test262.test.js test/semantic-dialect.test.js test/semantic-boundaries.test.js",
82
+ "test:semantics": "node scripts/test-suite-files.mjs --file test/semantic-test262.test.js --file test/semantic-dialect.test.js --file test/semantic-boundaries.test.js --file test/semantic-boundary-storage.test.js --file test/semantic-boundary-promises.test.js --file test/semantic-boundary-reflection.test.js",
94
83
  "semantic:check": "node scripts/semantic-obligations.mjs",
95
84
  "test:client:web": "node scripts/client-web-smoke.mjs",
85
+ "test:client:tool-hover:focus": "node scripts/client-tool-hover.mjs --focus",
86
+ "test:client:fallback:focus": "node scripts/client-fallback-smoke.mjs --focus",
87
+ "test:client:layout:focus": "node scripts/client-binding-layout.mjs --focus",
96
88
  "test:client:fallback": "node scripts/client-fallback-smoke.mjs",
97
89
  "test:client:layout": "node scripts/client-binding-layout.mjs",
90
+ "readme:screenshots": "node scripts/readme-screenshots.mjs",
98
91
  "host:baseline:update": "node scripts/dsh-dependency-baseline.mjs update",
99
92
  "host:baseline:verify": "node scripts/dsh-dependency-baseline.mjs verify",
100
93
  "test:host-contract": "node scripts/dsh-host-contract-suite.mjs",
@@ -112,12 +105,16 @@
112
105
  "session:migrate": "node scripts/migrate-session-log.mjs",
113
106
  "coverage": "node scripts/coverage.mjs",
114
107
  "coverage:focus": "node scripts/coverage.mjs --focus",
108
+ "test:backend:focus": "node scripts/test-suite-files.mjs --focus",
109
+ "test:backend": "node scripts/test-suite-files.mjs",
115
110
  "test:ci:runtime": "node --test --test-concurrency=1 test/package-contract.test.js test/compiler-platform.test.js test/direct-surface-policy.test.js test/isolated-entrance.test.js test/plugin-native-call-transport.test.js",
116
- "verify": "npm run review:validate && npm run host:baseline:verify && npm run semantic:check && npm run build:check && node --check index.js && node --check internal/kernel-worker.js && node --check scripts/migrate-session-log.mjs && node --check scripts/expensive-headless-acceptance.mjs && node --check scripts/ab-headless-trajectories.mjs && npm run test:client:visual && npm run coverage",
117
- "verify:ci": "npm run review:validate && npm run host:baseline:verify && npm run semantic:check && npm run build:check && node --check index.js && node --check internal/kernel-worker.js && node --check scripts/migrate-session-log.mjs && npm run test:client:visual && npm run test:ci:runtime",
111
+ "verify": "npm run review:validate && npm run host:baseline:verify && npm run semantic:check && npm run build:check && node --check index.js && node --check internal/kernel-worker.js && node --check scripts/migrate-session-log.mjs && node --check scripts/expensive-headless-acceptance.mjs && node --check scripts/ab-headless-trajectories.mjs && node scripts/deterministic-test-groups.mjs",
112
+ "verify:ci": "npm run review:validate && npm run host:baseline:verify && npm run semantic:check && npm run build:check && node --check index.js && node --check internal/kernel-worker.js && node --check scripts/migrate-session-log.mjs && node scripts/deterministic-test-groups.mjs --ci",
118
113
  "check": "node scripts/review-findings.mjs check",
114
+ "verify:docs": "node scripts/review-findings.mjs docs",
119
115
  "test:expensive": "node scripts/expensive-headless-acceptance.mjs",
120
- "test:ab": "node scripts/ab-headless-trajectories.mjs"
116
+ "test:ab": "node scripts/ab-headless-trajectories.mjs",
117
+ "test:package:consumer": "node scripts/npm-consumer-smoke.mjs"
121
118
  },
122
119
  "keywords": [
123
120
  "deepseek-harness",
@@ -155,6 +152,18 @@
155
152
  }
156
153
  },
157
154
  "devDependencies": {
155
+ "@babel/core": "^7.29.7",
156
+ "@babel/generator": "^7.29.8",
157
+ "@babel/helper-create-class-features-plugin": "^7.29.7",
158
+ "@babel/plugin-proposal-decorators": "^7.29.7",
159
+ "@babel/plugin-transform-explicit-resource-management": "^7.29.7",
160
+ "@babel/plugin-transform-optional-chaining": "^7.29.7",
161
+ "@babel/plugin-transform-typescript": "^7.29.7",
162
+ "@babel/traverse": "^7.29.0",
163
+ "@codemirror/lang-javascript": "^6.2.5",
164
+ "@codemirror/language": "^6.12.4",
165
+ "@codemirror/state": "^6.7.4",
166
+ "@codemirror/view": "^6.43.11",
158
167
  "@deepseek-ai/dsh": "next",
159
168
  "@deepseek-ai/dsh-agent-loop": "next",
160
169
  "@deepseek-ai/dsh-api-gateway": "next",
@@ -169,6 +178,7 @@
169
178
  "@deepseek-ai/dsh-client-ui-session": "next",
170
179
  "@deepseek-ai/dsh-client-ui-slots": "next",
171
180
  "@deepseek-ai/dsh-commands": "next",
181
+ "@deepseek-ai/dsh-compaction-image-offload": "next",
172
182
  "@deepseek-ai/dsh-home-paths": "next",
173
183
  "@deepseek-ai/dsh-llm": "next",
174
184
  "@deepseek-ai/dsh-session-format-catalog": "next",
@@ -181,14 +191,19 @@
181
191
  "@deepseek-ai/dsh-typert-protocol": "next",
182
192
  "@deepseek-ai/dsh-typert-registry": "next",
183
193
  "@deepseek-ai/dsh-util-workspace-path": "next",
194
+ "@lezer/highlight": "^1.2.3",
195
+ "@radix-ui/react-select": "^2.2.6",
184
196
  "@shikijs/langs": "^4.3.1",
185
197
  "@testing-library/react": "^16.3.3",
198
+ "amaro": "1.1.11",
186
199
  "anser": "^2.3.5",
187
200
  "c8": "^12.0.0",
188
201
  "clsx": "^2.0.0",
202
+ "codemirror": "^6.0.2",
189
203
  "convert-source-map": "^2.0.0",
190
204
  "diff": "^9.0.0",
191
205
  "esbuild": "0.28.2",
206
+ "fflate": "^0.8.3",
192
207
  "immer": "^10.1.1",
193
208
  "jsdom": "^29.1.1",
194
209
  "katex": "^0.16.47",
@@ -203,11 +218,15 @@
203
218
  "micromark-util-classify-character": "^2.0.1",
204
219
  "micromark-util-sanitize-uri": "^2.0.1",
205
220
  "micromark-util-symbol": "^2.0.1",
221
+ "pathe": "^2.0.3",
206
222
  "playwright": "^1.63.0",
207
223
  "react": "^18.3.1",
208
224
  "react-dom": "^18.3.1",
225
+ "react-is": "^19.3.0",
209
226
  "shiki": "^4.3.1",
210
227
  "simple-icons": "16.31.0",
228
+ "tslib": "^2.8.1",
229
+ "typescript": "^6.0.3",
211
230
  "vitest": "^5.0.0",
212
231
  "yaml": "^2.9.0",
213
232
  "zustand": "~4.4.7"
@@ -506,6 +506,7 @@ export function derivePtcToolView(block, toolName = undefined) {
506
506
  ? block.meta[GENERATED_RUN_CODE_DESCRIPTION_KEY]
507
507
  : undefined
508
508
  const description = (explicitDescription ?? generatedDescription)?.split(/\r?\n/, 1)[0] ?? ''
509
+ const descriptionGenerated = explicitDescription === undefined && generatedDescription !== undefined
509
510
  const output = settled ? resultText(block) : ''
510
511
  const state = !settled ? 'running'
511
512
  : block.error?.code === 'interrupted' ? 'stopped'
@@ -514,7 +515,7 @@ export function derivePtcToolView(block, toolName = undefined) {
514
515
  ? block.meta.dshPtcPlus
515
516
  : undefined
516
517
  if (journal === undefined) {
517
- return Object.freeze({ state, description, code, output, ptc: false, features: Object.freeze([]) })
518
+ return Object.freeze({ state, description, descriptionGenerated, code, output, ptc: false, features: Object.freeze([]) })
518
519
  }
519
520
  const rewrites = isValidRewrites(block.meta.dshPtcPlusRewrites, journal)
520
521
  ? block.meta.dshPtcPlusRewrites
@@ -542,5 +543,5 @@ export function derivePtcToolView(block, toolName = undefined) {
542
543
  : undefined,
543
544
  ...operationFeatures(journal.operations),
544
545
  ])
545
- return Object.freeze({ state, description, code, output, ptc: true, features: Object.freeze(features) })
546
+ return Object.freeze({ state, description, descriptionGenerated, code, output, ptc: true, features: Object.freeze(features) })
546
547
  }
@@ -1,5 +1,6 @@
1
1
  import { bindingDraftProjection, bindingReviewStatus } from './client-binding-review.js'
2
2
  import { bindingModelPreferences } from '../internal/user-binding-model-context.js'
3
+ import { capabilityCommandText } from './client-capabilities-view.js'
3
4
  import { featureEnabled } from './client-feature-gates.js'
4
5
  import {
5
6
  MENU_CHILDREN_PROBE_SENTINEL,
@@ -25,7 +26,7 @@ export function createAuthoringView(React, deps) {
25
26
  const {
26
27
  ActionButton, IconButton, Menu, MenuFallback, Toast, Tooltip, CodeBlock, Modal, createPortal, BindingsDialog, PTCPlusSettingsDialog,
27
28
  useWorkbenchController, useBindingReview, catalogOwner, callUserBindings, subscribeReset,
28
- settingsCardSeat, updateSetting, menuChildren,
29
+ settingsCardSeat, updateSetting, updateCapabilityConfiguration, menuChildren, SessionCapabilityControls,
29
30
  icons: {
30
31
  sparkle: IconSparkle16, chevron: IconChevronDownOutline14,
31
32
  close: IconCloseOutline16, check: IconCheckOutline14,
@@ -64,10 +65,11 @@ export function createAuthoringView(React, deps) {
64
65
  }[settingsCardSeat?.()] ?? 'settings.menuEntry')
65
66
 
66
67
  function BindingAuthorButton({
67
- sessionId, t, useInput, inputActions, usePtcSettings, useBindingCommand,
68
+ sessionId, t, useInput, inputActions, usePtcSettings, useBindingCommand, useCapabilityCommand,
68
69
  }) {
69
70
  const input = useInput?.(snapshot => snapshot)
70
71
  const available = useBindingCommand(snapshot => snapshot)
72
+ const capabilityAvailable = useCapabilityCommand?.(snapshot => snapshot) === true
71
73
  const settings = usePtcSettings(snapshot => snapshot)
72
74
  const [review, view] = useBindingReview(sessionId)
73
75
  const anchorRef = React.useRef(null)
@@ -84,6 +86,7 @@ export function createAuthoringView(React, deps) {
84
86
  const hoverTimer = React.useRef(undefined)
85
87
  const [menu, setMenu] = React.useState(null)
86
88
  const [managing, setManaging] = React.useState(false)
89
+ const [capabilitiesOpen, setCapabilitiesOpen] = React.useState(false)
87
90
  const [settingsOpen, setSettingsOpen] = React.useState(false)
88
91
  // The composer entry's four actions live in the host Menu's `children`
89
92
  // region. Preceding Clients accept that prop and mount nothing, so the entry
@@ -105,16 +108,25 @@ export function createAuthoringView(React, deps) {
105
108
  const catalogFocus = React.useRef(null)
106
109
  const toastSequence = React.useRef(0)
107
110
  const [toast, setToast] = React.useState(null)
108
- const hasDraft = view.mounted && view.candidate !== null && view.action === null
111
+ const hasDraft = featureEnabled(settings, 'bindings') && view.mounted && view.candidate !== null && view.action === null
109
112
  const quickAccess = featureEnabled(settings, 'authorButton')
110
- const canAuthor = quickAccess && available && typeof useInput === 'function' && typeof inputActions?.setDraft === 'function'
111
- const menuOpen = (hasDraft || quickAccess) && view.reachable && !managing && !settingsOpen
113
+ const pluginEnabled = featureEnabled(settings, 'plugin') && (hasDraft || featureEnabled(settings, 'ptcMenu'))
114
+ const canPrefill = typeof useInput === 'function' && typeof inputActions?.setDraft === 'function'
115
+ const canAuthor = quickAccess && available && canPrefill
116
+ const canRefresh = pluginEnabled && settings.status === 'ready' && settings.writable === true
117
+ && capabilityAvailable && canPrefill
118
+ const menuOpen = pluginEnabled && view.reachable && !managing && !capabilitiesOpen && !settingsOpen
112
119
  && menu !== null && menu.key === view.candidateKey
113
120
  // The dialog owns the management session; losing the input surface only
114
121
  // suspends reads, so a draft survives a temporary Host takeover.
115
122
  const workbench = useWorkbenchController({
116
123
  enabled: quickAccess && managing, active: view.reachable, callUserBindings,
117
124
  })
125
+ React.useEffect(() => {
126
+ if (!quickAccess) {
127
+ setManaging(false)
128
+ }
129
+ }, [quickAccess])
118
130
  const refreshCatalog = React.useCallback(async (reload = false) => {
119
131
  if (reload) captureCatalogFocus()
120
132
  await catalogSource.read({ reload })
@@ -124,6 +136,7 @@ export function createAuthoringView(React, deps) {
124
136
  catalogSource.reset()
125
137
  setMenu(null)
126
138
  setManaging(false)
139
+ setCapabilitiesOpen(false)
127
140
  setSettingsOpen(false)
128
141
  }
129
142
  const unsubscribe = subscribeReset(reset)
@@ -135,9 +148,14 @@ export function createAuthoringView(React, deps) {
135
148
  const toggleBinding = async entry => {
136
149
  if (!catalogReady || !quickAccess) return
137
150
  captureCatalogFocus()
138
- await catalogSource.write(() => callUserBindings(entry.enabled ? 'disable' : 'enable', {
151
+ const next = await catalogSource.write(() => callUserBindings(entry.enabled ? 'disable' : 'enable', {
139
152
  id: entry.id, expectedRevision: catalog.revision,
140
153
  }))
154
+ const saved = next?.entries.find(item => item.id === entry.id)
155
+ if (saved) {
156
+ toastSequence.current++
157
+ setToast({ sequence: toastSequence.current, key: saved.enabled ? 'bindings.toggleEnabled' : 'bindings.toggleDisabled', params: { name: saved.name } })
158
+ }
141
159
  }
142
160
  const cancelHoverOpen = React.useCallback(() => {
143
161
  if (hoverTimer.current === undefined) return
@@ -260,13 +278,12 @@ export function createAuthoringView(React, deps) {
260
278
  return () => clearTimeout(timer)
261
279
  }, [toast])
262
280
  const label = hasDraft ? t('bindings.reviewMenuLabel', { count: 1 })
263
- + (view.message ? ` · ${t('bindings.reviewAttention')}` : '') : t('bindings.open')
264
- const hint = t(hasDraft ? 'bindings.draftHint' : 'bindings.openHint')
265
- // Every authoring action goes through the same busy guard and input owner, so a
266
- // new or revision request can never overwrite an unsent user draft.
267
- const prefillAuthoring = value => {
281
+ + (view.message ? ` · ${t('bindings.reviewAttention')}` : '') : t(quickAccess ? 'bindings.open' : 'settings.toolsMenu')
282
+ const hint = t(hasDraft ? 'bindings.draftHint' : quickAccess ? 'bindings.openHint' : 'settings.toolsMenu')
283
+ // Command shortcuts share the composer guard; submission remains the user's action.
284
+ const prefillCommand = (value, allowed) => {
268
285
  hideMenu()
269
- if (!canAuthor) return
286
+ if (!allowed) return
270
287
  if (typeof input?.draft === 'string' && input.draft.trim() !== '') {
271
288
  toastSequence.current += 1
272
289
  setToast({ sequence: toastSequence.current, text: t('bindings.composerBusy') })
@@ -274,8 +291,8 @@ export function createAuthoringView(React, deps) {
274
291
  }
275
292
  inputActions.setDraft(value)
276
293
  }
277
- const openAuthoring = () => prefillAuthoring('/binding new ')
278
- const openAuthoringEdit = entry => prefillAuthoring(`/binding edit ${entry.id} `)
294
+ const openAuthoring = () => prefillCommand('/binding new ', canAuthor)
295
+ const openAuthoringEdit = entry => prefillCommand(`/binding edit ${entry.id} `, canAuthor)
279
296
  const starButton = h('button', {
280
297
  type: 'button', className: 'ptcPlusAuthorButton', 'aria-label': label,
281
298
  // Older UI-kit lines ship no Tooltip primitive; the native title carries the hint there.
@@ -309,7 +326,6 @@ export function createAuthoringView(React, deps) {
309
326
  const enabledEntries = (catalog?.entries ?? []).filter(entry => entry.enabled)
310
327
  const disabledEntries = (catalog?.entries ?? []).filter(entry => !entry.enabled)
311
328
  const catalogItems = !quickAccess ? [] : [
312
- { id: 'global-heading', type: 'label', text: t('bindings.quickHeading') },
313
329
  ...(catalog === null || catalog.entries.length === 0 ? [] : [
314
330
  { id: 'enabled-heading', type: 'label', text: t('bindings.quickEnabled', { count: enabledEntries.length }) },
315
331
  ...enabledEntries.map(catalogRow),
@@ -335,14 +351,7 @@ export function createAuthoringView(React, deps) {
335
351
  // submenu here would lose the bounded, scrollable catalog this step needs.
336
352
  const editItems = [
337
353
  { id: 'edit-heading', type: 'label', text: t('bindings.quickEditHeading') },
338
- ...(catalog?.entries ?? []).map(entry => ({
339
- id: `edit:${entry.id}`,
340
- disabled: catalog === null,
341
- label: h('span', { className: 'ptcPlusBindingQuickRow' },
342
- h('span', { className: 'ptcPlusBindingQuickName' }, h('strong', { title: entry.name }, entry.name),
343
- h('span', { className: 'ptcPlusBindingQuickState' }, t(entry.enabled ? 'bindings.enabled' : 'bindings.disabledEntry'))),
344
- h('span', { className: 'ptcPlusBindingQuickPurpose', title: entry.purpose }, entry.purpose)),
345
- })),
354
+ ...(catalog?.entries ?? []).map(entry => ({ ...catalogRow(entry), id: `edit:${entry.id}` })),
346
355
  ]
347
356
  const selectMenuItem = id => {
348
357
  if (!anchorRef.current?.getClientRects().length) return
@@ -371,6 +380,8 @@ export function createAuthoringView(React, deps) {
371
380
  setSettingsOpen(true)
372
381
  return
373
382
  }
383
+ if (id === 'capabilities') { hideMenu(); setCapabilitiesOpen(true); return }
384
+ if (id === 'refresh') { prefillCommand('/capabilities refresh', canRefresh); return }
374
385
  hideMenu()
375
386
  if (id === 'new') openAuthoring()
376
387
  else if (id === 'manage') {
@@ -381,12 +392,27 @@ export function createAuthoringView(React, deps) {
381
392
  }
382
393
  // One copy of each action label serves the grid the plugin renders itself,
383
394
  // in whichever carrier the installed Menu publishes for it.
384
- const actionCopy = (id, copy) => h('span', {
395
+ const actionPaths = {
396
+ new: 'M8 3v10M3 8h10',
397
+ edit: 'm10 3 3 3M3 13l3-.7 7-7a1.4 1.4 0 0 0-3-3l-7 7L3 13Z',
398
+ refresh: 'M13 6a5 5 0 0 0-9-2L2 6m0-3v3h3M3 10a5 5 0 0 0 9 2l2-2m0 3v-3h-3',
399
+ manage: 'M2 4h5l1.5 2H14v7H2V4Z',
400
+ capabilities: 'M3 4h10M3 8h10M3 12h10M6 2v4M10 6v4M6 10v4',
401
+ settings: 'M6 2h4l.5 2 1.5 1.5 2 .5v4l-2 .5-1.5 1.5-.5 2H6l-.5-2L4 10.5 2 10V6l2-.5L5.5 4 6 2ZM10 8a2 2 0 1 0-4 0 2 2 0 0 0 4 0Z',
402
+ 'edit-back': 'm7 3-5 5 5 5M2 8h12',
403
+ }
404
+ const actionCopy = (id, copy) => h(React.Fragment, null,
405
+ h('svg', { className: 'ptcPlusMenuActionIcon', width: 16, height: 16, viewBox: '0 0 16 16',
406
+ fill: 'none', 'aria-hidden': true }, h('path', { d: actionPaths[id], stroke: 'currentColor',
407
+ strokeWidth: 1.4, strokeLinecap: 'round', strokeLinejoin: 'round' })), h('span', {
385
408
  className: 'ptcPlusBindingMenuAction',
386
409
  title: id === 'settings' ? settingsPath(t) : undefined,
387
- }, t(copy))
410
+ }, t(copy)))
388
411
  const actionButton = (id, copy) => h('button', {
389
412
  type: 'button', role: 'menuitem', className: 'ptcPlusMenuButton',
413
+ disabled: id === 'refresh' && !canRefresh,
414
+ title: id === 'refresh' && !capabilityAvailable ? t('capabilities.refreshUnavailable')
415
+ : id === 'settings' ? settingsPath(t) : t(copy),
390
416
  onClick: () => selectMenuItem(id),
391
417
  }, actionCopy(id, copy))
392
418
  const actionSections = [
@@ -396,7 +422,9 @@ export function createAuthoringView(React, deps) {
396
422
  actionButton('new', 'bindings.authorNewDraft'),
397
423
  (catalog?.entries?.length ?? 0) > 0 ? actionButton('edit', 'bindings.authorEdit') : null)) : null,
398
424
  h('div', { key: 'utilities', className: 'ptcPlusMenuUtilities' },
425
+ actionButton('refresh', 'capabilities.applySession'),
399
426
  quickAccess ? actionButton('manage', 'bindings.manage') : null,
427
+ SessionCapabilityControls ? actionButton('capabilities', 'capabilities.quick.title') : null,
400
428
  actionButton('settings', 'settings.menuEntry')),
401
429
  ]
402
430
  const menuEntries = menu?.step === 'edit' ? editItems : [
@@ -421,7 +449,8 @@ export function createAuthoringView(React, deps) {
421
449
  : entry.type === 'label'
422
450
  ? h('div', { key: entry.id, className: 'ptcPlusMenuGroupLabel' }, entry.text)
423
451
  : h('button', {
424
- key: entry.id, type: 'button', role: 'menuitem', className: 'ptcPlusOwnedMenuRow',
452
+ key: entry.id, type: 'button', role: 'menuitem',
453
+ className: entry.id === 'disabled-group' ? 'ptcPlusOwnedMenuRow ptcPlusMenuDisclosure' : 'ptcPlusOwnedMenuRow',
425
454
  disabled: entry.disabled, 'data-binding-id': entry.bindingId,
426
455
  ref: entry.id === 'reload' ? reloadItemRef : entry.id === 'disabled-group' ? disabledGroupRef : undefined,
427
456
  'aria-expanded': entry.id === 'disabled-group' ? disabledOpen : undefined,
@@ -435,7 +464,7 @@ export function createAuthoringView(React, deps) {
435
464
  'data-text': isHostIconComponent(IconSparkle16) ? undefined : true,
436
465
  ref: attachAnchor,
437
466
  },
438
- !hasDraft && !quickAccess ? null : h(menuChildrenRegion ? Menu : MenuFallback, {
467
+ !pluginEnabled ? null : h(menuChildrenRegion ? Menu : MenuFallback, {
439
468
  className: 'ptcPlusAuthorButtonShell', open: menuOpen,
440
469
  anchor: isHostComponent(Tooltip)
441
470
  ? h(Tooltip, { label: hint, delayMs: 700, disabled: menuOpen || managing || settingsOpen }, starButton) : starButton,
@@ -465,9 +494,17 @@ export function createAuthoringView(React, deps) {
465
494
  controller: workbench, t, onClose: () => setManaging(false), returnFocusRef: dialogReturnFocus,
466
495
  getReturnFocus: () => anchorRef.current?.querySelector('.ptcPlusAuthorButton'),
467
496
  }) : null,
497
+ SessionCapabilityControls && capabilitiesOpen && view.reachable
498
+ ? h(SessionCapabilityControls, { sessionId, open: true, t,
499
+ enabled: pluginEnabled && settings.status === 'ready' && settings.writable === true,
500
+ onClose: () => {
501
+ setCapabilitiesOpen(false)
502
+ queueMicrotask(() => anchorRef.current?.querySelector('.ptcPlusAuthorButton')
503
+ ?.focus({ preventScroll: true }))
504
+ } }) : null,
468
505
  settingsOpen && view.reachable
469
506
  ? h(PTCPlusSettingsDialog, {
470
- t, usePtcSettings, updateSetting, callUserBindings,
507
+ t, usePtcSettings, updateSetting, updateCapabilityConfiguration, callUserBindings,
471
508
  onClose: () => {
472
509
  setSettingsOpen(false)
473
510
  queueMicrotask(() => anchorRef.current?.querySelector('.ptcPlusAuthorButton')
@@ -477,13 +514,13 @@ export function createAuthoringView(React, deps) {
477
514
  toast === null ? null : isHostComponent(Toast)
478
515
  ? h(Toast, {
479
516
  key: toast.sequence,
480
- text: toast.text,
517
+ text: toast.key ? t(toast.key, toast.params) : capabilityCommandText(t, toast.text),
481
518
  anchor: anchorRef.current,
482
519
  onDone: () => setToast(null),
483
520
  })
484
521
  : h('span', {
485
522
  key: toast.sequence, className: 'ptcPlusComposerNotice', role: 'status',
486
- }, toast.text))
523
+ }, toast.key ? t(toast.key, toast.params) : capabilityCommandText(t, toast.text)))
487
524
  }
488
525
 
489
526
  function BindingCandidateContent({ candidate, t, showName = true }) {
@@ -202,7 +202,8 @@ export function createBindingReviews(call) {
202
202
  return () => {
203
203
  attached = false
204
204
  invalidate()
205
- publish({ mounted: false, reachable: false, writable: false })
205
+ // The composer observer owns reachability independently of draft mounting.
206
+ publish({ mounted: false, writable: false })
206
207
  }
207
208
  },
208
209
  reset() {
@@ -0,0 +1,18 @@
1
+ import { readBindingToggle } from '../internal/binding-toggle-receipt.js'
2
+
3
+ /** A separate Chat node keeps configuration receipts distinct from ordinary hidden contexts. */
4
+ export const bindingToggleConversationDefinition = Object.freeze({
5
+ kind: 'ptc-plus-binding-toggle', target: 'chat',
6
+ match(event) {
7
+ return event.type === 'user/message' && event.surfaceOp === 'append' && readBindingToggle(event.data)
8
+ ? { id: event.data.id, role: 'start' } : null
9
+ },
10
+ start(_context, match) { return { seq: match.event.seq, receipt: readBindingToggle(match.event.data) } },
11
+ update(context) { return context.state },
12
+ buildViewNode(context) {
13
+ if (!context.state?.receipt) return null
14
+ return { key: context.key, id: context.id, kind: 'ptc-plus-binding-toggle', target: 'chat',
15
+ anchorSeq: context.state.seq, location: context.start?.location ?? { kind: 'unresolved' },
16
+ visibility: 'visible', data: context.state.receipt }
17
+ },
18
+ })