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
@@ -43,8 +43,36 @@ function assertField(field, value) {
43
43
  * @param config - raw config object.
44
44
  * @returns a resolved config object.
45
45
  */
46
+ /**
47
+ * A volatile schema field validates to a live `{ get }` handle. A field that
48
+ * has no schema default arrives as a handle with an undefined live value and
49
+ * keeps the runtime default.
50
+ */
51
+ function liveConfigValue(value, fallback) {
52
+ if (value !== null && typeof value === 'object'
53
+ && typeof value.get === 'function' && Object.keys(value).length === 1) {
54
+ const live = value.get()
55
+ return live === undefined ? fallback : live
56
+ }
57
+ return value
58
+ }
59
+
46
60
  export function resolveConfig(config = {}) {
61
+ const provided = new Set()
62
+ for (const field of CONFIG_FIELDS) {
63
+ if (!Object.hasOwn(config, field.key)) continue
64
+ const value = config[field.key]
65
+ if (value !== null && typeof value === 'object'
66
+ && typeof value.get === 'function' && Object.keys(value).length === 1) {
67
+ if (value.get() !== undefined) provided.add(field.key)
68
+ } else {
69
+ provided.add(field.key)
70
+ }
71
+ }
47
72
  const resolved = { ...CONFIG_DEFAULTS, ...config }
73
+ for (const field of CONFIG_FIELDS) {
74
+ resolved[field.key] = liveConfigValue(resolved[field.key], CONFIG_DEFAULTS[field.key])
75
+ }
48
76
  for (const field of CONFIG_FIELDS) assertField(field, resolved[field.key])
49
77
  // Migrate complete old policies; mixed choices retain their explicit
50
78
  // compatibility state until the user selects the unified language.
@@ -52,8 +80,8 @@ export function resolveConfig(config = {}) {
52
80
  'looseTopLevelRedeclarations', 'looseTopLevelFunctionClassRedeclarations',
53
81
  'autoRewriteImports', 'autoStripExports', 'autoSplitRedeclarations',
54
82
  ]
55
- if (!Object.hasOwn(config, 'bindingUpdates')
56
- && legacyKeys.some(key => Object.hasOwn(config, key))) {
83
+ if (!provided.has('bindingUpdates')
84
+ && legacyKeys.some(key => provided.has(key))) {
57
85
  if (legacyKeys.every(key => resolved[key] === true)) {
58
86
  resolved.bindingUpdates = 'stateful'
59
87
  } else if (legacyKeys.slice(0, 2).every(key => resolved[key] === false)
@@ -65,6 +93,38 @@ export function resolveConfig(config = {}) {
65
93
  return resolved
66
94
  }
67
95
 
96
+ /** Whether a validated config carries live volatile field handles. */
97
+ export function hasVolatileFields(config) {
98
+ if (config === null || typeof config !== 'object') return false
99
+ return Object.values(config).some(value => value !== null && typeof value === 'object'
100
+ && typeof value.get === 'function' && Object.keys(value).length === 1)
101
+ }
102
+
103
+ /**
104
+ * Follow the host's volatile-only commit into the running fiber.
105
+ *
106
+ * The loader updates the retained handles in place and emits
107
+ * `loader/volatile-update` on the owning fiber; listeners on a child scope are
108
+ * filtered out, so the listener belongs to the plugin's own context and is
109
+ * released with the runtime scope it serves.
110
+ *
111
+ * @param host - the plugin's own context, owning the loader event listener.
112
+ * @param scope - the runtime scope whose disposal removes the listener.
113
+ * @param config - the validated config carrying the volatile handles.
114
+ * @param listener - receives the newly resolved plain configuration.
115
+ * @returns whether a volatile listener was installed.
116
+ */
117
+ export function watchVolatileConfig(host, scope, config, listener) {
118
+ if (!hasVolatileFields(config) || typeof host?.on !== 'function') return false
119
+ const update = () => listener(resolveConfig(config))
120
+ if (typeof scope?.effect === 'function') {
121
+ scope.effect(() => host.on('loader/volatile-update', update), 'ptc-plus volatile settings lifecycle')
122
+ } else {
123
+ host.on('loader/volatile-update', update)
124
+ }
125
+ return true
126
+ }
127
+
68
128
  /**
69
129
  * Validate one wall-clock ceiling against the platform timer limit.
70
130
  * @param value - the configured maximum wall time in milliseconds.
@@ -0,0 +1,104 @@
1
+ import { compilerDescriptors } from './compiler-descriptors.js'
2
+
3
+ const uncurry = Function.prototype.bind.bind(Function.prototype.call)
4
+ const mapKeys = uncurry(Map.prototype.keys)
5
+ const mapIteratorNext = uncurry(Object.getPrototypeOf(new Map().keys()).next)
6
+ const weakMapGet = uncurry(WeakMap.prototype.get)
7
+ const weakMapSet = uncurry(WeakMap.prototype.set)
8
+
9
+ function appendArray(target, value) {
10
+ compilerDescriptors.defineProperty(target, target.length,
11
+ { value, writable: true, enumerable: true, configurable: true })
12
+ return target
13
+ }
14
+
15
+ function copyArray(source, start = 0, end = source.length, target = []) {
16
+ for (let index = start; index < end; index++) appendArray(target, source[index])
17
+ return target
18
+ }
19
+
20
+ /** Host-private operations captured before any owned realm executes user code. */
21
+ export const runtimeIntrinsics = {
22
+ Object: compilerDescriptors.Object,
23
+ Reflect: {
24
+ apply: Reflect.apply,
25
+ construct: Reflect.construct,
26
+ deleteProperty: Reflect.deleteProperty,
27
+ defineProperty: compilerDescriptors.reflectDefineProperty,
28
+ has: Reflect.has,
29
+ ownKeys: Reflect.ownKeys,
30
+ set: Reflect.set,
31
+ },
32
+ Error, TypeError, Array, Map, Set, WeakMap, WeakSet, RegExp, Proxy,
33
+ objectCreate: Object.create,
34
+ objectDefineProperty: Object.defineProperty,
35
+ objectEntries: Object.entries,
36
+ objectFreeze: Object.freeze,
37
+ objectGetOwnPropertyDescriptor: Object.getOwnPropertyDescriptor,
38
+ objectGetPrototypeOf: Object.getPrototypeOf,
39
+ objectHasOwn: Object.hasOwn,
40
+ objectIs: Object.is,
41
+ objectKeys: Object.keys,
42
+ objectPropertyIsEnumerable: uncurry(Object.prototype.propertyIsEnumerable),
43
+ reflectOwnKeys: Reflect.ownKeys,
44
+ numberIsFinite: Number.isFinite,
45
+ numberIsNaN: Number.isNaN,
46
+ numberIsSafeInteger: Number.isSafeInteger,
47
+ toBigInt: BigInt,
48
+ toNumber: Number,
49
+ toString: String,
50
+ bigintToString: uncurry(BigInt.prototype.toString),
51
+ jsonStringify: JSON.stringify,
52
+ mapGet: uncurry(Map.prototype.get),
53
+ mapHas: uncurry(Map.prototype.has),
54
+ mapSet: uncurry(Map.prototype.set),
55
+ mapDelete: uncurry(Map.prototype.delete),
56
+ mapClear: uncurry(Map.prototype.clear),
57
+ mapSize: uncurry(Object.getOwnPropertyDescriptor(Map.prototype, 'size').get),
58
+ mapFirstKey: value => mapIteratorNext(mapKeys(value)).value,
59
+ mapForEach: uncurry(Map.prototype.forEach),
60
+ setHas: uncurry(Set.prototype.has),
61
+ setAdd: uncurry(Set.prototype.add),
62
+ setDelete: uncurry(Set.prototype.delete),
63
+ setClear: uncurry(Set.prototype.clear),
64
+ setSize: uncurry(Object.getOwnPropertyDescriptor(Set.prototype, 'size').get),
65
+ setForEach: uncurry(Set.prototype.forEach),
66
+ weakMapHas: uncurry(WeakMap.prototype.has),
67
+ weakMapGet,
68
+ weakMapSet,
69
+ weakSetHas: uncurry(WeakSet.prototype.has),
70
+ weakSetAdd: uncurry(WeakSet.prototype.add),
71
+ isArray: Array.isArray,
72
+ everyArray: uncurry(Array.prototype.every),
73
+ includes: uncurry(Array.prototype.includes),
74
+ join: uncurry(Array.prototype.join),
75
+ popArray: uncurry(Array.prototype.pop),
76
+ prependArray: uncurry(Array.prototype.unshift),
77
+ someArray: uncurry(Array.prototype.some),
78
+ sliceString: uncurry(String.prototype.slice),
79
+ splitString: uncurry(String.prototype.split),
80
+ searchString: uncurry(String.prototype.search),
81
+ replaceString: uncurry(String.prototype.replace),
82
+ trimString: uncurry(String.prototype.trim),
83
+ replaceAllString: uncurry(String.prototype.replaceAll),
84
+ includesString: uncurry(String.prototype.includes),
85
+ startsWith: uncurry(String.prototype.startsWith),
86
+ endsWith: uncurry(String.prototype.endsWith),
87
+ regexpExec: uncurry(RegExp.prototype.exec),
88
+ regexpTest: uncurry(RegExp.prototype.test),
89
+ floor: Math.floor,
90
+ min: Math.min,
91
+ max: Math.max,
92
+ appendArray,
93
+ copyArray,
94
+ sort: uncurry(Array.prototype.sort),
95
+ compare: uncurry(String.prototype.localeCompare),
96
+ }
97
+
98
+ if (typeof Buffer === 'function') {
99
+ runtimeIntrinsics.bufferByteLength = Buffer.byteLength
100
+ runtimeIntrinsics.bufferConcat = Buffer.concat
101
+ runtimeIntrinsics.bufferIsBuffer = Buffer.isBuffer
102
+ runtimeIntrinsics.bufferSubarray = uncurry(Buffer.prototype.subarray)
103
+ runtimeIntrinsics.bufferToString = uncurry(Buffer.prototype.toString)
104
+ }
@@ -1,5 +1,7 @@
1
1
  import { createUserMessage } from '@deepseek-ai/dsh-llm'
2
2
 
3
+ import { isPtcMessageSource, PTC_MESSAGE_SOURCE_KIND } from './message-sources.js'
4
+
3
5
  export const PTC_DELIVERY_CONTEXT = 'tools:ptc-plus-message-delivery'
4
6
  export const PTC_BINDING_CATALOG = 'tools:ptc-plus-user-binding-defaults'
5
7
  export const PTC_STATE_NAMES = Object.freeze([
@@ -54,7 +56,7 @@ function stateText(sections, prefix = STATE_PREFIX) {
54
56
  /** Recognize only bounded, formed PTC records, never authoring tasks or Skills. */
55
57
  export function readRuntimeMessage(message) {
56
58
  const source = message?.source
57
- if (source?.kind !== 'plugin' || source.plugin !== 'ptc-plus'
59
+ if (!isPtcMessageSource(source)
58
60
  || !Array.isArray(message.content) || message.content.length !== 1
59
61
  || message.content[0]?.type !== 'text') return undefined
60
62
  const text = message.content[0].text
@@ -87,7 +89,7 @@ export function runtimeBindingCatalogMessage(context) {
87
89
  throw new Error('invalid PTC binding catalog')
88
90
  }
89
91
  return createUserMessage({
90
- source: { kind: 'plugin', plugin: 'ptc-plus', form: 'catalog' },
92
+ source: { kind: PTC_MESSAGE_SOURCE_KIND, form: 'catalog' },
91
93
  content: [{ type: 'text', text: `${CATALOG_PREFIX}\n\n${context?.text ?? EMPTY_CATALOG}` }],
92
94
  })
93
95
  }
@@ -96,14 +98,14 @@ export function runtimeStateMessage(value) {
96
98
  const sections = stateSections(value)
97
99
  if (sections === undefined) throw new Error('invalid PTC runtime state sections')
98
100
  return createUserMessage({
99
- source: { kind: 'plugin', plugin: 'ptc-plus', form: 'snapshot', sections },
101
+ source: { kind: PTC_MESSAGE_SOURCE_KIND, form: 'snapshot', sections },
100
102
  content: [{ type: 'text', text: stateText(sections) }],
101
103
  })
102
104
  }
103
105
 
104
106
  export function runtimeNoticeMessage(tip) {
105
107
  const message = createUserMessage({
106
- source: { kind: 'plugin', plugin: 'ptc-plus', form: 'notice', summary: tip.name },
108
+ source: { kind: PTC_MESSAGE_SOURCE_KIND, form: 'notice', summary: tip.name },
107
109
  content: [{ type: 'text', text: tip.text }],
108
110
  })
109
111
  if (readRuntimeMessage(message) === undefined) throw new Error('invalid PTC recovery notice')
@@ -1,3 +1,5 @@
1
+ import { resolve } from 'node:path'
2
+ import { pathToFileURL } from 'node:url'
1
3
  import {
2
4
  decodeValue,
3
5
  encodeValue,
@@ -414,7 +416,9 @@ export class SessionCellExecutor {
414
416
  }
415
417
  const priorBindingCatalog = userBindingPlan.catalog
416
418
  const catalog = priorBindingCatalog.inputs()
417
- const { bindingPolicy, rewritesEnabled, moduleSemantics, languageSemantics } = executionPolicies(config, replayRecord)
419
+ const { bindingPolicy, rewritesEnabled, moduleSemantics, languageSemantics, moduleTransform } = executionPolicies(config, replayRecord)
420
+ const moduleDirectory = kernel.cwd ?? process.cwd()
421
+ const moduleFilename = resolve(moduleDirectory, 'repl')
418
422
  const prepareCell = program => prepareProgram(program, {
419
423
  knownBindings: catalog.knownBindings,
420
424
  bindingPolicy,
@@ -430,6 +434,11 @@ export class SessionCellExecutor {
430
434
  dynamicOrigins: catalog.dynamicOrigins,
431
435
  establishedRoots: catalog.establishedRoots,
432
436
  moduleSemantics,
437
+ importMeta: {
438
+ url: pathToFileURL(moduleFilename).href,
439
+ filename: moduleFilename,
440
+ dirname: moduleDirectory,
441
+ },
433
442
  })
434
443
  let prepared
435
444
  try {
@@ -576,6 +585,7 @@ export class SessionCellExecutor {
576
585
  type: 'run', id, program: prepared.code, namespaces: bindings.workerDescriptors,
577
586
  moduleLoads: prepared.moduleLoads,
578
587
  languageSemantics,
588
+ moduleTransform,
579
589
  rootRuntimeName: prepared.rootRuntimeName,
580
590
  rootBindings: prepared.rootBindings,
581
591
  importBindingNamespaces: new Map([...catalog.importBindings].map(([name, binding]) => [name, binding.namespace])),
@@ -711,9 +721,10 @@ export class SessionCellExecutor {
711
721
  volatile: this.handleVolatile,
712
722
  call: this.handleCall,
713
723
  'output-limit': this.handleOutputLimit,
724
+ 'worker-output-error': this.handleWorkerOutputError,
714
725
  done: this.handleDone,
715
726
  }[message.type]
716
- handler?.call(this, message)
727
+ return handler?.call(this, message)
717
728
  }
718
729
 
719
730
  handleVolatile(message) {
@@ -741,6 +752,19 @@ export class SessionCellExecutor {
741
752
  }, true)
742
753
  }
743
754
 
755
+ handleWorkerOutputError(message) {
756
+ const active = this.kernel.active
757
+ if (active?.id !== message.id) return false
758
+ const detail = typeof message.message === 'string'
759
+ ? message.message
760
+ : 'the kernel returned an invalid output-attribution event'
761
+ active.resolve({
762
+ logs: [],
763
+ error: { kind: 'worker-exit', message: `worker output attribution failed: ${detail}` },
764
+ }, true)
765
+ return true
766
+ }
767
+
744
768
  handleDone(message) {
745
769
  const kernel = this.kernel
746
770
  if (kernel.active?.id !== message.id) return