dsh-ptc-plus 0.4.2 → 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 (83) hide show
  1. package/README.en.md +166 -76
  2. package/README.md +167 -77
  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 +593 -306
  7. package/compiler-core.cjs +162 -160
  8. package/compiler-core.cjs.map +3 -3
  9. package/docs/README.md +3 -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 +1 -1
  18. package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +23 -7
  19. package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +4 -0
  20. package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +26 -6
  21. package/docs/adr/0031-close-semantic-obligations-by-owner.md +29 -0
  22. package/docs/adr/README.md +4 -4
  23. package/docs/architecture.md +6 -5
  24. package/docs/binding-review-placement-design.md +1 -1
  25. package/docs/client-ui.md +8 -4
  26. package/docs/durability-design.md +7 -7
  27. package/docs/installation.md +26 -8
  28. package/docs/runtime-reference.md +18 -11
  29. package/docs/semantic-validation.md +7 -3
  30. package/docs/user-bindings.en.md +7 -6
  31. package/docs/user-bindings.md +6 -5
  32. package/docs/value-wire.md +1 -1
  33. package/docs/verification.md +38 -1
  34. package/index.js +39 -34
  35. package/internal/async-local-storage-intrinsics.js +12 -7
  36. package/internal/callable-source-facts.js +37 -1
  37. package/internal/cell-analysis.js +64 -9
  38. package/internal/config-spec.js +1 -1
  39. package/internal/cordis-tools-owner.js +20 -10
  40. package/internal/edit-transport-owner.js +80 -20
  41. package/internal/execution-seam-compat.js +192 -30
  42. package/internal/kernel-worker.js +162 -72
  43. package/internal/message-sources.js +14 -0
  44. package/internal/module-compilation.js +2 -1
  45. package/internal/repl-memory-projection.js +10 -0
  46. package/internal/repl-scope-normalizer.js +72 -35
  47. package/internal/repl-value-observer.js +141 -45
  48. package/internal/runtime-config.js +62 -2
  49. package/internal/runtime-intrinsics.js +3 -0
  50. package/internal/runtime-messages.js +6 -4
  51. package/internal/session-cell-executor.js +24 -1
  52. package/internal/session-journal-recovery.js +267 -67
  53. package/internal/session-journal.js +466 -40
  54. package/internal/session-log-view.js +5 -4
  55. package/internal/session-runtime.js +45 -21
  56. package/internal/session-state.js +7 -29
  57. package/internal/settings-compat.js +66 -1
  58. package/internal/source-position-map.js +19 -0
  59. package/internal/user-binding-console-worker.js +23 -9
  60. package/internal/user-binding-draft-projection.js +3 -2
  61. package/internal/user-bindings-owner.js +2 -1
  62. package/internal/user-bindings.js +6 -1
  63. package/internal/validated-parse-repair.js +6 -2
  64. package/internal/value-wire.js +95 -30
  65. package/internal/worker-client.js +99 -8
  66. package/internal/worker-cwd-virtualization.js +10 -9
  67. package/internal/worker-output-capture.js +187 -0
  68. package/internal/worker-output-fence.js +26 -0
  69. package/internal/worker-realm-surfaces.js +197 -0
  70. package/internal/worker-repl-realm.js +10 -4
  71. package/package.json +36 -4
  72. package/src/client-activity.js +4 -1
  73. package/src/client-authoring-view.js +121 -62
  74. package/src/client-console.js +4 -2
  75. package/src/client-copy.js +4 -2
  76. package/src/client-feature-gates.js +7 -9
  77. package/src/client-host-compat.js +104 -27
  78. package/src/client-repl-view.js +3 -1
  79. package/src/client-styles.js +12 -1
  80. package/src/client-tool-view.js +3 -1
  81. package/src/client-transport.js +2 -2
  82. package/src/client-workbench.js +4 -3
  83. package/src/client.js +56 -17
@@ -0,0 +1,26 @@
1
+ import { runtimeIntrinsics as internal } from './runtime-intrinsics.js'
2
+
3
+ const FENCE_PREFIX = '\u001eDSH_PTC_OUTPUT_FENCE_V1:'
4
+ const FENCE_SUFFIX = '\u001f'
5
+ const TOKEN = /^[a-f0-9]{48}$/u
6
+ const FENCE = /\u001eDSH_PTC_OUTPUT_FENCE_V1:(?:stdout|stderr):(?:start|end):[a-f0-9]{48}\u001f/gu
7
+ const { regexpTest, replaceString, toString } = internal
8
+
9
+ export const OUTPUT_FENCE_ALLOWANCE_BYTES = 512
10
+
11
+ export function validOutputFenceToken(token) {
12
+ return typeof token === 'string' && regexpTest(TOKEN, token)
13
+ }
14
+
15
+ export function outputFenceMarker(token, channel, phase) {
16
+ if (!validOutputFenceToken(token)
17
+ || (channel !== 'stdout' && channel !== 'stderr')
18
+ || (phase !== 'start' && phase !== 'end')) {
19
+ throw new TypeError('invalid worker output fence')
20
+ }
21
+ return `${FENCE_PREFIX}${channel}:${phase}:${token}${FENCE_SUFFIX}`
22
+ }
23
+
24
+ export function stripOutputFenceMarkers(value) {
25
+ return replaceString(toString(value ?? ''), FENCE, '')
26
+ }
@@ -0,0 +1,197 @@
1
+ import { runtimeIntrinsics as internal } from './runtime-intrinsics.js'
2
+
3
+ const { Error, TypeError, includes, objectFreeze,
4
+ objectGetOwnPropertyDescriptor, objectHasOwn, toString, Reflect: privateReflect } = internal
5
+
6
+ export const WORKER_REALM_MUTATION = objectFreeze({
7
+ STABLE: 'stable-native-divergence',
8
+ TEMPORARY: 'temporary-compiler-name',
9
+ PROGRAM: 'per-cell-program-binding',
10
+ USER: 'user-declaration-binding',
11
+ RESTORE: 'restoration-only',
12
+ })
13
+
14
+ const MUTATION_CLASSES = objectFreeze([
15
+ WORKER_REALM_MUTATION.STABLE,
16
+ WORKER_REALM_MUTATION.TEMPORARY,
17
+ WORKER_REALM_MUTATION.PROGRAM,
18
+ WORKER_REALM_MUTATION.USER,
19
+ WORKER_REALM_MUTATION.RESTORE,
20
+ ])
21
+
22
+ export const PROCESS_CONTROL_ERROR_CODE = 'ERR_PTC_PROCESS_CONTROL'
23
+ export const WORKER_REALM_SURFACE_CONTRACT = objectFreeze([
24
+ objectFreeze({
25
+ surface: 'console',
26
+ owner: 'internal/kernel-worker.js and internal/user-binding-console-worker.js',
27
+ classification: WORKER_REALM_MUTATION.STABLE,
28
+ contract: 'native Console methods; output enters the per-cell log budget',
29
+ reference: 'plain Node worker console methods',
30
+ verification: 'console-methods',
31
+ }),
32
+ objectFreeze({
33
+ surface: 'process.stdout.write/process.stderr.write',
34
+ owner: 'internal/kernel-worker.js',
35
+ classification: WORKER_REALM_MUTATION.STABLE,
36
+ contract: 'captured per cell as writable own methods; direct descriptor output remains covered by the helper transport',
37
+ reference: 'plain Node worker stream descriptors',
38
+ verification: 'stream-descriptors',
39
+ }),
40
+ objectFreeze({
41
+ surface: 'process.cwd',
42
+ owner: 'internal/worker-cwd-virtualization.js',
43
+ classification: WORKER_REALM_MUTATION.STABLE,
44
+ contract: 'recorded cwd; immutable and non-configurable when a session cwd exists',
45
+ reference: 'plain Node worker process descriptor',
46
+ verification: 'cwd-descriptor',
47
+ }),
48
+ objectFreeze({
49
+ surface: 'cwd-projected Node builtins',
50
+ owner: 'internal/worker-cwd-virtualization.js',
51
+ classification: WORKER_REALM_MUTATION.STABLE,
52
+ contract: 'relative fs, path, and child-process calls use the recorded cwd while retaining callable descriptors',
53
+ reference: 'plain Node worker fs, fs/promises, path, and child_process callable descriptors',
54
+ verification: 'cwd-projected-builtins',
55
+ }),
56
+ objectFreeze({
57
+ surface: 'process.exit/abort/kill/chdir',
58
+ owner: 'internal/worker-realm-surfaces.js',
59
+ classification: WORKER_REALM_MUTATION.STABLE,
60
+ contract: `immutable lifecycle guards; failures use ${PROCESS_CONTROL_ERROR_CODE}`,
61
+ reference: 'plain Node worker process descriptors',
62
+ verification: 'process-controls',
63
+ }),
64
+ objectFreeze({
65
+ surface: 'fd 1/2 and inherited child output',
66
+ owner: 'internal/worker-client.js and internal/worker-output-capture.js',
67
+ classification: 'transport-boundary',
68
+ contract: 'captured in the acknowledged per-cell dual-stream fence window; out-of-round output resets the worker',
69
+ reference: 'helper stdout/stderr transport contract in ADR 0027',
70
+ verification: 'descriptor-output',
71
+ }),
72
+ objectFreeze({
73
+ surface: 'cell module metadata',
74
+ owner: 'internal/session-cell-executor.js and internal/kernel-worker.js and internal/user-binding-console-worker.js',
75
+ classification: WORKER_REALM_MUTATION.STABLE,
76
+ contract: 'current cells expose import.meta url/filename/dirname; require subset exists without ambient CommonJS wrapper globals',
77
+ reference: 'plain Node worker CommonJS surface and the session module parent',
78
+ verification: 'module-surface',
79
+ }),
80
+ objectFreeze({
81
+ surface: 'private AsyncLocalStorage receiver methods',
82
+ owner: 'internal/async-local-storage-intrinsics.js',
83
+ classification: WORKER_REALM_MUTATION.STABLE,
84
+ contract: 'immutable captured getStore, enterWith, and run methods on plugin-private receivers',
85
+ reference: 'the corresponding native AsyncLocalStorage methods bound to a fresh receiver',
86
+ verification: 'private-async-local-storage',
87
+ }),
88
+ objectFreeze({
89
+ surface: 'AsyncLocalStorage.prototype.run',
90
+ owner: 'internal/async-local-storage-intrinsics.js',
91
+ classification: WORKER_REALM_MUTATION.TEMPORARY,
92
+ contract: 'native at rest; synchronously replaced only during private REPL bootstrap',
93
+ reference: 'plain Node worker AsyncLocalStorage descriptors',
94
+ verification: 'async-local-storage',
95
+ }),
96
+ ])
97
+
98
+ function assertMutation(classification, surface) {
99
+ if (!includes(MUTATION_CLASSES, classification)) {
100
+ throw new TypeError(`unknown worker realm mutation classification: ${classification}`)
101
+ }
102
+ if (classification !== WORKER_REALM_MUTATION.STABLE) return
103
+ let registered = false
104
+ for (let index = 0; index < WORKER_REALM_SURFACE_CONTRACT.length; index += 1) {
105
+ const entry = WORKER_REALM_SURFACE_CONTRACT[index]
106
+ if (entry.surface === surface && entry.classification === classification) registered = true
107
+ }
108
+ if (!registered) {
109
+ throw new TypeError(`stable worker realm mutation requires a matching registered classification: ${surface}`)
110
+ }
111
+ }
112
+
113
+ /** The only supported write boundary for worker globals, process surfaces and native prototypes. */
114
+ export function defineWorkerRealmProperty(classification, surface, owner, property, descriptor) {
115
+ assertMutation(classification, surface)
116
+ if (!privateReflect.defineProperty(owner, property, descriptor)) {
117
+ throw new TypeError(`worker realm property ${toString(property)} could not be defined`)
118
+ }
119
+ return owner
120
+ }
121
+
122
+ export function assignWorkerRealmProperty(classification, surface, owner, property, value) {
123
+ assertMutation(classification, surface)
124
+ if (!privateReflect.set(owner, property, value)) {
125
+ throw new TypeError(`worker realm property ${toString(property)} could not be assigned`)
126
+ }
127
+ return value
128
+ }
129
+
130
+ export function deleteWorkerRealmProperty(classification, surface, owner, property) {
131
+ assertMutation(classification, surface)
132
+ return privateReflect.deleteProperty(owner, property)
133
+ }
134
+
135
+ function codedError(code, message) {
136
+ const error = new Error(message)
137
+ privateReflect.defineProperty(error, 'code', {
138
+ configurable: true,
139
+ enumerable: true,
140
+ writable: true,
141
+ value: code,
142
+ })
143
+ return error
144
+ }
145
+
146
+ export function installProjectedProcessCwd(processObject, cwd) {
147
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.STABLE, 'process.cwd', processObject, 'cwd', {
148
+ configurable: false,
149
+ enumerable: true,
150
+ writable: false,
151
+ value: () => cwd,
152
+ })
153
+ }
154
+
155
+ export function installProcessControlGuards(processObject, properties) {
156
+ for (let index = 0; index < properties.length; index += 1) {
157
+ const property = properties[index]
158
+ const descriptor = objectGetOwnPropertyDescriptor(processObject, property)
159
+ defineWorkerRealmProperty(
160
+ WORKER_REALM_MUTATION.STABLE,
161
+ 'process.exit/abort/kill/chdir',
162
+ processObject,
163
+ property,
164
+ {
165
+ configurable: false,
166
+ enumerable: descriptor?.enumerable ?? true,
167
+ writable: false,
168
+ value: () => {
169
+ throw codedError(PROCESS_CONTROL_ERROR_CODE, `process.${property} is forbidden inside the REPL kernel`)
170
+ },
171
+ },
172
+ )
173
+ }
174
+ }
175
+
176
+ /** Stable facts consumed by the conformance test and runtime documentation. */
177
+ export function workerRealmSurfaceFacts(processObject) {
178
+ const descriptor = (owner, name) => {
179
+ const value = objectGetOwnPropertyDescriptor(owner, name)
180
+ return value === undefined ? undefined : {
181
+ configurable: value.configurable,
182
+ enumerable: value.enumerable,
183
+ writable: value.writable,
184
+ own: true,
185
+ }
186
+ }
187
+ const controls = []
188
+ for (const name of ['exit', 'abort', 'kill', 'chdir']) controls.push([name, descriptor(processObject, name)])
189
+ return {
190
+ cwd: descriptor(processObject, 'cwd'),
191
+ controls,
192
+ stdoutWrite: descriptor(processObject.stdout, 'write'),
193
+ stderrWrite: descriptor(processObject.stderr, 'write'),
194
+ stdoutWriteOwn: objectHasOwn(processObject.stdout, 'write'),
195
+ stderrWriteOwn: objectHasOwn(processObject.stderr, 'write'),
196
+ }
197
+ }
@@ -1,13 +1,16 @@
1
1
  import { createContext, runInContext, runInThisContext } from 'node:vm'
2
2
  import { types } from 'node:util'
3
3
  import { protectNextAsyncLocalStorageRun } from './async-local-storage-intrinsics.js'
4
+ import {
5
+ WORKER_REALM_MUTATION,
6
+ defineWorkerRealmProperty,
7
+ deleteWorkerRealmProperty,
8
+ } from './worker-realm-surfaces.js'
4
9
 
5
10
  export const WORKER_REPL_OPTIONS = Object.freeze({ useGlobal: true })
6
11
  const NativeMap = Map
7
12
  const ownKeys = Reflect.ownKeys
8
- const deleteProperty = Reflect.deleteProperty
9
13
  const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor
10
- const defineProperty = Object.defineProperty
11
14
  const createObject = Object.create
12
15
  const hasOwn = Object.hasOwn
13
16
  const sameValue = Object.is
@@ -54,13 +57,16 @@ export function restoreWorkerReplGlobals(baseline, globalObject = globalThis) {
54
57
  const keys = ownKeys(globalObject)
55
58
  for (let index = 0; index < keys.length; index += 1) {
56
59
  const key = keys[index]
57
- if (typeof key === 'string' && !mapHas(baseline, key) && !deleteProperty(globalObject, key)) {
60
+ if (typeof key === 'string' && !mapHas(baseline, key)
61
+ && !deleteWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, undefined, globalObject, key)) {
58
62
  throw new Error(`worker REPL added a non-configurable global ${String(key)}`)
59
63
  }
60
64
  }
61
65
  mapForEach(baseline, (descriptor, key) => {
62
66
  if (!descriptorsEqual(getOwnPropertyDescriptor(globalObject, key), descriptor)) {
63
- defineProperty(globalObject, key, detachedDescriptor(descriptor))
67
+ defineWorkerRealmProperty(
68
+ WORKER_REALM_MUTATION.RESTORE, undefined, globalObject, key, detachedDescriptor(descriptor),
69
+ )
64
70
  }
65
71
  })
66
72
  return globalObject
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "dsh-ptc-plus",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
+ "icon": "assets/ptc-plus-icon.svg",
4
5
  "description": "A session-bound agent-native REPL for DeepSeek Harness PTC mode.",
5
6
  "type": "module",
6
7
  "main": "./index.js",
@@ -25,6 +26,7 @@
25
26
  "src",
26
27
  "internal",
27
28
  "docs",
29
+ "assets/ptc-plus-icon.svg",
28
30
  "assets/dsh-ptc-plus-banner-en.webp",
29
31
  "assets/dsh-ptc-plus-banner-zh.webp",
30
32
  "assets/ptc-plus-bindings-en.png",
@@ -61,7 +63,7 @@
61
63
  "@codemirror/language": "^6.12.4",
62
64
  "@codemirror/state": "^6.7.4",
63
65
  "@codemirror/view": "^6.43.11",
64
- "@deepseek-ai/schemastery": "^3.18.1",
66
+ "@deepseek-ai/schemastery": "^3.18.3",
65
67
  "@jridgewell/trace-mapping": "^0.3.31",
66
68
  "@lezer/highlight": "^1.2.3",
67
69
  "acorn": "^8.15.0",
@@ -87,17 +89,24 @@
87
89
  "build:check": "node scripts/client-bundle.mjs check && node scripts/compiler-bundle.mjs check",
88
90
  "test:client": "vitest run --config scripts/client-test.config.mjs",
89
91
  "test:semantics": "node --test --test-concurrency=1 test/semantic-test262.test.js test/semantic-dialect.test.js test/semantic-boundaries.test.js",
92
+ "semantic:check": "node scripts/semantic-obligations.mjs",
90
93
  "test:client:web": "node scripts/client-web-smoke.mjs",
91
94
  "test:client:layout": "node scripts/client-binding-layout.mjs",
92
95
  "prepack": "npm run build:check",
93
96
  "hooks:install": "node scripts/review-findings.mjs install-hook",
94
97
  "review:new": "node scripts/review-findings.mjs create",
98
+ "review:plan:new": "node scripts/review-findings.mjs plan-create",
99
+ "review:plan": "node scripts/review-findings.mjs plan",
100
+ "review:status": "node scripts/review-findings.mjs status",
101
+ "review:lane": "node scripts/review-findings.mjs lane-verdict",
102
+ "review:finalize": "node scripts/review-findings.mjs finalize",
103
+ "review:clear": "node scripts/review-findings.mjs review-clear",
95
104
  "review:validate": "node scripts/review-findings.mjs validate",
96
105
  "review:gate": "node scripts/review-findings.mjs gate",
97
106
  "session:migrate": "node scripts/migrate-session-log.mjs",
98
107
  "coverage": "node scripts/coverage.mjs",
99
108
  "coverage:focus": "node scripts/coverage.mjs --focus",
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",
109
+ "verify": "npm run review:validate && npm run semantic:check && 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",
101
110
  "check": "node scripts/review-findings.mjs check",
102
111
  "test:expensive": "node scripts/expensive-headless-acceptance.mjs",
103
112
  "test:ab": "node scripts/ab-headless-trajectories.mjs"
@@ -157,6 +166,8 @@
157
166
  "@deepseek-ai/dsh-commands": "next",
158
167
  "@deepseek-ai/dsh-home-paths": "next",
159
168
  "@deepseek-ai/dsh-llm": "next",
169
+ "@deepseek-ai/dsh-session-format-catalog": "next",
170
+ "@deepseek-ai/dsh-session-format-v0-to-v1": "next",
160
171
  "@deepseek-ai/dsh-session-persistence-jsonl": "next",
161
172
  "@deepseek-ai/dsh-settings": "next",
162
173
  "@deepseek-ai/dsh-skill-filesystem": "next",
@@ -164,15 +175,36 @@
164
175
  "@deepseek-ai/dsh-tools": "next",
165
176
  "@deepseek-ai/dsh-typert-protocol": "next",
166
177
  "@deepseek-ai/dsh-typert-registry": "next",
178
+ "@deepseek-ai/dsh-util-workspace-path": "next",
179
+ "@shikijs/langs": "^4.3.1",
167
180
  "@testing-library/react": "^16.3.3",
181
+ "anser": "^2.3.5",
168
182
  "c8": "^12.0.0",
183
+ "clsx": "^2.0.0",
169
184
  "convert-source-map": "^2.0.0",
185
+ "diff": "^9.0.0",
170
186
  "esbuild": "0.28.2",
187
+ "immer": "^10.1.1",
171
188
  "jsdom": "^29.1.1",
189
+ "katex": "^0.16.47",
190
+ "mdast-util-from-markdown": "^2.0.3",
191
+ "mdast-util-gfm": "^3.1.0",
192
+ "mdast-util-math": "^3.0.0",
193
+ "micromark-core-commonmark": "^2.0.3",
194
+ "micromark-extension-gfm": "^3.0.0",
195
+ "micromark-extension-math": "^3.1.0",
196
+ "micromark-factory-space": "^2.0.1",
197
+ "micromark-util-character": "^2.1.1",
198
+ "micromark-util-classify-character": "^2.0.1",
199
+ "micromark-util-sanitize-uri": "^2.0.1",
200
+ "micromark-util-symbol": "^2.0.1",
172
201
  "playwright": "^1.63.0",
173
202
  "react": "^18.3.1",
174
203
  "react-dom": "^18.3.1",
204
+ "shiki": "^4.3.1",
205
+ "simple-icons": "16.31.0",
175
206
  "vitest": "^5.0.0",
176
- "yaml": "^2.9.0"
207
+ "yaml": "^2.9.0",
208
+ "zustand": "~4.4.7"
177
209
  }
178
210
  }
@@ -105,7 +105,7 @@ function isLine(value) {
105
105
  }
106
106
 
107
107
  function isSafeSequence(value) {
108
- return Number.isSafeInteger(value) && value >= 0
108
+ return Number.isSafeInteger(value) && value >= 0 && !Object.is(value, -0)
109
109
  }
110
110
 
111
111
  function isValidPosition(value) {
@@ -187,6 +187,7 @@ function isValidValueWire(value) {
187
187
  nodeCount: value.nodes.length,
188
188
  discovered: new Set(),
189
189
  edges: 0,
190
+ arraySlots: 0,
190
191
  textBytes: 0,
191
192
  }
192
193
  if (!isValidValueAtom(value.root, state)) return false
@@ -196,6 +197,8 @@ function isValidValueWire(value) {
196
197
  if (!hasExactOrderedFields(node, VALUE_ARRAY_FIELDS)
197
198
  || !Number.isSafeInteger(node.length) || node.length < 0 || node.length > MAX_ARRAY_LENGTH
198
199
  || !Array.isArray(node.entries)) return false
200
+ state.arraySlots += node.length
201
+ if (state.arraySlots > MAX_ARRAY_LENGTH) return false
199
202
  let previous = -1
200
203
  for (const entry of node.entries) {
201
204
  if (!Array.isArray(entry) || entry.length !== 2 || !Number.isSafeInteger(entry[0])