dsh-ptc-plus 0.5.3 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.en.md +3 -3
  2. package/README.md +3 -3
  3. package/client.cjs +3326 -2881
  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/0005-temporary-rejected-cell-edit-transport.md +2 -2
  8. package/docs/adr/0017-track-the-latest-dsh-public-surface.md +4 -4
  9. package/docs/architecture.md +1 -1
  10. package/docs/capability-planning-components.md +6 -6
  11. package/docs/capability-projection.md +1 -1
  12. package/docs/capability-surface.md +133 -0
  13. package/docs/client-ui.md +17 -15
  14. package/docs/client-visual-contract.md +7 -7
  15. package/docs/installation.md +14 -7
  16. package/docs/panel-concept.md +171 -0
  17. package/docs/publishing.md +1 -1
  18. package/docs/release-notes.md +1 -1
  19. package/docs/releases/v0.5.4.md +79 -0
  20. package/docs/releases/v0.5.5.md +41 -0
  21. package/docs/releases/v0.5.6.md +41 -0
  22. package/docs/runtime-reference.md +14 -0
  23. package/docs/verification.md +1 -1
  24. package/index.js +117 -30
  25. package/internal/binding-adoption.js +103 -11
  26. package/internal/capability-command-receipt.js +99 -15
  27. package/internal/capability-environment.js +1108 -348
  28. package/internal/capability-plan.js +18 -2
  29. package/internal/capability-policy.js +13 -0
  30. package/internal/capability-selection.js +330 -0
  31. package/internal/direct-surface-owner.js +17 -4
  32. package/internal/edit-transport-owner.js +3 -2
  33. package/internal/host-tool-sdk.js +17 -29
  34. package/internal/message-sources.js +21 -0
  35. package/internal/program-bindings.js +13 -4
  36. package/internal/runtime-bridge-owner.js +9 -9
  37. package/internal/runtime-config.js +10 -0
  38. package/internal/session-log-view.js +47 -5
  39. package/internal/settings-compat.js +6 -0
  40. package/internal/user-bindings-owner.js +20 -3
  41. package/package.json +45 -31
  42. package/src/client-authoring-view.js +7 -8
  43. package/src/client-capabilities-view.js +871 -948
  44. package/src/client-capability-sort.js +22 -9
  45. package/src/client-copy.js +116 -294
  46. package/src/client-settings-view.js +90 -33
  47. package/src/client-styles.js +67 -19
  48. package/src/client.js +46 -21
  49. package/internal/capability-delivery-proof.js +0 -70
@@ -9,9 +9,38 @@ import { deepEqualJson, isJsonValue } from '@deepseek-ai/dsh-util-values'
9
9
  import { deepFreeze, isRecord } from './record-utils.js'
10
10
  import { advanceRuntimeHistory, createRuntimeHistory } from './recovery-tips.js'
11
11
 
12
- import { isHostRuntimeContextSource } from './message-sources.js'
12
+ import { isHostRuntimeContextSource, isPtcMessageSource } from './message-sources.js'
13
+ import { BINDING_ADOPTION_FORM, readBindingAdoption } from './binding-adoption.js'
13
14
  const SYSTEM_PROMPT_CLEARED = 'Current runtime context: none. Earlier runtime-context snapshots no longer apply.'
14
15
 
16
+ /**
17
+ * A PTC binding-adoption statement, readable or not.
18
+ *
19
+ * The fold has to recognize the statement even when this build cannot resolve its
20
+ * wording: the fact it keeps is what a restored checkpoint replays, so a fact that
21
+ * dropped the form could not reproduce the record the fold appended.
22
+ */
23
+ function isBindingAdoptionStatement(message) {
24
+ const source = message?.source
25
+ return isPtcMessageSource(source) && source.form === BINDING_ADOPTION_FORM
26
+ }
27
+
28
+ /**
29
+ * An adoption statement this build cannot resolve, kept as the statement it is.
30
+ *
31
+ * Both the panel and the `/binding` command answer from this projection, and the
32
+ * answer has to be this session's *newest* statement. A projection that dropped a
33
+ * statement it could not resolve would let the record the session superseded answer
34
+ * instead, so the statement is retained with no declined list — the absence is what
35
+ * marks it unreadable. It never reaches the model-visible surface, which is built
36
+ * from resolvable runtime messages only.
37
+ */
38
+ function unreadableBindingAdoption(message) {
39
+ if (!isBindingAdoptionStatement(message)) return undefined
40
+ if (readBindingAdoption(message) !== undefined) return undefined
41
+ return Object.freeze({ form: BINDING_ADOPTION_FORM })
42
+ }
43
+
15
44
  /** DSH's empty aggregate uses an exact owner marker without a snapshot form. */
16
45
  export function systemPromptSnapshotSections(message) {
17
46
  const source = message?.source
@@ -72,7 +101,8 @@ export function runtimeMessageFacts(records, initial = {}) {
72
101
 
73
102
  function presentationEvent(event) {
74
103
  const recognized = event?.type === 'user/message'
75
- && (systemPromptSnapshotSections(event.data) !== undefined || readRuntimeMessage(event.data) !== undefined)
104
+ && (systemPromptSnapshotSections(event.data) !== undefined || readRuntimeMessage(event.data) !== undefined
105
+ || isBindingAdoptionStatement(event.data))
76
106
  const data = recognized ? { source: event.data.source, content: event.data.content }
77
107
  : event?.type === 'user/message' && typeof event.data?.source?.kind === 'string'
78
108
  ? { source: { kind: event.data.source.kind } } : undefined
@@ -86,11 +116,14 @@ function applyLogEvent(state, event, index = event?.seq,
86
116
  timeline = advanceSessionTimeline(state.timeline, event, index)) {
87
117
  const sections = event?.type === 'user/message' ? systemPromptSnapshotSections(event.data) : undefined
88
118
  const ptc = event?.type === 'user/message' ? readRuntimeMessage(event.data) : undefined
119
+ // An adoption statement this build cannot resolve is still this session's statement,
120
+ // so it stays in the projection as the newest one rather than disappearing.
121
+ const unresolvedAdoption = ptc === undefined ? unreadableBindingAdoption(event?.data) : undefined
89
122
  const contextStep = state.contextStep + (isContextStep(event) ? 1 : 0)
90
123
  const surfaceNodes = advanceSessionSurface(state.surfaceNodes, event)
91
124
  let visibleRuntimeMessages = state.visibleRuntimeMessages
92
- if (timeline === state.timeline && sections === undefined && ptc === undefined && contextStep === state.contextStep
93
- && surfaceNodes === state.surfaceNodes) return state
125
+ if (timeline === state.timeline && sections === undefined && ptc === undefined && unresolvedAdoption === undefined
126
+ && contextStep === state.contextStep && surfaceNodes === state.surfaceNodes) return state
94
127
  const next = { ...state, timeline, contextStep,
95
128
  logFacts: [...state.logFacts, deepFreeze({ index, event: presentationEvent(event) })],
96
129
  }
@@ -110,6 +143,11 @@ function applyLogEvent(state, event, index = event?.seq,
110
143
  const record = Object.freeze({ ...ptc, index, contextStep: state.contextStep, producer: 'ptc-plus' })
111
144
  next.ptcMessages = [...state.ptcMessages, record]
112
145
  next.runtimeMessagesBySeq = { ...next.runtimeMessagesBySeq, [`k${event.seq}`]: record }
146
+ } else if (unresolvedAdoption !== undefined) {
147
+ // Listed as this session's newest statement, but not as a resolvable runtime
148
+ // message: the ordered surface below is built from `runtimeMessagesBySeq`.
149
+ next.ptcMessages = [...state.ptcMessages,
150
+ Object.freeze({ ...unresolvedAdoption, index, contextStep: state.contextStep, producer: 'ptc-plus' })]
113
151
  }
114
152
  if (event?.surfaceOp === 'append') {
115
153
  const record = next.runtimeMessagesBySeq[`k${event.seq}`]
@@ -133,7 +171,11 @@ export function createSessionLogProjection(options = {}) {
133
171
  const replayBudget = admissionReplayBudget(options.replayBudgetBytes)
134
172
  return Object.freeze({
135
173
  key: SESSION_LOG_PROJECTION_KEY,
136
- stateVersion: 13,
174
+ // 14 records the binding-adoption statement this build cannot resolve: the fact it
175
+ // keeps now carries the form its own replay needs, and `ptcMessages` gained a record
176
+ // for it. A checkpoint written by an earlier fold answers a different question, so
177
+ // the version moves and stored checkpoints are refolded rather than admitted.
178
+ stateVersion: 14,
137
179
  stateSchema: Object.freeze({ parse(value) {
138
180
  if (!isJsonValue(value) || !isRecord(value) || !isRecord(value.timeline)
139
181
  || typeof value.timeline.openTurn !== 'boolean' || typeof value.timeline.found !== 'boolean'
@@ -74,6 +74,12 @@ export function installSettingsSectionCompat({
74
74
  && typeof writeNamespace === 'string' && writeNamespace.length > 0
75
75
  ? Object.freeze({
76
76
  update: (patch, expectedRevision) => provider.update(writeNamespace, patch, expectedRevision),
77
+ // The service applies field edits as `{ op: 'set' | 'unset', path }`; `update` can only
78
+ // restate values, so expose the primitive when it exists instead of narrowing the callers
79
+ // to a writer that cannot remove a field.
80
+ ...(typeof provider.mutate === 'function'
81
+ ? { mutate: (ops, expectedRevision) => provider.mutate(writeNamespace, ops, expectedRevision) }
82
+ : {}),
77
83
  read: () => provider.describe?.().find(descriptor => descriptor.ns === writeNamespace),
78
84
  }) : undefined
79
85
  const unbind = onProvider(provider, writer)
@@ -2,11 +2,14 @@ import { PTC_MESSAGE_SOURCE_KIND } from './message-sources.js'
2
2
  import { sessionExecutionAvailable } from './host-tool-sdk.js'
3
3
  import { bindingToggleMessage } from './binding-toggle-receipt.js'
4
4
  import {
5
- bindingAdoptionMessage,
5
+ BINDING_ADOPTION_RECORD,
6
6
  clearedBindingAdoption,
7
7
  declinedBindingEntries,
8
8
  withBindingAdoption,
9
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'
10
13
  import { RPC_CONTRACTS } from './rpc-contract.js'
11
14
  import { randomUUID } from 'node:crypto'
12
15
  import { dirname } from 'node:path'
@@ -232,6 +235,11 @@ export function createUserBindingsOwner(ctx, options = {}) {
232
235
  let disposed = false
233
236
  const codeConsole = new UserBindingConsole(currentOptions)
234
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
235
243
  const sessionAgents = new Map()
236
244
  const sessionGenerations = new Map()
237
245
  const agentLifecycles = new WeakMap()
@@ -331,8 +339,17 @@ export function createUserBindingsOwner(ctx, options = {}) {
331
339
  if (session === undefined || typeof session.append !== 'function') {
332
340
  throw new Error('binding adoption requires a live session')
333
341
  }
334
- if (session.append('user/message', bindingAdoptionMessage(adoption), { surfaceOp: 'append' }) === undefined) {
335
- throw new Error('the session did not record the binding adoption decision')
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)}`)
336
353
  }
337
354
  return { kind: 'success', text: adoptionReceipt(adoption) }
338
355
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ptc-plus",
3
- "version": "0.5.3",
3
+ "version": "0.5.6",
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.2",
56
56
  "@jridgewell/trace-mapping": "^0.3.31",
57
57
  "acorn": "^8.15.0"
58
58
  },
@@ -174,36 +174,50 @@
174
174
  "@codemirror/language": "^6.12.4",
175
175
  "@codemirror/state": "^6.7.4",
176
176
  "@codemirror/view": "^6.43.11",
177
- "@deepseek-ai/dsh": "next",
178
- "@deepseek-ai/dsh-agent-loop": "next",
179
- "@deepseek-ai/dsh-api-gateway": "next",
180
- "@deepseek-ai/dsh-api-remotes": "next",
181
- "@deepseek-ai/dsh-atomic-write": "next",
182
- "@deepseek-ai/dsh-client-locale": "next",
183
- "@deepseek-ai/dsh-client-store": "next",
184
- "@deepseek-ai/dsh-client-test-runtime": "next",
185
- "@deepseek-ai/dsh-client-ui-conversation": "next",
186
- "@deepseek-ai/dsh-client-ui-primitives": "next",
187
- "@deepseek-ai/dsh-client-ui-renderer": "next",
188
- "@deepseek-ai/dsh-client-ui-session": "next",
189
- "@deepseek-ai/dsh-client-ui-slots": "next",
190
- "@deepseek-ai/dsh-commands": "next",
191
- "@deepseek-ai/dsh-compaction-image-offload": "next",
192
- "@deepseek-ai/dsh-home-paths": "next",
193
- "@deepseek-ai/dsh-llm": "next",
194
- "@deepseek-ai/dsh-session-format-catalog": "next",
195
- "@deepseek-ai/dsh-session-format-v0-to-v1": "next",
196
- "@deepseek-ai/dsh-session-persistence-jsonl": "next",
197
- "@deepseek-ai/dsh-settings": "next",
198
- "@deepseek-ai/dsh-skill-filesystem": "next",
199
- "@deepseek-ai/dsh-tool-cordis": "next",
200
- "@deepseek-ai/dsh-tools": "next",
201
- "@deepseek-ai/dsh-typert-protocol": "next",
202
- "@deepseek-ai/dsh-typert-registry": "next",
203
- "@deepseek-ai/dsh-util-workspace-path": "next",
177
+ "@deepseek-ai/cordis": "4.0.6-alpha.2",
178
+ "@deepseek-ai/cordis-plugin-group": "1.0.6-alpha.2",
179
+ "@deepseek-ai/cordis-plugin-include": "1.0.11-alpha.2",
180
+ "@deepseek-ai/cordis-plugin-loader": "1.0.7-alpha.2",
181
+ "@deepseek-ai/cordis-plugin-timer": "1.1.8-alpha.2",
182
+ "@deepseek-ai/cosmokit": "1.8.7-alpha.2",
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",
204
217
  "@lezer/highlight": "^1.2.3",
205
218
  "@radix-ui/react-select": "^2.2.6",
206
219
  "@shikijs/langs": "^4.3.1",
220
+ "@tanstack/react-virtual": "^3.14.24",
207
221
  "@testing-library/react": "^16.3.3",
208
222
  "amaro": "1.1.11",
209
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
  }