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.
- package/README.en.md +166 -76
- package/README.md +167 -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 +593 -306
- package/compiler-core.cjs +162 -160
- package/compiler-core.cjs.map +3 -3
- package/docs/README.md +3 -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 +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/0031-close-semantic-obligations-by-owner.md +29 -0
- package/docs/adr/README.md +4 -4
- package/docs/architecture.md +6 -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 +26 -8
- package/docs/runtime-reference.md +18 -11
- 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 +38 -1
- 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 +20 -10
- 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/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 +36 -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 +4 -3
- package/src/client.js +56 -17
package/index.js
CHANGED
|
@@ -14,12 +14,12 @@ import { PTC_DELIVERY_CONTEXT } from './internal/runtime-messages.js'
|
|
|
14
14
|
import { createCordisToolsOwner } from './internal/cordis-tools-owner.js'
|
|
15
15
|
import { createEditTransportOwner, EDIT_RUN_CODE } from './internal/edit-transport-owner.js'
|
|
16
16
|
import { createRuntimeBridgeOwner, RUN_CODE } from './internal/runtime-bridge-owner.js'
|
|
17
|
-
import { resolveConfig } from './internal/runtime-config.js'
|
|
17
|
+
import { resolveConfig, watchVolatileConfig } from './internal/runtime-config.js'
|
|
18
18
|
import {
|
|
19
19
|
CONFIG_FIELDS,
|
|
20
20
|
SETTINGS_NAMESPACE,
|
|
21
21
|
} from './internal/config-spec.js'
|
|
22
|
-
import { installSettingsSectionCompat } from './internal/settings-compat.js'
|
|
22
|
+
import { configFieldSchema, installSettingsSectionCompat } from './internal/settings-compat.js'
|
|
23
23
|
import { installExecutionSeam } from './internal/execution-seam-compat.js'
|
|
24
24
|
import { valueLimitsFromConfig } from './internal/value-wire-schema.js'
|
|
25
25
|
import { createReplMemoryProjection } from './internal/repl-memory-projection.js'
|
|
@@ -35,19 +35,13 @@ const INSTALL_CLEANUP = Symbol('ptc-plus install cleanup')
|
|
|
35
35
|
/** Plugin name used by loader diagnostics. */
|
|
36
36
|
export const name = 'ptc-plus'
|
|
37
37
|
|
|
38
|
-
/** Runtime limits and behavior exposed to Cordis configuration. */
|
|
39
|
-
function configSchemaField(field) {
|
|
40
|
-
const base = field.type === 'boolean'
|
|
41
|
-
? Schema.boolean().default(field.default)
|
|
42
|
-
: field.type === 'enum'
|
|
43
|
-
// Keep omission visible until resolveConfig migrates legacy policies.
|
|
44
|
-
? Schema.union(field.options.map(option => Schema.const(option)))
|
|
45
|
-
: Schema.number().step(1).min(field.min).max(field.max).default(field.default)
|
|
46
|
-
return base.description(field.description)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
38
|
export const Config = Schema.object(Object.fromEntries(
|
|
50
|
-
CONFIG_FIELDS.map(field => [
|
|
39
|
+
CONFIG_FIELDS.map(field => [
|
|
40
|
+
field.key,
|
|
41
|
+
// Runtime limits and behavior exposed to Cordis configuration. The shape
|
|
42
|
+
// each field takes is owned by the installed settings generation.
|
|
43
|
+
configFieldSchema({ Schema, field, settingsModule: dshSettings }),
|
|
44
|
+
]),
|
|
51
45
|
))
|
|
52
46
|
|
|
53
47
|
/**
|
|
@@ -408,22 +402,27 @@ function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessi
|
|
|
408
402
|
if (exec.name === EDIT_RUN_CODE) return editTransport.handleResult(exec, result)
|
|
409
403
|
if (exec.name === RUN_CODE) return runtimeBridge.handleResult(exec, result)
|
|
410
404
|
}))
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
405
|
+
const disposeLifecycleOwners = async (operations, message) => {
|
|
406
|
+
const results = await Promise.allSettled(operations.map(operation => (
|
|
407
|
+
Promise.resolve().then(operation)
|
|
408
|
+
)))
|
|
409
|
+
const failures = results
|
|
410
|
+
.filter(result => result.status === 'rejected')
|
|
411
|
+
.map(result => result.reason)
|
|
412
|
+
if (failures.length > 0) throw new AggregateError(failures, message)
|
|
413
|
+
}
|
|
414
|
+
disposers.push(ctx.on('agent/disposed', ({ agent }) => disposeLifecycleOwners([
|
|
415
|
+
() => directSurface.disposeAgent(agent),
|
|
416
|
+
() => editTransport.disposeAgent(agent),
|
|
417
|
+
() => runtimeBridge.disposeAgent(agent),
|
|
418
|
+
() => userBindings.clearAgentPresentation(agent),
|
|
419
|
+
], 'ptc-plus: agent disposal failed')))
|
|
420
|
+
disposers.push(ctx.on('session/disposed', session => disposeLifecycleOwners([
|
|
421
|
+
() => directSurface.disposeSession(session),
|
|
422
|
+
() => editTransport.disposeSession(session),
|
|
423
|
+
() => runtimeBridge.disposeSession(session),
|
|
424
|
+
() => userBindings.clearSessionPresentation(session?.id ?? session),
|
|
425
|
+
], 'ptc-plus: session disposal failed')))
|
|
427
426
|
disposers.push(ctx.on('agent-preset/selected', (sessionId) => {
|
|
428
427
|
directSurface.resetSessionComposition(sessionId)
|
|
429
428
|
const agent = ctx.agents?.get?.(String(sessionId))
|
|
@@ -512,18 +511,22 @@ function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessi
|
|
|
512
511
|
}
|
|
513
512
|
|
|
514
513
|
/** Register the session-bound REPL runtime. */
|
|
515
|
-
export
|
|
514
|
+
export const apply = (ctx, config = {}) => {
|
|
516
515
|
// Configuration is validated before the plugin waits for the host's seam, so
|
|
517
516
|
// an unusable configuration fails at the load call rather than later.
|
|
518
517
|
const resolvedConfig = resolveConfig(config)
|
|
519
518
|
return installExecutionSeam(ctx, {
|
|
520
519
|
services: inject,
|
|
521
|
-
attach: (scope, seam) => applyWithExecutionSeam(scope, resolvedConfig, seam),
|
|
520
|
+
attach: (scope, seam) => applyWithExecutionSeam(scope, resolvedConfig, seam, ctx, config),
|
|
521
|
+
reportFailure: (error, seamName) => ctx.logger?.warn?.(
|
|
522
|
+
`ptc-plus: execution seam ${seamName} failed to attach`,
|
|
523
|
+
error,
|
|
524
|
+
),
|
|
522
525
|
})
|
|
523
526
|
}
|
|
524
527
|
|
|
525
528
|
/** Install the plugin into the scope of the seam the host registered. */
|
|
526
|
-
function applyWithExecutionSeam(ctx, resolvedConfig, seam) {
|
|
529
|
+
function applyWithExecutionSeam(ctx, resolvedConfig, seam, volatileHost, volatileConfig) {
|
|
527
530
|
const toolSchemasForAgent = agent => typeof ctx.tools.schemas === 'function'
|
|
528
531
|
? ctx.tools.schemas(agent)
|
|
529
532
|
: []
|
|
@@ -767,7 +770,7 @@ function applyWithExecutionSeam(ctx, resolvedConfig, seam) {
|
|
|
767
770
|
ctx.effect(() => ctx.on('agent/disposed', ({ agent }) => messageOwner.disposeAgent(agent)), 'ptc-plus message agent disposal')
|
|
768
771
|
ctx.effect(() => () => messageOwner.dispose(), 'ptc-plus dynamic message lifecycle')
|
|
769
772
|
|
|
770
|
-
let configSource = () => resolvedConfig
|
|
773
|
+
let configSource = () => resolveConfig(volatileConfig ?? resolvedConfig)
|
|
771
774
|
let configurationGeneration = 0
|
|
772
775
|
let settingsWriter
|
|
773
776
|
let configurationRollback = false
|
|
@@ -826,6 +829,7 @@ function applyWithExecutionSeam(ctx, resolvedConfig, seam) {
|
|
|
826
829
|
if (typeof ctx.inject === 'function') {
|
|
827
830
|
installSettingsSectionCompat({
|
|
828
831
|
ctx,
|
|
832
|
+
ownerFiber: volatileHost.fiber,
|
|
829
833
|
settingsModule: dshSettings,
|
|
830
834
|
namespace: SETTINGS_NAMESPACE,
|
|
831
835
|
schema: Config,
|
|
@@ -841,5 +845,6 @@ function applyWithExecutionSeam(ctx, resolvedConfig, seam) {
|
|
|
841
845
|
},
|
|
842
846
|
})
|
|
843
847
|
}
|
|
848
|
+
watchVolatileConfig(volatileHost, ctx, volatileConfig, () => reconcile())
|
|
844
849
|
if (configurationGeneration === 0) return reconcile(settingsWriter === undefined)
|
|
845
850
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
2
|
+
import { WORKER_REALM_MUTATION, defineWorkerRealmProperty } from './worker-realm-surfaces.js'
|
|
2
3
|
|
|
3
4
|
const bind = Function.prototype.call.bind(Function.prototype.bind)
|
|
4
|
-
const defineProperty = Object.defineProperty
|
|
5
5
|
const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor
|
|
6
6
|
const apply = Reflect.apply
|
|
7
7
|
const nativeGetStore = AsyncLocalStorage.prototype.getStore
|
|
@@ -10,9 +10,12 @@ const nativeRun = AsyncLocalStorage.prototype.run
|
|
|
10
10
|
const nativeRunDescriptor = getOwnPropertyDescriptor(AsyncLocalStorage.prototype, 'run')
|
|
11
11
|
|
|
12
12
|
function protect(scope) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
defineWorkerRealmProperty(WORKER_REALM_MUTATION.STABLE, 'private AsyncLocalStorage receiver methods',
|
|
14
|
+
scope, 'getStore', { value: bind(nativeGetStore, scope) })
|
|
15
|
+
defineWorkerRealmProperty(WORKER_REALM_MUTATION.STABLE, 'private AsyncLocalStorage receiver methods',
|
|
16
|
+
scope, 'enterWith', { value: bind(nativeEnterWith, scope) })
|
|
17
|
+
defineWorkerRealmProperty(WORKER_REALM_MUTATION.STABLE, 'private AsyncLocalStorage receiver methods',
|
|
18
|
+
scope, 'run', { value: bind(nativeRun, scope) })
|
|
16
19
|
return scope
|
|
17
20
|
}
|
|
18
21
|
|
|
@@ -22,17 +25,19 @@ export function createPrivateAsyncLocalStorage() {
|
|
|
22
25
|
|
|
23
26
|
export function protectNextAsyncLocalStorageRun(invoke) {
|
|
24
27
|
let scope
|
|
25
|
-
|
|
28
|
+
defineWorkerRealmProperty(WORKER_REALM_MUTATION.TEMPORARY, 'AsyncLocalStorage.prototype.run',
|
|
29
|
+
AsyncLocalStorage.prototype, 'run', {
|
|
26
30
|
...nativeRunDescriptor,
|
|
27
31
|
value: function (...args) {
|
|
28
32
|
scope ??= protect(this)
|
|
29
33
|
return apply(nativeRun, this, args)
|
|
30
34
|
},
|
|
31
|
-
|
|
35
|
+
})
|
|
32
36
|
try {
|
|
33
37
|
invoke()
|
|
34
38
|
} finally {
|
|
35
|
-
|
|
39
|
+
defineWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, 'AsyncLocalStorage.prototype.run',
|
|
40
|
+
AsyncLocalStorage.prototype, 'run', nativeRunDescriptor)
|
|
36
41
|
}
|
|
37
42
|
return scope
|
|
38
43
|
}
|
|
@@ -43,6 +43,16 @@ function callableSourceStart(source, node) {
|
|
|
43
43
|
return start + tokens.getToken().start
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
function removeCallableSourceMarks(source, prefix, suffix, sources, options) {
|
|
47
|
+
const tree = parse(prefix + source + suffix, options)
|
|
48
|
+
const markers = new Set(sources.entries.map(entry => entry.marker))
|
|
49
|
+
const offset = prefix.length
|
|
50
|
+
const edits = tree.comments.filter(comment => markers.has(comment.value)
|
|
51
|
+
&& comment.start >= offset && comment.end <= offset + source.length)
|
|
52
|
+
.map(comment => ({ start: comment.start - offset, end: comment.end - offset, text: '' }))
|
|
53
|
+
return edits.length === 0 ? source : applySourceEdits(source, identitySourceMap(source.length), edits).code
|
|
54
|
+
}
|
|
55
|
+
|
|
46
56
|
export function callableMarkerPosition(node, source, comments) {
|
|
47
57
|
if (node.type !== 'ArrowFunctionExpression') return node.body.start + 1
|
|
48
58
|
let start = source.lastIndexOf('=>', node.body.start - 1)
|
|
@@ -129,6 +139,7 @@ export function markCallableSources(source, sourceMap = identitySourceMap(source
|
|
|
129
139
|
const nativeFunctions = typedRecords.map(record => markedNodes.get(record.marker))
|
|
130
140
|
.filter(node => t.isFunction(node) && !isMethod(node)).sort((left, right) => left.start - right.start)
|
|
131
141
|
const nativeRecipes = new Map()
|
|
142
|
+
const aliasMarkers = []
|
|
132
143
|
const nativeFunctionIndex = start => {
|
|
133
144
|
let low = 0, high = nativeFunctions.length
|
|
134
145
|
while (low < high) {
|
|
@@ -175,7 +186,17 @@ export function markCallableSources(source, sourceMap = identitySourceMap(source
|
|
|
175
186
|
original = lowered.source
|
|
176
187
|
const entries = [...originals].map(([key, text]) => [key, key === marker ? original : text])
|
|
177
188
|
for (const [node, fact] of markedCallableOwners(lowered.emitted, entries)) {
|
|
178
|
-
|
|
189
|
+
const generated = lowered.emittedSource.slice(node.start, node.end)
|
|
190
|
+
aliasMarkers.push([generated, fact.marker])
|
|
191
|
+
const generatedMethod = isMethod(node)
|
|
192
|
+
const markerFree = removeCallableSourceMarks(
|
|
193
|
+
generated,
|
|
194
|
+
generatedMethod ? 'class Source {' : '(',
|
|
195
|
+
generatedMethod ? '}' : ')',
|
|
196
|
+
sources,
|
|
197
|
+
options,
|
|
198
|
+
)
|
|
199
|
+
if (markerFree !== generated) aliasMarkers.push([markerFree, fact.marker])
|
|
179
200
|
}
|
|
180
201
|
}
|
|
181
202
|
// Prefer source-preserving erasure. Runtime TypeScript constructs such as
|
|
@@ -202,6 +223,7 @@ export function markCallableSources(source, sourceMap = identitySourceMap(source
|
|
|
202
223
|
} catch {
|
|
203
224
|
lowerCallable()
|
|
204
225
|
}
|
|
226
|
+
original = removeCallableSourceMarks(original, prefix, suffix, sources, options)
|
|
205
227
|
entry.buffer = sources.buffers.length
|
|
206
228
|
sources.buffers.push(original)
|
|
207
229
|
entry.start = 0
|
|
@@ -209,6 +231,8 @@ export function markCallableSources(source, sourceMap = identitySourceMap(source
|
|
|
209
231
|
originals.set(marker, original)
|
|
210
232
|
if (!method && t.isFunction(node)) nativeRecipes.set(node, { node, start, end: node.end, text: original })
|
|
211
233
|
}
|
|
234
|
+
const finalFacts = factMap(sources)
|
|
235
|
+
for (const [generated, marker] of aliasMarkers) sources.aliases.push([generated, originalText(finalFacts.get(marker))])
|
|
212
236
|
return { ...marked, callableSources: sources, nativeJavaScript }
|
|
213
237
|
}
|
|
214
238
|
|
|
@@ -273,6 +297,18 @@ export function collectCallableSources(code, sources, parserOptions = {}, transf
|
|
|
273
297
|
}).concat(sources.aliases ?? [])
|
|
274
298
|
}
|
|
275
299
|
|
|
300
|
+
/** Restore only callable marks whose transformed starts have exact source-map ownership. */
|
|
301
|
+
export function restoreMappedCallableSourceMarks(mapped, input, transformation, sources, parserOptions = {}) {
|
|
302
|
+
if (sources === undefined || sources.length === 0) return mapped
|
|
303
|
+
const { tree, owners } = callableSourceOwners(mapped.code, sources, parserOptions,
|
|
304
|
+
{ code: input, map: transformation.map })
|
|
305
|
+
const edits = [...owners].map(([node, fact]) => {
|
|
306
|
+
const start = callableMarkerPosition(node, mapped.code, tree.comments)
|
|
307
|
+
return { start, end: start, text: `/*${fact.marker}*/` }
|
|
308
|
+
})
|
|
309
|
+
return edits.length === 0 ? mapped : applySourceEdits(mapped.code, mapped.sourceMap, edits)
|
|
310
|
+
}
|
|
311
|
+
|
|
276
312
|
/** Emission ranges come from typed nodes and exact generator mappings. */
|
|
277
313
|
export function regionCallableRanges(input, generated, sourceRanges, parserOptions) {
|
|
278
314
|
if (!sourceRanges?.length) return []
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
import { applySourceEdits, mapSourcePosition, mapSourceSpan, sourceRangeHasOriginalText } from './source-position-map.js'
|
|
21
21
|
import { SKIP_AST_CHILDREN, walkAst } from './ast-traversal.js'
|
|
22
22
|
import { compileStatefulRoot } from './stateful-root-compiler.js'
|
|
23
|
-
import { CELL_PARSER_PLUGINS, bindCompilerIntrinsics, lowerStatefulDecorators, lowerStatefulResources, lowerNativeLanguageSource, normalizeStatefulScopes } from './repl-scope-normalizer.js'
|
|
23
|
+
import { CELL_PARSER_PLUGINS, bindCompilerIntrinsics, lowerStatefulDecorators, lowerStatefulResources, lowerNativeLanguageSource, normalizationFailureInput, normalizeStatefulScopes } from './repl-scope-normalizer.js'
|
|
24
24
|
import { markCallableSources, collectCallableSources } from './callable-source-facts.js'
|
|
25
25
|
import { LegacyPreflightError, prepareLegacyProgram } from './legacy-cell-analysis.js'
|
|
26
26
|
import { adaptLegacyModuleImports } from './managed-module-operations.js'
|
|
@@ -220,6 +220,49 @@ function isFunction(node) {
|
|
|
220
220
|
|| node.type === 'ArrowFunctionExpression'
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
function lowerCellImportMeta(result, importMeta, originalSource) {
|
|
224
|
+
if (!result.code.includes('import')) return result
|
|
225
|
+
const tree = parseSource(result.code, {
|
|
226
|
+
sourceType: 'unambiguous',
|
|
227
|
+
errorRecovery: true,
|
|
228
|
+
allowAwaitOutsideFunction: true,
|
|
229
|
+
allowImportExportEverywhere: true,
|
|
230
|
+
allowReturnOutsideFunction: true,
|
|
231
|
+
allowUndeclaredExports: true,
|
|
232
|
+
plugins: CELL_PARSER_PLUGINS,
|
|
233
|
+
}).program
|
|
234
|
+
const references = []
|
|
235
|
+
walkAst(tree, (node) => {
|
|
236
|
+
if (node.type === 'MetaProperty' && node.meta?.name === 'import' && node.property?.name === 'meta') {
|
|
237
|
+
references.push(node)
|
|
238
|
+
}
|
|
239
|
+
})
|
|
240
|
+
if (references.length === 0) return result
|
|
241
|
+
if (importMeta === null || typeof importMeta !== 'object'
|
|
242
|
+
|| typeof importMeta.url !== 'string' || typeof importMeta.filename !== 'string'
|
|
243
|
+
|| typeof importMeta.dirname !== 'string') {
|
|
244
|
+
const node = references[0]
|
|
245
|
+
throw new ModuleRewriteError('import.meta requires the session module base', mapSourcePosition({
|
|
246
|
+
line: node.loc.start.line,
|
|
247
|
+
column: node.loc.start.column + 1,
|
|
248
|
+
}, result.code, originalSource, result.sourceMap))
|
|
249
|
+
}
|
|
250
|
+
const binding = createGeneratedNameAllocator(tree)('import_meta')
|
|
251
|
+
const directiveEnd = tree.directives.at(-1)?.end ?? tree.interpreter?.end ?? 0
|
|
252
|
+
const declaration = `\nconst ${binding}={__proto__:null,url:${JSON.stringify(importMeta.url)},filename:${JSON.stringify(importMeta.filename)},dirname:${JSON.stringify(importMeta.dirname)}};\n`
|
|
253
|
+
const edits = [
|
|
254
|
+
{ start: directiveEnd, end: directiveEnd, text: declaration },
|
|
255
|
+
...references.map(node => ({
|
|
256
|
+
start: node.start,
|
|
257
|
+
end: node.end,
|
|
258
|
+
text: binding,
|
|
259
|
+
mappings: [{ generatedStart: 0, generatedEnd: binding.length,
|
|
260
|
+
originalStart: node.start, originalEnd: node.end }],
|
|
261
|
+
})),
|
|
262
|
+
]
|
|
263
|
+
return { ...result, ...applySourceEdits(result.code, result.sourceMap, edits), importMetaBinding: binding }
|
|
264
|
+
}
|
|
265
|
+
|
|
223
266
|
|
|
224
267
|
function staticModuleClassification(moduleLoads) {
|
|
225
268
|
const reasons = new Map()
|
|
@@ -282,14 +325,16 @@ export function classifyDurability(code, knownBindings = new Set(), {
|
|
|
282
325
|
const reasons = new Map()
|
|
283
326
|
const addReason = reason => reasons.set(renderDurabilityReason(reason), reason)
|
|
284
327
|
for (const reason of staticModuleClassification(moduleLoads)) addReason(reason)
|
|
285
|
-
const classifyModule = (source) => {
|
|
328
|
+
const classifyModule = (source, form) => {
|
|
286
329
|
if (source?.type !== 'Literal' || typeof source.value !== 'string') {
|
|
287
330
|
addReason(DYNAMIC_MODULE_REASON)
|
|
288
331
|
return
|
|
289
332
|
}
|
|
290
333
|
const classification = classifyModuleSource(source.value)
|
|
291
334
|
if (classification.status === 'forbidden') {
|
|
292
|
-
|
|
335
|
+
const span = sourceMap === undefined || originalSource === undefined ? declarationSpan(source)
|
|
336
|
+
: mapSourceSpan(declarationSpan(source), code, originalSource, sourceMap)
|
|
337
|
+
throw new PreflightError(`cell ${form} of ${source.value} is forbidden because it exposes kernel control`, undefined, span)
|
|
293
338
|
}
|
|
294
339
|
if (classification.reason !== undefined) addReason(classification.reason)
|
|
295
340
|
}
|
|
@@ -327,7 +372,7 @@ export function classifyDurability(code, knownBindings = new Set(), {
|
|
|
327
372
|
nestedScopes = [...scopes, loopBindings(node)]
|
|
328
373
|
}
|
|
329
374
|
if (node.type === 'ImportExpression') {
|
|
330
|
-
classifyModule(node.source)
|
|
375
|
+
classifyModule(node.source, 'import')
|
|
331
376
|
}
|
|
332
377
|
const sourceOwnedThis = sourceMap === undefined
|
|
333
378
|
|| sourceRangeHasOriginalText(sourceMap, code, originalSource, node.start, node.end)
|
|
@@ -336,7 +381,7 @@ export function classifyDurability(code, knownBindings = new Set(), {
|
|
|
336
381
|
}
|
|
337
382
|
if (node.type === 'CallExpression' && node.callee?.type === 'Identifier' && node.callee.name === 'require'
|
|
338
383
|
&& !isBound('require', nestedScopes)) {
|
|
339
|
-
classifyModule(node.arguments[0])
|
|
384
|
+
classifyModule(node.arguments[0], 'require')
|
|
340
385
|
}
|
|
341
386
|
if (node.type === 'Identifier' && isReferenceIdentifier(node, parent, parentKey)
|
|
342
387
|
&& !isBound(node.name, nestedScopes) && AMBIENT_GLOBALS.has(node.name)
|
|
@@ -462,21 +507,31 @@ export function prepareProgram(program, options = {}) {
|
|
|
462
507
|
let normalized
|
|
463
508
|
let prepared
|
|
464
509
|
let callableSources
|
|
510
|
+
let diagnosticInput
|
|
465
511
|
const intrinsicContext = { bindings: new Set() }
|
|
466
512
|
try {
|
|
467
513
|
const marked = markCallableSources(program, undefined, { plugins: CELL_PARSER_PLUGINS, allowImportExportEverywhere: true, allowUndeclaredExports: true },
|
|
468
514
|
{ lowerNativeSource: lowerNativeLanguageSource, nativeUsing: options.nativeUsing })
|
|
515
|
+
const moduleMetadata = lowerCellImportMeta(marked, options.importMeta, program)
|
|
516
|
+
diagnosticInput = moduleMetadata
|
|
469
517
|
callableSources = marked.callableSources
|
|
470
|
-
normalized = normalizeStatefulScopes(
|
|
471
|
-
nativeJavaScript: marked.nativeJavaScript,
|
|
518
|
+
normalized = normalizeStatefulScopes(moduleMetadata.code, moduleMetadata.sourceMap, { mode: options.languageSemantics,
|
|
519
|
+
callableSources, nativeJavaScript: marked.nativeJavaScript,
|
|
520
|
+
deferDecorators: true, deferResources: true, intrinsicContext })
|
|
521
|
+
if (moduleMetadata.importMetaBinding !== undefined) {
|
|
522
|
+
normalized.internalBindings = new Set(normalized.internalBindings)
|
|
523
|
+
normalized.internalBindings.add(moduleMetadata.importMetaBinding)
|
|
524
|
+
}
|
|
525
|
+
diagnosticInput = normalized
|
|
472
526
|
prepared = compileStatefulRoot(normalized.code, { ...options, sourceMap: normalized.sourceMap,
|
|
473
527
|
internalBindings: normalized.internalBindings, dynamicBindings: normalized.dynamicBindings,
|
|
474
528
|
privateBindings: normalized.privateBindings, originalSource: program })
|
|
475
529
|
} catch (error) {
|
|
476
530
|
if (error instanceof ModuleRewriteError) throw error
|
|
477
531
|
const position = error.loc === undefined ? undefined : { line: error.loc.line, column: error.loc.column + 1 }
|
|
478
|
-
|
|
479
|
-
|
|
532
|
+
const failureInput = normalizationFailureInput(error) ?? diagnosticInput
|
|
533
|
+
throw new ModuleRewriteError(error.message, failureInput === undefined ? position
|
|
534
|
+
: mapSourcePosition(position, failureInput.code, program, failureInput.sourceMap))
|
|
480
535
|
}
|
|
481
536
|
prepared = { ...prepared, deferredHelpers: normalized.deferredHelpers }
|
|
482
537
|
intrinsicContext.expression = `this[${JSON.stringify(prepared.rootRuntimeName)}].intrinsics`
|
package/internal/config-spec.js
CHANGED
|
@@ -96,7 +96,7 @@ export const CONFIG_FIELDS = Object.freeze([
|
|
|
96
96
|
{
|
|
97
97
|
key: 'userBindingsEnabled',
|
|
98
98
|
type: 'boolean',
|
|
99
|
-
default:
|
|
99
|
+
default: true,
|
|
100
100
|
label: '启用全局用户 Binding',
|
|
101
101
|
labelEn: 'Enable Global User Bindings',
|
|
102
102
|
description: '管理 TypeScript helper,并将已启用条目作为跨会话的默认 REPL 绑定。',
|
|
@@ -133,12 +133,22 @@ function requireCompanionServices(agent) {
|
|
|
133
133
|
|
|
134
134
|
async function companionSkillDirectory(agentPresets) {
|
|
135
135
|
const preset = await agentPresets.resolve(CORDIS_PRESET_ID)
|
|
136
|
-
if (typeof preset?.path !== 'string' || !isAbsolute(preset.path)) {
|
|
137
|
-
throw new Error('ptc-plus: DSH cordis preset did not expose an absolute composition path')
|
|
138
|
-
}
|
|
139
136
|
if (typeof preset.broken === 'string' && preset.broken.length > 0) {
|
|
140
137
|
throw new Error(`ptc-plus: DSH cordis preset is unavailable: ${preset.broken}`)
|
|
141
138
|
}
|
|
139
|
+
// The generations that discover presets from their composition directories
|
|
140
|
+
// publish an absolute path, and the preset's sibling `skills` directory is
|
|
141
|
+
// then the companion Skill root. A generation that declares presets without
|
|
142
|
+
// any composition path publishes no root through this surface at all, so the
|
|
143
|
+
// report names that missing publication instead of claiming a shape failure
|
|
144
|
+
// on a healthy host. A published path that is not absolute is still a
|
|
145
|
+
// contract contradiction.
|
|
146
|
+
if (preset?.path === undefined) {
|
|
147
|
+
throw new Error('ptc-plus: the installed DSH agentPresets generation publishes no composition path for the "cordis" preset, so it does not publish the companion Skill root that cordisToolsEnabled mounts')
|
|
148
|
+
}
|
|
149
|
+
if (typeof preset.path !== 'string' || !isAbsolute(preset.path)) {
|
|
150
|
+
throw new Error('ptc-plus: DSH cordis preset did not expose an absolute composition path')
|
|
151
|
+
}
|
|
142
152
|
return join(dirname(preset.path), 'skills')
|
|
143
153
|
}
|
|
144
154
|
|
|
@@ -329,6 +339,7 @@ export function createCordisToolsOwner(
|
|
|
329
339
|
|
|
330
340
|
const installAgent = (agent) => {
|
|
331
341
|
if (disposed) return Promise.resolve()
|
|
342
|
+
if (withdrawn.has(agent)) return Promise.resolve()
|
|
332
343
|
const mounted = mounts.get(agent)
|
|
333
344
|
if (mounted !== undefined) {
|
|
334
345
|
return mounted.withdrawn === true ? Promise.resolve() : mounted.activation
|
|
@@ -416,11 +427,11 @@ export function createCordisToolsOwner(
|
|
|
416
427
|
/**
|
|
417
428
|
* Install for a call the host makes on its own behalf.
|
|
418
429
|
*
|
|
419
|
-
*
|
|
420
|
-
* when a listener rejects, so a scope this owner cannot
|
|
421
|
-
* instead of failing an operation another plugin asked
|
|
422
|
-
* enumeration stays strict: there the plugin speaks for
|
|
423
|
-
* have published, and `ready` reports the same failure.
|
|
430
|
+
* The public host lifecycle announces `agent/created` serially and rejects
|
|
431
|
+
* `agents.create()` when a listener rejects, so a scope this owner cannot
|
|
432
|
+
* serve must withdraw instead of failing an operation another plugin asked
|
|
433
|
+
* for. Install-time enumeration stays strict: there the plugin speaks for
|
|
434
|
+
* what it claims to have published, and `ready` reports the same failure.
|
|
424
435
|
*/
|
|
425
436
|
const installForHost = (agent) => {
|
|
426
437
|
let activation
|
|
@@ -451,8 +462,7 @@ export function createCordisToolsOwner(
|
|
|
451
462
|
if (disposed) return
|
|
452
463
|
for (const agent of [...pending]) {
|
|
453
464
|
void Promise.resolve().then(() => installAgent(agent)).catch(error => {
|
|
454
|
-
|
|
455
|
-
ctx.logger?.warn?.('ptc-plus: deferred Cordis activation failed', error)
|
|
465
|
+
withdraw(agent, error)
|
|
456
466
|
})
|
|
457
467
|
}
|
|
458
468
|
})
|
|
@@ -238,11 +238,34 @@ export function createEditTransportOwner(ctx, {
|
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
+
const disposeInstalled = (installed) => {
|
|
242
|
+
const failures = []
|
|
243
|
+
for (const key of ['disposePresentation', 'disposeRegistration']) {
|
|
244
|
+
const dispose = installed[key]
|
|
245
|
+
if (dispose === undefined) continue
|
|
246
|
+
try {
|
|
247
|
+
dispose()
|
|
248
|
+
installed[key] = undefined
|
|
249
|
+
} catch (error) {
|
|
250
|
+
failures.push(error)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (failures.length > 0) {
|
|
254
|
+
throw new AggregateError(failures, 'ptc-plus: edit_run_code scope disposal failed')
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
241
258
|
const uninstall = (agent) => {
|
|
242
259
|
const installed = installedScopes.get(agent)
|
|
243
260
|
if (installed === undefined) return
|
|
244
|
-
|
|
245
|
-
|
|
261
|
+
try {
|
|
262
|
+
disposeInstalled(installed)
|
|
263
|
+
} finally {
|
|
264
|
+
if (installed.disposePresentation === undefined
|
|
265
|
+
&& installed.disposeRegistration === undefined) {
|
|
266
|
+
installedScopes.delete(agent)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
246
269
|
}
|
|
247
270
|
|
|
248
271
|
return Object.freeze({
|
|
@@ -251,11 +274,19 @@ export function createEditTransportOwner(ctx, {
|
|
|
251
274
|
currentDurableReplay = nextConfig.durableReplay
|
|
252
275
|
},
|
|
253
276
|
isInstalled(agent) {
|
|
254
|
-
|
|
277
|
+
const installed = installedScopes.get(agent)
|
|
278
|
+
return installed?.disposePresentation !== undefined
|
|
279
|
+
&& installed.disposeRegistration !== undefined
|
|
255
280
|
},
|
|
256
281
|
ensureInstalled(agent) {
|
|
257
282
|
const installed = installedScopes.get(agent)
|
|
258
|
-
if (installed !== undefined)
|
|
283
|
+
if (installed !== undefined) {
|
|
284
|
+
if (installed.disposePresentation !== undefined
|
|
285
|
+
&& installed.disposeRegistration !== undefined) {
|
|
286
|
+
return installed.definition
|
|
287
|
+
}
|
|
288
|
+
uninstall(agent)
|
|
289
|
+
}
|
|
259
290
|
const liveSchemas = typeof toolSchemasForAgent === 'function'
|
|
260
291
|
? toolSchemasForAgent(agent)
|
|
261
292
|
: []
|
|
@@ -271,24 +302,31 @@ export function createEditTransportOwner(ctx, {
|
|
|
271
302
|
if (typeof disposeRegistration !== 'function') {
|
|
272
303
|
throw new Error('ptc-plus: DSH tools.register did not return a disposer')
|
|
273
304
|
}
|
|
274
|
-
|
|
305
|
+
const nextInstalled = {
|
|
306
|
+
definition,
|
|
307
|
+
disposePresentation: undefined,
|
|
308
|
+
disposeRegistration,
|
|
309
|
+
sessionId: sessionId(agent),
|
|
310
|
+
}
|
|
311
|
+
installedScopes.set(agent, nextInstalled)
|
|
275
312
|
try {
|
|
276
|
-
disposePresentation = scopedTools.presentAs('both')
|
|
313
|
+
const disposePresentation = scopedTools.presentAs('both')
|
|
277
314
|
if (typeof disposePresentation !== 'function') {
|
|
278
315
|
throw new Error('ptc-plus: DSH tools.presentAs did not return a disposer')
|
|
279
316
|
}
|
|
317
|
+
nextInstalled.disposePresentation = disposePresentation
|
|
280
318
|
} catch (error) {
|
|
281
|
-
|
|
319
|
+
try {
|
|
320
|
+
uninstall(agent)
|
|
321
|
+
} catch (cleanupError) {
|
|
322
|
+
throw new AggregateError(
|
|
323
|
+
[error, cleanupError],
|
|
324
|
+
'ptc-plus: edit_run_code installation and rollback failed',
|
|
325
|
+
{ cause: error },
|
|
326
|
+
)
|
|
327
|
+
}
|
|
282
328
|
throw error
|
|
283
329
|
}
|
|
284
|
-
let active = true
|
|
285
|
-
const dispose = () => {
|
|
286
|
-
if (!active) return
|
|
287
|
-
active = false
|
|
288
|
-
disposePresentation()
|
|
289
|
-
disposeRegistration()
|
|
290
|
-
}
|
|
291
|
-
installedScopes.set(agent, { definition, dispose, sessionId: sessionId(agent) })
|
|
292
330
|
return definition
|
|
293
331
|
},
|
|
294
332
|
handleResult(exec, result) {
|
|
@@ -305,28 +343,50 @@ export function createEditTransportOwner(ctx, {
|
|
|
305
343
|
releaseClaim(pending.id, pending.targetCallSeq)
|
|
306
344
|
},
|
|
307
345
|
disposeAgent(agent) {
|
|
308
|
-
|
|
346
|
+
let failure
|
|
347
|
+
try {
|
|
348
|
+
uninstall(agent)
|
|
349
|
+
} catch (error) {
|
|
350
|
+
failure = error
|
|
351
|
+
}
|
|
309
352
|
const id = sessionId(agent)
|
|
310
353
|
if (id !== undefined) {
|
|
311
354
|
discardSettlements(id)
|
|
312
355
|
editClaims.delete(id)
|
|
313
356
|
}
|
|
357
|
+
if (failure !== undefined) throw failure
|
|
314
358
|
},
|
|
315
359
|
disposeSession(session) {
|
|
316
360
|
const id = String(session.id)
|
|
361
|
+
const failures = []
|
|
317
362
|
for (const [agent, installed] of installedScopes) {
|
|
318
363
|
if (installed.sessionId !== id) continue
|
|
319
|
-
|
|
320
|
-
|
|
364
|
+
try {
|
|
365
|
+
uninstall(agent)
|
|
366
|
+
} catch (error) {
|
|
367
|
+
failures.push(error)
|
|
368
|
+
}
|
|
321
369
|
}
|
|
322
370
|
discardSettlements(id)
|
|
323
371
|
editClaims.delete(id)
|
|
372
|
+
if (failures.length > 0) {
|
|
373
|
+
throw new AggregateError(failures, 'ptc-plus: edit_run_code session disposal failed')
|
|
374
|
+
}
|
|
324
375
|
},
|
|
325
376
|
dispose() {
|
|
326
377
|
discardSettlements()
|
|
327
378
|
editClaims.clear()
|
|
328
|
-
|
|
329
|
-
installedScopes.
|
|
379
|
+
const failures = []
|
|
380
|
+
for (const agent of [...installedScopes.keys()]) {
|
|
381
|
+
try {
|
|
382
|
+
uninstall(agent)
|
|
383
|
+
} catch (error) {
|
|
384
|
+
failures.push(error)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (failures.length > 0) {
|
|
388
|
+
throw new AggregateError(failures, 'ptc-plus: edit_run_code owner disposal failed')
|
|
389
|
+
}
|
|
330
390
|
},
|
|
331
391
|
})
|
|
332
392
|
}
|