dsh-ptc-plus 0.4.8 → 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.
Files changed (139) hide show
  1. package/README.en.md +61 -123
  2. package/README.md +61 -123
  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 +42336 -0
  10. package/cordis.patch.yml +13 -2
  11. package/docs/README.md +7 -2
  12. package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +31 -179
  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 +2 -2
  17. package/docs/adr/0019-plugin-settings-and-kill-switch.md +14 -3
  18. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
  19. package/docs/adr/0022-stage-npm-releases-with-oidc.md +4 -2
  20. package/docs/adr/0023-global-user-bindings.md +3 -1
  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 +101 -35
  23. package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +3 -3
  24. package/docs/adr/0031-close-semantic-obligations-by-owner.md +1 -1
  25. package/docs/adr/0032-own-session-log-facts-in-a-host-only-projection.md +27 -0
  26. package/docs/adr/0033-gate-client-visual-usability-in-a-browser.md +23 -0
  27. package/docs/adr/0034-keep-program-values-separate-from-output-projections.md +29 -0
  28. package/docs/adr/0035-run-coverage-only-before-a-new-release.md +27 -0
  29. package/docs/adr/0036-separate-focused-development-from-delivery-verification.md +29 -0
  30. package/docs/adr/0037-keep-tool-hover-previews-passive.md +52 -0
  31. package/docs/adr/0038-freeze-session-capability-plans.md +71 -0
  32. package/docs/adr/0039-verify-documentation-without-runtime-suites.md +29 -0
  33. package/docs/adr/0040-separate-capability-configurations-from-session-observations.md +39 -0
  34. package/docs/adr/0041-record-cell-execution-directories.md +65 -0
  35. package/docs/adr/0042-persist-provider-availability-and-handback.md +41 -0
  36. package/docs/adr/0043-compose-capability-and-context-policies.md +37 -0
  37. package/docs/adr/README.md +6 -0
  38. package/docs/architecture.md +25 -27
  39. package/docs/binding-review-placement-design.md +29 -21
  40. package/docs/capability-planning-components.md +93 -0
  41. package/docs/capability-projection.md +7 -0
  42. package/docs/client-ui.md +88 -29
  43. package/docs/client-visual-contract.md +65 -0
  44. package/docs/context-management.md +22 -0
  45. package/docs/context-optimization-plan.md +353 -0
  46. package/docs/context-public-interface-coverage.md +25 -0
  47. package/docs/durability-design.md +31 -14
  48. package/docs/evaluation.md +41 -6
  49. package/docs/evaluations/command-goat-deepseek-v4.1-flash-20261006.md +126 -0
  50. package/docs/evaluations/command-goat-deepseek-v4.1-flash.md +281 -0
  51. package/docs/installation.md +50 -8
  52. package/docs/program-data-plane.md +5 -3
  53. package/docs/publishing.md +5 -5
  54. package/docs/release-coverage.md +73 -0
  55. package/docs/release-notes.md +1 -1
  56. package/docs/review-procedure.md +56 -0
  57. package/docs/runtime-reference.md +48 -16
  58. package/docs/semantic-validation.md +14 -2
  59. package/docs/stateful-binding-updates-proposal.md +2 -2
  60. package/docs/user-bindings.en.md +2 -2
  61. package/docs/user-bindings.md +3 -3
  62. package/docs/value-wire.md +11 -1
  63. package/docs/verification-optimization-work.md +52 -15
  64. package/docs/verification.md +30 -86
  65. package/index.js +147 -23
  66. package/internal/binding-toggle-receipt.js +24 -0
  67. package/internal/capability-command-receipt.js +29 -0
  68. package/internal/capability-environment.js +1247 -0
  69. package/internal/capability-plan.js +285 -0
  70. package/internal/capability-policy.js +96 -0
  71. package/internal/capability-statistics-store.js +180 -0
  72. package/internal/capability-statistics.js +210 -0
  73. package/internal/compiler-platform.js +5 -3
  74. package/internal/compiler-service.js +2 -9
  75. package/internal/config-spec.js +79 -9
  76. package/internal/cordis-tools-owner.js +22 -19
  77. package/internal/direct-surface-owner.js +29 -29
  78. package/internal/dynamic-environment-runtime.js +9 -9
  79. package/internal/edit-transport-owner.js +183 -139
  80. package/internal/execution-provider-owner.js +160 -0
  81. package/internal/execution-seam-compat.js +76 -120
  82. package/internal/isolated-worker.js +12 -8
  83. package/internal/kernel-worker.js +104 -32
  84. package/internal/native-root-dynamic.js +12 -13
  85. package/internal/program-bindings.js +190 -0
  86. package/internal/recovery-tips.js +53 -23
  87. package/internal/rejected-cell-editor.js +8 -8
  88. package/internal/repl-observation-interest.js +11 -3
  89. package/internal/rpc-contract.js +1 -0
  90. package/internal/runtime-bridge-owner.js +351 -82
  91. package/internal/runtime-config.js +58 -0
  92. package/internal/runtime-contexts.js +85 -16
  93. package/internal/runtime-intrinsics.js +1 -0
  94. package/internal/runtime-messages.js +41 -0
  95. package/internal/sdk-projection.js +10 -82
  96. package/internal/session-cell-executor.js +42 -19
  97. package/internal/session-events.js +74 -4
  98. package/internal/session-journal-recovery.js +904 -184
  99. package/internal/session-journal-schema.js +6 -3
  100. package/internal/session-journal.js +31 -2
  101. package/internal/session-log-view.js +189 -43
  102. package/internal/session-runtime.js +175 -23
  103. package/internal/settings-compat.js +23 -6
  104. package/internal/stateful-module-compiler.js +3 -2
  105. package/internal/stateful-module-runtime.js +7 -3
  106. package/internal/stateful-root-runtime.js +9 -4
  107. package/internal/user-binding-authoring.js +2 -7
  108. package/internal/user-bindings-owner.js +47 -7
  109. package/internal/validated-parse-repair.js +1 -1
  110. package/internal/value-wire.js +68 -21
  111. package/internal/worker-cwd-virtualization.js +20 -18
  112. package/internal/worker-realm-surfaces.js +3 -3
  113. package/locale/en.json +6 -0
  114. package/locale/zh.json +6 -0
  115. package/package.json +55 -38
  116. package/src/client-authoring-view.js +286 -168
  117. package/src/client-binding-review.js +4 -1
  118. package/src/client-binding-toggle.js +18 -0
  119. package/src/client-capabilities-view.js +1341 -0
  120. package/src/client-capability-sort.js +29 -0
  121. package/src/client-code-editor.js +6 -0
  122. package/src/client-copy.js +881 -8
  123. package/src/client-feature-gates.js +15 -5
  124. package/src/client-host-compat.js +23 -2
  125. package/src/client-indicator-view.js +4 -6
  126. package/src/client-primitives.js +280 -0
  127. package/src/client-repl-view.js +23 -16
  128. package/src/client-settings-view.js +412 -69
  129. package/src/client-styles.js +193 -41
  130. package/src/client-tool-hover.js +185 -0
  131. package/src/client-tool-view.js +45 -11
  132. package/src/client-transport.js +44 -9
  133. package/src/client-workbench.js +9 -10
  134. package/src/client.js +148 -68
  135. package/assets/ptc-plus-bindings-en.png +0 -0
  136. package/assets/ptc-plus-bindings-zh.png +0 -0
  137. package/assets/ptc-plus-repair-en.png +0 -0
  138. package/assets/ptc-plus-repair-zh.png +0 -0
  139. package/client.js +0 -31857
@@ -5,7 +5,7 @@ import {
5
5
  JOURNAL_KEY,
6
6
  RECOVERY_BOUNDARY_KEY,
7
7
  REWRITES_KEY,
8
- derivedEditResultsEqual,
8
+ isCanonicalSequence,
9
9
  liveToolCallSeq,
10
10
  normalizeJournal,
11
11
  normalizeRecoveryBoundaries,
@@ -17,8 +17,12 @@ import {
17
17
  editRejectedCell,
18
18
  editRunCodeSchema,
19
19
  } from './rejected-cell-editor.js'
20
- import { editTargetForCall, projectSessionLog } from './session-log-view.js'
20
+ import { projectSessionLog } from './session-log-view.js'
21
+ import { sessionEvents } from './session-events.js'
22
+ import { deepEqualJson, snapshotJsonValue } from '@deepseek-ai/dsh-util-values'
23
+ import { scopeTarget } from '@deepseek-ai/dsh-scope'
21
24
  import { RUN_CODE } from './runtime-bridge-owner.js'
25
+ import { CAPABILITY_EVIDENCE_KEY } from './capability-environment.js'
22
26
  import { isRecord } from './record-utils.js'
23
27
  import {
24
28
  userBindingDraftCapabilityFromMeta,
@@ -35,6 +39,34 @@ import {
35
39
 
36
40
  export const EDIT_RUN_CODE = 'edit_run_code'
37
41
 
42
+ function nestedEditStart(sessionLog, exec) {
43
+ const events = sessionEvents(sessionLog)
44
+ if (!Array.isArray(events) || typeof exec?.callId !== 'string'
45
+ || typeof exec?.rootCallId !== 'string') return undefined
46
+ const rootCallSeq = liveToolCallSeq(sessionLog, exec.rootCallId, RUN_CODE)
47
+ if (rootCallSeq === undefined) return undefined
48
+ const roots = events.flatMap((event, index) => event?.seq === rootCallSeq
49
+ ? [{ event, index }] : [])
50
+ if (roots.length !== 1 || roots[0].event?.type !== 'tool/call'
51
+ || roots[0].event.data?.callId !== exec.rootCallId
52
+ || roots[0].event.data?.name !== RUN_CODE) return undefined
53
+ const matches = events.flatMap((event, index) => (
54
+ index > roots[0].index && event?.type === 'tool/ptc-dispatch-start'
55
+ && event.data?.subCallId === exec.callId
56
+ && event.data?.name === EDIT_RUN_CODE
57
+ ? [{ event, index }] : []
58
+ ))
59
+ if (matches.length !== 1) return undefined
60
+ const { event, index } = matches[0]
61
+ if (!isCanonicalSequence(event.seq) || event.seq <= rootCallSeq
62
+ || events.filter(candidate => candidate?.seq === event.seq).length !== 1
63
+ || event.data.rootCallId !== exec.rootCallId
64
+ || event.data.parentCallId !== exec.rootCallId
65
+ || !deepEqualJson(event.data.arguments, exec.arguments)) return undefined
66
+ const target = projectSessionLog({ session: { events: events.slice(0, index + 1) } }).editableRun
67
+ return { startSeq: event.seq, rootCallSeq, target }
68
+ }
69
+
38
70
  function unavailableDerivedResult(inner) {
39
71
  if (inner?.isError !== true) {
40
72
  throw new Error('ptc-plus: derived run_code result did not contain a valid execution journal')
@@ -61,17 +93,25 @@ function derivedEditResult(inner) {
61
93
  return result
62
94
  }
63
95
 
96
+ function derivedPresentation(inner) {
97
+ return {
98
+ content: inner.content,
99
+ ...(inner.additionalContexts === undefined ? {} : { additionalContexts: inner.additionalContexts }),
100
+ ...(inner.concludesTurn === true ? { concludesTurn: true } : {}),
101
+ }
102
+ }
103
+
64
104
  export function createEditTransportOwner(ctx, {
65
- durableReplay,
105
+ canScheduleEdit,
66
106
  executeTentative,
67
107
  presentationGeneration,
108
+ scheduleEdit,
68
109
  sessionId,
69
110
  toolSchemasForAgent,
111
+ readSessionLog,
70
112
  }) {
71
- let currentDurableReplay = durableReplay
72
- const editExecutionMetadata = new WeakMap()
73
113
  const editClaims = new Map()
74
- const pendingSettlements = new Map()
114
+ const pendingAcceptances = new Map()
75
115
  const installedScopes = new Map()
76
116
 
77
117
  const releaseClaim = (id, targetCallSeq) => {
@@ -81,10 +121,10 @@ export function createEditTransportOwner(ctx, {
81
121
  }
82
122
 
83
123
  const discardSettlements = (id) => {
84
- for (const [exec, pending] of pendingSettlements) {
124
+ for (const [exec, pending] of pendingAcceptances) {
85
125
  if (id !== undefined && pending.id !== id) continue
86
- pendingSettlements.delete(exec)
87
- pending.finalize(false)
126
+ pendingAcceptances.delete(exec)
127
+ pending.finish(false)
88
128
  }
89
129
  }
90
130
 
@@ -98,6 +138,7 @@ export function createEditTransportOwner(ctx, {
98
138
  description: derived.description,
99
139
  },
100
140
  }
141
+ if (derived.capabilityEvidence !== undefined) meta[CAPABILITY_EVIDENCE_KEY] = derived.capabilityEvidence
101
142
  if (derived.rewrites !== undefined) {
102
143
  meta[REWRITES_KEY] = normalizeRewrites(derived.rewrites)
103
144
  }
@@ -123,124 +164,162 @@ export function createEditTransportOwner(ctx, {
123
164
  type: 'object',
124
165
  additionalProperties: false,
125
166
  properties: {
126
- edited: { type: 'boolean' },
167
+ accepted: { type: 'boolean' },
168
+ scheduled: { type: 'boolean' },
169
+ target_call_seq: { type: 'integer' },
127
170
  reason: { type: 'string' },
128
- error: { type: 'string' },
129
- value: {},
130
- logs: { type: 'array', items: { type: 'string' } },
131
171
  },
132
- required: ['edited'],
172
+ required: ['accepted', 'scheduled'],
133
173
  },
134
174
  render: (_args, value) => [{ type: 'text', text: JSON.stringify(value ?? {}) }],
135
- presentationMeta: (args) => {
136
- const meta = {}
137
- if (!isRecord(args)) return meta
138
- const derived = editExecutionMetadata.get(args)
139
- if (derived === undefined) return meta
140
- return derivedMetadata(derived)
141
- },
142
175
  }
143
176
  definition.execute = async (args, exec) => {
177
+ if (canScheduleEdit?.(exec) !== true) {
178
+ return { accepted: false, scheduled: false, reason: 'edit requires an active root run_code cell' }
179
+ }
144
180
  const agent = exec?.agent
145
- const callId = typeof exec?.callId === 'string' ? exec.callId : undefined
146
- const persistedTargetCallSeq = liveToolCallSeq(agent?.session, callId, EDIT_RUN_CODE)
147
- if (persistedTargetCallSeq === undefined && projectSessionLog(agent).editableRun !== undefined) {
148
- throw new Error('ptc-plus: edit_run_code requires a unique persisted tool/call event')
181
+ const sessionLog = await readSessionLog(agent?.session, exec.signal)
182
+ const start = nestedEditStart(sessionLog, exec)
183
+ if (start === undefined) {
184
+ throw new Error('ptc-plus: edit_run_code requires a unique persisted PTC sub-call start')
149
185
  }
150
- const target = persistedTargetCallSeq === undefined
151
- ? undefined
152
- : editTargetForCall(agent, callId, persistedTargetCallSeq)
186
+ const target = start.target
153
187
  if (target?.source === undefined || target.callSeq === undefined) {
154
- return { edited: false, reason: 'no run_code cell is currently eligible for safe editing' }
188
+ return { accepted: false, scheduled: false, reason: 'no run_code cell is currently eligible for safe editing' }
155
189
  }
156
190
  const edited = editRejectedCell(args, target.source)
157
- if (!edited.edited) return edited
191
+ if (!edited.edited) return { accepted: false, scheduled: false, reason: edited.reason }
158
192
  const expectedTargetCallSeq = args[EXPECTED_TARGET_CALL_SEQ]
159
193
  if (expectedTargetCallSeq !== undefined && expectedTargetCallSeq !== target.callSeq) {
160
194
  return {
161
- edited: false,
195
+ accepted: false,
196
+ scheduled: false,
162
197
  reason: `validated repair targets run_code call ${expectedTargetCallSeq}, but this edit captured call ${target.callSeq}`,
163
198
  }
164
199
  }
165
200
  if (typeof ctx.tools.execute !== 'function') {
166
201
  throw new Error('ptc-plus: DSH tools.execute is required for derived edit execution')
167
202
  }
168
- const persistedCallSeq = persistedTargetCallSeq
169
203
  const id = sessionId(agent)
170
204
  let claims = editClaims.get(id)
171
205
  if (claims === undefined) {
172
206
  claims = new Map()
173
207
  editClaims.set(id, claims)
174
- } else {
175
- for (const [callSeq, phase] of claims) {
176
- if (phase === 'settled' && callSeq !== target.callSeq) claims.delete(callSeq)
177
- }
178
208
  }
179
209
  if (claims.has(target.callSeq)) {
180
- return { edited: false, reason: 'the current edit target already has a derived execution' }
210
+ return { accepted: false, scheduled: false, reason: 'the current edit target already has a derived execution' }
181
211
  }
182
- claims.set(target.callSeq, 'executing')
183
- let retainClaim = false
184
- let finalizeTentative
185
- try {
186
- const dispatch = () => ctx.tools.execute({
187
- callId: `${String(exec.callId)}:derived`,
188
- name: RUN_CODE,
189
- arguments: { code: edited.code, description: edited.description },
190
- agent,
191
- signal: exec.signal,
192
- })
193
- const tentative = await executeTentative(persistedCallSeq, dispatch)
194
- const inner = tentative.result
195
- finalizeTentative = tentative.finalize
196
- const journalValue = isRecord(inner?.meta) && Object.hasOwn(inner.meta, JOURNAL_KEY)
197
- ? inner.meta[JOURNAL_KEY]
198
- : undefined
199
- const journal = journalValue === undefined ? undefined : normalizeJournal(journalValue)
200
- if (journal === undefined || journal.status === 'noop') return unavailableDerivedResult(inner)
201
- const rewrites = validatedRewrites(inner.meta)
202
- const recoveryBoundaries = isRecord(inner?.meta)
203
- && Object.hasOwn(inner.meta, RECOVERY_BOUNDARY_KEY)
204
- ? normalizeRecoveryBoundaries(inner.meta[RECOVERY_BOUNDARY_KEY])
205
- : undefined
206
- const replMemory = validatedReplMemorySnapshot(inner?.meta, presentationGeneration)
207
- const userBindings = userBindingsSnapshotFromMeta(inner?.meta)
208
- const draftCapability = userBindingDraftCapabilityFromMeta(
209
- inner?.meta,
210
- presentationGeneration,
211
- )
212
- const value = derivedEditResult(inner)
213
- const derived = {
214
- targetCallSeq: target.callSeq,
215
- journal,
216
- rewrites,
217
- recoveryBoundaries,
218
- replMemory,
219
- userBindings,
220
- draftCapability,
221
- code: edited.code,
222
- description: edited.description,
223
- }
224
- editExecutionMetadata.set(args, derived)
225
- claims.set(target.callSeq, 'settled')
226
- pendingSettlements.set(exec, {
227
- id,
228
- targetCallSeq: target.callSeq,
229
- expectedMeta: derivedMetadata(derived),
230
- finalize: finalizeTentative,
231
- })
232
- finalizeTentative = undefined
233
- retainClaim = true
234
- return value
235
- } finally {
236
- finalizeTentative?.(false)
237
- if (!retainClaim) releaseClaim(id, target.callSeq)
212
+ claims.set(target.callSeq, 'scheduled')
213
+ const receipt = { accepted: true, scheduled: true, target_call_seq: target.callSeq }
214
+ const pending = {
215
+ id,
216
+ receipt,
217
+ confirmed: false,
218
+ startSeq: start.startSeq,
219
+ rootCallSeq: start.rootCallSeq,
220
+ subCallId: exec.callId,
221
+ targetCallSeq: target.callSeq,
222
+ finalized: false,
223
+ finalizeTentative: undefined,
224
+ finish(confirmed) {
225
+ if (this.finalized) return
226
+ this.finalized = true
227
+ pendingAcceptances.delete(exec)
228
+ this.finalizeTentative?.(confirmed)
229
+ releaseClaim(id, target.callSeq)
230
+ },
231
+ async dispatch(rootSignal, rootExec) {
232
+ if (!this.confirmed || this.finalized || rootSignal.aborted) {
233
+ this.finish(false)
234
+ return { outcome: { edited: false, reason: 'accepted edit was cancelled before derived execution' } }
235
+ }
236
+ if (rootExec?.name !== RUN_CODE || rootExec.callId !== exec.rootCallId
237
+ || rootExec.agent !== agent || rootExec.token === undefined) {
238
+ throw new Error('ptc-plus: derived edit requires its actual root transport execution')
239
+ }
240
+ const argumentsValue = snapshotJsonValue({ code: edited.code, description: edited.description })
241
+ if (argumentsValue === undefined) throw new Error('ptc-plus: derived source is not losslessly JSON-serializable')
242
+ const executionLog = await readSessionLog(agent.session, rootSignal)
243
+ if (rootSignal.aborted || this.finalized) {
244
+ this.finish(false)
245
+ return { outcome: { edited: false, reason: 'accepted edit was cancelled before derived execution' } }
246
+ }
247
+ const data = { rootCallId: exec.rootCallId, parentCallId: rootExec.callId,
248
+ subCallId: `${String(exec.callId)}:derived`, name: RUN_CODE, arguments: argumentsValue }
249
+ const executionStart = agent.session.append('tool/ptc-dispatch-start', data)
250
+ const derivedExec = { callId: data.subCallId, rootCallId: data.rootCallId,
251
+ parent: rootExec.token, name: RUN_CODE, arguments: argumentsValue, agent, signal: rootSignal }
252
+ const dispatch = () => ctx.tools.execute(derivedExec)
253
+ const tentative = await executeTentative(executionStart.seq, start.rootCallSeq,
254
+ dispatch, executionLog, derivedExec)
255
+ const inner = tentative.result
256
+ const metadata = tentative.metadata ?? inner.meta
257
+ this.finalizeTentative = tentative.finalize
258
+ let content = inner.content
259
+ try {
260
+ content = await ctx.waterfall(scopeTarget(ctx.tools, agent), 'tools/ptc-dispatch-log', {
261
+ exec: rootExec, agent, subCallId: data.subCallId, name: RUN_CODE,
262
+ isError: inner.isError, content: inner.content,
263
+ }, () => Promise.resolve(inner.content))
264
+ } catch {
265
+ // A log projector cannot change the settled execution or omit its fact.
266
+ content = inner.content
267
+ }
268
+ try {
269
+ agent.session.append('tool/ptc-dispatch', { ...data, isError: inner.isError,
270
+ ...(inner.error?.info === undefined ? {} : { error: inner.error.info }),
271
+ content,
272
+ ...(metadata === undefined ? {} : { meta: metadata }),
273
+ })
274
+ } catch (error) {
275
+ this.finish(false)
276
+ throw error
277
+ }
278
+ const journalValue = isRecord(metadata) && Object.hasOwn(metadata, JOURNAL_KEY)
279
+ ? metadata[JOURNAL_KEY] : undefined
280
+ const journal = journalValue === undefined ? undefined : normalizeJournal(journalValue)
281
+ if (journal === undefined || journal.status === 'noop'
282
+ || inner.isError !== (journal.completion?.kind === 'throw' || journal.status === 'discarded')) {
283
+ this.finish(false)
284
+ return { outcome: unavailableDerivedResult(inner), presentation: derivedPresentation(inner) }
285
+ }
286
+ const derived = {
287
+ targetCallSeq: target.callSeq,
288
+ journal,
289
+ rewrites: validatedRewrites(metadata),
290
+ recoveryBoundaries: isRecord(metadata) && Object.hasOwn(metadata, RECOVERY_BOUNDARY_KEY)
291
+ ? normalizeRecoveryBoundaries(metadata[RECOVERY_BOUNDARY_KEY]) : undefined,
292
+ replMemory: validatedReplMemorySnapshot(metadata, presentationGeneration),
293
+ userBindings: userBindingsSnapshotFromMeta(metadata),
294
+ draftCapability: userBindingDraftCapabilityFromMeta(metadata, presentationGeneration),
295
+ capabilityEvidence: metadata?.[CAPABILITY_EVIDENCE_KEY],
296
+ code: edited.code,
297
+ description: edited.description,
298
+ }
299
+ return {
300
+ outcome: derivedEditResult(inner),
301
+ presentation: derivedPresentation(inner),
302
+ provenance: {
303
+ startSeq: start.startSeq,
304
+ executionStartSeq: executionStart.seq,
305
+ rootCallSeq: start.rootCallSeq,
306
+ subCallId: exec.callId,
307
+ meta: derivedMetadata(derived),
308
+ },
309
+ }
310
+ },
311
+ }
312
+ if (scheduleEdit?.(exec, pending) !== true) {
313
+ pending.finish(false)
314
+ return { accepted: false, scheduled: false, reason: 'edit requires an active root run_code cell' }
238
315
  }
316
+ pendingAcceptances.set(exec, pending)
317
+ return receipt
239
318
  }
240
319
 
241
320
  const disposeInstalled = (installed) => {
242
321
  const failures = []
243
- for (const key of ['disposePresentation', 'disposeRegistration']) {
322
+ for (const key of ['disposeRegistration']) {
244
323
  const dispose = installed[key]
245
324
  if (dispose === undefined) continue
246
325
  try {
@@ -261,8 +340,7 @@ export function createEditTransportOwner(ctx, {
261
340
  try {
262
341
  disposeInstalled(installed)
263
342
  } finally {
264
- if (installed.disposePresentation === undefined
265
- && installed.disposeRegistration === undefined) {
343
+ if (installed.disposeRegistration === undefined) {
266
344
  installedScopes.delete(agent)
267
345
  }
268
346
  }
@@ -270,23 +348,13 @@ export function createEditTransportOwner(ctx, {
270
348
 
271
349
  return Object.freeze({
272
350
  definition,
273
- reconfigure(nextConfig) {
274
- currentDurableReplay = nextConfig.durableReplay
275
- },
276
351
  isInstalled(agent) {
277
352
  const installed = installedScopes.get(agent)
278
- return installed?.disposePresentation !== undefined
279
- && installed.disposeRegistration !== undefined
353
+ return installed?.disposeRegistration !== undefined
280
354
  },
281
355
  ensureInstalled(agent) {
282
356
  const installed = installedScopes.get(agent)
283
- if (installed !== undefined) {
284
- if (installed.disposePresentation !== undefined
285
- && installed.disposeRegistration !== undefined) {
286
- return installed.definition
287
- }
288
- uninstall(agent)
289
- }
357
+ if (installed !== undefined) return installed.definition
290
358
  const liveSchemas = typeof toolSchemasForAgent === 'function'
291
359
  ? toolSchemasForAgent(agent)
292
360
  : []
@@ -295,8 +363,8 @@ export function createEditTransportOwner(ctx, {
295
363
  throw new Error('ptc-plus: cannot install edit_run_code; the agent scope already owns that tool name')
296
364
  }
297
365
  const scopedTools = agent?.ctx?.tools
298
- if (typeof scopedTools?.register !== 'function' || typeof scopedTools.presentAs !== 'function') {
299
- throw new Error('ptc-plus: DSH agent-scoped tools.register and tools.presentAs are required to expose edit_run_code')
366
+ if (typeof scopedTools?.register !== 'function') {
367
+ throw new Error('ptc-plus: DSH agent-scoped tools.register is required to expose edit_run_code')
300
368
  }
301
369
  const disposeRegistration = scopedTools.register(definition)
302
370
  if (typeof disposeRegistration !== 'function') {
@@ -304,43 +372,19 @@ export function createEditTransportOwner(ctx, {
304
372
  }
305
373
  const nextInstalled = {
306
374
  definition,
307
- disposePresentation: undefined,
308
375
  disposeRegistration,
309
376
  sessionId: sessionId(agent),
310
377
  }
311
378
  installedScopes.set(agent, nextInstalled)
312
- try {
313
- const disposePresentation = scopedTools.presentAs('both')
314
- if (typeof disposePresentation !== 'function') {
315
- throw new Error('ptc-plus: DSH tools.presentAs did not return a disposer')
316
- }
317
- nextInstalled.disposePresentation = disposePresentation
318
- } catch (error) {
319
- try {
320
- uninstall(agent)
321
- } catch (cleanupError) {
322
- throw new AggregateError(
323
- [error, cleanupError],
324
- 'ptc-plus: edit_run_code installation and rollback failed',
325
- { cause: error },
326
- )
327
- }
328
- throw error
329
- }
330
379
  return definition
331
380
  },
332
381
  handleResult(exec, result) {
333
- const pending = pendingSettlements.get(exec)
334
- pendingSettlements.delete(exec)
382
+ const pending = pendingAcceptances.get(exec)
335
383
  if (pending === undefined) return
336
- const persisted = derivedEditResultsEqual(
337
- result?.meta,
338
- pending.expectedMeta,
339
- pending.targetCallSeq,
340
- )
341
- pending.finalize(persisted)
342
- if (persisted) return
343
- releaseClaim(pending.id, pending.targetCallSeq)
384
+ const accepted = result?.isError === false
385
+ && deepEqualJson(result.value, pending.receipt)
386
+ pending.confirmed = accepted
387
+ if (!accepted) pending.finish(false)
344
388
  },
345
389
  disposeAgent(agent) {
346
390
  let failure
@@ -0,0 +1,160 @@
1
+ import { createExecutionSeam, EXECUTION_SEAM_SERVICE, LEGACY_EXECUTION_SEAM_SERVICE } from './execution-seam-compat.js'
2
+
3
+ export const name = 'ptc-plus-execution-provider'
4
+ export const inject = ['loader']
5
+
6
+ export function apply(ctx, config = {}) {
7
+ const ownerFiber = ctx.fiber
8
+ const { originalEntryId = 'ptc-runtime', isolation = 'ptc-plus-original', serviceName = EXECUTION_SEAM_SERVICE } = config
9
+ if (typeof originalEntryId !== 'string' || originalEntryId.length === 0
10
+ || typeof isolation !== 'string' || isolation.length === 0) {
11
+ throw new TypeError('ptc-plus: execution provider requires an original entry id and named isolation')
12
+ }
13
+ if (![EXECUTION_SEAM_SERVICE, LEGACY_EXECUTION_SEAM_SERVICE].includes(serviceName)) {
14
+ throw new TypeError('ptc-plus: execution provider serviceName must be codeRuntime or ptcRuntime')
15
+ }
16
+ // Profile rows belong to the Include's tree; the global Loader resolves root IDs.
17
+ const entryTree = ownerFiber.entry?.parent.tree ?? ctx.loader
18
+ const originalEntry = entryTree.resolve(originalEntryId)
19
+ const isolated = () => originalEntry.options.isolate?.[serviceName] === isolation
20
+ if (!isolated()) throw new Error(`ptc-plus: original entry ${originalEntryId} must isolate ${serviceName} as ${isolation}`)
21
+ const originalContext = originalEntry.context.extend({ [originalEntry.constructor.key]: undefined })
22
+ let watcher
23
+ let live
24
+ let stopped = false
25
+ let suspended = false
26
+ let generation
27
+ const pending = new Set()
28
+ let resolveAttachment
29
+ let rejectAttachment
30
+ const attachment = new Promise((resolve, reject) => {
31
+ resolveAttachment = resolve
32
+ rejectAttachment = reject
33
+ })
34
+ const readOriginal = () => {
35
+ const original = isolated() ? originalContext.get(serviceName) : undefined
36
+ if (stopped || original === undefined) {
37
+ throw new Error(`ptc-plus: original ${serviceName} generation is unavailable`)
38
+ }
39
+ return original
40
+ }
41
+ const release = () => {
42
+ const entry = live
43
+ if (entry === undefined) return
44
+ live = undefined
45
+ entry.seam.retire()
46
+ const unregistration = entry.unregister?.()
47
+ const disposal = Promise.allSettled([unregistration, entry.disposeChild?.()]).then(results => {
48
+ const failures = results.filter(result => result.status === 'rejected').map(result => result.reason)
49
+ if (failures.length > 0) throw new AggregateError(failures, 'ptc-plus: execution provider disposal failed')
50
+ })
51
+ pending.add(disposal)
52
+ disposal.then(() => pending.delete(disposal), error => {
53
+ pending.delete(disposal)
54
+ ctx.logger.warn(error)
55
+ })
56
+ return disposal
57
+ }
58
+ const suspend = () => {
59
+ suspended = true
60
+ const disposal = release()
61
+ const previous = watcher
62
+ watcher = undefined
63
+ return Promise.all([disposal, previous?.dispose()])
64
+ }
65
+ const publish = () => {
66
+ const resolutions = new WeakMap()
67
+ const delegate = {
68
+ get language() { return readOriginal().language },
69
+ get isolation() { return readOriginal().isolation },
70
+ get executionInstructions() { return readOriginal().executionInstructions },
71
+ get sandboxMode() { return readOriginal().sandboxMode },
72
+ get timeout() { return readOriginal().timeout },
73
+ resolve(request) {
74
+ const original = readOriginal()
75
+ const spec = serviceName === EXECUTION_SEAM_SERVICE ? original.resolve(request) : request
76
+ if (spec !== null && typeof spec === 'object') {
77
+ resolutions.set(spec, { original, generation })
78
+ }
79
+ return spec
80
+ },
81
+ run(spec) {
82
+ const resolved = resolutions.get(spec)
83
+ if (resolved?.generation?.active === false) throw new Error('ptc-plus: resolved spec belongs to an unavailable original generation')
84
+ const original = resolved?.original ?? readOriginal()
85
+ return original.run(spec)
86
+ },
87
+ }
88
+ const seam = createExecutionSeam(delegate, serviceName)
89
+ const entry = { seam }
90
+ live = entry
91
+ entry.disposeChild = ctx.effect(() => {
92
+ const child = ctx.plugin({
93
+ name: 'ptc-plus-composed-runtime',
94
+ apply(childContext) {
95
+ childContext.effect(() => {
96
+ if (stopped || live !== entry || !isolated()) return () => {}
97
+ entry.unregister = childContext.provide(serviceName, seam.provider)
98
+ return () => entry.unregister()
99
+ }, 'ptc-plus owned execution registration')
100
+ },
101
+ })
102
+ entry.child = child
103
+ return () => child.dispose()
104
+ }, 'ptc-plus owned execution child')
105
+ return entry.child.await().then(() => {
106
+ if (!stopped && live === entry && entry.unregister !== undefined && entry.child.state === 2) resolveAttachment()
107
+ }, error => {
108
+ rejectAttachment(error)
109
+ throw error
110
+ })
111
+ }
112
+ const watch = () => {
113
+ if (stopped || watcher !== undefined || !isolated()) return
114
+ ctx.effect(() => {
115
+ const observer = originalContext.inject([serviceName], scope => {
116
+ if (stopped || !isolated()) return
117
+ createExecutionSeam(scope.get(serviceName), serviceName)
118
+ const currentGeneration = { active: true }
119
+ generation = currentGeneration
120
+ scope.effect(() => () => {
121
+ currentGeneration.active = false
122
+ if (generation === currentGeneration) generation = undefined
123
+ }, 'ptc-plus original generation scope')
124
+ if (live === undefined) return publish()
125
+ return live.seam.sourceChanged()
126
+ })
127
+ watcher = observer
128
+ return () => observer.dispose()
129
+ }, 'ptc-plus original execution observer')
130
+ }
131
+ ctx.on('loader/partial-dispose', (entry, _legacy, active) => {
132
+ if (entry === originalEntry && (!active || !isolated())) void suspend().catch(error => ctx.logger.warn(error))
133
+ }, { prepend: true, global: true })
134
+ ctx.on('loader/patch-context', (entry, next) => {
135
+ if (entry === originalEntry && !isolated()) void suspend().catch(error => ctx.logger.warn(error))
136
+ next()
137
+ if (entry === originalEntry && isolated() && suspended && !stopped && ownerFiber.state === 2) {
138
+ stopped = true
139
+ resolveAttachment()
140
+ void suspend().catch(error => ctx.logger.warn(error))
141
+ void ownerFiber.restart().catch(error => ctx.logger.warn(error))
142
+ }
143
+ }, { prepend: true, global: true })
144
+ ctx.effect(() => () => {
145
+ stopped = true
146
+ resolveAttachment()
147
+ const suspension = suspend()
148
+ return Promise.all([suspension, ...pending])
149
+ }, 'ptc-plus execution provider composition')
150
+ if (originalEntry.disabled) resolveAttachment()
151
+ const timer = setTimeout(() => rejectAttachment(new Error(
152
+ `ptc-plus: original execution entry ${originalEntryId} did not publish ${serviceName}`,
153
+ )), 30_000)
154
+ ctx.effect(() => () => clearTimeout(timer), 'ptc-plus original execution provider wait')
155
+ const originalReady = originalEntry.fiber?.await() ?? Promise.resolve()
156
+ return originalReady.then(() => {
157
+ watch()
158
+ return attachment
159
+ }).finally(() => clearTimeout(timer))
160
+ }