dsh-ptc-plus 0.3.2 → 0.4.2

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 (206) hide show
  1. package/README.en.md +63 -80
  2. package/README.md +63 -80
  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 +30827 -742
  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 +556 -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 +7 -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 +5 -3
  25. package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +1 -1
  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 +3 -1
  31. package/docs/adr/0014-persist-cell-rewrite-policy.md +6 -2
  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 +5 -3
  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 +12 -0
  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 +77 -21
  48. package/docs/binding-review-placement-design.md +200 -0
  49. package/docs/capability-projection.md +1 -1
  50. package/docs/client-ui.md +95 -19
  51. package/docs/durability-design.md +28 -10
  52. package/docs/evaluation.md +78 -6
  53. package/docs/installation.md +41 -6
  54. package/docs/publishing.md +5 -3
  55. package/docs/runtime-reference.md +76 -25
  56. package/docs/semantic-validation.md +29 -0
  57. package/docs/stateful-binding-updates-proposal.md +759 -0
  58. package/docs/user-bindings.en.md +66 -0
  59. package/docs/user-bindings.md +66 -0
  60. package/docs/value-wire.md +3 -3
  61. package/docs/verification-optimization-work.md +41 -0
  62. package/docs/verification.md +71 -0
  63. package/index.js +369 -92
  64. package/internal/ast-traversal.js +5 -2
  65. package/internal/async-local-storage-intrinsics.js +38 -0
  66. package/internal/binding-declaration-contract.js +8 -0
  67. package/internal/binding-descriptors.js +1 -0
  68. package/internal/binding-pattern-traversal.js +25 -0
  69. package/internal/binding-pattern.js +26 -29
  70. package/internal/binding-source-analysis.js +708 -0
  71. package/internal/binding-update-policy.js +72 -0
  72. package/internal/bounded-worker-round.js +56 -0
  73. package/internal/callable-definition-environment.js +42 -0
  74. package/internal/callable-source-catalog.js +129 -0
  75. package/internal/callable-source-encoding.js +47 -0
  76. package/internal/callable-source-facts.js +459 -0
  77. package/internal/callable-source-format.js +2 -0
  78. package/internal/callable-source-lowering.js +123 -0
  79. package/internal/cell-analysis-contract.js +25 -0
  80. package/internal/cell-analysis.js +253 -290
  81. package/internal/cell-completion.js +15 -0
  82. package/internal/cell-error.js +7 -0
  83. package/internal/cell-parser.js +66 -0
  84. package/internal/cell-rewriter.js +3 -677
  85. package/internal/commonjs-export-evidence.js +63 -0
  86. package/internal/commonjs-source-evidence.js +72 -0
  87. package/internal/compiler-amaro.cjs +2 -0
  88. package/internal/compiler-compression.js +12 -0
  89. package/internal/compiler-data.js +58 -0
  90. package/internal/compiler-descriptors.js +54 -0
  91. package/internal/compiler-dynamic-cache.js +48 -0
  92. package/internal/compiler-entry.js +35 -0
  93. package/internal/compiler-generated-names.js +17 -0
  94. package/internal/compiler-intrinsics.js +50 -0
  95. package/internal/compiler-module-links.js +37 -0
  96. package/internal/compiler-operations.js +61 -0
  97. package/internal/compiler-platform-factory.js +84 -0
  98. package/internal/compiler-platform.js +136 -0
  99. package/internal/compiler-record-roles.js +53 -0
  100. package/internal/compiler-region-output.js +259 -0
  101. package/internal/compiler-resource-lowering.js +103 -0
  102. package/internal/compiler-scope-facts.js +115 -0
  103. package/internal/compiler-service.js +109 -0
  104. package/internal/compiler-source-cache.js +32 -0
  105. package/internal/compiler-source-pieces.js +93 -0
  106. package/internal/compiler-source-regions.js +132 -0
  107. package/internal/compiler-storage-source.js +54 -0
  108. package/internal/compiler-text.js +15 -0
  109. package/internal/compiler-typescript.cjs +2 -0
  110. package/internal/config-spec.js +83 -27
  111. package/internal/cordis-tools-owner.js +130 -21
  112. package/internal/diagnostic.js +27 -1
  113. package/internal/direct-surface-owner.js +76 -23
  114. package/internal/dynamic-binding-evidence.js +16 -0
  115. package/internal/dynamic-environment-compiler.js +779 -0
  116. package/internal/dynamic-environment-integration.js +91 -0
  117. package/internal/dynamic-environment-runtime.js +383 -0
  118. package/internal/dynamic-native-calls.js +153 -0
  119. package/internal/dynamic-native-runtime.js +262 -0
  120. package/internal/dynamic-scope-analysis.js +90 -0
  121. package/internal/edit-transport-owner.js +27 -2
  122. package/internal/execution-seam-compat.js +193 -0
  123. package/internal/failure-reporting.js +136 -28
  124. package/internal/host-rpc.js +66 -0
  125. package/internal/isolated-worker.js +555 -0
  126. package/internal/kernel-child.js +139 -0
  127. package/internal/kernel-worker.js +1179 -233
  128. package/internal/language-semantics.js +14 -0
  129. package/internal/legacy-binding-catalog.js +48 -0
  130. package/internal/legacy-cell-analysis.js +600 -0
  131. package/internal/legacy-cell-parser.js +50 -0
  132. package/internal/legacy-cell-rewriter.js +631 -0
  133. package/internal/legacy-repl-convenience.js +291 -0
  134. package/internal/managed-module-operations.js +170 -0
  135. package/internal/module-compilation.js +322 -0
  136. package/internal/module-runtime-platform.js +19 -0
  137. package/internal/module-transform-contract.js +42 -0
  138. package/internal/native-await-declarations.js +68 -0
  139. package/internal/native-root-compilation.js +54 -0
  140. package/internal/native-root-dynamic.js +67 -0
  141. package/internal/record-utils.js +24 -15
  142. package/internal/recovery-tips.js +27 -15
  143. package/internal/rejected-cell-editor.js +13 -8
  144. package/internal/repl-convenience.js +2 -289
  145. package/internal/repl-memory-projection.js +81 -12
  146. package/internal/repl-observation-interest.js +70 -0
  147. package/internal/repl-rewrite-contract.js +6 -0
  148. package/internal/repl-scope-normalizer.js +2091 -0
  149. package/internal/repl-value-observer.js +117 -0
  150. package/internal/rpc-contract.js +52 -0
  151. package/internal/runtime-bridge-owner.js +172 -28
  152. package/internal/runtime-config.js +48 -52
  153. package/internal/runtime-contexts.js +82 -10
  154. package/internal/runtime-intrinsics.js +101 -0
  155. package/internal/runtime-messages.js +111 -0
  156. package/internal/session-cell-executor.js +402 -87
  157. package/internal/session-journal-recovery.js +684 -0
  158. package/internal/session-journal-schema.js +123 -0
  159. package/internal/session-journal.js +199 -654
  160. package/internal/session-log-view.js +42 -9
  161. package/internal/session-runtime.js +257 -95
  162. package/internal/session-state.js +247 -71
  163. package/internal/source-position-map.js +230 -71
  164. package/internal/stateful-module-compiler.js +154 -0
  165. package/internal/stateful-module-runtime.js +303 -0
  166. package/internal/stateful-root-compiler.js +600 -0
  167. package/internal/stateful-root-runtime.js +340 -0
  168. package/internal/typert-codec-compat.js +28 -0
  169. package/internal/typescript-transform.js +31 -0
  170. package/internal/user-binding-authoring.js +70 -0
  171. package/internal/user-binding-console-worker.js +244 -0
  172. package/internal/user-binding-console.js +148 -0
  173. package/internal/user-binding-draft-projection.js +335 -0
  174. package/internal/user-binding-model-context.js +31 -0
  175. package/internal/user-binding-runner.js +56 -0
  176. package/internal/user-bindings-owner.js +1065 -0
  177. package/internal/user-bindings-store.js +293 -0
  178. package/internal/user-bindings.js +361 -0
  179. package/internal/value-wire-schema.js +28 -0
  180. package/internal/value-wire.js +130 -91
  181. package/internal/worker-client.js +57 -67
  182. package/internal/worker-cwd-virtualization.js +80 -53
  183. package/internal/worker-environment.js +65 -0
  184. package/internal/worker-output-relay.js +29 -0
  185. package/internal/worker-repl-realm.js +134 -0
  186. package/internal/worker-shutdown.js +14 -0
  187. package/package.json +76 -7
  188. package/src/client-activity.js +95 -53
  189. package/src/client-authoring-view.js +580 -0
  190. package/src/client-binding-review.js +230 -0
  191. package/src/client-bindings-data.js +29 -0
  192. package/src/client-catalog.js +127 -0
  193. package/src/client-code-editor.js +84 -0
  194. package/src/client-console.js +112 -0
  195. package/src/client-copy.js +370 -0
  196. package/src/client-feature-gates.js +62 -0
  197. package/src/client-host-compat.js +150 -0
  198. package/src/client-indicator-view.js +267 -0
  199. package/src/client-repl-view.js +292 -0
  200. package/src/client-rpc.js +20 -0
  201. package/src/client-settings-view.js +221 -0
  202. package/src/client-styles.js +106 -0
  203. package/src/client-tool-view.js +108 -0
  204. package/src/client-transport.js +159 -0
  205. package/src/client-workbench.js +521 -0
  206. package/src/client.js +233 -615
@@ -0,0 +1,335 @@
1
+ import { isRecord } from './record-utils.js'
2
+ import { normalizeBindingModelContext } from './user-binding-model-context.js'
3
+
4
+ export const USER_BINDING_DRAFT_KEY = 'ptcPlusBindingDraft'
5
+ export const USER_BINDING_DRAFT_META_KEY = 'dshPtcPlusBindingDraft'
6
+
7
+ const META_VERSION = 1
8
+ const MAX_GENERATION_LENGTH = 128
9
+ const MAX_CAPABILITY_LENGTH = 128
10
+ const MAX_COMMAND_ID_LENGTH = 256
11
+ const META_FIELDS = new Set(['version', 'generation', 'capability'])
12
+ const STATE_FIELDS = new Set([
13
+ 'generation', 'phase', 'capability', 'commandId', 'fallbackReady', 'fallbackCommandId',
14
+ 'turnStarted',
15
+ ])
16
+ const VIEW_FIELDS = new Set(['phase', 'capability', 'commandId'])
17
+ const PHASES = new Set(['idle', 'pending', 'ready', 'failed'])
18
+ const ACTION_PREFIX = 'Global User Binding action (persistence does not imply session activation):\n'
19
+
20
+ function normalizeCandidate(value) {
21
+ if (!isRecord(value) || typeof value.requestId !== 'string' || value.requestId.length === 0
22
+ || value.requestId.length > 128 || !Number.isSafeInteger(value.version) || value.version < 1
23
+ || !['new', 'edit'].includes(value.mode) || !isRecord(value.entry)
24
+ || !['namespace', 'top-level'].includes(value.entry.scope)
25
+ || value.entry.enabled !== false || !Array.isArray(value.entry.symbols)
26
+ || value.entry.symbols.some(symbol => typeof symbol !== 'string')
27
+ || ['id', 'name', 'purpose', 'source'].some(key => typeof value.entry[key] !== 'string')
28
+ || value.entry.source.length === 0 || value.entry.source.length > 65536) {
29
+ throw new Error('invalid dsh-ptc-plus accepted binding candidate')
30
+ }
31
+ const modelContext = normalizeBindingModelContext(value.entry.modelContext)
32
+ return Object.freeze({
33
+ requestId: value.requestId, commandId: normalizeCommandId(value.commandId), version: value.version, mode: value.mode,
34
+ entry: Object.freeze({
35
+ id: value.entry.id, name: value.entry.name, scope: value.entry.scope,
36
+ symbols: Object.freeze([...value.entry.symbols]), purpose: value.entry.purpose,
37
+ source: value.entry.source, enabled: false,
38
+ ...(modelContext === undefined ? {} : { modelContext }),
39
+ }),
40
+ })
41
+ }
42
+
43
+ function normalizeAction(value) {
44
+ if (!exactFields(value, new Set(['requestId', 'id', 'state', 'enabled']))
45
+ || typeof value.requestId !== 'string' || value.requestId.length === 0 || value.requestId.length > 128
46
+ || typeof value.id !== 'string' || value.id.length === 0 || value.id.length > 64
47
+ || !['saved', 'discarded'].includes(value.state) || typeof value.enabled !== 'boolean'
48
+ || (value.state === 'discarded' && value.enabled)) throw new Error('invalid binding action receipt')
49
+ return Object.freeze({ requestId: value.requestId, id: value.id, state: value.state, enabled: value.enabled })
50
+ }
51
+
52
+ export function bindingActionNotice(action) {
53
+ return {
54
+ source: { kind: 'plugin', plugin: 'ptc-plus', form: 'notice', summary: 'Global User Binding action' },
55
+ content: [{ type: 'text', text: ACTION_PREFIX + JSON.stringify(normalizeAction(action)) }],
56
+ }
57
+ }
58
+
59
+ export function readBindingAction(message) {
60
+ if (message?.source?.kind !== 'plugin' || message.source.plugin !== 'ptc-plus'
61
+ || message.source.form !== 'notice' || message.source.summary !== 'Global User Binding action'
62
+ || message.content?.length !== 1 || message.content[0]?.type !== 'text') return undefined
63
+ try {
64
+ const text = message.content[0].text
65
+ if (typeof text !== 'string' || !text.startsWith(ACTION_PREFIX)) return undefined
66
+ const action = normalizeAction(JSON.parse(text.slice(ACTION_PREFIX.length)))
67
+ return text === ACTION_PREFIX + JSON.stringify(action) ? action : undefined
68
+ } catch {
69
+ return undefined
70
+ }
71
+ }
72
+
73
+ function normalizeHistory(value) {
74
+ if (!Array.isArray(value)) throw new Error('invalid binding review history')
75
+ return Object.freeze(value.map(record => {
76
+ const candidate = normalizeCandidate(record.candidate)
77
+ const action = record.action === null ? null : normalizeAction(record.action)
78
+ if (action !== null && (action.requestId !== candidate.requestId || action.id !== candidate.entry.id)) {
79
+ throw new Error('binding action does not identify its candidate')
80
+ }
81
+ if (!Number.isSafeInteger(record.acceptedSeq) || record.acceptedSeq < 0) {
82
+ throw new Error('invalid binding acceptance sequence')
83
+ }
84
+ if (record.commandId !== candidate.commandId) throw new Error('binding review does not identify its command')
85
+ return Object.freeze({
86
+ commandId: normalizeCommandId(record.commandId), acceptedSeq: record.acceptedSeq, candidate, action,
87
+ })
88
+ }))
89
+ }
90
+
91
+ function historyFields(value) {
92
+ return value.history === undefined ? {} : { history: normalizeHistory(value.history) }
93
+ }
94
+
95
+ function exactFields(value, fields) {
96
+ if (!isRecord(value)) return false
97
+ const keys = Reflect.ownKeys(value)
98
+ return keys.length === fields.size && keys.every(key => (
99
+ typeof key === 'string' && fields.has(key)
100
+ && Object.prototype.propertyIsEnumerable.call(value, key)
101
+ ))
102
+ }
103
+
104
+ function normalizeGeneration(value) {
105
+ if (typeof value !== 'string' || value.length === 0 || value.length > MAX_GENERATION_LENGTH) {
106
+ throw new Error('invalid dsh-ptc-plus binding draft generation')
107
+ }
108
+ return value
109
+ }
110
+
111
+ export function normalizeUserBindingDraftCapability(value) {
112
+ if (value === null) return null
113
+ if (typeof value !== 'string' || value.length === 0 || value.length > MAX_CAPABILITY_LENGTH) {
114
+ throw new Error('invalid dsh-ptc-plus binding draft capability')
115
+ }
116
+ return value
117
+ }
118
+
119
+ function normalizePhase(value) {
120
+ if (typeof value !== 'string' || !PHASES.has(value)) {
121
+ throw new Error('invalid dsh-ptc-plus binding draft phase')
122
+ }
123
+ return value
124
+ }
125
+
126
+ function normalizeCommandId(value) {
127
+ if (value === null) return null
128
+ if (typeof value !== 'string' || value.length === 0 || value.length > MAX_COMMAND_ID_LENGTH) {
129
+ throw new Error('invalid dsh-ptc-plus binding draft command id')
130
+ }
131
+ return value
132
+ }
133
+
134
+ /** Read a current-generation draft locator from private result metadata. */
135
+ export function userBindingDraftCapabilityFromMeta(meta, generation) {
136
+ if (!isRecord(meta) || !Object.hasOwn(meta, USER_BINDING_DRAFT_META_KEY)) return undefined
137
+ try {
138
+ const normalized = normalizeMetadata(meta[USER_BINDING_DRAFT_META_KEY])
139
+ return normalized.generation === normalizeGeneration(generation)
140
+ ? normalized.capability
141
+ : undefined
142
+ } catch {
143
+ return undefined
144
+ }
145
+ }
146
+
147
+ function normalizeMetadata(value) {
148
+ const modern = value?.version === 2
149
+ if (!exactFields(value, modern ? new Set([...META_FIELDS, 'candidate']) : META_FIELDS)
150
+ || (!modern && value.version !== META_VERSION)) {
151
+ throw new Error('invalid dsh-ptc-plus binding draft metadata')
152
+ }
153
+ return Object.freeze({
154
+ version: value.version,
155
+ generation: normalizeGeneration(value.generation),
156
+ capability: normalizeUserBindingDraftCapability(value.capability),
157
+ ...(modern ? { candidate: value.candidate === null ? null : normalizeCandidate(value.candidate) } : {}),
158
+ })
159
+ }
160
+
161
+ function projectionState(generation, options = {}) {
162
+ const state = {
163
+ generation: normalizeGeneration(generation),
164
+ phase: normalizePhase(options.phase ?? 'idle'),
165
+ capability: normalizeUserBindingDraftCapability(options.capability ?? null),
166
+ commandId: normalizeCommandId(options.commandId ?? null),
167
+ fallbackReady: options.fallbackReady === true,
168
+ fallbackCommandId: normalizeCommandId(options.fallbackCommandId ?? null),
169
+ turnStarted: options.turnStarted === true,
170
+ }
171
+ if (state.phase === 'pending' ? state.commandId === null : state.phase === 'idle' && state.commandId !== null) {
172
+ throw new Error('invalid dsh-ptc-plus binding draft command state')
173
+ }
174
+ if (state.phase !== 'pending' && state.turnStarted) {
175
+ throw new Error('invalid dsh-ptc-plus binding draft turn state')
176
+ }
177
+ if (state.fallbackReady && (state.phase !== 'pending' || state.capability === null)) {
178
+ throw new Error('invalid dsh-ptc-plus binding draft fallback state')
179
+ }
180
+ if (!state.fallbackReady && state.fallbackCommandId !== null) {
181
+ throw new Error('invalid dsh-ptc-plus binding draft fallback state')
182
+ }
183
+ return Object.freeze(state)
184
+ }
185
+
186
+ function failedAuthoringState(generation, state) {
187
+ return !state.fallbackReady
188
+ ? projectionState(generation, {
189
+ phase: 'failed', capability: state.capability, commandId: state.commandId,
190
+ })
191
+ : projectionState(generation, {
192
+ phase: 'ready', capability: state.capability, commandId: state.fallbackCommandId,
193
+ })
194
+ }
195
+
196
+ function normalizeProjectionState(value, generation) {
197
+ if (!exactFields(value, value?.history === undefined ? STATE_FIELDS : new Set([...STATE_FIELDS, 'history']))) {
198
+ throw new Error('invalid dsh-ptc-plus binding draft projection state')
199
+ }
200
+ const currentGeneration = normalizeGeneration(generation)
201
+ if (normalizeGeneration(value.generation) !== currentGeneration) {
202
+ return Object.freeze({ ...projectionState(currentGeneration), ...historyFields(value) })
203
+ }
204
+ return Object.freeze({ ...projectionState(currentGeneration, value), ...historyFields(value) })
205
+ }
206
+
207
+ /** Validate authoring state and source-owned review history for a session Client. */
208
+ export function normalizeUserBindingDraftView(value) {
209
+ if (!exactFields(value, value?.history === undefined ? VIEW_FIELDS : new Set([...VIEW_FIELDS, 'history']))) {
210
+ throw new Error('invalid dsh-ptc-plus binding draft projection view')
211
+ }
212
+ return Object.freeze({
213
+ phase: normalizePhase(value.phase),
214
+ capability: normalizeUserBindingDraftCapability(value.capability),
215
+ commandId: normalizeCommandId(value.commandId ?? null),
216
+ ...historyFields(value),
217
+ })
218
+ }
219
+
220
+ /** Attach the revocable locator and optional accepted source to private result metadata. */
221
+ export function withUserBindingDraftCapability(meta, capability, generation, candidate) {
222
+ const base = isRecord(meta) ? { ...meta } : meta === undefined ? {} : { value: meta }
223
+ base[USER_BINDING_DRAFT_META_KEY] = normalizeMetadata({
224
+ version: candidate === undefined ? META_VERSION : 2,
225
+ generation,
226
+ capability,
227
+ ...(candidate === undefined ? {} : { candidate }),
228
+ })
229
+ return base
230
+ }
231
+
232
+ /** Project the latest current-generation draft locator for one session. */
233
+ export function createUserBindingDraftProjection(generation) {
234
+ const currentGeneration = normalizeGeneration(generation)
235
+ const projection = {
236
+ key: USER_BINDING_DRAFT_KEY,
237
+ stateVersion: 4,
238
+ stateSchema: Object.freeze({
239
+ parse: value => normalizeProjectionState(value, currentGeneration),
240
+ }),
241
+ init: () => projectionState(currentGeneration),
242
+ apply(state, event) {
243
+ if (event?.type === 'session/end-seed') return projectionState(currentGeneration)
244
+ if (event?.type === 'command/run' && event.data?.name === 'binding') {
245
+ try {
246
+ return projectionState(currentGeneration, {
247
+ phase: 'pending',
248
+ capability: state.capability,
249
+ commandId: event.data.commandId,
250
+ fallbackReady: state.phase === 'ready'
251
+ ? state.capability !== null
252
+ : state.phase === 'pending' && state.fallbackReady,
253
+ fallbackCommandId: state.phase === 'ready'
254
+ ? state.capability === null ? null : state.commandId
255
+ : state.phase === 'pending' ? state.fallbackCommandId : null,
256
+ })
257
+ } catch {
258
+ return state
259
+ }
260
+ }
261
+ if (event?.type === 'command/done' && state.phase === 'pending'
262
+ && event.data?.commandId === state.commandId) {
263
+ return event.data.kind === 'error' ? failedAuthoringState(currentGeneration, state) : state
264
+ }
265
+ if (event?.type === 'turn/start' && state.phase === 'pending' && !state.turnStarted) {
266
+ return projectionState(currentGeneration, { ...state, turnStarted: true })
267
+ }
268
+ if (event?.type === 'turn/end' && state.phase === 'pending' && state.turnStarted) {
269
+ return failedAuthoringState(currentGeneration, state)
270
+ }
271
+ if (event?.type !== 'tool/result'
272
+ || (event.surfaceOp !== undefined && event.surfaceOp !== 'append')) {
273
+ return state
274
+ }
275
+ const raw = event.data?.meta?.[USER_BINDING_DRAFT_META_KEY]
276
+ if (raw === undefined) return state
277
+ try {
278
+ const metadata = normalizeMetadata(raw)
279
+ if (metadata.generation !== currentGeneration) return state
280
+ if (metadata.version === 2 && (state.phase === 'pending' || state.phase === 'failed')
281
+ && metadata.candidate?.commandId !== state.commandId) return state
282
+ if ((state.phase === 'pending' || state.phase === 'failed')
283
+ && metadata.capability === state.capability) return state
284
+ if (metadata.capability !== null) {
285
+ return projectionState(currentGeneration, {
286
+ phase: 'ready',
287
+ capability: metadata.capability,
288
+ commandId: state.commandId,
289
+ })
290
+ }
291
+ return projectionState(currentGeneration)
292
+ } catch {
293
+ return state
294
+ }
295
+ },
296
+ wire: Object.freeze({
297
+ viewSchema: Object.freeze({ parse: normalizeUserBindingDraftView }),
298
+ view: state => normalizeUserBindingDraftView({
299
+ phase: state.phase,
300
+ capability: state.capability,
301
+ commandId: state.commandId,
302
+ ...historyFields(state),
303
+ }),
304
+ }),
305
+ }
306
+ const reduce = projection.apply
307
+ projection.apply = (state, event) => {
308
+ const next = reduce(state, event)
309
+ let history = state.history
310
+ if (event?.type === 'tool/result' && event.surfaceOp === 'append') {
311
+ try {
312
+ const metadata = normalizeMetadata(event.data?.meta?.[USER_BINDING_DRAFT_META_KEY])
313
+ if (metadata.candidate != null && !history?.some(record => (
314
+ record.candidate.requestId === metadata.candidate.requestId
315
+ ))) {
316
+ history = normalizeHistory([...(history ?? []), {
317
+ commandId: metadata.candidate.commandId, acceptedSeq: event.seq, candidate: metadata.candidate, action: null,
318
+ }])
319
+ }
320
+ } catch {
321
+ // Invalid historical presentation cannot invalidate current authoring.
322
+ }
323
+ }
324
+ const action = event?.type === 'user/message' && event.surfaceOp === 'append'
325
+ ? readBindingAction(event.data) : undefined
326
+ if (action !== undefined && history !== undefined) {
327
+ history = normalizeHistory(history.map(record => record.action === null
328
+ && record.candidate.requestId === action.requestId && record.candidate.entry.id === action.id
329
+ && event.sourceEventSeqs?.includes(record.acceptedSeq)
330
+ ? { ...record, action } : record))
331
+ }
332
+ return history === undefined ? next : Object.freeze({ ...next, history })
333
+ }
334
+ return Object.freeze(projection)
335
+ }
@@ -0,0 +1,31 @@
1
+ import { assertOwnFields, isRecord } from './record-utils.js'
2
+
3
+ const FIELDS = new Set(['includeDeclaration', 'instructions', 'enabled', 'declaration'])
4
+
5
+ /** Validate presentation metadata without loading the source parser in the Client. */
6
+ export function normalizeBindingModelContext(value) {
7
+ if (value === undefined) return undefined
8
+ if (!isRecord(value)) throw new TypeError('binding modelContext must be an object')
9
+ assertOwnFields(value, FIELDS, 'binding modelContext')
10
+ const { includeDeclaration = true, instructions = '', enabled = true, declaration = '' } = value
11
+ if (typeof includeDeclaration !== 'boolean') throw new TypeError('binding modelContext.includeDeclaration must be a boolean')
12
+ if (typeof enabled !== 'boolean') throw new TypeError('binding modelContext.enabled must be a boolean')
13
+ for (const [name, text, limit] of [['instructions', instructions, 4096], ['declaration', declaration, 8192]]) {
14
+ if (typeof text !== 'string' || text.length > limit) {
15
+ throw new TypeError(`binding modelContext.${name} must be a string of at most ${limit} characters`)
16
+ }
17
+ }
18
+ // Preserve fingerprints of previously saved prompt metadata; new edits use only the current fields.
19
+ if (Object.hasOwn(value, 'enabled') || Object.hasOwn(value, 'declaration')) {
20
+ return Object.freeze({ enabled, instructions: instructions.trim(), declaration: declaration.trim(),
21
+ ...(Object.hasOwn(value, 'includeDeclaration') ? { includeDeclaration } : {}) })
22
+ }
23
+ return Object.freeze({ includeDeclaration, instructions: instructions.trim() })
24
+ }
25
+
26
+ export function bindingModelPreferences(value) {
27
+ return {
28
+ includeDeclaration: value?.includeDeclaration ?? value?.enabled ?? true,
29
+ instructions: value?.enabled === false && value.includeDeclaration === undefined ? '' : value?.instructions ?? '',
30
+ }
31
+ }
@@ -0,0 +1,56 @@
1
+ import { registerHooks } from 'node:module'
2
+ import { isAbsolute, resolve } from 'node:path'
3
+ import { pathToFileURL } from 'node:url'
4
+ import { parentPort, workerData } from 'node:worker_threads'
5
+ import { encodeValue } from './value-wire.js'
6
+ import { managedModuleImport } from './stateful-module-runtime.js'
7
+ import { compileStatefulModule, createUserModuleCompilationHooks } from './stateful-module-compiler.js'
8
+ import { WORKER_SHUTDOWN_ACKNOWLEDGEMENT, WORKER_SHUTDOWN_REQUEST } from './worker-shutdown.js'
9
+
10
+ if (parentPort === null) throw new Error('user binding runner requires a parent port')
11
+ const candidateCwd = workerData.cwd
12
+ if (typeof candidateCwd !== 'string' || !isAbsolute(candidateCwd)) {
13
+ /* c8 ignore next */
14
+ throw new Error('user binding runner requires an absolute cwd')
15
+ }
16
+ const candidateParent = pathToFileURL(resolve(candidateCwd, 'ptc-plus-bindings-candidate')).href
17
+ let candidateUrl
18
+ const moduleCompilation = createUserModuleCompilationHooks()
19
+ registerHooks({
20
+ resolve(specifier, context, nextResolve) {
21
+ return moduleCompilation.resolve(specifier, context, (selected, original) =>
22
+ nextResolve(selected, original.parentURL === candidateUrl
23
+ ? { ...original, parentURL: candidateParent } : original))
24
+ },
25
+ load: moduleCompilation.load,
26
+ })
27
+
28
+ parentPort.on('message', (message) => {
29
+ if (message?.type !== WORKER_SHUTDOWN_REQUEST) return
30
+ parentPort.postMessage({ type: WORKER_SHUTDOWN_ACKNOWLEDGEMENT })
31
+ parentPort.close()
32
+ })
33
+
34
+ try {
35
+ const prepared = compileStatefulModule(workerData.source)
36
+ candidateUrl = `data:text/javascript,${encodeURIComponent(prepared.code)}#candidate`
37
+ moduleCompilation.mark(candidateUrl, { compiled: true, moduleInterface: prepared.moduleInterface, sourceRegions: prepared.sourceRegions })
38
+ const namespace = await managedModuleImport(candidateUrl, candidateUrl)
39
+ let value = { symbols: Object.keys(namespace).sort() }
40
+ if (workerData.invocation !== undefined) {
41
+ const { symbol, args } = workerData.invocation
42
+ if (!Object.hasOwn(namespace, symbol) || typeof namespace[symbol] !== 'function') {
43
+ throw new TypeError(`candidate export ${JSON.stringify(symbol)} is not callable`)
44
+ }
45
+ value = await namespace[symbol](...args)
46
+ }
47
+ parentPort.postMessage({ ok: true, value: encodeValue(value, workerData.valueLimits) })
48
+ // The result is delivered; nothing this worker owns may keep it alive now.
49
+ parentPort.unref()
50
+ } catch (error) {
51
+ parentPort.postMessage({
52
+ ok: false,
53
+ error: error instanceof Error ? `${error.name}: ${error.message}` : String(error),
54
+ })
55
+ parentPort.unref()
56
+ }