dsh-ptc-plus 0.3.1 → 0.4.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 (200) hide show
  1. package/README.en.md +66 -73
  2. package/README.md +66 -73
  3. package/assets/ptc-plus-bindings-en.png +0 -0
  4. package/assets/ptc-plus-bindings-zh.png +0 -0
  5. package/assets/ptc-plus-repair-en.png +0 -0
  6. package/assets/ptc-plus-repair-zh.png +0 -0
  7. package/assets/ptc-plus-repl-workspace-en.png +0 -0
  8. package/assets/ptc-plus-repl-workspace-zh.png +0 -0
  9. package/assets/ptc-plus-settings-en.png +0 -0
  10. package/assets/ptc-plus-settings-zh.png +0 -0
  11. package/client.js +30610 -722
  12. package/compiler-amaro.cjs +5 -0
  13. package/compiler-amaro.cjs.map +6 -0
  14. package/compiler-asset-124f80c1bba92ff5fe79cfec6e8ad518f476de4a42514cae3919538887f510e8.txt +1 -0
  15. package/compiler-assets.json +3 -0
  16. package/compiler-core.cjs +554 -0
  17. package/compiler-core.cjs.map +6 -0
  18. package/compiler-platform.cjs +2 -0
  19. package/compiler-platform.cjs.map +6 -0
  20. package/compiler-typescript.cjs +388 -0
  21. package/compiler-typescript.cjs.map +6 -0
  22. package/docs/README.md +6 -0
  23. package/docs/adr/0004-declare-program-bindings-at-the-host.md +1 -1
  24. package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +6 -4
  25. package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +15 -3
  26. package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -0
  27. package/docs/adr/0010-session-log-derived-recovery-tips.md +59 -5
  28. package/docs/adr/0011-isolate-the-local-recovery-tip-provider.md +1 -1
  29. package/docs/adr/0012-preserve-mixed-destructuring-initialization.md +3 -1
  30. package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +9 -3
  31. package/docs/adr/0014-persist-cell-rewrite-policy.md +11 -5
  32. package/docs/adr/0015-preserve-source-positions-through-rewrites.md +1 -1
  33. package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
  34. package/docs/adr/0017-track-the-latest-dsh-public-surface.md +49 -0
  35. package/docs/adr/0018-use-versioned-fixture-for-ab-benchmark.md +3 -3
  36. package/docs/adr/0019-plugin-settings-and-kill-switch.md +8 -6
  37. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
  38. package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +17 -3
  39. package/docs/adr/0023-global-user-bindings.md +104 -0
  40. package/docs/adr/0024-repl-console-observation.md +33 -0
  41. package/docs/adr/0025-use-versioned-logical-binding-identities.md +119 -0
  42. package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +21 -0
  43. package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +37 -0
  44. package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +103 -0
  45. package/docs/adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md +27 -0
  46. package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +27 -0
  47. package/docs/architecture.md +82 -22
  48. package/docs/binding-review-placement-design.md +200 -0
  49. package/docs/capability-projection.md +1 -1
  50. package/docs/client-ui.md +96 -20
  51. package/docs/durability-design.md +61 -24
  52. package/docs/evaluation.md +78 -6
  53. package/docs/installation.md +41 -6
  54. package/docs/program-data-plane.md +6 -0
  55. package/docs/publishing.md +5 -3
  56. package/docs/runtime-reference.md +79 -25
  57. package/docs/semantic-validation.md +29 -0
  58. package/docs/stateful-binding-updates-proposal.md +755 -0
  59. package/docs/user-bindings.en.md +64 -0
  60. package/docs/user-bindings.md +64 -0
  61. package/docs/value-wire.md +3 -3
  62. package/docs/verification.md +57 -0
  63. package/index.js +374 -92
  64. package/internal/ast-traversal.js +5 -2
  65. package/internal/binding-descriptors.js +1 -0
  66. package/internal/binding-pattern-traversal.js +25 -0
  67. package/internal/binding-pattern.js +26 -29
  68. package/internal/binding-source-analysis.js +391 -0
  69. package/internal/binding-update-policy.js +66 -0
  70. package/internal/bounded-worker-round.js +56 -0
  71. package/internal/callable-definition-environment.js +42 -0
  72. package/internal/callable-source-catalog.js +129 -0
  73. package/internal/callable-source-encoding.js +47 -0
  74. package/internal/callable-source-facts.js +459 -0
  75. package/internal/callable-source-format.js +2 -0
  76. package/internal/callable-source-lowering.js +123 -0
  77. package/internal/cell-analysis-contract.js +25 -0
  78. package/internal/cell-analysis.js +252 -242
  79. package/internal/cell-completion.js +15 -0
  80. package/internal/cell-error.js +7 -0
  81. package/internal/cell-parser.js +66 -0
  82. package/internal/cell-rewriter.js +3 -553
  83. package/internal/commonjs-export-evidence.js +63 -0
  84. package/internal/commonjs-source-evidence.js +72 -0
  85. package/internal/compiler-amaro.cjs +2 -0
  86. package/internal/compiler-compression.js +12 -0
  87. package/internal/compiler-data.js +58 -0
  88. package/internal/compiler-descriptors.js +54 -0
  89. package/internal/compiler-dynamic-cache.js +48 -0
  90. package/internal/compiler-entry.js +35 -0
  91. package/internal/compiler-generated-names.js +17 -0
  92. package/internal/compiler-intrinsics.js +84 -0
  93. package/internal/compiler-module-links.js +34 -0
  94. package/internal/compiler-operations.js +61 -0
  95. package/internal/compiler-platform-factory.js +84 -0
  96. package/internal/compiler-platform.js +136 -0
  97. package/internal/compiler-record-roles.js +53 -0
  98. package/internal/compiler-region-output.js +259 -0
  99. package/internal/compiler-resource-lowering.js +103 -0
  100. package/internal/compiler-scope-facts.js +115 -0
  101. package/internal/compiler-service.js +109 -0
  102. package/internal/compiler-source-cache.js +32 -0
  103. package/internal/compiler-source-pieces.js +93 -0
  104. package/internal/compiler-source-regions.js +132 -0
  105. package/internal/compiler-storage-source.js +54 -0
  106. package/internal/compiler-text.js +15 -0
  107. package/internal/compiler-typescript.cjs +2 -0
  108. package/internal/config-spec.js +91 -25
  109. package/internal/cordis-tools-owner.js +130 -21
  110. package/internal/diagnostic.js +27 -1
  111. package/internal/direct-surface-owner.js +76 -23
  112. package/internal/dynamic-binding-evidence.js +16 -0
  113. package/internal/dynamic-environment-compiler.js +779 -0
  114. package/internal/dynamic-environment-integration.js +87 -0
  115. package/internal/dynamic-environment-runtime.js +362 -0
  116. package/internal/dynamic-native-calls.js +150 -0
  117. package/internal/dynamic-native-runtime.js +248 -0
  118. package/internal/dynamic-scope-analysis.js +90 -0
  119. package/internal/edit-transport-owner.js +28 -3
  120. package/internal/execution-seam-compat.js +193 -0
  121. package/internal/failure-reporting.js +118 -18
  122. package/internal/host-rpc.js +66 -0
  123. package/internal/isolated-worker.js +548 -0
  124. package/internal/kernel-child.js +139 -0
  125. package/internal/kernel-worker.js +911 -116
  126. package/internal/language-semantics.js +14 -0
  127. package/internal/legacy-binding-catalog.js +48 -0
  128. package/internal/legacy-cell-analysis.js +600 -0
  129. package/internal/legacy-cell-parser.js +50 -0
  130. package/internal/legacy-cell-rewriter.js +631 -0
  131. package/internal/legacy-repl-convenience.js +291 -0
  132. package/internal/managed-module-operations.js +170 -0
  133. package/internal/module-compilation.js +321 -0
  134. package/internal/module-runtime-platform.js +19 -0
  135. package/internal/module-transform-contract.js +12 -0
  136. package/internal/native-await-declarations.js +68 -0
  137. package/internal/native-root-compilation.js +54 -0
  138. package/internal/native-root-dynamic.js +67 -0
  139. package/internal/recovery-tips.js +27 -15
  140. package/internal/rejected-cell-editor.js +13 -8
  141. package/internal/repl-convenience.js +2 -201
  142. package/internal/repl-memory-projection.js +81 -12
  143. package/internal/repl-observation-interest.js +70 -0
  144. package/internal/repl-rewrite-contract.js +21 -0
  145. package/internal/repl-scope-normalizer.js +2091 -0
  146. package/internal/repl-value-observer.js +114 -0
  147. package/internal/rpc-contract.js +52 -0
  148. package/internal/runtime-bridge-owner.js +172 -28
  149. package/internal/runtime-config.js +48 -51
  150. package/internal/runtime-contexts.js +82 -10
  151. package/internal/runtime-messages.js +111 -0
  152. package/internal/session-cell-executor.js +455 -91
  153. package/internal/session-journal-recovery.js +684 -0
  154. package/internal/session-journal-schema.js +106 -0
  155. package/internal/session-journal.js +237 -440
  156. package/internal/session-log-view.js +42 -9
  157. package/internal/session-runtime.js +325 -80
  158. package/internal/session-state.js +249 -40
  159. package/internal/source-position-map.js +211 -71
  160. package/internal/stateful-module-compiler.js +154 -0
  161. package/internal/stateful-module-runtime.js +303 -0
  162. package/internal/stateful-root-compiler.js +598 -0
  163. package/internal/stateful-root-runtime.js +340 -0
  164. package/internal/typert-codec-compat.js +28 -0
  165. package/internal/typescript-transform.js +29 -0
  166. package/internal/user-binding-authoring.js +70 -0
  167. package/internal/user-binding-console-worker.js +184 -0
  168. package/internal/user-binding-console.js +148 -0
  169. package/internal/user-binding-draft-projection.js +335 -0
  170. package/internal/user-binding-model-context.js +31 -0
  171. package/internal/user-binding-runner.js +56 -0
  172. package/internal/user-bindings-owner.js +1065 -0
  173. package/internal/user-bindings-store.js +293 -0
  174. package/internal/user-bindings.js +353 -0
  175. package/internal/value-wire-schema.js +28 -0
  176. package/internal/value-wire.js +29 -20
  177. package/internal/worker-client.js +57 -67
  178. package/internal/worker-environment.js +45 -0
  179. package/internal/worker-output-relay.js +29 -0
  180. package/internal/worker-shutdown.js +14 -0
  181. package/package.json +81 -11
  182. package/src/client-activity.js +109 -45
  183. package/src/client-authoring-view.js +580 -0
  184. package/src/client-binding-review.js +230 -0
  185. package/src/client-bindings-data.js +29 -0
  186. package/src/client-catalog.js +127 -0
  187. package/src/client-code-editor.js +84 -0
  188. package/src/client-console.js +112 -0
  189. package/src/client-copy.js +370 -0
  190. package/src/client-feature-gates.js +62 -0
  191. package/src/client-host-compat.js +150 -0
  192. package/src/client-indicator-view.js +267 -0
  193. package/src/client-repl-view.js +292 -0
  194. package/src/client-rpc.js +20 -0
  195. package/src/client-settings-view.js +221 -0
  196. package/src/client-styles.js +106 -0
  197. package/src/client-tool-view.js +108 -0
  198. package/src/client-transport.js +159 -0
  199. package/src/client-workbench.js +521 -0
  200. package/src/client.js +233 -615
@@ -0,0 +1,248 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks'
2
+ import { createContext, runInContext } from 'node:vm'
3
+ import { recordExceptionOrigin } from './failure-reporting.js'
4
+ import { createCallableSourceRegistry } from './callable-source-catalog.js'
5
+ import { captureCompilerIntrinsics, moduleRuntimeIntrinsics as internal } from './compiler-intrinsics.js'
6
+
7
+ const realmStates = new internal.WeakMap()
8
+ const exposedIntrinsics = new internal.WeakMap()
9
+ // Source facts describe canonical values and emitted source, independent of
10
+ // which owned realm's toString observes a callable crossing a module boundary.
11
+ const sources = createCallableSourceRegistry()
12
+ const callableSources = new internal.WeakMap()
13
+ const callableSourceTargets = new internal.WeakMap()
14
+ const sourceTarget = origin => typeof origin === 'string' ? origin : origin?.target
15
+ const copyArguments = (source, start = 0, result = []) => internal.copyArray(source, start, source.length, result)
16
+ const invocationScopes = new AsyncLocalStorage()
17
+ const getScope = AsyncLocalStorage.prototype.getStore
18
+ const enterScope = AsyncLocalStorage.prototype.enterWith
19
+ const currentScope = () => internal.Reflect.apply(getScope, invocationScopes, [])
20
+ const selectScope = value => internal.Reflect.apply(enterScope, invocationScopes, [value])
21
+ // Node 22 installs promise hooks lazily through a host array iterator. Complete
22
+ // that platform initialization before source can mutate the module realm.
23
+ selectScope(undefined)
24
+ // Native eval has no callable activation in the source caller chain. Its
25
+ // private realm gives each invocation a native try/finally lifetime without
26
+ // moving source parameters or declarations, or changing source realm globals.
27
+ let pendingInvocation
28
+ const invocationRealm = createContext({
29
+ take() {
30
+ const invocation = pendingInvocation
31
+ pendingInvocation = undefined
32
+ return invocation
33
+ },
34
+ enter(invocation) {
35
+ const previous = currentScope()
36
+ selectScope({ invoke: invocation.invoke, origin: invocation.origin,
37
+ interface: invocation.interface, parent: previous })
38
+ return previous
39
+ },
40
+ failed(invocation, error) { recordExceptionOrigin(error, sourceTarget(invocation.origin)) },
41
+ leave(previous) { selectScope(previous) },
42
+ })
43
+ // A distinct source identity keeps V8 eval-origin notes out of user frames.
44
+ const nativeContinuation = runInContext(`eval.bind(undefined, ${JSON.stringify(`
45
+ const invocation = take();
46
+ const previous = enter(invocation);
47
+ try {
48
+ while (invocation.advance(invocation)) invocation.args = invocation.collect(invocation.arrayLike);
49
+ invocation.complete(invocation, invocation.dispatch())
50
+ }
51
+ catch (error) { failed(invocation, error); throw error }
52
+ finally { leave(previous) }
53
+ //# sourceURL=ptc-plus:native-invocation
54
+ `)})`, invocationRealm)
55
+
56
+ /** Native callable identities are classified once for their owning realm. */
57
+ export function createNativeCallAdapter({ realmFunction, intrinsicEval, reflect, errors, evaluate, rootEnvironment, interfaceOwner }) {
58
+ reflect = { apply: reflect.apply, construct: reflect.construct }
59
+ const { getPrototypeOf, setPrototypeOf } = captureCompilerIntrinsics(realmFunction).Object
60
+ let state = internal.weakMapGet(realmStates, realmFunction)
61
+ if (state === undefined) {
62
+ const intrinsics = realmFunction('return {constructors:[Function,(async function(){}).constructor,(function*(){}).constructor,(async function*(){}).constructor],call:Function.prototype.call,apply:Function.prototype.apply,bind:Function.prototype.bind,toString:Function.prototype.toString,reflectApply:Reflect.apply,reflectConstruct:Reflect.construct}')()
63
+ const constructors = new internal.Set()
64
+ for (let index = 0; index < intrinsics.constructors.length; index++) internal.setAdd(constructors, intrinsics.constructors[index])
65
+ state = { ...intrinsics, realmFunction, intrinsicEval, constructors,
66
+ bound: new internal.WeakMap(), prepared: new internal.WeakMap(), sources, callableSources, callableSourceTargets,
67
+ exposed: new internal.Map(), interfaces: new internal.WeakMap(),
68
+ nativeApply: reflect.apply(intrinsics.bind, intrinsics.reflectApply, [undefined]),
69
+ nativeConstruct: reflect.apply(intrinsics.bind, intrinsics.reflectConstruct, [undefined]),
70
+ templateArguments: realmFunction('return Array.of')() }
71
+ internal.weakMapSet(realmStates, realmFunction, state)
72
+ }
73
+ let selected = interfaceOwner === undefined ? state : internal.weakMapGet(state.interfaces, interfaceOwner)
74
+ if (selected === undefined) {
75
+ selected = { exposed: new internal.Map() }
76
+ internal.weakMapSet(state.interfaces, interfaceOwner, selected)
77
+ }
78
+ // Realm owners share intrinsic classification and source facts. An explicit
79
+ // generation owner retains its own root through opaque and asynchronous calls.
80
+ const install = () => {
81
+ if (selected.installed) return
82
+ selected.installed = true
83
+ const expose = (target, constructable = false) => {
84
+ const dispatch = (receiver, args, newTarget) => {
85
+ const active = currentScope()
86
+ const owner = active?.interface === selected ? active : undefined
87
+ return (owner?.invoke ?? invoke)(target, receiver, args, owner?.origin, newTarget)
88
+ }
89
+ const wrapper = new internal.Proxy(target, {
90
+ apply: (_, receiver, args) => dispatch(receiver, args),
91
+ ...(constructable ? { construct: (_, args, newTarget) => dispatch(undefined, args, newTarget) } : {}),
92
+ })
93
+ internal.mapSet(selected.exposed, target, wrapper)
94
+ if (interfaceOwner === undefined) internal.weakMapSet(exposedIntrinsics, target, wrapper)
95
+ internal.weakMapSet(state.callableSources, wrapper, reflect.apply(state.toString, target, []))
96
+ return wrapper
97
+ }
98
+ // Source reads select interfaces without replacing native global or
99
+ // prototype properties, including in escaped asynchronous continuations.
100
+ expose(state.intrinsicEval)
101
+ expose(state.toString)
102
+ internal.setForEach(state.constructors, constructor => expose(constructor, true))
103
+ const exposedFunction = internal.mapGet(selected.exposed, state.realmFunction)
104
+ internal.weakMapSet(realmStates, exposedFunction, state)
105
+ }
106
+ const isNativeConstructor = target => {
107
+ while (internal.weakMapHas(state.bound, target)) target = internal.weakMapGet(state.bound, target).callee
108
+ return internal.setHas(state.constructors, target)
109
+ }
110
+ const adaptConstructed = (native, origin) => {
111
+ const source = reflect.apply(state.toString, native, [])
112
+ const compiled = evaluate(state.intrinsicEval, undefined, [`(${source})`], rootEnvironment(sourceTarget(origin)), { functionConstructor: true })
113
+ setPrototypeOf(compiled, getPrototypeOf(native))
114
+ internal.weakMapSet(state.callableSources, compiled, source)
115
+ return compiled
116
+ }
117
+ const exposedValue = value => internal.mapGet(selected.exposed, value) ?? internal.weakMapGet(exposedIntrinsics, value) ?? value
118
+ const propagate = (error, origin) => {
119
+ recordExceptionOrigin(error, sourceTarget(origin), { reset: true })
120
+ for (let scope = currentScope(); scope !== undefined; scope = scope.parent) {
121
+ recordExceptionOrigin(error, sourceTarget(scope.origin))
122
+ }
123
+ return error
124
+ }
125
+ const invoke = (callee, receiver, args, origin, newTarget) => {
126
+ // Exposed intrinsic proxies already selected their owning interface.
127
+ const plan = invocation(callee, receiver, origin, false, newTarget !== undefined, false)
128
+ plan.newTarget = newTarget
129
+ return beginInvocation(plan, args)()
130
+ }
131
+ const prepareCall = (callee, receiver, origin, optional = false) => {
132
+ const plan = invocation(callee, receiver, origin, optional)
133
+ return plan === undefined ? undefined : prepareTagInvocation(plan)
134
+ }
135
+ const invocation = (callee, receiver, origin, optional = false, construct = false, selectInterface = true) => {
136
+ if (optional && callee == null) return undefined
137
+ const reference = internal.weakMapGet(state.prepared, callee)
138
+ if (reference !== undefined && !construct) ({ callee, receiver } = reference)
139
+ return { callee: selectInterface ? exposedValue(callee) : callee, receiver, origin, construct }
140
+ }
141
+ const validateInvocation = ({ callee, origin, construct }) => {
142
+ if (!construct && typeof callee !== 'function' && typeof origin?.callee === 'string') {
143
+ const error = new errors.TypeError(`${origin.callee} is not a function`)
144
+ recordExceptionOrigin(error, origin.target, { sourceFailure: true })
145
+ throw error
146
+ }
147
+ }
148
+ const collectArguments = reflect.apply(state.bind, state.reflectApply, [undefined, state.templateArguments, undefined])
149
+ const intrinsicTarget = callee => {
150
+ while (internal.weakMapHas(state.bound, callee)) callee = internal.weakMapGet(state.bound, callee).callee
151
+ return internal.setHas(state.constructors, callee) || callee === state.intrinsicEval || callee === state.toString
152
+ }
153
+ // Native array-like conversion runs in the continuation, after target
154
+ // validation and before dispatch. User getters therefore retain their source
155
+ // caller, and nested call/apply aliases consume each argument list once.
156
+ const advanceInvocation = plan => {
157
+ while (true) {
158
+ const { callee, receiver, args, construct } = plan
159
+ const bound = internal.weakMapGet(state.bound, callee)
160
+ if (bound !== undefined && intrinsicTarget(callee)) {
161
+ plan.callee = bound.callee
162
+ if (!construct) plan.receiver = bound.receiver
163
+ if (plan.newTarget === callee) plan.newTarget = bound.callee
164
+ plan.args = copyArguments(args, 0, copyArguments(bound.args))
165
+ continue
166
+ }
167
+ if (!construct && callee === state.call) {
168
+ plan.callee = receiver
169
+ plan.receiver = args[0]
170
+ plan.args = copyArguments(args, 1)
171
+ continue
172
+ }
173
+ if (!construct && (callee === state.apply && typeof receiver === 'function'
174
+ || callee === state.reflectApply && typeof args[0] === 'function')) {
175
+ plan.callee = callee === state.apply ? receiver : args[0]
176
+ plan.receiver = callee === state.apply ? args[0] : args[1]
177
+ plan.arrayLike = callee === state.apply ? args[1] : args[2]
178
+ if (callee === state.apply && plan.arrayLike == null) { plan.args = []; continue }
179
+ return true
180
+ }
181
+ plan.adaptConstructed = internal.setHas(state.constructors, callee)
182
+ || !construct && callee === state.reflectConstruct && isNativeConstructor(args[0])
183
+ plan.dispatch = !construct && callee === state.intrinsicEval
184
+ ? () => evaluate(callee, receiver, args, rootEnvironment(sourceTarget(plan.origin)))
185
+ : reflect.apply(state.bind, construct ? state.nativeConstruct : state.nativeApply,
186
+ construct ? [undefined, callee, args, plan.newTarget] : [undefined, callee, receiver, args])
187
+ return false
188
+ }
189
+ }
190
+ const completeInvocation = (plan, value) => {
191
+ if (plan.adaptConstructed) return adaptConstructed(value, plan.origin)
192
+ if (!plan.construct && plan.callee === state.bind) internal.weakMapSet(state.bound, value,
193
+ { callee: plan.receiver, receiver: plan.args[0], args: copyArguments(plan.args, 1) })
194
+ if (!plan.construct && plan.callee === state.toString) {
195
+ let receiver = plan.receiver
196
+ while (!internal.weakMapHas(state.callableSources, receiver) && internal.weakMapHas(state.callableSourceTargets, receiver)) {
197
+ receiver = internal.weakMapGet(state.callableSourceTargets, receiver)
198
+ value = reflect.apply(state.toString, receiver, [])
199
+ }
200
+ return internal.weakMapGet(state.callableSources, receiver) ?? state.sources.get(value) ?? value
201
+ }
202
+ return exposedValue(value)
203
+ }
204
+ const beginInvocation = (plan, args) => {
205
+ validateInvocation(plan)
206
+ pendingInvocation = {
207
+ ...plan, args, invoke, interface: selected, complete: completeInvocation,
208
+ newTarget: plan.construct && plan.newTarget === undefined ? plan.callee : plan.newTarget,
209
+ advance: advanceInvocation, collect: collectArguments,
210
+ }
211
+ return nativeContinuation
212
+ }
213
+ const prepareTagInvocation = plan => {
214
+ const result = (...args) => invoke(plan.callee, plan.receiver, args, plan.origin)
215
+ internal.weakMapSet(state.prepared, result, plan)
216
+ return result
217
+ }
218
+ return {
219
+ install,
220
+ exposedValue,
221
+ isEval: callee => callee === state.intrinsicEval || internal.mapHas(selected.exposed, state.intrinsicEval) && callee === internal.mapGet(selected.exposed, state.intrinsicEval),
222
+ evaluate(compiled, environment) {
223
+ // Bootstrap originals never leave this owner. Direct eval must use the
224
+ // actual intrinsic identity inside its private explicit lexical frame.
225
+ const run = state.realmFunction(compiled.environmentName, 'eval', 'source', 'return eval(source)')
226
+ return reflect.apply(run, undefined, [environment, state.intrinsicEval, compiled.code])
227
+ },
228
+ declareGlobals(names) { if (names.length > 0) reflect.apply(state.intrinsicEval, undefined, [`var ${internal.join(names, ',')};`]) },
229
+ propagate,
230
+ prepareCall,
231
+ prepareInvocation: invocation,
232
+ releaseMemberReceiver(receiver) { return receiver },
233
+ prepareMemberInvocation(receiver, callee, origin, optional) { return invocation(callee, receiver, origin, optional) },
234
+ beginInvocation,
235
+ prepareTagInvocation,
236
+ templateArguments: state.templateArguments,
237
+ referenceCall(callee, receiver) {
238
+ if (callee == null) return callee
239
+ callee = exposedValue(callee)
240
+ const result = (...args) => invoke(callee, receiver, args)
241
+ internal.weakMapSet(state.prepared, result, { callee, receiver })
242
+ return result
243
+ },
244
+ registerCallableSourceTarget(adapter, target) { internal.weakMapSet(state.callableSourceTargets, adapter, target); return adapter },
245
+ registerSources(sources) { state.sources.register(sources) },
246
+ originalSource(source) { return state.sources.get(source) },
247
+ }
248
+ }
@@ -0,0 +1,90 @@
1
+ import { visitSource } from './compiler-source-regions.js'
2
+
3
+ /** A switch creates its case environment after evaluating the discriminant.
4
+ * Babel parents nested discriminant scopes to the switch, so filter that
5
+ * synthetic edge using the reference's actual source ancestry. */
6
+ export function* sourceScopePath(path, origin = path) {
7
+ const excluded = new Set()
8
+ for (let child = origin; child.parentPath; child = child.parentPath) {
9
+ if (child.key === 'discriminant' && child.parentPath.isSwitchStatement()) excluded.add(child.parentPath.scope)
10
+ }
11
+ for (let scope = path.scope; scope; scope = scope.parent) {
12
+ if (!excluded.has(scope)) yield scope
13
+ }
14
+ }
15
+
16
+ export function sourceBinding(path, name) {
17
+ for (const scope of sourceScopePath(path)) {
18
+ const binding = scope.getOwnBinding(name)
19
+ if (binding !== undefined && sourceBindingVisible(path, binding)) return binding
20
+ }
21
+ return undefined
22
+ }
23
+
24
+ /** Lookup and dynamic descriptor capture share the same visibility test. */
25
+ export function sourceBindingVisible(path, binding) {
26
+ return !sourceInParameters(path, binding.scope) || binding.kind === 'param'
27
+ || binding.kind === 'local' && binding.path.node === binding.scope.block
28
+ }
29
+
30
+ /** A parameter expression (including a closure inside it) cannot see its
31
+ * function's body declarations. Source ancestry owns this visibility edge. */
32
+ export function sourceInParameters(path, scope) {
33
+ for (let child = path; child.parentPath; child = child.parentPath) {
34
+ if (child.parentPath.node === scope.block) return child.listKey === 'params'
35
+ }
36
+ return false
37
+ }
38
+
39
+ /** Source environments that can introduce a binding between a reference and
40
+ * its static owner. Strict closures still inherit outer sloppy activations. */
41
+ export function createDynamicScopeAnalysis(tree) {
42
+ const evalActivations = new WeakSet()
43
+ const parameterEvalActivations = new WeakSet()
44
+ const directEvalSubtrees = new WeakSet()
45
+ visitSource(tree, { CallExpression(path) {
46
+ if (path.node.callee.type !== 'Identifier' || path.node.callee.name !== 'eval') return
47
+ // Parameter plans need syntactic eval containment, including strict and
48
+ // nested callables; shadow introduction separately follows real owners.
49
+ for (let current = path; current && !directEvalSubtrees.has(current.node); current = current.parentPath) {
50
+ directEvalSubtrees.add(current.node)
51
+ }
52
+ if (path.isInStrictMode()) return
53
+ let child = path
54
+ for (let parent = path.parentPath; parent; child = parent, parent = parent.parentPath) {
55
+ if (parent.isFunction()) {
56
+ if (child.key === 'body') { evalActivations.add(parent.node); return }
57
+ if (child.listKey === 'params') { parameterEvalActivations.add(parent.node); return }
58
+ }
59
+ if (parent.isProgram()) evalActivations.add(parent.node)
60
+ }
61
+ } })
62
+ return {
63
+ containsDirectEval(node) { return directEvalSubtrees.has(node) },
64
+ mayEvalShadow(path, ownerNode, self = false) {
65
+ let child = path
66
+ for (let parent = path.parentPath; parent; child = parent, parent = parent.parentPath) {
67
+ if (parent.node === ownerNode && !self) return false
68
+ if (evalActivations.has(parent.node) && (parent.isProgram() || child.key === 'body')) return true
69
+ if (parameterEvalActivations.has(parent.node) && (child.key === 'body' || child.listKey === 'params')) return true
70
+ if (parent.node === ownerNode) return false
71
+ }
72
+ return false
73
+ },
74
+ }
75
+ }
76
+
77
+ /** Var storage belongs to its variable scope, while initialization can select
78
+ * a simple catch binding or an intervening with object. Ordinary block lexical
79
+ * declarations retain their independent stateful identity. */
80
+ export function varInitializerTarget(path, ownerNode, name, sourceName = node => node.name) {
81
+ const withNodes = []
82
+ let child = path
83
+ for (let parent = path.parentPath; parent && parent.node !== ownerNode;
84
+ child = parent, parent = parent.parentPath) {
85
+ if (parent.isWithStatement() && child.key === 'body') withNodes.push(parent.node)
86
+ if (parent.isCatchClause() && child.key === 'body' && parent.node.param?.type === 'Identifier'
87
+ && sourceName(parent.node.param) === name) return { catchNode: parent.node, withNodes }
88
+ }
89
+ return { withNodes }
90
+ }
@@ -20,6 +20,14 @@ import {
20
20
  import { editTargetForCall, projectSessionLog } from './session-log-view.js'
21
21
  import { RUN_CODE } from './runtime-bridge-owner.js'
22
22
  import { isRecord } from './record-utils.js'
23
+ import {
24
+ userBindingDraftCapabilityFromMeta,
25
+ withUserBindingDraftCapability,
26
+ } from './user-binding-draft-projection.js'
27
+ import {
28
+ userBindingsSnapshotFromMeta,
29
+ withUserBindingsSnapshot,
30
+ } from './user-bindings.js'
23
31
  import {
24
32
  validatedReplMemorySnapshot,
25
33
  withReplMemorySnapshot,
@@ -96,9 +104,19 @@ export function createEditTransportOwner(ctx, {
96
104
  if (derived.recoveryBoundaries !== undefined) {
97
105
  meta[RECOVERY_BOUNDARY_KEY] = normalizeRecoveryBoundaries(derived.recoveryBoundaries)
98
106
  }
99
- return derived.replMemory === undefined
107
+ const withBindings = derived.userBindings === undefined
100
108
  ? meta
101
- : withReplMemorySnapshot(meta, derived.replMemory, presentationGeneration)
109
+ : withUserBindingsSnapshot(meta, derived.userBindings)
110
+ const withDraft = derived.draftCapability === undefined
111
+ ? withBindings
112
+ : withUserBindingDraftCapability(
113
+ withBindings,
114
+ derived.draftCapability,
115
+ presentationGeneration,
116
+ )
117
+ return derived.replMemory === undefined
118
+ ? withDraft
119
+ : withReplMemorySnapshot(withDraft, derived.replMemory, presentationGeneration)
102
120
  }
103
121
  definition.output = {
104
122
  schema: {
@@ -147,7 +165,7 @@ export function createEditTransportOwner(ctx, {
147
165
  if (typeof ctx.tools.execute !== 'function') {
148
166
  throw new Error('ptc-plus: DSH tools.execute is required for derived edit execution')
149
167
  }
150
- const persistedCallSeq = currentDurableReplay ? persistedTargetCallSeq : undefined
168
+ const persistedCallSeq = persistedTargetCallSeq
151
169
  const id = sessionId(agent)
152
170
  let claims = editClaims.get(id)
153
171
  if (claims === undefined) {
@@ -186,6 +204,11 @@ export function createEditTransportOwner(ctx, {
186
204
  ? normalizeRecoveryBoundaries(inner.meta[RECOVERY_BOUNDARY_KEY])
187
205
  : undefined
188
206
  const replMemory = validatedReplMemorySnapshot(inner?.meta, presentationGeneration)
207
+ const userBindings = userBindingsSnapshotFromMeta(inner?.meta)
208
+ const draftCapability = userBindingDraftCapabilityFromMeta(
209
+ inner?.meta,
210
+ presentationGeneration,
211
+ )
189
212
  const value = derivedEditResult(inner)
190
213
  const derived = {
191
214
  targetCallSeq: target.callSeq,
@@ -193,6 +216,8 @@ export function createEditTransportOwner(ctx, {
193
216
  rewrites,
194
217
  recoveryBoundaries,
195
218
  replMemory,
219
+ userBindings,
220
+ draftCapability,
196
221
  code: edited.code,
197
222
  description: edited.description,
198
223
  }
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Own how PTC Plus attaches to the host's program-execution seam.
3
+ *
4
+ * DSH registers one execution seam per generation. The current release
5
+ * registers `ctx.ptcRuntime` (`@deepseek-ai/dsh-ptc-runtime`); the preceding one
6
+ * registered `ctx.codeRuntime` (`@deepseek-ai/dsh-code-runtime`). Both run one
7
+ * model-written program against host async bindings. They differ in the service
8
+ * name, in the call shape (`resolve(request)` then `run(spec)` against a single
9
+ * `run(request)`), and in the capability descriptors a provider publishes.
10
+ * Which seam is live is decided by the registered service, never by a version
11
+ * string, so a deployment on either generation obtains the same behavior.
12
+ *
13
+ * PTC Plus replaces the execution of session cells with its own persistent
14
+ * REPL. The object DSH describes to the model is therefore the plugin's
15
+ * execution, not the wrapped provider's, so `takeOver()` answers the
16
+ * descriptors the plugin can honor and withholds the ones it cannot. The REPL
17
+ * runs every cell in one long-lived worker rather than a fresh process per
18
+ * call, applies the session's configured budgets rather than a per-call host
19
+ * deadline, and performs no file confinement. DSH gates `timeoutMs` on
20
+ * `timeout` and resolves or escalates file policy on `sandboxMode`, so
21
+ * withholding those descriptors keeps DSH's authority decisions with DSH
22
+ * instead of having this plugin accept an input it would then ignore.
23
+ *
24
+ * A seam-neutral request carries exactly `program`, `bindings`, and `signal`:
25
+ * the fields PTC Plus consumes. A resolved spec's directory, deadline, and
26
+ * authority describe the provider whose execution was replaced, and are neither
27
+ * forwarded to the session kernel nor re-derived here.
28
+ */
29
+
30
+ /** Cordis service name registered by the current DSH generation. */
31
+ export const EXECUTION_SEAM_SERVICE = 'ptcRuntime'
32
+
33
+ /** Cordis service name registered by the preceding DSH generation. */
34
+ export const LEGACY_EXECUTION_SEAM_SERVICE = 'codeRuntime'
35
+
36
+ const SEAM_SERVICES = Object.freeze([EXECUTION_SEAM_SERVICE, LEGACY_EXECUTION_SEAM_SERVICE])
37
+
38
+ /**
39
+ * Descriptors the plugin replaces with the capabilities of its own execution.
40
+ * `executionInstructions` describes the wrapped provider's program model, which
41
+ * no longer applies; `timeout` and `sandboxMode` gate host inputs the plugin
42
+ * cannot honor. Withdrawal is unconditional because the rule is a property of
43
+ * the plugin's execution, not of a generation: the preceding generation's
44
+ * contract defines none of these members and nothing in its install closure
45
+ * reads them, so withdrawing there is inert, while a provider that published
46
+ * them stays unable to outlive the takeover.
47
+ */
48
+ const WITHHELD_DESCRIPTORS = Object.freeze(['executionInstructions', 'sandboxMode', 'timeout'])
49
+
50
+ /** Read a registered service without declaring an injection requirement. */
51
+ function readService(ctx, name) {
52
+ if (typeof ctx?.get === 'function') return ctx.get(name)
53
+ return ctx?.[name]
54
+ }
55
+
56
+ function descriptorsMatch(left, right) {
57
+ if (left === undefined || right === undefined) return left === right
58
+ return left.value === right.value
59
+ && left.get === right.get
60
+ && left.set === right.set
61
+ && left.writable === right.writable
62
+ && left.enumerable === right.enumerable
63
+ && left.configurable === right.configurable
64
+ }
65
+
66
+ /**
67
+ * Build the seam handle for one live execution service.
68
+ * @param {object} service - the registered `ctx.ptcRuntime` or `ctx.codeRuntime`.
69
+ * @param {string} serviceName - the service name it was registered under.
70
+ */
71
+ export function createExecutionSeam(service, serviceName) {
72
+ if (service === null || typeof service !== 'object') {
73
+ throw new TypeError(`ptc-plus: ${serviceName} must be an execution service object`)
74
+ }
75
+ const current = serviceName === EXECUTION_SEAM_SERVICE
76
+ const upstreamRun = service.run
77
+ if (typeof upstreamRun !== 'function') {
78
+ throw new TypeError(`ptc-plus: ${serviceName}.run must be a function`)
79
+ }
80
+ const upstreamResolve = service.resolve
81
+ if (current && typeof upstreamResolve !== 'function') {
82
+ throw new TypeError('ptc-plus: ptcRuntime.resolve must be a function')
83
+ }
84
+ let installed
85
+ return Object.freeze({
86
+ serviceName,
87
+ language: service.language,
88
+ /**
89
+ * Run one program through the provider this plugin wraps. The current
90
+ * generation resolves the request before running it, exactly as its own
91
+ * consumer does; the preceding generation accepts the request directly.
92
+ */
93
+ invokeUpstream(request) {
94
+ return current
95
+ ? upstreamRun.call(service, upstreamResolve.call(service, request))
96
+ : upstreamRun.call(service, request)
97
+ },
98
+ /**
99
+ * Install the plugin's execution entry, and its capability descriptors, on
100
+ * the live service. Only one takeover is live at a time.
101
+ * @param {(request: object) => Promise<object>} execute - the plugin's entry.
102
+ * @returns {() => void} restores every descriptor this call replaced.
103
+ */
104
+ takeOver(execute) {
105
+ if (installed !== undefined) throw new Error('ptc-plus: execution seam already taken over')
106
+ const saved = new Map()
107
+ const written = new Map()
108
+ const define = (name, descriptor) => {
109
+ const previous = Object.getOwnPropertyDescriptor(service, name)
110
+ if (!saved.has(name)) saved.set(name, previous)
111
+ // Preserve the shadowed property's visibility so a consumer that
112
+ // enumerates the provider sees the same member set.
113
+ Object.defineProperty(service, name, { enumerable: previous?.enumerable ?? false, ...descriptor })
114
+ written.set(name, Object.getOwnPropertyDescriptor(service, name))
115
+ }
116
+ define('run', {
117
+ configurable: true,
118
+ writable: true,
119
+ value: current
120
+ ? spec => execute({
121
+ program: spec.program,
122
+ bindings: spec.bindings,
123
+ signal: spec.signal,
124
+ })
125
+ : request => execute(request),
126
+ })
127
+ for (const name of WITHHELD_DESCRIPTORS) {
128
+ define(name, { configurable: true, value: name === 'executionInstructions' ? '' : undefined })
129
+ }
130
+ const restore = () => {
131
+ if (installed !== restore) return
132
+ installed = undefined
133
+ for (const [name, descriptor] of saved) {
134
+ if (!descriptorsMatch(Object.getOwnPropertyDescriptor(service, name), written.get(name))) continue
135
+ if (descriptor === undefined) delete service[name]
136
+ else Object.defineProperty(service, name, descriptor)
137
+ }
138
+ }
139
+ installed = restore
140
+ return restore
141
+ },
142
+ })
143
+ }
144
+
145
+ /**
146
+ * Wait for the host's execution seam and attach the plugin to the live one.
147
+ *
148
+ * A deployment registers one generation, so the current service is preferred
149
+ * whenever both are live; the preceding generation attaches only when the
150
+ * current one is absent. The injected scope requires the plugin's own core
151
+ * services as well, so the callback receives a context the plugin can work in,
152
+ * and everything the plugin registers there belongs to the seam the host
153
+ * registered: unloading that service unloads the plugin, and registering it
154
+ * again attaches once more, which is how a host that reloads its runtime
155
+ * provider reloads this dependent.
156
+ *
157
+ * @param {object} ctx - the plugin context.
158
+ * @param {object} options - `services` are the plugin's own required services,
159
+ * and `attach` is invoked with the injected scope and the seam handle.
160
+ * @returns {Promise<unknown>} settles with the attach result.
161
+ */
162
+ export function installExecutionSeam(ctx, { services = [], attach }) {
163
+ let live
164
+ const select = (name, scope) => {
165
+ if (live !== undefined) return live.attached
166
+ if (name === LEGACY_EXECUTION_SEAM_SERVICE && readService(ctx, EXECUTION_SEAM_SERVICE) !== undefined) {
167
+ return undefined
168
+ }
169
+ const entry = { name }
170
+ live = entry
171
+ try {
172
+ entry.attached = attach(scope, createExecutionSeam(scope?.[name] ?? readService(ctx, name), name))
173
+ } catch (error) {
174
+ live = undefined
175
+ throw error
176
+ }
177
+ scope.effect?.(() => () => {
178
+ if (live === entry) live = undefined
179
+ }, 'ptc-plus execution seam lifecycle')
180
+ return entry.attached
181
+ }
182
+ const pending = SEAM_SERVICES.map(name => new Promise((resolve, reject) => {
183
+ ctx.inject([...services, name], scope => {
184
+ try {
185
+ resolve(select(name, scope))
186
+ } catch (error) {
187
+ reject(error)
188
+ throw error
189
+ }
190
+ })
191
+ }))
192
+ return Promise.race(pending)
193
+ }