dsh-ptc-plus 0.4.1 → 0.4.3

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 (103) hide show
  1. package/README.en.md +164 -78
  2. package/README.md +165 -79
  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-icon.svg +5 -0
  6. package/client.js +854 -336
  7. package/compiler-core.cjs +161 -157
  8. package/compiler-core.cjs.map +3 -3
  9. package/docs/README.md +4 -1
  10. package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +8 -7
  11. package/docs/adr/0008-define-agent-operating-contract.md +2 -2
  12. package/docs/adr/0010-session-log-derived-recovery-tips.md +7 -0
  13. package/docs/adr/0016-archive-resolved-review-findings.md +3 -3
  14. package/docs/adr/0019-plugin-settings-and-kill-switch.md +3 -3
  15. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -0
  16. package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +14 -3
  17. package/docs/adr/0023-global-user-bindings.md +7 -7
  18. package/docs/adr/0025-use-versioned-logical-binding-identities.md +3 -3
  19. package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +23 -7
  20. package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +9 -5
  21. package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +26 -6
  22. package/docs/adr/0031-close-semantic-obligations-by-owner.md +29 -0
  23. package/docs/adr/README.md +4 -4
  24. package/docs/architecture.md +15 -10
  25. package/docs/binding-review-placement-design.md +1 -1
  26. package/docs/client-ui.md +9 -5
  27. package/docs/durability-design.md +12 -9
  28. package/docs/installation.md +27 -9
  29. package/docs/runtime-reference.md +23 -12
  30. package/docs/semantic-validation.md +7 -3
  31. package/docs/stateful-binding-updates-proposal.md +8 -4
  32. package/docs/user-bindings.en.md +10 -7
  33. package/docs/user-bindings.md +9 -6
  34. package/docs/value-wire.md +1 -1
  35. package/docs/verification-optimization-work.md +41 -0
  36. package/docs/verification.md +53 -2
  37. package/index.js +39 -34
  38. package/internal/async-local-storage-intrinsics.js +43 -0
  39. package/internal/binding-declaration-contract.js +8 -0
  40. package/internal/binding-source-analysis.js +381 -64
  41. package/internal/binding-update-policy.js +7 -1
  42. package/internal/callable-source-facts.js +37 -1
  43. package/internal/cell-analysis.js +85 -17
  44. package/internal/compiler-intrinsics.js +9 -43
  45. package/internal/compiler-module-links.js +7 -4
  46. package/internal/config-spec.js +1 -1
  47. package/internal/cordis-tools-owner.js +20 -10
  48. package/internal/dynamic-environment-integration.js +8 -4
  49. package/internal/dynamic-environment-runtime.js +24 -3
  50. package/internal/dynamic-native-calls.js +4 -1
  51. package/internal/dynamic-native-runtime.js +15 -1
  52. package/internal/edit-transport-owner.js +80 -20
  53. package/internal/execution-seam-compat.js +192 -30
  54. package/internal/failure-reporting.js +38 -30
  55. package/internal/isolated-worker.js +17 -10
  56. package/internal/kernel-worker.js +645 -393
  57. package/internal/message-sources.js +14 -0
  58. package/internal/module-compilation.js +7 -5
  59. package/internal/module-transform-contract.js +32 -2
  60. package/internal/record-utils.js +24 -15
  61. package/internal/repl-memory-projection.js +10 -0
  62. package/internal/repl-scope-normalizer.js +72 -35
  63. package/internal/repl-value-observer.js +145 -46
  64. package/internal/runtime-config.js +62 -2
  65. package/internal/runtime-intrinsics.js +104 -0
  66. package/internal/runtime-messages.js +6 -4
  67. package/internal/session-cell-executor.js +26 -2
  68. package/internal/session-journal-recovery.js +267 -67
  69. package/internal/session-journal-schema.js +30 -13
  70. package/internal/session-journal.js +485 -44
  71. package/internal/session-log-view.js +5 -4
  72. package/internal/session-runtime.js +46 -21
  73. package/internal/session-state.js +7 -29
  74. package/internal/settings-compat.js +66 -1
  75. package/internal/source-position-map.js +38 -0
  76. package/internal/stateful-root-compiler.js +3 -1
  77. package/internal/typescript-transform.js +4 -2
  78. package/internal/user-binding-console-worker.js +115 -41
  79. package/internal/user-binding-draft-projection.js +3 -2
  80. package/internal/user-bindings-owner.js +2 -1
  81. package/internal/user-bindings.js +16 -3
  82. package/internal/validated-parse-repair.js +6 -2
  83. package/internal/value-wire.js +189 -94
  84. package/internal/worker-client.js +99 -8
  85. package/internal/worker-cwd-virtualization.js +82 -54
  86. package/internal/worker-environment.js +20 -0
  87. package/internal/worker-output-capture.js +187 -0
  88. package/internal/worker-output-fence.js +26 -0
  89. package/internal/worker-realm-surfaces.js +197 -0
  90. package/internal/worker-repl-realm.js +140 -0
  91. package/package.json +37 -4
  92. package/src/client-activity.js +12 -2
  93. package/src/client-authoring-view.js +121 -62
  94. package/src/client-console.js +4 -2
  95. package/src/client-copy.js +4 -2
  96. package/src/client-feature-gates.js +7 -9
  97. package/src/client-host-compat.js +104 -27
  98. package/src/client-repl-view.js +3 -1
  99. package/src/client-styles.js +12 -1
  100. package/src/client-tool-view.js +3 -1
  101. package/src/client-transport.js +2 -2
  102. package/src/client-workbench.js +4 -3
  103. package/src/client.js +56 -17
@@ -1,6 +1,7 @@
1
1
  import { LIVE_MODULE_SEMANTICS } from './repl-rewrite-contract.js'
2
2
  import { LEGACY_LANGUAGE_SEMANTICS, STATEFUL_LANGUAGE_SEMANTICS,
3
3
  PROTECTED_LANGUAGE_SEMANTICS, normalizeLanguageSemantics } from './language-semantics.js'
4
+ import { moduleTransformForLanguage, normalizeModuleTransform } from './module-transform-contract.js'
4
5
 
5
6
  /**
6
7
  * Translate the public compatibility settings into the execution policy used
@@ -15,8 +16,10 @@ export function executionPolicies(config, replayRecord = undefined) {
15
16
  const bindingPolicy = Object.freeze({ ...replayRecord.bindingPolicy })
16
17
  const rewritesEnabled = Object.freeze({ ...replayRecord.rewritePolicy })
17
18
  const moduleSemantics = Object.freeze({ ...replayRecord.moduleSemantics })
19
+ const languageSemantics = normalizeLanguageSemantics(replayRecord.languageSemantics ?? LEGACY_LANGUAGE_SEMANTICS)
18
20
  return Object.freeze({
19
- languageSemantics: normalizeLanguageSemantics(replayRecord.languageSemantics ?? LEGACY_LANGUAGE_SEMANTICS),
21
+ languageSemantics,
22
+ moduleTransform: normalizeModuleTransform(replayRecord.moduleTransform, languageSemantics),
20
23
  bindingPolicy,
21
24
  rewritesEnabled,
22
25
  moduleSemantics,
@@ -25,6 +28,7 @@ export function executionPolicies(config, replayRecord = undefined) {
25
28
  if (config.bindingUpdates === 'stateful' && config.legacyBindingSettings !== true) {
26
29
  return Object.freeze({
27
30
  languageSemantics: STATEFUL_LANGUAGE_SEMANTICS,
31
+ moduleTransform: moduleTransformForLanguage(STATEFUL_LANGUAGE_SEMANTICS),
28
32
  bindingPolicy: Object.freeze({ variableRedeclarations: true, functionClassRedeclarations: true }),
29
33
  rewritesEnabled: Object.freeze({ autoRewriteImports: true, autoStripExports: true, autoSplitRedeclarations: true }),
30
34
  moduleSemantics: LIVE_MODULE_SEMANTICS,
@@ -33,6 +37,7 @@ export function executionPolicies(config, replayRecord = undefined) {
33
37
  if (config.bindingUpdates === 'protected' && config.legacyBindingSettings !== true) {
34
38
  return Object.freeze({
35
39
  languageSemantics: PROTECTED_LANGUAGE_SEMANTICS,
40
+ moduleTransform: moduleTransformForLanguage(PROTECTED_LANGUAGE_SEMANTICS),
36
41
  bindingPolicy: Object.freeze({ variableRedeclarations: false, functionClassRedeclarations: false }),
37
42
  // Module syntax is part of the language in protected mode too. The
38
43
  // policy protects existing names; it does not remove valid syntax.
@@ -42,6 +47,7 @@ export function executionPolicies(config, replayRecord = undefined) {
42
47
  }
43
48
  return Object.freeze({
44
49
  languageSemantics: LEGACY_LANGUAGE_SEMANTICS,
50
+ moduleTransform: moduleTransformForLanguage(LEGACY_LANGUAGE_SEMANTICS),
45
51
  bindingPolicy: Object.freeze({
46
52
  variableRedeclarations: config.looseTopLevelRedeclarations === true,
47
53
  functionClassRedeclarations: config.looseTopLevelFunctionClassRedeclarations === true,
@@ -43,6 +43,16 @@ function callableSourceStart(source, node) {
43
43
  return start + tokens.getToken().start
44
44
  }
45
45
 
46
+ function removeCallableSourceMarks(source, prefix, suffix, sources, options) {
47
+ const tree = parse(prefix + source + suffix, options)
48
+ const markers = new Set(sources.entries.map(entry => entry.marker))
49
+ const offset = prefix.length
50
+ const edits = tree.comments.filter(comment => markers.has(comment.value)
51
+ && comment.start >= offset && comment.end <= offset + source.length)
52
+ .map(comment => ({ start: comment.start - offset, end: comment.end - offset, text: '' }))
53
+ return edits.length === 0 ? source : applySourceEdits(source, identitySourceMap(source.length), edits).code
54
+ }
55
+
46
56
  export function callableMarkerPosition(node, source, comments) {
47
57
  if (node.type !== 'ArrowFunctionExpression') return node.body.start + 1
48
58
  let start = source.lastIndexOf('=>', node.body.start - 1)
@@ -129,6 +139,7 @@ export function markCallableSources(source, sourceMap = identitySourceMap(source
129
139
  const nativeFunctions = typedRecords.map(record => markedNodes.get(record.marker))
130
140
  .filter(node => t.isFunction(node) && !isMethod(node)).sort((left, right) => left.start - right.start)
131
141
  const nativeRecipes = new Map()
142
+ const aliasMarkers = []
132
143
  const nativeFunctionIndex = start => {
133
144
  let low = 0, high = nativeFunctions.length
134
145
  while (low < high) {
@@ -175,7 +186,17 @@ export function markCallableSources(source, sourceMap = identitySourceMap(source
175
186
  original = lowered.source
176
187
  const entries = [...originals].map(([key, text]) => [key, key === marker ? original : text])
177
188
  for (const [node, fact] of markedCallableOwners(lowered.emitted, entries)) {
178
- sources.aliases.push([lowered.emittedSource.slice(node.start, node.end), fact.original])
189
+ const generated = lowered.emittedSource.slice(node.start, node.end)
190
+ aliasMarkers.push([generated, fact.marker])
191
+ const generatedMethod = isMethod(node)
192
+ const markerFree = removeCallableSourceMarks(
193
+ generated,
194
+ generatedMethod ? 'class Source {' : '(',
195
+ generatedMethod ? '}' : ')',
196
+ sources,
197
+ options,
198
+ )
199
+ if (markerFree !== generated) aliasMarkers.push([markerFree, fact.marker])
179
200
  }
180
201
  }
181
202
  // Prefer source-preserving erasure. Runtime TypeScript constructs such as
@@ -202,6 +223,7 @@ export function markCallableSources(source, sourceMap = identitySourceMap(source
202
223
  } catch {
203
224
  lowerCallable()
204
225
  }
226
+ original = removeCallableSourceMarks(original, prefix, suffix, sources, options)
205
227
  entry.buffer = sources.buffers.length
206
228
  sources.buffers.push(original)
207
229
  entry.start = 0
@@ -209,6 +231,8 @@ export function markCallableSources(source, sourceMap = identitySourceMap(source
209
231
  originals.set(marker, original)
210
232
  if (!method && t.isFunction(node)) nativeRecipes.set(node, { node, start, end: node.end, text: original })
211
233
  }
234
+ const finalFacts = factMap(sources)
235
+ for (const [generated, marker] of aliasMarkers) sources.aliases.push([generated, originalText(finalFacts.get(marker))])
212
236
  return { ...marked, callableSources: sources, nativeJavaScript }
213
237
  }
214
238
 
@@ -273,6 +297,18 @@ export function collectCallableSources(code, sources, parserOptions = {}, transf
273
297
  }).concat(sources.aliases ?? [])
274
298
  }
275
299
 
300
+ /** Restore only callable marks whose transformed starts have exact source-map ownership. */
301
+ export function restoreMappedCallableSourceMarks(mapped, input, transformation, sources, parserOptions = {}) {
302
+ if (sources === undefined || sources.length === 0) return mapped
303
+ const { tree, owners } = callableSourceOwners(mapped.code, sources, parserOptions,
304
+ { code: input, map: transformation.map })
305
+ const edits = [...owners].map(([node, fact]) => {
306
+ const start = callableMarkerPosition(node, mapped.code, tree.comments)
307
+ return { start, end: start, text: `/*${fact.marker}*/` }
308
+ })
309
+ return edits.length === 0 ? mapped : applySourceEdits(mapped.code, mapped.sourceMap, edits)
310
+ }
311
+
276
312
  /** Emission ranges come from typed nodes and exact generator mappings. */
277
313
  export function regionCallableRanges(input, generated, sourceRanges, parserOptions) {
278
314
  if (!sourceRanges?.length) return []
@@ -17,10 +17,10 @@ import {
17
17
  renderDurabilityReason,
18
18
  renderDurabilityReasons,
19
19
  } from './module-policy.js'
20
- import { applySourceEdits, mapSourcePosition, mapSourceSpan } from './source-position-map.js'
20
+ import { applySourceEdits, mapSourcePosition, mapSourceSpan, sourceRangeHasOriginalText } from './source-position-map.js'
21
21
  import { SKIP_AST_CHILDREN, walkAst } from './ast-traversal.js'
22
22
  import { compileStatefulRoot } from './stateful-root-compiler.js'
23
- import { CELL_PARSER_PLUGINS, bindCompilerIntrinsics, lowerStatefulDecorators, lowerStatefulResources, lowerNativeLanguageSource, normalizeStatefulScopes } from './repl-scope-normalizer.js'
23
+ import { CELL_PARSER_PLUGINS, bindCompilerIntrinsics, lowerStatefulDecorators, lowerStatefulResources, lowerNativeLanguageSource, normalizationFailureInput, normalizeStatefulScopes } from './repl-scope-normalizer.js'
24
24
  import { markCallableSources, collectCallableSources } from './callable-source-facts.js'
25
25
  import { LegacyPreflightError, prepareLegacyProgram } from './legacy-cell-analysis.js'
26
26
  import { adaptLegacyModuleImports } from './managed-module-operations.js'
@@ -220,6 +220,49 @@ function isFunction(node) {
220
220
  || node.type === 'ArrowFunctionExpression'
221
221
  }
222
222
 
223
+ function lowerCellImportMeta(result, importMeta, originalSource) {
224
+ if (!result.code.includes('import')) return result
225
+ const tree = parseSource(result.code, {
226
+ sourceType: 'unambiguous',
227
+ errorRecovery: true,
228
+ allowAwaitOutsideFunction: true,
229
+ allowImportExportEverywhere: true,
230
+ allowReturnOutsideFunction: true,
231
+ allowUndeclaredExports: true,
232
+ plugins: CELL_PARSER_PLUGINS,
233
+ }).program
234
+ const references = []
235
+ walkAst(tree, (node) => {
236
+ if (node.type === 'MetaProperty' && node.meta?.name === 'import' && node.property?.name === 'meta') {
237
+ references.push(node)
238
+ }
239
+ })
240
+ if (references.length === 0) return result
241
+ if (importMeta === null || typeof importMeta !== 'object'
242
+ || typeof importMeta.url !== 'string' || typeof importMeta.filename !== 'string'
243
+ || typeof importMeta.dirname !== 'string') {
244
+ const node = references[0]
245
+ throw new ModuleRewriteError('import.meta requires the session module base', mapSourcePosition({
246
+ line: node.loc.start.line,
247
+ column: node.loc.start.column + 1,
248
+ }, result.code, originalSource, result.sourceMap))
249
+ }
250
+ const binding = createGeneratedNameAllocator(tree)('import_meta')
251
+ const directiveEnd = tree.directives.at(-1)?.end ?? tree.interpreter?.end ?? 0
252
+ const declaration = `\nconst ${binding}={__proto__:null,url:${JSON.stringify(importMeta.url)},filename:${JSON.stringify(importMeta.filename)},dirname:${JSON.stringify(importMeta.dirname)}};\n`
253
+ const edits = [
254
+ { start: directiveEnd, end: directiveEnd, text: declaration },
255
+ ...references.map(node => ({
256
+ start: node.start,
257
+ end: node.end,
258
+ text: binding,
259
+ mappings: [{ generatedStart: 0, generatedEnd: binding.length,
260
+ originalStart: node.start, originalEnd: node.end }],
261
+ })),
262
+ ]
263
+ return { ...result, ...applySourceEdits(result.code, result.sourceMap, edits), importMetaBinding: binding }
264
+ }
265
+
223
266
 
224
267
  function staticModuleClassification(moduleLoads) {
225
268
  const reasons = new Map()
@@ -240,7 +283,9 @@ function staticModuleClassification(moduleLoads) {
240
283
  }
241
284
 
242
285
  /** Conservatively classify a cell before giving it non-journalable capability. */
243
- export function classifyDurability(code, knownBindings = new Set(), { sourceType = 'script', internalModules = new Set() } = {}) {
286
+ export function classifyDurability(code, knownBindings = new Set(), {
287
+ sourceType = 'script', internalModules = new Set(), sourceMap = undefined, originalSource = undefined,
288
+ } = {}) {
244
289
  let body
245
290
  let moduleLoads = []
246
291
  if (sourceType === 'module') {
@@ -280,14 +325,16 @@ export function classifyDurability(code, knownBindings = new Set(), { sourceType
280
325
  const reasons = new Map()
281
326
  const addReason = reason => reasons.set(renderDurabilityReason(reason), reason)
282
327
  for (const reason of staticModuleClassification(moduleLoads)) addReason(reason)
283
- const classifyModule = (source) => {
328
+ const classifyModule = (source, form) => {
284
329
  if (source?.type !== 'Literal' || typeof source.value !== 'string') {
285
330
  addReason(DYNAMIC_MODULE_REASON)
286
331
  return
287
332
  }
288
333
  const classification = classifyModuleSource(source.value)
289
334
  if (classification.status === 'forbidden') {
290
- throw new PreflightError(`cell import of ${source.value} is forbidden because it exposes kernel control`, source)
335
+ const span = sourceMap === undefined || originalSource === undefined ? declarationSpan(source)
336
+ : mapSourceSpan(declarationSpan(source), code, originalSource, sourceMap)
337
+ throw new PreflightError(`cell ${form} of ${source.value} is forbidden because it exposes kernel control`, undefined, span)
291
338
  }
292
339
  if (classification.reason !== undefined) addReason(classification.reason)
293
340
  }
@@ -297,11 +344,12 @@ export function classifyDurability(code, knownBindings = new Set(), { sourceType
297
344
  }
298
345
  return false
299
346
  }
300
- walkAst(body, (node, parent, parentKey, { scopes, strict }) => {
347
+ walkAst(body, (node, parent, parentKey, { scopes, strict, thisIsGlobal }) => {
301
348
  let nestedScopes = scopes
302
349
  if (isFunction(node)) {
303
350
  strict ||= hasStrictDirective(node.body)
304
351
  nestedScopes = [...scopes, functionParameterBindings(node)]
352
+ if (node.type !== 'ArrowFunctionExpression') thisIsGlobal = false
305
353
  } else if (node.type === 'BlockStatement' && node !== body) {
306
354
  const names = directBlockBindings(node.body)
307
355
  if (isFunction(parent)) addFunctionVariables(node, names, strict)
@@ -324,11 +372,16 @@ export function classifyDurability(code, knownBindings = new Set(), { sourceType
324
372
  nestedScopes = [...scopes, loopBindings(node)]
325
373
  }
326
374
  if (node.type === 'ImportExpression') {
327
- classifyModule(node.source)
375
+ classifyModule(node.source, 'import')
376
+ }
377
+ const sourceOwnedThis = sourceMap === undefined
378
+ || sourceRangeHasOriginalText(sourceMap, code, originalSource, node.start, node.end)
379
+ if (node.type === 'ThisExpression' && thisIsGlobal && sourceOwnedThis) {
380
+ addReason(Object.freeze({ kind: 'ambient', name: 'globalThis' }))
328
381
  }
329
382
  if (node.type === 'CallExpression' && node.callee?.type === 'Identifier' && node.callee.name === 'require'
330
383
  && !isBound('require', nestedScopes)) {
331
- classifyModule(node.arguments[0])
384
+ classifyModule(node.arguments[0], 'require')
332
385
  }
333
386
  if (node.type === 'Identifier' && isReferenceIdentifier(node, parent, parentKey)
334
387
  && !isBound(node.name, nestedScopes) && AMBIENT_GLOBALS.has(node.name)
@@ -367,8 +420,8 @@ export function classifyDurability(code, knownBindings = new Set(), { sourceType
367
420
  addReason(Object.freeze({ kind: 'ambient', name: 'Math' }))
368
421
  }
369
422
  }
370
- return { scopes: nestedScopes, strict }
371
- }, undefined, { scopes: [rootBindings], strict: rootStrict })
423
+ return { scopes: nestedScopes, strict, thisIsGlobal }
424
+ }, undefined, { scopes: [rootBindings], strict: rootStrict, thisIsGlobal: sourceType === 'script' })
372
425
  return {
373
426
  durability: reasons.size === 0 ? 'durable' : 'volatile',
374
427
  reasons: Object.freeze([...reasons.values()]),
@@ -454,31 +507,46 @@ export function prepareProgram(program, options = {}) {
454
507
  let normalized
455
508
  let prepared
456
509
  let callableSources
510
+ let diagnosticInput
457
511
  const intrinsicContext = { bindings: new Set() }
458
512
  try {
459
513
  const marked = markCallableSources(program, undefined, { plugins: CELL_PARSER_PLUGINS, allowImportExportEverywhere: true, allowUndeclaredExports: true },
460
514
  { lowerNativeSource: lowerNativeLanguageSource, nativeUsing: options.nativeUsing })
515
+ const moduleMetadata = lowerCellImportMeta(marked, options.importMeta, program)
516
+ diagnosticInput = moduleMetadata
461
517
  callableSources = marked.callableSources
462
- normalized = normalizeStatefulScopes(marked.code, marked.sourceMap, { mode: options.languageSemantics,
463
- nativeJavaScript: marked.nativeJavaScript, deferDecorators: true, deferResources: true, intrinsicContext })
518
+ normalized = normalizeStatefulScopes(moduleMetadata.code, moduleMetadata.sourceMap, { mode: options.languageSemantics,
519
+ callableSources, nativeJavaScript: marked.nativeJavaScript,
520
+ deferDecorators: true, deferResources: true, intrinsicContext })
521
+ if (moduleMetadata.importMetaBinding !== undefined) {
522
+ normalized.internalBindings = new Set(normalized.internalBindings)
523
+ normalized.internalBindings.add(moduleMetadata.importMetaBinding)
524
+ }
525
+ diagnosticInput = normalized
464
526
  prepared = compileStatefulRoot(normalized.code, { ...options, sourceMap: normalized.sourceMap,
465
527
  internalBindings: normalized.internalBindings, dynamicBindings: normalized.dynamicBindings,
466
528
  privateBindings: normalized.privateBindings, originalSource: program })
467
529
  } catch (error) {
468
530
  if (error instanceof ModuleRewriteError) throw error
469
531
  const position = error.loc === undefined ? undefined : { line: error.loc.line, column: error.loc.column + 1 }
470
- throw new ModuleRewriteError(error.message, normalized === undefined ? position
471
- : mapSourcePosition(position, normalized.code, program, normalized.sourceMap))
532
+ const failureInput = normalizationFailureInput(error) ?? diagnosticInput
533
+ throw new ModuleRewriteError(error.message, failureInput === undefined ? position
534
+ : mapSourcePosition(position, failureInput.code, program, failureInput.sourceMap))
472
535
  }
473
536
  prepared = { ...prepared, deferredHelpers: normalized.deferredHelpers }
474
537
  intrinsicContext.expression = `this[${JSON.stringify(prepared.rootRuntimeName)}].intrinsics`
475
538
  const classificationSource = bindCompilerIntrinsics(lowerStatefulDecorators(
476
- { ...prepared, code: prepared.classificationCode }, undefined, intrinsicContext), intrinsicContext).code
539
+ { ...prepared, code: prepared.classificationCode, sourceMap: prepared.classificationSourceMap },
540
+ undefined, intrinsicContext), intrinsicContext)
477
541
  prepared = bindCompilerIntrinsics(lowerStatefulDecorators(prepared, undefined, intrinsicContext), intrinsicContext)
478
542
  prepared = lowerStatefulResources(prepared, { intrinsicContext, nativeUsing: options.nativeUsing })
479
543
  const parsed = parseExecutableCell(prepared.code, { eraseTypes: true })
480
- const classification = classifyDurability(parseExecutableCell(classificationSource, { eraseTypes: true }).code,
481
- new Set([...(options.knownBindings ?? []), ...prepared.declared]))
544
+ const classificationCode = parseExecutableCell(classificationSource.code, { eraseTypes: true }).code
545
+ const classification = classifyDurability(classificationCode,
546
+ new Set([...(options.knownBindings ?? []), ...prepared.declared]), {
547
+ sourceMap: classificationSource.sourceMap,
548
+ originalSource: program,
549
+ })
482
550
  const reasons = [...staticModuleClassification(prepared.moduleLoads), ...classification.reasons]
483
551
  const lowered = rewriteCellReturns(parsed.code, prepared.sourceMap,
484
552
  new Set([...prepared.declared, ...(options.knownBindings ?? [])]))
@@ -1,15 +1,12 @@
1
1
  import { stringifyCompilerData } from './compiler-data.js'
2
2
  import { compilerStorageSource } from './compiler-storage-source.js'
3
3
  import { compilerDescriptorSource } from './compiler-descriptors.js'
4
+ import { runtimeIntrinsics } from './runtime-intrinsics.js'
4
5
 
5
6
  /** Compiler operations are captured before an owned realm executes user code. */
6
7
  const defaultRealmFunction = Function
7
8
  const realms = new WeakMap()
8
- const uncurry = Function.prototype.bind.bind(Function.prototype.call)
9
- const weakMapGet = uncurry(WeakMap.prototype.get)
10
- const weakMapSet = uncurry(WeakMap.prototype.set)
11
- const mapKeys = uncurry(Map.prototype.keys)
12
- const mapIteratorNext = uncurry(Object.getPrototypeOf(new Map().keys()).next)
9
+ const { weakMapGet, weakMapSet } = runtimeIntrinsics
13
10
 
14
11
  export function captureCompilerIntrinsics(realmFunction = defaultRealmFunction) {
15
12
  let intrinsics = weakMapGet(realms, realmFunction)
@@ -21,7 +18,8 @@ export function captureCompilerIntrinsics(realmFunction = defaultRealmFunction)
21
18
  const object=properties.Object;
22
19
  const symbol=Symbol;
23
20
  return {Object:object,WeakMap,WeakSet,unscopables:symbol.unscopables,Reflect:{apply:nativeReflect.apply,
24
- construct:nativeReflect.construct,ownKeys:nativeReflect.ownKeys,defineProperty:properties.reflectDefineProperty,
21
+ construct:nativeReflect.construct,deleteProperty:nativeReflect.deleteProperty,has:nativeReflect.has,
22
+ ownKeys:nativeReflect.ownKeys,defineProperty:properties.reflectDefineProperty,
25
23
  get decorate(){return nativeReflect.decorate}},TypeError,ReferenceError,SyntaxError,Error,Symbol,String,Number,
26
24
  Promise:{resolve:Promise.resolve.bind(Promise),reject:Promise.reject.bind(Promise)},
27
25
  SuppressedError:typeof SuppressedError==='function'?SuppressedError:undefined,
@@ -36,49 +34,17 @@ export const moduleCompilerIntrinsics = captureCompilerIntrinsics()
36
34
  /** Internal graph records use captured collection operations. Source-owned
37
35
  * objects and callbacks keep their original realm and observable methods. */
38
36
  export const moduleRuntimeIntrinsics = {
37
+ ...runtimeIntrinsics,
39
38
  Object: moduleCompilerIntrinsics.Object,
40
39
  Reflect: moduleCompilerIntrinsics.Reflect,
41
40
  Error: moduleCompilerIntrinsics.Error,
42
- Map, Set, WeakMap, Proxy,
43
- mapGet: uncurry(Map.prototype.get),
44
- mapHas: uncurry(Map.prototype.has),
45
- mapSet: uncurry(Map.prototype.set),
46
- mapDelete: uncurry(Map.prototype.delete),
47
- mapClear: uncurry(Map.prototype.clear),
48
- mapSize: uncurry(Object.getOwnPropertyDescriptor(Map.prototype, 'size').get),
49
- mapFirstKey: value => mapIteratorNext(mapKeys(value)).value,
50
- mapForEach: uncurry(Map.prototype.forEach),
51
- setHas: uncurry(Set.prototype.has),
52
- setAdd: uncurry(Set.prototype.add),
53
- setDelete: uncurry(Set.prototype.delete),
54
- setClear: uncurry(Set.prototype.clear),
55
- setForEach: uncurry(Set.prototype.forEach),
56
- weakMapHas: uncurry(WeakMap.prototype.has),
57
- weakMapGet, weakMapSet,
58
- isArray: Array.isArray,
59
- includes: uncurry(Array.prototype.includes),
60
- join: uncurry(Array.prototype.join),
61
- sliceString: uncurry(String.prototype.slice),
62
- replaceAllString: uncurry(String.prototype.replaceAll),
63
- includesString: uncurry(String.prototype.includes),
64
- floor: Math.floor,
65
- min: Math.min,
66
- max: Math.max,
41
+ TypeError: moduleCompilerIntrinsics.TypeError,
67
42
  // Only private native Promises enter this boundary. Source promises and the
68
43
  // public result keep their prototypes; private reactions cannot select a
69
44
  // source constructor/species or dynamically dispatch through then/catch.
70
45
  observeOwnedPromise: moduleCompilerIntrinsics.observeOwnedPromise,
71
- appendArray(target, value) {
72
- moduleCompilerIntrinsics.Object.defineProperty(target, target.length,
73
- { value, writable: true, enumerable: true, configurable: true })
74
- return target
75
- },
76
- copyArray(source, start = 0, end = source.length, target = []) {
77
- for (let index = start; index < end; index++) this.appendArray(target, source[index])
78
- return target
79
- },
80
- sort: uncurry(Array.prototype.sort),
81
- compare: uncurry(String.prototype.localeCompare),
82
- startsWith: uncurry(String.prototype.startsWith),
46
+ promiseThen: moduleCompilerIntrinsics.promiseThen,
47
+ promiseResolve: moduleCompilerIntrinsics.Promise.resolve,
48
+ promiseReject: moduleCompilerIntrinsics.Promise.reject,
83
49
  stringify: stringifyCompilerData,
84
50
  }
@@ -1,6 +1,9 @@
1
1
  import { fileURLToPath } from 'node:url'
2
2
 
3
3
  const encodeReference = encodeURIComponent
4
+ const objectEntries = Object.entries
5
+ const sortArray = Function.prototype.call.bind(Array.prototype.sort)
6
+ const jsonStringify = JSON.stringify
4
7
 
5
8
  // These ambient inputs must remain outside source-owned CommonJS bindings.
6
9
  export const commonJsCompilerGlobals = new Set(['process'])
@@ -21,14 +24,14 @@ const ATTRIBUTED_LINK_KIND = 'static-attributed'
21
24
  * attribute-free links keep the reference they had before attributes were
22
25
  * distinguished. */
23
26
  export function staticModuleLinkReference(source, attributes) {
24
- const entries = Object.entries(attributes ?? {})
27
+ const entries = objectEntries(attributes ?? {})
25
28
  if (entries.length === 0) return compilerModuleReference(STATIC_LINK_KIND, source)
26
- entries.sort((left, right) => left[0] < right[0] ? -1 : left[0] > right[0] ? 1 : 0)
27
- return compilerModuleReference(ATTRIBUTED_LINK_KIND, JSON.stringify([source, entries]))
29
+ sortArray(entries, (left, right) => left[0] < right[0] ? -1 : left[0] > right[0] ? 1 : 0)
30
+ return compilerModuleReference(ATTRIBUTED_LINK_KIND, jsonStringify([source, entries]))
28
31
  }
29
32
 
30
33
  /** Compiler helpers resolve from their own URL, independently of source-owned
31
34
  * wrapper bindings and the ESM translator's restricted CommonJS require. */
32
35
  export function commonJsCompilerImport(url) {
33
- return `process.getBuiltinModule('module').createRequire(${JSON.stringify(url.href)})(${JSON.stringify(fileURLToPath(url))})`
36
+ return `process.getBuiltinModule('module').createRequire(${jsonStringify(url.href)})(${jsonStringify(fileURLToPath(url))})`
34
37
  }
@@ -96,7 +96,7 @@ export const CONFIG_FIELDS = Object.freeze([
96
96
  {
97
97
  key: 'userBindingsEnabled',
98
98
  type: 'boolean',
99
- default: false,
99
+ default: true,
100
100
  label: '启用全局用户 Binding',
101
101
  labelEn: 'Enable Global User Bindings',
102
102
  description: '管理 TypeScript helper,并将已启用条目作为跨会话的默认 REPL 绑定。',
@@ -133,12 +133,22 @@ function requireCompanionServices(agent) {
133
133
 
134
134
  async function companionSkillDirectory(agentPresets) {
135
135
  const preset = await agentPresets.resolve(CORDIS_PRESET_ID)
136
- if (typeof preset?.path !== 'string' || !isAbsolute(preset.path)) {
137
- throw new Error('ptc-plus: DSH cordis preset did not expose an absolute composition path')
138
- }
139
136
  if (typeof preset.broken === 'string' && preset.broken.length > 0) {
140
137
  throw new Error(`ptc-plus: DSH cordis preset is unavailable: ${preset.broken}`)
141
138
  }
139
+ // The generations that discover presets from their composition directories
140
+ // publish an absolute path, and the preset's sibling `skills` directory is
141
+ // then the companion Skill root. A generation that declares presets without
142
+ // any composition path publishes no root through this surface at all, so the
143
+ // report names that missing publication instead of claiming a shape failure
144
+ // on a healthy host. A published path that is not absolute is still a
145
+ // contract contradiction.
146
+ if (preset?.path === undefined) {
147
+ throw new Error('ptc-plus: the installed DSH agentPresets generation publishes no composition path for the "cordis" preset, so it does not publish the companion Skill root that cordisToolsEnabled mounts')
148
+ }
149
+ if (typeof preset.path !== 'string' || !isAbsolute(preset.path)) {
150
+ throw new Error('ptc-plus: DSH cordis preset did not expose an absolute composition path')
151
+ }
142
152
  return join(dirname(preset.path), 'skills')
143
153
  }
144
154
 
@@ -329,6 +339,7 @@ export function createCordisToolsOwner(
329
339
 
330
340
  const installAgent = (agent) => {
331
341
  if (disposed) return Promise.resolve()
342
+ if (withdrawn.has(agent)) return Promise.resolve()
332
343
  const mounted = mounts.get(agent)
333
344
  if (mounted !== undefined) {
334
345
  return mounted.withdrawn === true ? Promise.resolve() : mounted.activation
@@ -416,11 +427,11 @@ export function createCordisToolsOwner(
416
427
  /**
417
428
  * Install for a call the host makes on its own behalf.
418
429
  *
419
- * DSH 0.1.6 announces `agent/created` serially and rejects `agents.create()`
420
- * when a listener rejects, so a scope this owner cannot serve must withdraw
421
- * instead of failing an operation another plugin asked for. Install-time
422
- * enumeration stays strict: there the plugin speaks for what it claims to
423
- * have published, and `ready` reports the same failure.
430
+ * The public host lifecycle announces `agent/created` serially and rejects
431
+ * `agents.create()` when a listener rejects, so a scope this owner cannot
432
+ * serve must withdraw instead of failing an operation another plugin asked
433
+ * for. Install-time enumeration stays strict: there the plugin speaks for
434
+ * what it claims to have published, and `ready` reports the same failure.
424
435
  */
425
436
  const installForHost = (agent) => {
426
437
  let activation
@@ -451,8 +462,7 @@ export function createCordisToolsOwner(
451
462
  if (disposed) return
452
463
  for (const agent of [...pending]) {
453
464
  void Promise.resolve().then(() => installAgent(agent)).catch(error => {
454
- pending.delete(agent)
455
- ctx.logger?.warn?.('ptc-plus: deferred Cordis activation failed', error)
465
+ withdraw(agent, error)
456
466
  })
457
467
  }
458
468
  })
@@ -34,15 +34,19 @@ export function adaptDynamicCell(code, sourceMap, options) {
34
34
  let setup
35
35
  if (options.module === true) {
36
36
  const runtimeUrl = new URL('./dynamic-environment-runtime.js', import.meta.url)
37
+ const interfaceOwner = allocate('dynamic_interface_owner')
38
+ const runtimeOptions = `{interfaceOwner:${interfaceOwner}${options.importOperation === undefined
39
+ ? '' : `,importModule:${options.importOperation}`}}`
37
40
  if (options.sourceType === 'module') {
38
41
  const factory = allocate('dynamic_factory')
39
- setup = `\nimport {createDynamicEnvironmentRuntime as ${factory}} from ${JSON.stringify(runtimeUrl.href)};\n`
40
- expression = `${factory}(${options.importOperation === undefined ? '' : `{importModule:${options.importOperation}}`}).installIntrinsics().environment({strict:true,getThis:()=>void 0})`
41
- options = { ...options, internalBindings: new Set([...options.internalBindings ?? [], factory]) }
42
+ setup = `\nimport {createDynamicEnvironmentRuntime as ${factory}} from ${JSON.stringify(runtimeUrl.href)};\nfunction ${interfaceOwner}(){}\n`
43
+ expression = `${factory}(${runtimeOptions}).installIntrinsics().environment({strict:true,getThis:()=>void 0})`
44
+ options = { ...options, internalBindings: new Set([...options.internalBindings ?? [], factory, interfaceOwner]) }
42
45
  } else {
43
46
  const names = ['exports', 'module', 'require', '__filename', '__dirname', 'arguments']
44
47
  const bindings = names.map(name => `[${JSON.stringify(name)},{kind:"param",get:()=>${name}${name === 'arguments' && strict ? '' : `,set:v=>${name}=v`}}]`).join(',')
45
- setup = `\nconst ${environmentName}=${commonJsCompilerImport(runtimeUrl)}.createDynamicEnvironmentRuntime(${options.importOperation === undefined ? '' : `{importModule:${options.importOperation}}`}).installIntrinsics().environment({strict:${strict},getThis:()=>this,getNewTarget:()=>new.target,allowNewTarget:true,nativeBindings:[${bindings}]});\n`
48
+ setup = `\nfunction ${interfaceOwner}(){}\nconst ${environmentName}=${commonJsCompilerImport(runtimeUrl)}.createDynamicEnvironmentRuntime(${runtimeOptions}).installIntrinsics().environment({strict:${strict},getThis:()=>this,getNewTarget:()=>new.target,allowNewTarget:true,nativeBindings:[${bindings}]});\n`
49
+ options = { ...options, internalBindings: new Set([...options.internalBindings ?? [], interfaceOwner]) }
46
50
  }
47
51
  } else setup = options.nativeRoot === true ? `\nconst ${environmentName}=${options.environmentGlobal};\n`
48
52
  : `\nconst ${environmentName}=${options.rootRuntime}.dynamic();\n`
@@ -16,6 +16,17 @@ function captureReflection(reflect) {
16
16
  return captured
17
17
  }
18
18
  const defaultIntrinsics = { intrinsicEval: eval, realmFunction: Function, globalObject: globalThis, reflect: captureReflection(Reflect) }
19
+ let programAmbientResolver
20
+
21
+ /** Install the worker-owned resolver used by explicitly adapted program modules. */
22
+ export function installProgramAmbientResolver(resolve) {
23
+ if (programAmbientResolver !== undefined) throw new Error('program ambient resolver is already installed')
24
+ if (typeof resolve !== 'function') throw new TypeError('program ambient resolver must be a function')
25
+ programAmbientResolver = resolve
26
+ return () => {
27
+ if (programAmbientResolver === resolve) programAmbientResolver = undefined
28
+ }
29
+ }
19
30
 
20
31
  const isObject = value => value !== null && (typeof value === 'object' || typeof value === 'function')
21
32
  const isMap = types.isMap
@@ -45,7 +56,7 @@ const asBindings = value => {
45
56
  export function createDynamicEnvironmentRuntime({
46
57
  importModule = (source, options) => import(source, options),
47
58
  intrinsicEval = defaultIntrinsics.intrinsicEval, realmFunction = defaultIntrinsics.realmFunction, globalObject = defaultIntrinsics.globalObject,
48
- errors, reflect = defaultIntrinsics.reflect, object, interfaceOwner,
59
+ errors, reflect = defaultIntrinsics.reflect, object, interfaceOwner, resolveAmbient = programAmbientResolver,
49
60
  } = {}) {
50
61
  const intrinsics = captureCompilerIntrinsics(realmFunction)
51
62
  const { unscopables, Object: intrinsicObject, array: internalArray } = intrinsics
@@ -143,7 +154,10 @@ export function createDynamicEnvironmentRuntime({
143
154
  return deleted
144
155
  },
145
156
  }, object, strict)
146
- const ambientReference = (name, strict) => reference(name, {
157
+ const ambientReference = (name, strict) => {
158
+ const resolved = resolveAmbient?.(name)
159
+ if (resolved !== undefined) return reference(name, resolved, undefined, strict)
160
+ return reference(name, {
147
161
  get() {
148
162
  if (!reflect.has(globalObject, name)) throw new errors.ReferenceError(`${name} is not defined`)
149
163
  return reflect.get(globalObject, name, globalObject)
@@ -154,7 +168,8 @@ export function createDynamicEnvironmentRuntime({
154
168
  },
155
169
  typeof: () => reflect.has(globalObject, name) ? typeof reflect.get(globalObject, name, globalObject) : 'undefined',
156
170
  delete: () => reflect.deleteProperty(globalObject, name),
157
- }, undefined, strict)
171
+ }, undefined, strict)
172
+ }
158
173
 
159
174
  function environment({ frames = [], varFrame = new internal.Map(), strict = false, origin, lexicalContext = {},
160
175
  getThis = () => globalObject, getNewTarget = () => undefined, allowNewTarget = false,
@@ -170,6 +185,12 @@ export function createDynamicEnvironmentRuntime({
170
185
  importModule,
171
186
  internalArray,
172
187
  expose: nativeCalls.exposedValue,
188
+ exposeProperty(receiver, key) {
189
+ if (receiver === null || receiver === undefined) {
190
+ throw new errors.TypeError(`Cannot read properties of ${receiver}`)
191
+ }
192
+ return nativeCalls.exposedMemberValue(receiver, reflect.get(object(receiver), key, receiver))
193
+ },
173
194
  prepareCall: nativeCalls.prepareCall,
174
195
  prepareInvocation: nativeCalls.prepareInvocation,
175
196
  releaseMemberReceiver: nativeCalls.releaseMemberReceiver,
@@ -42,7 +42,10 @@ export function rewriteNativeCalls(tree, environment, { origin, skip, operations
42
42
  parent.node.computed = true
43
43
  parent.node.key = t.stringLiteral(path.node.name)
44
44
  }
45
- path.replaceWith(t.inherits(helper('expose', [path.node]), path.node))
45
+ if (path.isMemberExpression() && !t.isSuper(path.node.object) && !t.isPrivateName(path.node.property)) {
46
+ const key = path.node.computed ? path.node.property : t.stringLiteral(path.node.property.name)
47
+ path.replaceWith(t.inherits(helper('exposeProperty', [path.node.object, key]), path.node))
48
+ } else path.replaceWith(t.inherits(helper('expose', [path.node]), path.node))
46
49
  path.skip()
47
50
  }
48
51
  const prepare = (callee, target, optional = false) => {
@@ -11,6 +11,7 @@ const exposedIntrinsics = new internal.WeakMap()
11
11
  const sources = createCallableSourceRegistry()
12
12
  const callableSources = new internal.WeakMap()
13
13
  const callableSourceTargets = new internal.WeakMap()
14
+ const callableInterfaces = new internal.WeakMap()
14
15
  const sourceTarget = origin => typeof origin === 'string' ? origin : origin?.target
15
16
  const copyArguments = (source, start = 0, result = []) => internal.copyArray(source, start, source.length, result)
16
17
  const invocationScopes = new AsyncLocalStorage()
@@ -114,7 +115,19 @@ export function createNativeCallAdapter({ realmFunction, intrinsicEval, reflect,
114
115
  internal.weakMapSet(state.callableSources, compiled, source)
115
116
  return compiled
116
117
  }
117
- const exposedValue = value => internal.mapGet(selected.exposed, value) ?? internal.weakMapGet(exposedIntrinsics, value) ?? value
118
+ const exposedValue = value => {
119
+ const exposed = internal.mapGet(selected.exposed, value) ?? internal.weakMapGet(exposedIntrinsics, value)
120
+ if (exposed !== undefined) return exposed
121
+ if (typeof value === 'function' && !internal.weakMapHas(callableInterfaces, value)) {
122
+ internal.weakMapSet(callableInterfaces, value, selected)
123
+ }
124
+ return value
125
+ }
126
+ const exposedMemberValue = (receiver, value) => {
127
+ const owner = typeof receiver === 'function' ? internal.weakMapGet(callableInterfaces, receiver) : undefined
128
+ const exposed = owner === undefined ? undefined : internal.mapGet(owner.exposed, value)
129
+ return exposed ?? exposedValue(value)
130
+ }
118
131
  const propagate = (error, origin) => {
119
132
  recordExceptionOrigin(error, sourceTarget(origin), { reset: true })
120
133
  for (let scope = currentScope(); scope !== undefined; scope = scope.parent) {
@@ -218,6 +231,7 @@ export function createNativeCallAdapter({ realmFunction, intrinsicEval, reflect,
218
231
  return {
219
232
  install,
220
233
  exposedValue,
234
+ exposedMemberValue,
221
235
  isEval: callee => callee === state.intrinsicEval || internal.mapHas(selected.exposed, state.intrinsicEval) && callee === internal.mapGet(selected.exposed, state.intrinsicEval),
222
236
  evaluate(compiled, environment) {
223
237
  // Bootstrap originals never leave this owner. Direct eval must use the