dsh-ptc-plus 0.4.2 → 0.4.8
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.
- package/README.en.md +170 -76
- package/README.md +171 -77
- package/assets/dsh-ptc-plus-banner-en.webp +0 -0
- package/assets/dsh-ptc-plus-banner-zh.webp +0 -0
- package/assets/ptc-plus-icon.svg +5 -0
- package/client.js +599 -310
- package/compiler-core.cjs +162 -160
- package/compiler-core.cjs.map +3 -3
- package/docs/README.md +4 -1
- package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +49 -11
- package/docs/adr/0008-define-agent-operating-contract.md +2 -2
- package/docs/adr/0010-session-log-derived-recovery-tips.md +7 -0
- package/docs/adr/0016-archive-resolved-review-findings.md +3 -3
- package/docs/adr/0017-track-the-latest-dsh-public-surface.md +5 -1
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +3 -3
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +3 -1
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +14 -3
- package/docs/adr/0023-global-user-bindings.md +1 -1
- package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +23 -7
- package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +4 -0
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +26 -6
- package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +2 -2
- package/docs/adr/0031-close-semantic-obligations-by-owner.md +29 -0
- package/docs/adr/README.md +4 -4
- package/docs/architecture.md +8 -5
- package/docs/binding-review-placement-design.md +1 -1
- package/docs/client-ui.md +8 -4
- package/docs/durability-design.md +7 -7
- package/docs/installation.md +48 -12
- package/docs/publishing.md +4 -4
- package/docs/release-notes.md +109 -0
- package/docs/runtime-reference.md +25 -12
- package/docs/semantic-validation.md +7 -3
- package/docs/user-bindings.en.md +7 -6
- package/docs/user-bindings.md +6 -5
- package/docs/value-wire.md +1 -1
- package/docs/verification.md +43 -2
- package/index.js +39 -34
- package/internal/async-local-storage-intrinsics.js +12 -7
- package/internal/callable-source-facts.js +37 -1
- package/internal/cell-analysis.js +64 -9
- package/internal/config-spec.js +1 -1
- package/internal/cordis-tools-owner.js +81 -15
- package/internal/direct-surface-owner.js +331 -7
- package/internal/edit-transport-owner.js +80 -20
- package/internal/execution-seam-compat.js +192 -30
- package/internal/kernel-worker.js +162 -72
- package/internal/message-sources.js +14 -0
- package/internal/module-compilation.js +2 -1
- package/internal/rejected-cell-editor.js +11 -21
- package/internal/repl-memory-projection.js +10 -0
- package/internal/repl-scope-normalizer.js +72 -35
- package/internal/repl-value-observer.js +141 -45
- package/internal/runtime-config.js +62 -2
- package/internal/runtime-intrinsics.js +3 -0
- package/internal/runtime-messages.js +6 -4
- package/internal/session-cell-executor.js +24 -1
- package/internal/session-journal-recovery.js +267 -67
- package/internal/session-journal.js +466 -40
- package/internal/session-log-view.js +5 -4
- package/internal/session-runtime.js +45 -21
- package/internal/session-state.js +7 -29
- package/internal/settings-compat.js +66 -1
- package/internal/source-position-map.js +19 -0
- package/internal/user-binding-console-worker.js +23 -9
- package/internal/user-binding-draft-projection.js +3 -2
- package/internal/user-bindings-owner.js +2 -1
- package/internal/user-bindings.js +6 -1
- package/internal/validated-parse-repair.js +6 -2
- package/internal/value-wire.js +95 -30
- package/internal/worker-client.js +99 -8
- package/internal/worker-cwd-virtualization.js +10 -9
- package/internal/worker-output-capture.js +187 -0
- package/internal/worker-output-fence.js +26 -0
- package/internal/worker-realm-surfaces.js +197 -0
- package/internal/worker-repl-realm.js +10 -4
- package/package.json +42 -4
- package/src/client-activity.js +4 -1
- package/src/client-authoring-view.js +121 -62
- package/src/client-console.js +4 -2
- package/src/client-copy.js +4 -2
- package/src/client-feature-gates.js +7 -9
- package/src/client-host-compat.js +104 -27
- package/src/client-repl-view.js +3 -1
- package/src/client-styles.js +12 -1
- package/src/client-tool-view.js +3 -1
- package/src/client-transport.js +2 -2
- package/src/client-workbench.js +10 -7
- package/src/client.js +56 -17
|
@@ -4,6 +4,7 @@ import { diagnostic, renderDiagnostic } from './diagnostic.js'
|
|
|
4
4
|
import { createFailureTracker, messageOf } from './failure-reporting.js'
|
|
5
5
|
import {
|
|
6
6
|
createJournal,
|
|
7
|
+
isCanonicalSequence,
|
|
7
8
|
liveToolCallSeq,
|
|
8
9
|
normalizeJournal,
|
|
9
10
|
reduceStateOperations,
|
|
@@ -29,6 +30,17 @@ import {
|
|
|
29
30
|
} from './repl-memory-projection.js'
|
|
30
31
|
|
|
31
32
|
const WORKER_URL = new URL('./kernel-worker.js', import.meta.url)
|
|
33
|
+
const UNAVAILABLE_SURFACE_GENERATION = Symbol('unavailableSurfaceGeneration')
|
|
34
|
+
|
|
35
|
+
function surfaceGeneration(session, provenanceRequired = true) {
|
|
36
|
+
try {
|
|
37
|
+
return session?.surface?.replaceGeneration
|
|
38
|
+
?? (provenanceRequired ? UNAVAILABLE_SURFACE_GENERATION : undefined)
|
|
39
|
+
} catch {
|
|
40
|
+
return provenanceRequired ? UNAVAILABLE_SURFACE_GENERATION : undefined
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
32
44
|
function recoveryDiagnostic(count) {
|
|
33
45
|
return diagnostic({
|
|
34
46
|
code: 'PTC-R002',
|
|
@@ -66,20 +78,16 @@ class ReplayCancelled extends Error {
|
|
|
66
78
|
}
|
|
67
79
|
|
|
68
80
|
class SessionKernel {
|
|
69
|
-
constructor({ config, history, cwd, session, userBindingsCwd, withInitiator, observeValues }) {
|
|
81
|
+
constructor({ config, history, cwd, session, surfaceProvenanceRequired, userBindingsCwd, withInitiator, observeValues }) {
|
|
70
82
|
this.config = config
|
|
71
83
|
this.history = history
|
|
72
84
|
this.initialRecoveryBoundary = recoveryBoundaryForHistory(history)
|
|
73
|
-
this.surfaceGeneration =
|
|
74
|
-
try {
|
|
75
|
-
this.surfaceGeneration = session?.surface?.replaceGeneration
|
|
76
|
-
} catch {
|
|
77
|
-
this.surfaceGeneration = undefined
|
|
78
|
-
}
|
|
85
|
+
this.surfaceGeneration = surfaceGeneration(session, surfaceProvenanceRequired)
|
|
79
86
|
this.cwd = cwd
|
|
80
87
|
this.userBindingsCwd = userBindingsCwd
|
|
81
88
|
this.observeValues = observeValues
|
|
82
89
|
this.session = session
|
|
90
|
+
this.surfaceProvenanceRequired = surfaceProvenanceRequired
|
|
83
91
|
this.withInitiator = withInitiator
|
|
84
92
|
this.durability = durabilityState()
|
|
85
93
|
this.bindingCatalog = new BindingCatalog()
|
|
@@ -103,10 +111,19 @@ class SessionKernel {
|
|
|
103
111
|
cwd,
|
|
104
112
|
compilerCache: compilerWorkerCache,
|
|
105
113
|
onMessage: message => this.cellExecutor.onMessage(message),
|
|
114
|
+
onUnmatchedOutputFailure: message => {
|
|
115
|
+
const active = this.active
|
|
116
|
+
if (active === undefined) return false
|
|
117
|
+
active.resolve({ logs: [], error: { kind: 'worker-exit', message } }, true)
|
|
118
|
+
return true
|
|
119
|
+
},
|
|
106
120
|
onFailure: message => {
|
|
107
121
|
this.pendingInspection?.finish()
|
|
108
122
|
this.workerObservation = undefined
|
|
109
|
-
this.active
|
|
123
|
+
const active = this.active
|
|
124
|
+
if (active === undefined) return false
|
|
125
|
+
active.resolve({ logs: [], error: { kind: 'worker-exit', message } }, true)
|
|
126
|
+
return true
|
|
110
127
|
},
|
|
111
128
|
})
|
|
112
129
|
this.failures = createFailureTracker()
|
|
@@ -181,10 +198,10 @@ class SessionKernel {
|
|
|
181
198
|
const current = () => {
|
|
182
199
|
if (this.disposed || !this.config.replViewEnabled || signal?.aborted
|
|
183
200
|
|| this.client.worker === undefined || this.unsettledCells > 0) return false
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
201
|
+
return surfaceGeneration(this.session, this.surfaceProvenanceRequired) === this.surfaceGeneration
|
|
202
|
+
&& (!this.surfaceProvenanceRequired
|
|
203
|
+
|| this.surfaceGeneration !== UNAVAILABLE_SURFACE_GENERATION)
|
|
204
|
+
&& isDeepStrictEqual(expected, createReplMemorySnapshot(this.bindingCatalog.snapshot()))
|
|
188
205
|
}
|
|
189
206
|
return new Promise(resolve => {
|
|
190
207
|
let finished = false
|
|
@@ -234,13 +251,10 @@ class SessionKernel {
|
|
|
234
251
|
const finishResult = result => recoveryBoundaries.length === 0
|
|
235
252
|
? result
|
|
236
253
|
: { ...result, recoveryBoundaries: recoveryBoundaries.map(boundary => ({ ...boundary })) }
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
currentGeneration = undefined
|
|
242
|
-
}
|
|
243
|
-
if (currentGeneration !== undefined && currentGeneration !== this.surfaceGeneration) {
|
|
254
|
+
const currentGeneration = surfaceGeneration(this.session, this.surfaceProvenanceRequired)
|
|
255
|
+
const hasLiveState = this.liveCallSeqs.size > 0
|
|
256
|
+
if (this.surfaceProvenanceRequired
|
|
257
|
+
&& (currentGeneration !== this.surfaceGeneration || hasLiveState)) {
|
|
244
258
|
this.surfaceGeneration = currentGeneration
|
|
245
259
|
const visibleCallSeqs = visibleExecutableCallSeqs(this.session)
|
|
246
260
|
const contractsLiveState = [...this.liveCallSeqs].some(callSeq => (
|
|
@@ -597,6 +611,7 @@ function sessionOf(sessionContext) {
|
|
|
597
611
|
callId: undefined,
|
|
598
612
|
persistedCallSeq: undefined,
|
|
599
613
|
cwd: undefined,
|
|
614
|
+
surfaceProvenanceRequired: false,
|
|
600
615
|
}
|
|
601
616
|
}
|
|
602
617
|
const session = sessionContext.session
|
|
@@ -606,6 +621,7 @@ function sessionOf(sessionContext) {
|
|
|
606
621
|
callId: sessionContext.callId,
|
|
607
622
|
persistedCallSeq: sessionContext.persistedCallSeq,
|
|
608
623
|
cwd: typeof session?.header?.cwd === 'string' ? session.header.cwd : undefined,
|
|
624
|
+
surfaceProvenanceRequired: true,
|
|
609
625
|
}
|
|
610
626
|
}
|
|
611
627
|
|
|
@@ -657,12 +673,19 @@ export class SessionRuntime {
|
|
|
657
673
|
return completed({ logs: [], error: { kind: 'exception', message: messageOf(error) } })
|
|
658
674
|
}
|
|
659
675
|
request = { ...request, bindings: bindingDescriptors.namespaces, bindingDescriptors }
|
|
660
|
-
const {
|
|
676
|
+
const {
|
|
677
|
+
id: sessionId,
|
|
678
|
+
session,
|
|
679
|
+
callId,
|
|
680
|
+
persistedCallSeq,
|
|
681
|
+
cwd,
|
|
682
|
+
surfaceProvenanceRequired,
|
|
683
|
+
} = sessionOf(sessionContext)
|
|
661
684
|
let callSeq
|
|
662
685
|
let sourceCallSeq
|
|
663
686
|
try {
|
|
664
687
|
if (persistedCallSeq !== undefined
|
|
665
|
-
&&
|
|
688
|
+
&& !isCanonicalSequence(persistedCallSeq)) {
|
|
666
689
|
throw new Error('persisted tool call sequence must be a non-negative safe integer')
|
|
667
690
|
}
|
|
668
691
|
if (cellConfig.durableReplay) {
|
|
@@ -700,6 +723,7 @@ export class SessionRuntime {
|
|
|
700
723
|
history,
|
|
701
724
|
cwd,
|
|
702
725
|
session,
|
|
726
|
+
surfaceProvenanceRequired,
|
|
703
727
|
userBindingsCwd: this.userBindingsCwd,
|
|
704
728
|
withInitiator: this.withInitiator,
|
|
705
729
|
observeValues: () => this.config.replViewEnabled && this.observeSession(sessionId),
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// Runtime state transitions shared by the session coordinator and cell executor.
|
|
2
2
|
|
|
3
3
|
import { userBindingCatalogEntries } from './user-bindings.js'
|
|
4
|
+
import { boundedDefinitionSource } from './repl-memory-projection.js'
|
|
4
5
|
import { LEGACY_USER_BINDINGS_SHADOW_POLICY } from './session-journal-schema.js'
|
|
5
6
|
import { advanceLegacyBindings } from './legacy-binding-catalog.js'
|
|
6
7
|
import { dynamicBindingOrigin } from './dynamic-binding-evidence.js'
|
|
7
8
|
import { LEGACY_LANGUAGE_SEMANTICS, normalizeLanguageSemantics } from './language-semantics.js'
|
|
9
|
+
import { sourceLineStarts, sourceOffsetAtPosition } from './source-position-map.js'
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Reuse counts belong to a logical binding identity, not to one declaration
|
|
@@ -236,7 +238,7 @@ export class BindingCatalog {
|
|
|
236
238
|
definition: previous?.origin === undefined && previous?.definition !== undefined
|
|
237
239
|
? previous.definition
|
|
238
240
|
: fact.state === 'local' && typeof source === 'string' && source.length > 0
|
|
239
|
-
? Object.freeze({ source: source
|
|
241
|
+
? Object.freeze({ source: boundedDefinitionSource(source), line: 1, column: 1 }) : undefined,
|
|
240
242
|
writable: previous?.writable ?? true,
|
|
241
243
|
userBindingState: fact.state,
|
|
242
244
|
unavailable: fact.state !== 'local',
|
|
@@ -290,41 +292,17 @@ function applyRootBindingFacts(entries, facts, rootCandidates, dynamicOrigins, i
|
|
|
290
292
|
}
|
|
291
293
|
}
|
|
292
294
|
|
|
293
|
-
const MAX_DEFINITION_SOURCE_LENGTH = 1024
|
|
294
|
-
|
|
295
|
-
function lineOffsets(source) {
|
|
296
|
-
const offsets = [0]
|
|
297
|
-
for (let index = 0; index < source.length; index += 1) {
|
|
298
|
-
if (source.charCodeAt(index) === 10) offsets.push(index + 1)
|
|
299
|
-
}
|
|
300
|
-
return offsets
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
function offsetAt(offsets, sourceLength, position) {
|
|
304
|
-
if (position === null || typeof position !== 'object'
|
|
305
|
-
|| !Number.isSafeInteger(position.line) || position.line < 1
|
|
306
|
-
|| !Number.isSafeInteger(position.column) || position.column < 1
|
|
307
|
-
|| position.line > offsets.length) return undefined
|
|
308
|
-
const lineStart = offsets[position.line - 1]
|
|
309
|
-
const lineEnd = position.line < offsets.length ? offsets[position.line] - 1 : sourceLength
|
|
310
|
-
const offset = lineStart + position.column - 1
|
|
311
|
-
return offset <= lineEnd ? offset : undefined
|
|
312
|
-
}
|
|
313
|
-
|
|
314
295
|
function sourceDefinitionExtractor(source) {
|
|
315
|
-
const offsets = typeof source === 'string' ?
|
|
296
|
+
const offsets = typeof source === 'string' ? sourceLineStarts(source) : undefined
|
|
316
297
|
const extracted = new Map()
|
|
317
298
|
return span => {
|
|
318
299
|
if (offsets === undefined || span === null || typeof span !== 'object') return undefined
|
|
319
|
-
const start =
|
|
320
|
-
const end =
|
|
300
|
+
const start = sourceOffsetAtPosition(source, span, offsets)
|
|
301
|
+
const end = sourceOffsetAtPosition(source, span.end, offsets)
|
|
321
302
|
if (start === undefined || end === undefined || end <= start) return undefined
|
|
322
303
|
const key = `${start}:${end}`
|
|
323
304
|
if (extracted.has(key)) return extracted.get(key)
|
|
324
|
-
const
|
|
325
|
-
const bounded = length <= MAX_DEFINITION_SOURCE_LENGTH
|
|
326
|
-
? source.slice(start, end)
|
|
327
|
-
: `${source.slice(start, start + MAX_DEFINITION_SOURCE_LENGTH - 3)}...`
|
|
305
|
+
const bounded = boundedDefinitionSource(source.slice(start, end))
|
|
328
306
|
if (bounded.length === 0) return undefined
|
|
329
307
|
const definition = Object.freeze({ source: bounded, line: span.line, column: span.column })
|
|
330
308
|
extracted.set(key, definition)
|
|
@@ -1,3 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the installed settings generation owns the plugin's exported `Config`
|
|
3
|
+
* document.
|
|
4
|
+
*
|
|
5
|
+
* A generation that still attaches through the provider section installer — on
|
|
6
|
+
* its settings service, or on the package helper an older release published —
|
|
7
|
+
* validates the plugin's own fields and has no volatile contract. The
|
|
8
|
+
* generation that serves the exported `Config` itself, and commits only fields
|
|
9
|
+
* marked volatile, publishes neither installer. Both probes read the installed
|
|
10
|
+
* public surface, so no release identity selects the shape.
|
|
11
|
+
*
|
|
12
|
+
* @param settingsModule - the installed public settings package surface.
|
|
13
|
+
* @returns whether that generation serves the plugin's Config document itself.
|
|
14
|
+
*/
|
|
15
|
+
export function hostOwnsSettingsDocument(settingsModule) {
|
|
16
|
+
return typeof settingsModule?.installSettingsSection !== 'function'
|
|
17
|
+
&& typeof settingsModule?.SettingsProvider?.prototype?.installSection !== 'function'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Build one exported `Config` field for the installed settings generation.
|
|
22
|
+
*
|
|
23
|
+
* A generation that serves the document itself keeps a form mounted and commits
|
|
24
|
+
* new values into the retained handle only for fields whose nearest schema
|
|
25
|
+
* ancestor is marked volatile, so the marker is applied there. A builder that
|
|
26
|
+
* does not publish that capability causes a dependency error during import; silently
|
|
27
|
+
* emitting ordinary fields would hide the form and every Client feature gated
|
|
28
|
+
* by its settings snapshot. The generation that installs its own section
|
|
29
|
+
* validates the same fields itself
|
|
30
|
+
* and must not receive the marker, because its validated defaults would become
|
|
31
|
+
* live handles its document path never commits.
|
|
32
|
+
*
|
|
33
|
+
* @param options.Schema - the schema builder the plugin imports.
|
|
34
|
+
* @param options.field - one field definition from `internal/config-spec.js`.
|
|
35
|
+
* @param options.settingsModule - the installed public settings package surface.
|
|
36
|
+
* @returns the Cordis schema field for that definition.
|
|
37
|
+
*/
|
|
38
|
+
export function configFieldSchema({ Schema, field, settingsModule }) {
|
|
39
|
+
const base = field.type === 'boolean'
|
|
40
|
+
? Schema.boolean().default(field.default)
|
|
41
|
+
: field.type === 'enum'
|
|
42
|
+
// Keep omission visible until resolveConfig migrates legacy policies.
|
|
43
|
+
? Schema.union(field.options.map(option => Schema.const(option)))
|
|
44
|
+
: Schema.number().step(1).min(field.min).max(field.max).default(field.default)
|
|
45
|
+
const described = base.description(field.description)
|
|
46
|
+
if (!hostOwnsSettingsDocument(settingsModule)) return described
|
|
47
|
+
if (typeof described.volatile !== 'function') {
|
|
48
|
+
throw new Error('ptc-plus: the host settings contract requires @deepseek-ai/schemastery with volatile(); reinstall the plugin to update its dependencies')
|
|
49
|
+
}
|
|
50
|
+
return described.volatile()
|
|
51
|
+
}
|
|
52
|
+
|
|
1
53
|
/** Install an optional settings section across current and legacy DSH settings APIs. */
|
|
2
54
|
export function installSettingsSectionCompat({
|
|
3
55
|
ctx,
|
|
@@ -6,6 +58,7 @@ export function installSettingsSectionCompat({
|
|
|
6
58
|
schema,
|
|
7
59
|
entry,
|
|
8
60
|
hooks,
|
|
61
|
+
ownerFiber,
|
|
9
62
|
onProvider,
|
|
10
63
|
}) {
|
|
11
64
|
ctx.inject(['settings'], settingsContext => {
|
|
@@ -16,7 +69,19 @@ export function installSettingsSectionCompat({
|
|
|
16
69
|
return
|
|
17
70
|
}
|
|
18
71
|
if (typeof settingsModule?.installSettingsSection !== 'function') {
|
|
19
|
-
|
|
72
|
+
// The newest settings generation owns its document and card from the
|
|
73
|
+
// plugin's exported Config, so it exposes no section installer to call.
|
|
74
|
+
// Treat that shape as a host-owned surface instead of a load failure;
|
|
75
|
+
// the host commits volatile-only changes into the retained handles and
|
|
76
|
+
// notifies this fiber through `loader/volatile-update`.
|
|
77
|
+
if (typeof provider?.configure === 'function') {
|
|
78
|
+
// The host records the own-page policy against the profile entry's own
|
|
79
|
+
// fiber, so the owner is the plugin's entry context rather than the
|
|
80
|
+
// scope this installation happens to run in.
|
|
81
|
+
const dispose = provider.configure({ auto: false }, ownerFiber ?? ctx.fiber)
|
|
82
|
+
ctx.effect?.(() => dispose, 'ptc-plus settings page policy')
|
|
83
|
+
}
|
|
84
|
+
return
|
|
20
85
|
}
|
|
21
86
|
const mountedOwner = new Proxy(ctx, {
|
|
22
87
|
get(target, property) {
|
|
@@ -287,6 +287,25 @@ export function sourceLineStarts(source) {
|
|
|
287
287
|
return starts
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
+
/** Convert one-based JavaScript source coordinates without admitting a
|
|
291
|
+
* position inside a line terminator. End coordinates may point immediately
|
|
292
|
+
* after the final content character. */
|
|
293
|
+
export function sourceOffsetAtPosition(source, position, starts) {
|
|
294
|
+
if (typeof source !== 'string' || position === null || typeof position !== 'object'
|
|
295
|
+
|| !Number.isSafeInteger(position.line) || position.line < 1
|
|
296
|
+
|| !Number.isSafeInteger(position.column) || position.column < 1) return undefined
|
|
297
|
+
const lineStarts = starts ?? sourceLineStarts(source)
|
|
298
|
+
if (!Array.isArray(lineStarts) || position.line > lineStarts.length) return undefined
|
|
299
|
+
const lineStart = lineStarts[position.line - 1]
|
|
300
|
+
const nextLineStart = lineStarts[position.line]
|
|
301
|
+
let lineEnd = source.length
|
|
302
|
+
if (nextLineStart !== undefined) {
|
|
303
|
+
lineEnd = nextLineStart - (source[nextLineStart - 2] === '\r' && source[nextLineStart - 1] === '\n' ? 2 : 1)
|
|
304
|
+
}
|
|
305
|
+
const offset = lineStart + position.column - 1
|
|
306
|
+
return offset <= lineEnd ? offset : undefined
|
|
307
|
+
}
|
|
308
|
+
|
|
290
309
|
/** Visit maintained emitter coordinates without re-encoding available raw maps.
|
|
291
310
|
* All line numbers at this boundary are zero-based; absent origins stay absent. */
|
|
292
311
|
export function visitSourceMappings(transformed, visit) {
|
|
@@ -16,6 +16,12 @@ import { USER_BINDING_TRANSFORM } from './module-transform-contract.js'
|
|
|
16
16
|
import { moduleRuntimeIntrinsics } from './compiler-intrinsics.js'
|
|
17
17
|
import { stringifyCompilerData } from './compiler-data.js'
|
|
18
18
|
import { createCellCompletionObserver } from './cell-completion.js'
|
|
19
|
+
import {
|
|
20
|
+
WORKER_REALM_MUTATION,
|
|
21
|
+
assignWorkerRealmProperty,
|
|
22
|
+
defineWorkerRealmProperty,
|
|
23
|
+
deleteWorkerRealmProperty,
|
|
24
|
+
} from './worker-realm-surfaces.js'
|
|
19
25
|
import { WORKER_SHUTDOWN_ACKNOWLEDGEMENT, WORKER_SHUTDOWN_REQUEST } from './worker-shutdown.js'
|
|
20
26
|
import { WORKER_REPL_OPTIONS, createWorkerControlPromise, createWorkerReplErrorHandler,
|
|
21
27
|
disableWorkerReplDomain,
|
|
@@ -31,10 +37,10 @@ const ControlPromise = createWorkerControlPromise()
|
|
|
31
37
|
const evaluations = createPrivateAsyncLocalStorage()
|
|
32
38
|
const context = globalThis
|
|
33
39
|
const completionObserver = createCellCompletionObserver(runInWorkerReplRealm('Function'))
|
|
34
|
-
|
|
40
|
+
assignWorkerRealmProperty(WORKER_REALM_MUTATION.STABLE, 'console', context, 'console', console)
|
|
35
41
|
let retainedEvaluator
|
|
36
42
|
const completionKey = `__ptc_console_${randomUUID().replaceAll('-', '')}`
|
|
37
|
-
|
|
43
|
+
defineWorkerRealmProperty(WORKER_REALM_MUTATION.TEMPORARY, undefined, context, completionKey, { value: value => {
|
|
38
44
|
evaluations.getStore().completed = true
|
|
39
45
|
return value
|
|
40
46
|
} })
|
|
@@ -65,7 +71,8 @@ const moduleCompilation = createUserModuleCompilationHooks({
|
|
|
65
71
|
transformForParent: parent => parent === replParent || parent === parentUrl ? USER_BINDING_TRANSFORM : undefined,
|
|
66
72
|
})
|
|
67
73
|
const providedRequire = managedRequire(parentUrl, createRequire(parentUrl))
|
|
68
|
-
|
|
74
|
+
defineWorkerRealmProperty(WORKER_REALM_MUTATION.STABLE, 'cell module metadata',
|
|
75
|
+
context, 'require', { configurable: true, value: providedRequire })
|
|
69
76
|
const importCanary = 'data:text/javascript,export default 1'
|
|
70
77
|
registerHooks({ resolve(specifier, context, nextResolve) {
|
|
71
78
|
if (specifier === importCanary) replParent = context.parentURL
|
|
@@ -86,10 +93,12 @@ process.on('uncaughtException', error => {
|
|
|
86
93
|
function evaluate(source) {
|
|
87
94
|
const prepared = prepareConsoleProgram(source, { languageSemantics: 'stateful-v1', knownBindings })
|
|
88
95
|
const helpers = [prepared.rootRuntimeName, prepared.returnSignal]
|
|
89
|
-
|
|
96
|
+
defineWorkerRealmProperty(WORKER_REALM_MUTATION.TEMPORARY, undefined,
|
|
97
|
+
context, prepared.rootRuntimeName, {
|
|
90
98
|
configurable: true, value: roots.begin({ ...prepared.rootBindings, committed: () => {} }),
|
|
91
99
|
})
|
|
92
|
-
|
|
100
|
+
defineWorkerRealmProperty(WORKER_REALM_MUTATION.TEMPORARY, undefined,
|
|
101
|
+
context, prepared.returnSignal, { configurable: true, value: ConsoleReturn })
|
|
93
102
|
if (prepared.moduleLoads.length === 0) {
|
|
94
103
|
const operation = evaluatePrepared(prepared.code, prepared.asyncCompletion)
|
|
95
104
|
return new ControlPromise((resolve, reject) => operation.then(
|
|
@@ -101,7 +110,9 @@ function evaluate(source) {
|
|
|
101
110
|
}
|
|
102
111
|
|
|
103
112
|
function removeHelpers(helpers) {
|
|
104
|
-
for (let index = 0; index < helpers.length; index++)
|
|
113
|
+
for (let index = 0; index < helpers.length; index++) {
|
|
114
|
+
deleteWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, undefined, context, helpers[index])
|
|
115
|
+
}
|
|
105
116
|
}
|
|
106
117
|
|
|
107
118
|
async function evaluateWithModules(prepared, helpers) {
|
|
@@ -132,7 +143,8 @@ async function evaluateWithModules(prepared, helpers) {
|
|
|
132
143
|
} finally { setDelete(adapterParents, adapter) }
|
|
133
144
|
if (load.global !== undefined) {
|
|
134
145
|
appendArray(helpers, load.global)
|
|
135
|
-
|
|
146
|
+
defineWorkerRealmProperty(WORKER_REALM_MUTATION.TEMPORARY, undefined,
|
|
147
|
+
context, load.global, { configurable: true, value: namespace })
|
|
136
148
|
}
|
|
137
149
|
}
|
|
138
150
|
return await evaluatePrepared(prepared.code, prepared.asyncCompletion)
|
|
@@ -152,7 +164,8 @@ function evaluatePrepared(javascript, asyncCompletion = false) {
|
|
|
152
164
|
protectWorkerReplAsyncContext(server)
|
|
153
165
|
workerReplContext(server)
|
|
154
166
|
restoreWorkerReplGlobals(workerGlobalBaseline, context)
|
|
155
|
-
|
|
167
|
+
defineWorkerRealmProperty(WORKER_REALM_MUTATION.STABLE, 'cell module metadata',
|
|
168
|
+
context, 'require', { configurable: true, value: providedRequire })
|
|
156
169
|
const errorDomain = disableWorkerReplDomain(server.eval.domain ?? createDomain())
|
|
157
170
|
const evaluator = { server, input, output, errorDomain }
|
|
158
171
|
retainedEvaluator = evaluator
|
|
@@ -205,7 +218,8 @@ try {
|
|
|
205
218
|
for (let index = 0; index < names.length; index++) {
|
|
206
219
|
const name = names[index]
|
|
207
220
|
setAdd(knownBindings, name)
|
|
208
|
-
|
|
221
|
+
defineWorkerRealmProperty(WORKER_REALM_MUTATION.USER, undefined,
|
|
222
|
+
context, name, { configurable: true, enumerable: true, get: () => namespace[name] })
|
|
209
223
|
}
|
|
210
224
|
parentPort.on('message', (message) => {
|
|
211
225
|
if (message?.type === WORKER_SHUTDOWN_REQUEST) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isPtcMessageSource, PTC_MESSAGE_SOURCE_KIND } from './message-sources.js'
|
|
1
2
|
import { isRecord } from './record-utils.js'
|
|
2
3
|
import { normalizeBindingModelContext } from './user-binding-model-context.js'
|
|
3
4
|
|
|
@@ -51,13 +52,13 @@ function normalizeAction(value) {
|
|
|
51
52
|
|
|
52
53
|
export function bindingActionNotice(action) {
|
|
53
54
|
return {
|
|
54
|
-
source: { kind:
|
|
55
|
+
source: { kind: PTC_MESSAGE_SOURCE_KIND, form: 'notice', summary: 'Global User Binding action' },
|
|
55
56
|
content: [{ type: 'text', text: ACTION_PREFIX + JSON.stringify(normalizeAction(action)) }],
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
export function readBindingAction(message) {
|
|
60
|
-
if (message?.source
|
|
61
|
+
if (!isPtcMessageSource(message?.source)
|
|
61
62
|
|| message.source.form !== 'notice' || message.source.summary !== 'Global User Binding action'
|
|
62
63
|
|| message.content?.length !== 1 || message.content[0]?.type !== 'text') return undefined
|
|
63
64
|
try {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PTC_MESSAGE_SOURCE_KIND } from './message-sources.js'
|
|
1
2
|
import { RPC_CONTRACTS } from './rpc-contract.js'
|
|
2
3
|
import { randomUUID } from 'node:crypto'
|
|
3
4
|
import { dirname } from 'node:path'
|
|
@@ -87,7 +88,7 @@ function authoringTask(command, current) {
|
|
|
87
88
|
function taskMessage(text) {
|
|
88
89
|
return createUserMessage({
|
|
89
90
|
content: [{ type: 'text', text }],
|
|
90
|
-
source: { kind:
|
|
91
|
+
source: { kind: PTC_MESSAGE_SOURCE_KIND, form: 'instructions' },
|
|
91
92
|
})
|
|
92
93
|
}
|
|
93
94
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto'
|
|
2
2
|
import { identifier, exportedSymbols, sourceDurability } from './compiler-service.js'
|
|
3
3
|
import { assertOwnFields, isRecord } from './record-utils.js'
|
|
4
|
+
import { boundedDefinitionSource } from './repl-memory-projection.js'
|
|
4
5
|
import { bindingModelPreferences, normalizeBindingModelContext } from './user-binding-model-context.js'
|
|
5
6
|
import { supportedUserBindingTransform, USER_BINDING_TRANSFORM } from './module-transform-contract.js'
|
|
6
7
|
|
|
@@ -296,7 +297,11 @@ export function userBindingCatalogEntries(snapshot) {
|
|
|
296
297
|
kind: binding.kind,
|
|
297
298
|
entryId: entry.id,
|
|
298
299
|
fingerprint: entry.fingerprint,
|
|
299
|
-
definition: Object.freeze({
|
|
300
|
+
definition: Object.freeze({
|
|
301
|
+
source: boundedDefinitionSource(binding.declaration),
|
|
302
|
+
line: 1,
|
|
303
|
+
column: 1,
|
|
304
|
+
}),
|
|
300
305
|
})))
|
|
301
306
|
}
|
|
302
307
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { EXPECTED_TARGET_CALL_SEQ, editRejectedCell } from './rejected-cell-editor.js'
|
|
2
2
|
|
|
3
3
|
const CLOSING_DELIMITERS = Object.freeze(['}', ')', ']'])
|
|
4
|
+
const CLOSING_SUFFIXES = Object.freeze([
|
|
5
|
+
...CLOSING_DELIMITERS,
|
|
6
|
+
...CLOSING_DELIMITERS.flatMap(first => CLOSING_DELIMITERS.map(second => first + second)),
|
|
7
|
+
])
|
|
4
8
|
const LINE_TERMINATORS = /\r\n|[\n\r\u2028\u2029]/gu
|
|
5
9
|
export const MAX_PARSE_REPAIR_ANCHOR_CODE_UNITS = 256
|
|
6
10
|
|
|
@@ -52,14 +56,14 @@ function renderInvocation(argumentsValue) {
|
|
|
52
56
|
return `edit_run_code(${json})`
|
|
53
57
|
}
|
|
54
58
|
|
|
55
|
-
/**
|
|
59
|
+
/** Requires exactly one valid suffix across all one- and two-token EOF closures. */
|
|
56
60
|
export function validatedEofClosureRepair({ source, position, prepare, targetCallSeq }) {
|
|
57
61
|
if (!Number.isSafeInteger(targetCallSeq) || targetCallSeq < 0
|
|
58
62
|
|| !isSourceEnd(source, position)) return undefined
|
|
59
63
|
const anchor = boundedUniqueSuffix(source)
|
|
60
64
|
if (anchor === undefined) return undefined
|
|
61
65
|
const candidates = []
|
|
62
|
-
for (const delimiter of
|
|
66
|
+
for (const delimiter of CLOSING_SUFFIXES) {
|
|
63
67
|
const code = source + delimiter
|
|
64
68
|
let prepared
|
|
65
69
|
try {
|