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.
- package/README.en.md +164 -78
- package/README.md +165 -79
- 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 +854 -336
- package/compiler-core.cjs +161 -157
- package/compiler-core.cjs.map +3 -3
- package/docs/README.md +4 -1
- package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +8 -7
- 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/0019-plugin-settings-and-kill-switch.md +3 -3
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -0
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +14 -3
- package/docs/adr/0023-global-user-bindings.md +7 -7
- package/docs/adr/0025-use-versioned-logical-binding-identities.md +3 -3
- 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 +9 -5
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +26 -6
- package/docs/adr/0031-close-semantic-obligations-by-owner.md +29 -0
- package/docs/adr/README.md +4 -4
- package/docs/architecture.md +15 -10
- package/docs/binding-review-placement-design.md +1 -1
- package/docs/client-ui.md +9 -5
- package/docs/durability-design.md +12 -9
- package/docs/installation.md +27 -9
- package/docs/runtime-reference.md +23 -12
- package/docs/semantic-validation.md +7 -3
- package/docs/stateful-binding-updates-proposal.md +8 -4
- package/docs/user-bindings.en.md +10 -7
- package/docs/user-bindings.md +9 -6
- package/docs/value-wire.md +1 -1
- package/docs/verification-optimization-work.md +41 -0
- package/docs/verification.md +53 -2
- package/index.js +39 -34
- package/internal/async-local-storage-intrinsics.js +43 -0
- package/internal/binding-declaration-contract.js +8 -0
- package/internal/binding-source-analysis.js +381 -64
- package/internal/binding-update-policy.js +7 -1
- package/internal/callable-source-facts.js +37 -1
- package/internal/cell-analysis.js +85 -17
- package/internal/compiler-intrinsics.js +9 -43
- package/internal/compiler-module-links.js +7 -4
- package/internal/config-spec.js +1 -1
- package/internal/cordis-tools-owner.js +20 -10
- package/internal/dynamic-environment-integration.js +8 -4
- package/internal/dynamic-environment-runtime.js +24 -3
- package/internal/dynamic-native-calls.js +4 -1
- package/internal/dynamic-native-runtime.js +15 -1
- package/internal/edit-transport-owner.js +80 -20
- package/internal/execution-seam-compat.js +192 -30
- package/internal/failure-reporting.js +38 -30
- package/internal/isolated-worker.js +17 -10
- package/internal/kernel-worker.js +645 -393
- package/internal/message-sources.js +14 -0
- package/internal/module-compilation.js +7 -5
- package/internal/module-transform-contract.js +32 -2
- package/internal/record-utils.js +24 -15
- package/internal/repl-memory-projection.js +10 -0
- package/internal/repl-scope-normalizer.js +72 -35
- package/internal/repl-value-observer.js +145 -46
- package/internal/runtime-config.js +62 -2
- package/internal/runtime-intrinsics.js +104 -0
- package/internal/runtime-messages.js +6 -4
- package/internal/session-cell-executor.js +26 -2
- package/internal/session-journal-recovery.js +267 -67
- package/internal/session-journal-schema.js +30 -13
- package/internal/session-journal.js +485 -44
- package/internal/session-log-view.js +5 -4
- package/internal/session-runtime.js +46 -21
- package/internal/session-state.js +7 -29
- package/internal/settings-compat.js +66 -1
- package/internal/source-position-map.js +38 -0
- package/internal/stateful-root-compiler.js +3 -1
- package/internal/typescript-transform.js +4 -2
- package/internal/user-binding-console-worker.js +115 -41
- package/internal/user-binding-draft-projection.js +3 -2
- package/internal/user-bindings-owner.js +2 -1
- package/internal/user-bindings.js +16 -3
- package/internal/validated-parse-repair.js +6 -2
- package/internal/value-wire.js +189 -94
- package/internal/worker-client.js +99 -8
- package/internal/worker-cwd-virtualization.js +82 -54
- package/internal/worker-environment.js +20 -0
- 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 +140 -0
- package/package.json +37 -4
- package/src/client-activity.js +12 -2
- 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 +4 -3
- package/src/client.js +56 -17
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
foldSessionTimeline,
|
|
3
3
|
} from './session-journal-recovery.js'
|
|
4
|
+
import { isCanonicalSequence } from './session-journal.js'
|
|
4
5
|
import { sessionEvents } from './session-events.js'
|
|
5
6
|
import { readRuntimeMessage } from './runtime-messages.js'
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
import { isHostRuntimeContextSource } from './message-sources.js'
|
|
8
9
|
const SYSTEM_PROMPT_CLEARED = 'Current runtime context: none. Earlier runtime-context snapshots no longer apply.'
|
|
9
10
|
|
|
10
11
|
/** DSH's empty aggregate uses an exact owner marker without a snapshot form. */
|
|
11
12
|
export function systemPromptSnapshotSections(message) {
|
|
12
13
|
const source = message?.source
|
|
13
|
-
if (source
|
|
14
|
+
if (!isHostRuntimeContextSource(source)) return undefined
|
|
14
15
|
if (source.form === undefined && Array.isArray(message.content) && message.content.length === 1
|
|
15
16
|
&& message.content[0]?.type === 'text' && message.content[0].text === SYSTEM_PROMPT_CLEARED) {
|
|
16
17
|
return Object.freeze([])
|
|
@@ -101,7 +102,7 @@ export function projectSessionLog(agent, requestedEdit) {
|
|
|
101
102
|
try {
|
|
102
103
|
const nodes = agent?.session?.surface?.nodes
|
|
103
104
|
const knownSeqs = new Set(events.map(event => event?.seq))
|
|
104
|
-
if (Array.isArray(nodes) && nodes.every(seq =>
|
|
105
|
+
if (Array.isArray(nodes) && nodes.every(seq => isCanonicalSequence(seq) && knownSeqs.has(seq))) {
|
|
105
106
|
visibleRuntimeMessages = Object.freeze(nodes.flatMap(seq => {
|
|
106
107
|
const record = runtimeMessagesBySeq.get(seq)
|
|
107
108
|
return record === undefined ? [] : [record]
|
|
@@ -126,6 +127,6 @@ export function projectSessionLog(agent, requestedEdit) {
|
|
|
126
127
|
/** Return the target snapshot captured at one persisted edit call event. */
|
|
127
128
|
export function editTargetForCall(agent, callId, callSeq) {
|
|
128
129
|
if (typeof callId !== 'string' || callId.length === 0
|
|
129
|
-
|| !
|
|
130
|
+
|| !isCanonicalSequence(callSeq)) return undefined
|
|
130
131
|
return projectSessionLog(agent, { callId, callSeq }).requestedEditTarget
|
|
131
132
|
}
|
|
@@ -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),
|
|
@@ -713,6 +737,7 @@ export class SessionRuntime {
|
|
|
713
737
|
policies.bindingPolicy,
|
|
714
738
|
policies.rewritesEnabled,
|
|
715
739
|
policies.languageSemantics,
|
|
740
|
+
policies.moduleTransform,
|
|
716
741
|
)
|
|
717
742
|
const workerReservation = kernel.reserveWorkerConfiguration(cellConfig)
|
|
718
743
|
let result
|
|
@@ -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) {
|
|
@@ -117,6 +117,25 @@ export function sourceOffsetAt(sourceMap, generatedOffset) {
|
|
|
117
117
|
return segmentOffsetAt(segment, generatedOffset)
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
/** Whether a complete generated interval still contains its exact original text. */
|
|
121
|
+
export function sourceRangeHasOriginalText(sourceMap, generatedSource, originalSource, generatedStart, generatedEnd) {
|
|
122
|
+
if (typeof generatedSource !== 'string' || typeof originalSource !== 'string'
|
|
123
|
+
|| generatedEnd <= generatedStart || generatedStart < 0 || generatedEnd > generatedSource.length) return false
|
|
124
|
+
let cursor = generatedStart
|
|
125
|
+
for (let index = segmentIndexAt(sourceMap, generatedStart); index < sourceMap.length && cursor < generatedEnd; index += 1) {
|
|
126
|
+
const segment = sourceMap.at(index)
|
|
127
|
+
if (segment.generatedStart > cursor || segment.generatedEnd <= cursor) break
|
|
128
|
+
const end = Math.min(generatedEnd, segment.generatedEnd)
|
|
129
|
+
const length = end - cursor
|
|
130
|
+
const originalStart = segmentOffsetAt(segment, cursor)
|
|
131
|
+
if (segment.originalEnd <= segment.originalStart
|
|
132
|
+
|| originalStart + length > originalSource.length
|
|
133
|
+
|| generatedSource.slice(cursor, end) !== originalSource.slice(originalStart, originalStart + length)) return false
|
|
134
|
+
cursor = end
|
|
135
|
+
}
|
|
136
|
+
return cursor === generatedEnd
|
|
137
|
+
}
|
|
138
|
+
|
|
120
139
|
function segmentOffsetAt(segment, generatedOffset) {
|
|
121
140
|
const generatedLength = segment.generatedEnd - segment.generatedStart
|
|
122
141
|
const originalLength = segment.originalEnd - segment.originalStart
|
|
@@ -268,6 +287,25 @@ export function sourceLineStarts(source) {
|
|
|
268
287
|
return starts
|
|
269
288
|
}
|
|
270
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
|
+
|
|
271
309
|
/** Visit maintained emitter coordinates without re-encoding available raw maps.
|
|
272
310
|
* All line numbers at this boundary are zero-based; absent origins stay absent. */
|
|
273
311
|
export function visitSourceMappings(transformed, visit) {
|
|
@@ -191,8 +191,10 @@ function rewriteRootReferences(code, sourceMap, runtime, candidateNames, candida
|
|
|
191
191
|
}
|
|
192
192
|
handled.add(path.node.start)
|
|
193
193
|
} })
|
|
194
|
+
const classificationSource = applySourceEdits(code, sourceMap, classificationEdits)
|
|
194
195
|
return { ...applySourceEdits(code, sourceMap, edits),
|
|
195
|
-
classificationCode:
|
|
196
|
+
classificationCode: classificationSource.code,
|
|
197
|
+
classificationSourceMap: classificationSource.sourceMap,
|
|
196
198
|
implicitDeclarations: [...implicitDeclarations.values()],
|
|
197
199
|
readNames: [...readNames] }
|
|
198
200
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import loadAmaro from './compiler-amaro.cjs'
|
|
2
2
|
|
|
3
|
-
export { LEGACY_USER_BINDING_TRANSFORM,
|
|
4
|
-
|
|
3
|
+
export { LEGACY_USER_BINDING_TRANSFORM, PREVIOUS_USER_BINDING_TRANSFORM, USER_BINDING_TRANSFORM,
|
|
4
|
+
PROTECTED_MODULE_TRANSFORM, isStatefulUserBindingTransform,
|
|
5
|
+
historicalModuleTransformForLanguage, moduleTransformForLanguage, moduleTransformMatchesLanguage,
|
|
6
|
+
normalizeModuleTransform, supportedUserBindingTransform } from './module-transform-contract.js'
|
|
5
7
|
|
|
6
8
|
/** Keep binding-module lowering independent of the Host's experimental transform API. */
|
|
7
9
|
export function transformTypeScriptModule(source) {
|