dsh-ptc-plus 0.5.2 → 0.5.4

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 (72) hide show
  1. package/README.en.md +4 -2
  2. package/README.md +4 -2
  3. package/client.cjs +3392 -2811
  4. package/compiler-core.cjs +127 -127
  5. package/compiler-core.cjs.map +2 -2
  6. package/docs/README.md +1 -0
  7. package/docs/adr/0017-track-the-latest-dsh-public-surface.md +4 -2
  8. package/docs/adr/0019-plugin-settings-and-kill-switch.md +2 -2
  9. package/docs/adr/0023-global-user-bindings.md +12 -0
  10. package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +1 -1
  11. package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +2 -2
  12. package/docs/adr/0044-coexist-with-the-official-persistent-repl.md +4 -4
  13. package/docs/adr/0046-gate-the-commit-with-the-semantic-evidence-tier.md +1 -1
  14. package/docs/adr/0048-supersede-a-rewritten-cell.md +48 -0
  15. package/docs/adr/0049-prove-a-settlement-against-its-published-record.md +53 -0
  16. package/docs/adr/0050-read-host-surfaces-through-their-published-contract.md +48 -0
  17. package/docs/adr/README.md +7 -1
  18. package/docs/architecture.md +1 -1
  19. package/docs/capability-planning-components.md +6 -6
  20. package/docs/capability-projection.md +1 -1
  21. package/docs/capability-surface.md +133 -0
  22. package/docs/client-ui.md +17 -15
  23. package/docs/client-visual-contract.md +7 -7
  24. package/docs/durability-design.md +9 -2
  25. package/docs/installation.md +17 -10
  26. package/docs/panel-concept.md +171 -0
  27. package/docs/releases/v0.5.2.md +26 -14
  28. package/docs/releases/v0.5.3.md +77 -0
  29. package/docs/releases/v0.5.4.md +75 -0
  30. package/docs/runtime-reference.md +21 -1
  31. package/docs/user-bindings.en.md +15 -0
  32. package/docs/user-bindings.md +15 -0
  33. package/docs/v0.5.3-goals.md +117 -0
  34. package/docs/verification.md +4 -4
  35. package/index.js +176 -41
  36. package/internal/binding-adoption.js +228 -0
  37. package/internal/capability-command-receipt.js +106 -8
  38. package/internal/capability-environment.js +1109 -312
  39. package/internal/capability-plan.js +18 -2
  40. package/internal/capability-policy.js +13 -0
  41. package/internal/capability-selection.js +330 -0
  42. package/internal/config-spec.js +14 -5
  43. package/internal/cordis-tools-owner.js +2 -1
  44. package/internal/direct-surface-owner.js +44 -17
  45. package/internal/edit-transport-owner.js +6 -5
  46. package/internal/execution-provider-owner.js +6 -0
  47. package/internal/execution-seam-compat.js +25 -0
  48. package/internal/host-tool-sdk.js +308 -0
  49. package/internal/message-sources.js +21 -0
  50. package/internal/official-repl-priority.js +31 -10
  51. package/internal/program-bindings.js +25 -5
  52. package/internal/repl-memory-projection.js +21 -2
  53. package/internal/runtime-bridge-owner.js +118 -9
  54. package/internal/runtime-config.js +10 -0
  55. package/internal/runtime-messages.js +5 -0
  56. package/internal/session-journal-recovery.js +210 -32
  57. package/internal/session-journal-schema.js +3 -0
  58. package/internal/session-journal.js +46 -9
  59. package/internal/session-log-view.js +69 -7
  60. package/internal/session-runtime.js +106 -39
  61. package/internal/settings-compat.js +6 -0
  62. package/internal/user-bindings-owner.js +126 -6
  63. package/package.json +59 -37
  64. package/src/client-authoring-view.js +7 -8
  65. package/src/client-capabilities-view.js +874 -909
  66. package/src/client-capability-sort.js +22 -9
  67. package/src/client-copy.js +118 -272
  68. package/src/client-indicator-view.js +2 -1
  69. package/src/client-repl-view.js +35 -16
  70. package/src/client-settings-view.js +90 -33
  71. package/src/client-styles.js +67 -19
  72. package/src/client.js +49 -22
@@ -1,5 +1,15 @@
1
1
  import { PTC_MESSAGE_SOURCE_KIND } from './message-sources.js'
2
+ import { sessionExecutionAvailable } from './host-tool-sdk.js'
2
3
  import { bindingToggleMessage } from './binding-toggle-receipt.js'
4
+ import {
5
+ BINDING_ADOPTION_RECORD,
6
+ clearedBindingAdoption,
7
+ declinedBindingEntries,
8
+ withBindingAdoption,
9
+ } from './binding-adoption.js'
10
+ // Namespace import: the record writer is one public surface of the host, and the module
11
+ // load must not depend on a named export that a future release could rename.
12
+ import * as dshSession from '@deepseek-ai/dsh-session'
3
13
  import { RPC_CONTRACTS } from './rpc-contract.js'
4
14
  import { randomUUID } from 'node:crypto'
5
15
  import { dirname } from 'node:path'
@@ -26,6 +36,7 @@ import {
26
36
  createUserBindingsSnapshot,
27
37
  normalizeUserBindingEntry,
28
38
  normalizeUserBindingsDocument,
39
+ selectUserBindingsSnapshot,
29
40
  storedUserBindingsDocument,
30
41
  } from './user-bindings.js'
31
42
 
@@ -78,6 +89,40 @@ function parseAuthoringCommand(rawInput) {
78
89
  return { kind: 'help' }
79
90
  }
80
91
 
92
+ /**
93
+ * The adoption subcommands of `/binding`. They state which supplied entries this
94
+ * session uses; authoring and adoption share one command because both answer
95
+ * questions about the same global catalog, and a second command would suggest
96
+ * they were separate features.
97
+ */
98
+ function parseAdoptionCommand(rawInput) {
99
+ const input = typeof rawInput === 'string' ? rawInput.trim() : ''
100
+ if (input === 'status') return { kind: 'status' }
101
+ if (input === 'reset') return { kind: 'reset' }
102
+ const decision = /^(use|skip)\s+(\S+)$/.exec(input)
103
+ if (decision === null) return undefined
104
+ return { kind: decision[1] === 'use' ? 'use' : 'skip', id: decision[2] }
105
+ }
106
+
107
+ function adoptionStatusText(catalog, adoption) {
108
+ const declined = declinedBindingEntries(adoption)
109
+ const supplied = catalog.entries
110
+ const listed = supplied.map(entry => `${entry.id} (${entry.name}): ${declined.has(entry.id)
111
+ ? 'not used by this session' : 'used by this session'}`)
112
+ const unavailable = [...declined].filter(id => !supplied.some(entry => entry.id === id))
113
+ return [
114
+ 'Global binding catalog supply and this session\'s adoption of it. Adoption decides which supplied entries this session uses; it does not report whether the worker installed them.',
115
+ ...(listed.length === 0 ? ['The global catalog supplies no enabled entries.'] : listed),
116
+ ...(unavailable.length === 0 ? [] : [`Declined but not supplied by the global catalog, therefore not used and not revivable here: ${JSON.stringify(unavailable)}`]),
117
+ ].join('\n')
118
+ }
119
+
120
+ function adoptionReceipt(adoption) {
121
+ return `Session binding adoption recorded. This session declines ${JSON.stringify([...declinedBindingEntries(adoption)])}. `
122
+ + 'The global catalog and other sessions are unchanged, and adoption can only withdraw a supplied entry. '
123
+ + 'Whether the worker installed the entries this session still uses is reported with the next accepted cell, not by this receipt.'
124
+ }
125
+
81
126
  function authoringTask(command, current) {
82
127
  if (command.kind === 'new') {
83
128
  return `Create a Global User Binding draft for this requirement:\n\n${command.requirement}`
@@ -190,6 +235,11 @@ export function createUserBindingsOwner(ctx, options = {}) {
190
235
  let disposed = false
191
236
  const codeConsole = new UserBindingConsole(currentOptions)
192
237
  const commandRegistrations = new Map()
238
+ // The host's writer for a fact that belongs in the log but not on the model-visible
239
+ // surface. Every supported release publishes it, so an adoption decision is taken when
240
+ // it is made instead of waiting for a step to carry a message.
241
+ const appendRecord = typeof options.appendRecord === 'function' ? options.appendRecord
242
+ : typeof dshSession.appendPluginRecord === 'function' ? dshSession.appendPluginRecord : undefined
193
243
  const sessionAgents = new Map()
194
244
  const sessionGenerations = new Map()
195
245
  const agentLifecycles = new WeakMap()
@@ -265,6 +315,57 @@ export function createUserBindingsOwner(ctx, options = {}) {
265
315
  const requireEnabled = () => {
266
316
  if (!enabled) throw new Error('Global User Bindings are disabled')
267
317
  }
318
+ // Adoption is read from the session's own recorded decision, never from a
319
+ // second in-memory map: the log is the owner, so a restart reconstructs the
320
+ // same environment without a migration step.
321
+ const adoptionForAgent = (agent) => {
322
+ if (typeof options.bindingAdoptionForAgent !== 'function' || agent === undefined || agent === null) return undefined
323
+ return options.bindingAdoptionForAgent(agent)
324
+ }
325
+ /**
326
+ * Supply minus this session's declines. The catalog decides which entries
327
+ * exist; adoption only withdraws one, so no entry can appear here that the
328
+ * global configuration did not supply.
329
+ */
330
+ const adoptedCatalog = (snapshot, adoption) => {
331
+ const declined = declinedBindingEntries(adoption)
332
+ if (declined.size === 0) return snapshot
333
+ const kept = snapshot.entries.filter(entry => !declined.has(entry.id))
334
+ if (kept.length === snapshot.entries.length) return snapshot
335
+ return selectUserBindingsSnapshot(snapshot, kept.map(entry => entry.id))
336
+ }
337
+ const recordAdoption = (agent, adoption) => {
338
+ const session = agent?.session
339
+ if (session === undefined || typeof session.append !== 'function') {
340
+ throw new Error('binding adoption requires a live session')
341
+ }
342
+ if (appendRecord === undefined) {
343
+ throw new Error('ptc-plus: this DSH release publishes no plugin-record writer, so a binding adoption decision cannot be recorded')
344
+ }
345
+ // Adoption is this session's own statement and the catalog the model reads already
346
+ // reflects it, so it is taken the moment the control is used: durable through the log,
347
+ // outside the model-visible surface, and requiring no prior step. A session that cannot
348
+ // take the record reports that instead of a decision nothing stored.
349
+ try {
350
+ appendRecord(session, BINDING_ADOPTION_RECORD, adoption)
351
+ } catch (error) {
352
+ throw new Error(`the session did not record the binding adoption decision: ${error?.message ?? String(error)}`)
353
+ }
354
+ return { kind: 'success', text: adoptionReceipt(adoption) }
355
+ }
356
+ const stateAdoption = async (agent, command) => {
357
+ requireEnabled()
358
+ // Status and validation read the full supplied catalog: an adopted subset
359
+ // cannot prove what it was derived from.
360
+ const catalog = await store.snapshot()
361
+ const current = adoptionForAgent(agent)
362
+ if (command.kind === 'status') return { kind: 'success', text: adoptionStatusText(catalog, current) }
363
+ if (command.kind === 'reset') return recordAdoption(agent, clearedBindingAdoption())
364
+ if (!catalog.entries.some(entry => entry.id === command.id)) {
365
+ throw new Error(`the global binding catalog does not supply entry ${JSON.stringify(command.id)}; this session can decline a supplied entry but cannot add or revive one`)
366
+ }
367
+ return recordAdoption(agent, withBindingAdoption(current, command.id, command.kind === 'use'))
368
+ }
268
369
  const draftFor = (capability) => {
269
370
  if (typeof capability !== 'string' || capability === '') {
270
371
  throw new TypeError('draft capability must be a non-empty string')
@@ -482,7 +583,7 @@ export function createUserBindingsOwner(ctx, options = {}) {
482
583
  const requireCurrentAgent = () => {
483
584
  if (!agentIsCurrent(agent, sessionId, agentGeneration)
484
585
  || commandRegistrations.get(sessionId)?.agent !== agent
485
- || ctx.tools.get('run_code', agent) === undefined) {
586
+ || !sessionExecutionAvailable(ctx.tools, agent)) {
486
587
  throw new Error('this binding authoring Agent is no longer active')
487
588
  }
488
589
  }
@@ -617,6 +718,14 @@ export function createUserBindingsOwner(ctx, options = {}) {
617
718
 
618
719
  const commandHandler = async ({ agent, rawInput, signal, commandId }) => {
619
720
  signal?.throwIfAborted?.()
721
+ const adoption = parseAdoptionCommand(rawInput)
722
+ if (adoption !== undefined) {
723
+ try {
724
+ return await stateAdoption(agent, adoption)
725
+ } catch (error) {
726
+ return { kind: 'error', text: error instanceof Error ? error.message : String(error) }
727
+ }
728
+ }
620
729
  const command = parseAuthoringCommand(rawInput)
621
730
  if (command.kind === 'help') return { kind: 'error', text: `Usage: ${COMMAND_USAGE}` }
622
731
  try {
@@ -660,14 +769,14 @@ export function createUserBindingsOwner(ctx, options = {}) {
660
769
  // still claiming one.
661
770
  if (disposed || !enabled || lifecycleGeneration !== registration.generation
662
771
  || !agentIsCurrent(agent, sessionId, agentGeneration)
663
- || ctx.tools.get('run_code', agent) === undefined
772
+ || !sessionExecutionAvailable(ctx.tools, agent)
664
773
  || commandRegistrations.get(sessionId) !== registration) {
665
774
  throw new Error('Global User Bindings binding command registration is no longer eligible')
666
775
  }
667
776
  registerOwned(commandCtx, commandCtx.commands, {
668
777
  name: 'binding',
669
- description: 'Create or revise a Global User Binding draft with the current agent',
670
- input: { hint: 'new <requirement> | edit <id> <requirement>' },
778
+ description: 'Author a Global User Binding draft, or state which supplied entries this session uses',
779
+ input: { hint: 'new <requirement> | edit <id> <requirement> | status | use <id> | skip <id> | reset' },
671
780
  handler: commandHandler,
672
781
  }, 'binding command')
673
782
  })
@@ -712,13 +821,13 @@ export function createUserBindingsOwner(ctx, options = {}) {
712
821
  }
713
822
  if (!agentIsCurrent(agent, sessionId, agentGeneration)) return
714
823
  const eligible = enabled && draftProjectionAvailable && !disposed
715
- && ctx.tools.get('run_code', agent) !== undefined
824
+ && sessionExecutionAvailable(ctx.tools, agent)
716
825
  const current = commandRegistrations.get(sessionId)
717
826
  if (eligible && current?.agent === agent) return
718
827
  if (current !== undefined) await removeCommandFor(sessionId, current)
719
828
  if (!agentIsCurrent(agent, sessionId, agentGeneration)) return
720
829
  const stillEligible = enabled && draftProjectionAvailable && !disposed
721
- && ctx.tools.get('run_code', agent) !== undefined
830
+ && sessionExecutionAvailable(ctx.tools, agent)
722
831
  if (stillEligible) {
723
832
  registerCommandFor(agent)
724
833
  return
@@ -918,6 +1027,17 @@ export function createUserBindingsOwner(ctx, options = {}) {
918
1027
  async snapshot() {
919
1028
  return enabled ? store.snapshot() : undefined
920
1029
  },
1030
+ /**
1031
+ * The catalog entries one session's worker should receive. Without a stated
1032
+ * adoption this is exactly the supplied catalog, so 0.5.2 behavior is
1033
+ * unchanged for every session that never declines an entry.
1034
+ */
1035
+ async snapshotForAgent(agent) {
1036
+ if (!enabled) return undefined
1037
+ const snapshot = await store.snapshot()
1038
+ const adoption = adoptionForAgent(agent)
1039
+ return adoption === undefined ? snapshot : adoptedCatalog(snapshot, adoption)
1040
+ },
921
1041
  async list() {
922
1042
  requireEnabled()
923
1043
  return store.list()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ptc-plus",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
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",
@@ -43,7 +43,7 @@
43
43
  ],
44
44
  "engines": {
45
45
  "node": "^22.19.0 || >=24.0.0",
46
- "dsh": "*"
46
+ "dsh": ">=0.2.1-0 <0.2.3"
47
47
  },
48
48
  "allowScripts": {
49
49
  "esbuild": false
@@ -51,8 +51,8 @@
51
51
  "dependencies": {
52
52
  "@babel/code-frame": "^7.29.7",
53
53
  "@babel/parser": "^7.29.0",
54
- "@deepseek-ai/dsh-util-values": "^0.2.0-rc.2 || ^0.2.1-alpha.1",
55
- "@deepseek-ai/schemastery": "^3.18.3",
54
+ "@deepseek-ai/dsh-util-values": "^0.2.2-alpha.2",
55
+ "@deepseek-ai/schemastery": "~3.18.6-alpha.1",
56
56
  "@jridgewell/trace-mapping": "^0.3.31",
57
57
  "acorn": "^8.15.0"
58
58
  },
@@ -69,9 +69,15 @@
69
69
  "@deepseek-ai/dsh-skill-filesystem": "*",
70
70
  "@deepseek-ai/dsh-spill-policy": "*",
71
71
  "@deepseek-ai/dsh-tool-cordis": "*",
72
+ "@deepseek-ai/dsh-tool-executor-ptc": "*",
72
73
  "@deepseek-ai/dsh-tools": "*",
73
74
  "@deepseek-ai/dsh-typert-protocol": "*"
74
75
  },
76
+ "peerDependenciesMeta": {
77
+ "@deepseek-ai/dsh-tool-executor-ptc": {
78
+ "optional": true
79
+ }
80
+ },
75
81
  "scripts": {
76
82
  "build": "node scripts/client-bundle.mjs build && node scripts/compiler-bundle.mjs build",
77
83
  "build:check": "node scripts/client-bundle.mjs check && node scripts/compiler-bundle.mjs check",
@@ -88,8 +94,9 @@
88
94
  "test:client:fallback": "node scripts/client-fallback-smoke.mjs",
89
95
  "test:client:layout": "node scripts/client-binding-layout.mjs",
90
96
  "readme:screenshots": "node scripts/readme-screenshots.mjs",
91
- "host:baseline:update": "node scripts/dsh-dependency-baseline.mjs update",
92
- "host:baseline:verify": "node scripts/dsh-dependency-baseline.mjs verify",
97
+ "host:channels:update": "node scripts/host-channel-manifest.mjs update",
98
+ "host:channels:graph": "node scripts/host-channel-manifest.mjs graph",
99
+ "host:channels:verify": "node scripts/host-channel-manifest.mjs verify",
93
100
  "test:host-contract": "node scripts/dsh-host-contract-suite.mjs",
94
101
  "prepack": "npm run build:check",
95
102
  "hooks:install": "node scripts/review-findings.mjs install-hook",
@@ -109,14 +116,15 @@
109
116
  "test:backend": "node scripts/test-suite-files.mjs",
110
117
  "test:backend:fast": "node scripts/test-suite-files.mjs --fast",
111
118
  "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",
112
- "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",
113
- "verify:check": "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 scripts/deterministic-test-groups.mjs --check",
114
- "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",
119
+ "verify": "npm run review:validate && npm run host:channels:verify && npm run host:shape-check && 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",
120
+ "verify:check": "npm run review:validate && npm run host:channels:verify && npm run host:shape-check && npm run semantic:check && npm run build:check && node --check index.js && node --check internal/kernel-worker.js && node scripts/deterministic-test-groups.mjs --check",
121
+ "verify:ci": "npm run review:validate && npm run host:channels:verify && npm run host:shape-check && 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",
115
122
  "check": "node scripts/review-findings.mjs check",
116
123
  "verify:docs": "node scripts/review-findings.mjs docs",
117
124
  "test:expensive": "node scripts/expensive-headless-acceptance.mjs",
118
125
  "test:ab": "node scripts/ab-headless-trajectories.mjs",
119
- "test:package:consumer": "node scripts/npm-consumer-smoke.mjs"
126
+ "test:package:consumer": "node scripts/npm-consumer-smoke.mjs",
127
+ "host:shape-check": "node scripts/check-host-version-literals.mjs"
120
128
  },
121
129
  "keywords": [
122
130
  "deepseek-harness",
@@ -166,36 +174,50 @@
166
174
  "@codemirror/language": "^6.12.4",
167
175
  "@codemirror/state": "^6.7.4",
168
176
  "@codemirror/view": "^6.43.11",
169
- "@deepseek-ai/dsh": "next",
170
- "@deepseek-ai/dsh-agent-loop": "next",
171
- "@deepseek-ai/dsh-api-gateway": "next",
172
- "@deepseek-ai/dsh-api-remotes": "next",
173
- "@deepseek-ai/dsh-atomic-write": "next",
174
- "@deepseek-ai/dsh-client-locale": "next",
175
- "@deepseek-ai/dsh-client-store": "next",
176
- "@deepseek-ai/dsh-client-test-runtime": "next",
177
- "@deepseek-ai/dsh-client-ui-conversation": "next",
178
- "@deepseek-ai/dsh-client-ui-primitives": "next",
179
- "@deepseek-ai/dsh-client-ui-renderer": "next",
180
- "@deepseek-ai/dsh-client-ui-session": "next",
181
- "@deepseek-ai/dsh-client-ui-slots": "next",
182
- "@deepseek-ai/dsh-commands": "next",
183
- "@deepseek-ai/dsh-compaction-image-offload": "next",
184
- "@deepseek-ai/dsh-home-paths": "next",
185
- "@deepseek-ai/dsh-llm": "next",
186
- "@deepseek-ai/dsh-session-format-catalog": "next",
187
- "@deepseek-ai/dsh-session-format-v0-to-v1": "next",
188
- "@deepseek-ai/dsh-session-persistence-jsonl": "next",
189
- "@deepseek-ai/dsh-settings": "next",
190
- "@deepseek-ai/dsh-skill-filesystem": "next",
191
- "@deepseek-ai/dsh-tool-cordis": "next",
192
- "@deepseek-ai/dsh-tools": "next",
193
- "@deepseek-ai/dsh-typert-protocol": "next",
194
- "@deepseek-ai/dsh-typert-registry": "next",
195
- "@deepseek-ai/dsh-util-workspace-path": "next",
177
+ "@deepseek-ai/cordis": "4.0.6-alpha.1",
178
+ "@deepseek-ai/cordis-plugin-group": "1.0.6-alpha.1",
179
+ "@deepseek-ai/cordis-plugin-include": "1.0.11-alpha.1",
180
+ "@deepseek-ai/cordis-plugin-loader": "1.0.7-alpha.1",
181
+ "@deepseek-ai/cordis-plugin-timer": "1.1.8-alpha.1",
182
+ "@deepseek-ai/cosmokit": "1.8.7-alpha.1",
183
+ "@deepseek-ai/dsh": "alpha",
184
+ "@deepseek-ai/dsh-agent-loop": "alpha",
185
+ "@deepseek-ai/dsh-agent-loop-testkit": "alpha",
186
+ "@deepseek-ai/dsh-api-gateway": "alpha",
187
+ "@deepseek-ai/dsh-api-remotes": "alpha",
188
+ "@deepseek-ai/dsh-atomic-write": "alpha",
189
+ "@deepseek-ai/dsh-client-locale": "alpha",
190
+ "@deepseek-ai/dsh-client-store": "alpha",
191
+ "@deepseek-ai/dsh-client-test-runtime": "alpha",
192
+ "@deepseek-ai/dsh-client-ui-conversation": "alpha",
193
+ "@deepseek-ai/dsh-client-ui-primitives": "alpha",
194
+ "@deepseek-ai/dsh-client-ui-renderer": "alpha",
195
+ "@deepseek-ai/dsh-client-ui-session": "alpha",
196
+ "@deepseek-ai/dsh-client-ui-slots": "alpha",
197
+ "@deepseek-ai/dsh-commands": "alpha",
198
+ "@deepseek-ai/dsh-compaction-image-offload": "alpha",
199
+ "@deepseek-ai/dsh-home-paths": "alpha",
200
+ "@deepseek-ai/dsh-llm": "alpha",
201
+ "@deepseek-ai/dsh-scope": "alpha",
202
+ "@deepseek-ai/dsh-session": "alpha",
203
+ "@deepseek-ai/dsh-session-format-catalog": "alpha",
204
+ "@deepseek-ai/dsh-session-format-v0-to-v1": "alpha",
205
+ "@deepseek-ai/dsh-session-persistence-jsonl": "alpha",
206
+ "@deepseek-ai/dsh-settings": "alpha",
207
+ "@deepseek-ai/dsh-skill": "alpha",
208
+ "@deepseek-ai/dsh-skill-filesystem": "alpha",
209
+ "@deepseek-ai/dsh-spill-policy": "alpha",
210
+ "@deepseek-ai/dsh-tool-cordis": "alpha",
211
+ "@deepseek-ai/dsh-tool-executor-native": "alpha",
212
+ "@deepseek-ai/dsh-tool-executor-ptc": "alpha",
213
+ "@deepseek-ai/dsh-tools": "alpha",
214
+ "@deepseek-ai/dsh-typert-protocol": "alpha",
215
+ "@deepseek-ai/dsh-typert-registry": "alpha",
216
+ "@deepseek-ai/dsh-util-workspace-path": "alpha",
196
217
  "@lezer/highlight": "^1.2.3",
197
218
  "@radix-ui/react-select": "^2.2.6",
198
219
  "@shikijs/langs": "^4.3.1",
220
+ "@tanstack/react-virtual": "^3.14.24",
199
221
  "@testing-library/react": "^16.3.3",
200
222
  "amaro": "1.1.11",
201
223
  "anser": "^2.3.5",
@@ -69,7 +69,11 @@ export function createAuthoringView(React, deps) {
69
69
  }) {
70
70
  const input = useInput?.(snapshot => snapshot)
71
71
  const available = useBindingCommand(snapshot => snapshot)
72
- const capabilityAvailable = useCapabilityCommand?.(snapshot => snapshot) === true
72
+ // The capability command source publishes nothing until a session observes it, and
73
+ // this entry is the surface that keeps it observed while mounted. Its value decides
74
+ // no branch here: whether `/capabilities` is listed is the command's own answer when
75
+ // it runs, so the refresh shortcut stays usable and the refusal carries its reason.
76
+ useCapabilityCommand?.(() => undefined)
73
77
  const settings = usePtcSettings(snapshot => snapshot)
74
78
  const [review, view] = useBindingReview(sessionId)
75
79
  const anchorRef = React.useRef(null)
@@ -113,8 +117,7 @@ export function createAuthoringView(React, deps) {
113
117
  const pluginEnabled = featureEnabled(settings, 'plugin') && (hasDraft || featureEnabled(settings, 'ptcMenu'))
114
118
  const canPrefill = typeof useInput === 'function' && typeof inputActions?.setDraft === 'function'
115
119
  const canAuthor = quickAccess && available && canPrefill
116
- const canRefresh = pluginEnabled && settings.status === 'ready' && settings.writable === true
117
- && capabilityAvailable && canPrefill
120
+ const canRefresh = pluginEnabled && settings.status === 'ready' && settings.writable === true && canPrefill
118
121
  const menuOpen = pluginEnabled && view.reachable && !managing && !capabilitiesOpen && !settingsOpen
119
122
  && menu !== null && menu.key === view.candidateKey
120
123
  // The dialog owns the management session; losing the input surface only
@@ -411,8 +414,7 @@ export function createAuthoringView(React, deps) {
411
414
  const actionButton = (id, copy) => h('button', {
412
415
  type: 'button', role: 'menuitem', className: 'ptcPlusMenuButton',
413
416
  disabled: id === 'refresh' && !canRefresh,
414
- title: id === 'refresh' && !capabilityAvailable ? t('capabilities.refreshUnavailable')
415
- : id === 'settings' ? settingsPath(t) : t(copy),
417
+ title: id === 'settings' ? settingsPath(t) : t(copy),
416
418
  onClick: () => selectMenuItem(id),
417
419
  }, actionCopy(id, copy))
418
420
  const actionSections = [
@@ -496,7 +498,6 @@ export function createAuthoringView(React, deps) {
496
498
  }) : null,
497
499
  SessionCapabilityControls && capabilitiesOpen && view.reachable
498
500
  ? h(SessionCapabilityControls, { sessionId, open: true, t,
499
- enabled: pluginEnabled && settings.status === 'ready' && settings.writable === true,
500
501
  onClose: () => {
501
502
  setCapabilitiesOpen(false)
502
503
  queueMicrotask(() => anchorRef.current?.querySelector('.ptcPlusAuthorButton')
@@ -747,8 +748,6 @@ export function createAuthoringView(React, deps) {
747
748
  node.outcome?.kind !== 'error' ? null : h('p', { className: 'ptcPlusMessage ptcPlusDanger' },
748
749
  node.outcome.text ?? t('bindings.commandFailed')),
749
750
  canOpen ? h(ActionButton, { onClick: () => openBindingReview(review) }, t('bindings.reviewOpen')) : null,
750
- !current.mounted && (candidate !== null || matches && projection.phase === 'ready')
751
- ? h('p', { className: 'ptcPlusMessage' }, t('bindings.reviewUnavailable')) : null,
752
751
  candidate === null ? null : h('details', { className: 'ptcPlusBindingSourceDetails' },
753
752
  h('summary', null, t('tool.source')), h(BindingCandidateContent, { candidate, t })))
754
753
  }