dsh-ptc-plus 0.4.1 → 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 (53) hide show
  1. package/README.en.md +6 -10
  2. package/README.md +6 -10
  3. package/client.js +262 -31
  4. package/compiler-core.cjs +132 -130
  5. package/compiler-core.cjs.map +3 -3
  6. package/docs/README.md +1 -0
  7. package/docs/adr/0023-global-user-bindings.md +6 -6
  8. package/docs/adr/0025-use-versioned-logical-binding-identities.md +3 -3
  9. package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +5 -5
  10. package/docs/architecture.md +9 -5
  11. package/docs/client-ui.md +1 -1
  12. package/docs/durability-design.md +6 -3
  13. package/docs/installation.md +4 -4
  14. package/docs/runtime-reference.md +5 -1
  15. package/docs/stateful-binding-updates-proposal.md +8 -4
  16. package/docs/user-bindings.en.md +5 -3
  17. package/docs/user-bindings.md +4 -2
  18. package/docs/verification-optimization-work.md +41 -0
  19. package/docs/verification.md +15 -1
  20. package/internal/async-local-storage-intrinsics.js +38 -0
  21. package/internal/binding-declaration-contract.js +8 -0
  22. package/internal/binding-source-analysis.js +381 -64
  23. package/internal/binding-update-policy.js +7 -1
  24. package/internal/cell-analysis.js +21 -8
  25. package/internal/compiler-intrinsics.js +9 -43
  26. package/internal/compiler-module-links.js +7 -4
  27. package/internal/dynamic-environment-integration.js +8 -4
  28. package/internal/dynamic-environment-runtime.js +24 -3
  29. package/internal/dynamic-native-calls.js +4 -1
  30. package/internal/dynamic-native-runtime.js +15 -1
  31. package/internal/failure-reporting.js +38 -30
  32. package/internal/isolated-worker.js +17 -10
  33. package/internal/kernel-worker.js +493 -331
  34. package/internal/module-compilation.js +5 -4
  35. package/internal/module-transform-contract.js +32 -2
  36. package/internal/record-utils.js +24 -15
  37. package/internal/repl-value-observer.js +6 -3
  38. package/internal/runtime-intrinsics.js +101 -0
  39. package/internal/session-cell-executor.js +2 -1
  40. package/internal/session-journal-schema.js +30 -13
  41. package/internal/session-journal.js +19 -4
  42. package/internal/session-runtime.js +1 -0
  43. package/internal/source-position-map.js +19 -0
  44. package/internal/stateful-root-compiler.js +3 -1
  45. package/internal/typescript-transform.js +4 -2
  46. package/internal/user-binding-console-worker.js +101 -41
  47. package/internal/user-bindings.js +10 -2
  48. package/internal/value-wire.js +101 -71
  49. package/internal/worker-cwd-virtualization.js +80 -53
  50. package/internal/worker-environment.js +20 -0
  51. package/internal/worker-repl-realm.js +134 -0
  52. package/package.json +2 -1
  53. package/src/client-activity.js +8 -1
@@ -0,0 +1,134 @@
1
+ import { createContext, runInContext, runInThisContext } from 'node:vm'
2
+ import { types } from 'node:util'
3
+ import { protectNextAsyncLocalStorageRun } from './async-local-storage-intrinsics.js'
4
+
5
+ export const WORKER_REPL_OPTIONS = Object.freeze({ useGlobal: true })
6
+ const NativeMap = Map
7
+ const ownKeys = Reflect.ownKeys
8
+ const deleteProperty = Reflect.deleteProperty
9
+ const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor
10
+ const defineProperty = Object.defineProperty
11
+ const createObject = Object.create
12
+ const hasOwn = Object.hasOwn
13
+ const sameValue = Object.is
14
+ const isMap = types.isMap
15
+ const mapSet = Function.prototype.call.bind(Map.prototype.set)
16
+ const mapHas = Function.prototype.call.bind(Map.prototype.has)
17
+ const mapForEach = Function.prototype.call.bind(Map.prototype.forEach)
18
+ const DESCRIPTOR_FIELDS = ['configurable', 'enumerable', 'writable', 'value', 'get', 'set']
19
+
20
+ /** Snapshot the native worker surface before Node installs REPL conveniences. */
21
+ export function captureWorkerReplGlobals(globalObject = globalThis) {
22
+ const baseline = new NativeMap()
23
+ const keys = ownKeys(globalObject)
24
+ for (let index = 0; index < keys.length; index += 1) {
25
+ mapSet(baseline, keys[index], getOwnPropertyDescriptor(globalObject, keys[index]))
26
+ }
27
+ return baseline
28
+ }
29
+
30
+ function descriptorsEqual(left, right) {
31
+ const field = (descriptor, name) => descriptor !== undefined && hasOwn(descriptor, name)
32
+ ? descriptor[name] : undefined
33
+ return left !== undefined && right !== undefined
34
+ && field(left, 'configurable') === field(right, 'configurable')
35
+ && field(left, 'enumerable') === field(right, 'enumerable')
36
+ && field(left, 'writable') === field(right, 'writable')
37
+ && sameValue(field(left, 'value'), field(right, 'value'))
38
+ && field(left, 'get') === field(right, 'get')
39
+ && field(left, 'set') === field(right, 'set')
40
+ }
41
+
42
+ function detachedDescriptor(descriptor) {
43
+ const detached = createObject(null)
44
+ for (let index = 0; index < DESCRIPTOR_FIELDS.length; index += 1) {
45
+ const name = DESCRIPTOR_FIELDS[index]
46
+ if (hasOwn(descriptor, name)) detached[name] = descriptor[name]
47
+ }
48
+ return detached
49
+ }
50
+
51
+ /** Remove Node's REPL-only module shortcuts and restore changed native globals. */
52
+ export function restoreWorkerReplGlobals(baseline, globalObject = globalThis) {
53
+ if (!isMap(baseline)) throw new TypeError('worker REPL global baseline must be a Map')
54
+ const keys = ownKeys(globalObject)
55
+ for (let index = 0; index < keys.length; index += 1) {
56
+ const key = keys[index]
57
+ if (typeof key === 'string' && !mapHas(baseline, key) && !deleteProperty(globalObject, key)) {
58
+ throw new Error(`worker REPL added a non-configurable global ${String(key)}`)
59
+ }
60
+ }
61
+ mapForEach(baseline, (descriptor, key) => {
62
+ if (!descriptorsEqual(getOwnPropertyDescriptor(globalObject, key), descriptor)) {
63
+ defineProperty(globalObject, key, detachedDescriptor(descriptor))
64
+ }
65
+ })
66
+ return globalObject
67
+ }
68
+
69
+ /** A dedicated worker is the native realm shared by its REPL and Node modules. */
70
+ export function workerReplContext(server) {
71
+ if (server.context !== globalThis) {
72
+ throw new Error('worker REPL did not use its native global realm')
73
+ }
74
+ return server.context
75
+ }
76
+
77
+ let replAsyncContextProtected = false
78
+
79
+ /** Newer Node REPLs own an AsyncLocalStorage instance whose public prototype
80
+ * remains user-mutable. Give only that platform instance its native operations
81
+ * before the first user evaluation. */
82
+ export function protectWorkerReplAsyncContext(server) {
83
+ if (replAsyncContextProtected) return
84
+ let callbackError
85
+ const scope = protectNextAsyncLocalStorageRun(() => {
86
+ server.eval('void 0\n', server.context, 'ptc-plus-repl-bootstrap', error => { callbackError = error })
87
+ })
88
+ if (callbackError !== null && callbackError !== undefined) throw callbackError
89
+ replAsyncContextProtected = true
90
+ return scope !== undefined
91
+ }
92
+
93
+ export function runInWorkerReplRealm(source, options) {
94
+ return runInThisContext(source, options)
95
+ }
96
+
97
+ /** Control promises stay outside the user-mutable worker realm. */
98
+ export function createWorkerControlPromise() {
99
+ return runInContext('Promise', createContext())
100
+ }
101
+
102
+ /** PTC owns settlement; the REPL's legacy domain would run Node bookkeeping
103
+ * through user-mutable prototypes in the shared worker realm. */
104
+ export function disableWorkerReplDomain(domain) {
105
+ domain.enter = () => domain
106
+ domain.exit = () => domain
107
+ return domain
108
+ }
109
+
110
+ /** Newer REPLs format errors before the eval callback unless an owned
111
+ * evaluation consumes the original thrown value through handleError. */
112
+ export function createWorkerReplErrorHandler(evaluationScope, settle) {
113
+ return error => {
114
+ const finish = evaluationScope.getStore()
115
+ if (finish === undefined) return 'unhandled'
116
+ settle(finish, error)
117
+ return 'ignore'
118
+ }
119
+ }
120
+
121
+ /** Older REPLs can bypass handleError. Keep their process-level fallback
122
+ * directly testable without requiring a version-specific uncaught event. */
123
+ export function createWorkerUncaughtExceptionHandler(evaluationScope, listenerOwner = process) {
124
+ const handleUncaughtException = error => {
125
+ const finish = evaluationScope.getStore()
126
+ if (finish !== undefined) {
127
+ finish(true, error)
128
+ return
129
+ }
130
+ listenerOwner.removeListener('uncaughtException', handleUncaughtException)
131
+ throw error
132
+ }
133
+ return handleUncaughtException
134
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ptc-plus",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "A session-bound agent-native REPL for DeepSeek Harness PTC mode.",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -96,6 +96,7 @@
96
96
  "review:gate": "node scripts/review-findings.mjs gate",
97
97
  "session:migrate": "node scripts/migrate-session-log.mjs",
98
98
  "coverage": "node scripts/coverage.mjs",
99
+ "coverage:focus": "node scripts/coverage.mjs --focus",
99
100
  "verify": "npm run review:validate && npm run build:check && node --check index.js && node --check internal/kernel-worker.js && node --check scripts/migrate-session-log.mjs && node --check scripts/expensive-headless-acceptance.mjs && node --check scripts/ab-headless-trajectories.mjs && npm run test:client && npm run coverage",
100
101
  "check": "node scripts/review-findings.mjs check",
101
102
  "test:expensive": "node scripts/expensive-headless-acceptance.mjs",
@@ -3,6 +3,7 @@ import {
3
3
  STATUSES as JOURNAL_STATUSES,
4
4
  BINDING_MODES,
5
5
  JOURNAL_FIELDS,
6
+ LANGUAGE_SEMANTICS_JOURNAL_FIELDS,
6
7
  PER_NAME_JOURNAL_FIELDS,
7
8
  LANGUAGE_SEMANTICS_JOURNAL_VERSION,
8
9
  WHOLE_ENTRY_JOURNAL_FIELDS,
@@ -33,6 +34,7 @@ import {
33
34
  USER_BINDINGS_REUSE_POLICIES,
34
35
  } from '../internal/session-journal-schema.js'
35
36
  import { normalizeLanguageSemantics } from '../internal/language-semantics.js'
37
+ import { normalizeModuleTransform } from '../internal/module-transform-contract.js'
36
38
  import {
37
39
  VALUE_CODEC,
38
40
  DEFAULT_VALUE_LIMITS,
@@ -292,7 +294,8 @@ function isReadableJournalUnchecked(value) {
292
294
  : value.version === 4 ? RELATIONLESS_JOURNAL_FIELDS
293
295
  : value.version === 5 ? FINGERPRINT_REUSE_JOURNAL_FIELDS
294
296
  : value.version < PER_NAME_USER_BINDINGS_JOURNAL_VERSION ? WHOLE_ENTRY_JOURNAL_FIELDS
295
- : value.version < LANGUAGE_SEMANTICS_JOURNAL_VERSION ? PER_NAME_JOURNAL_FIELDS : JOURNAL_FIELDS
297
+ : value.version < LANGUAGE_SEMANTICS_JOURNAL_VERSION ? PER_NAME_JOURNAL_FIELDS
298
+ : value.version === LANGUAGE_SEMANTICS_JOURNAL_VERSION ? LANGUAGE_SEMANTICS_JOURNAL_FIELDS : JOURNAL_FIELDS
296
299
  const required = predecessor
297
300
  ? ['version', 'bindingMode', 'status', 'calls', 'operations', 'diagnostics']
298
301
  : ['version', 'bindingPolicy', 'rewritePolicy', 'moduleSemantics', 'status', 'calls', 'operations', 'diagnostics']
@@ -307,6 +310,10 @@ function isReadableJournalUnchecked(value) {
307
310
  required.push('languageSemantics')
308
311
  normalizeLanguageSemantics(value.languageSemantics)
309
312
  }
313
+ if (value.version > LANGUAGE_SEMANTICS_JOURNAL_VERSION) {
314
+ required.push('moduleTransform')
315
+ normalizeModuleTransform(value.moduleTransform, value.languageSemantics)
316
+ }
310
317
  if (!hasClosedFields(value, fields, required)
311
318
  || (predecessor && !BINDING_MODES.has(value.bindingMode))
312
319
  || (value.version >= 4 && !isValidBindingPolicy(value.bindingPolicy))