dsh-ptc-plus 0.3.2 → 0.4.1
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 +65 -78
- package/README.md +65 -78
- package/assets/ptc-plus-bindings-en.png +0 -0
- package/assets/ptc-plus-bindings-zh.png +0 -0
- package/assets/ptc-plus-repair-en.png +0 -0
- package/assets/ptc-plus-repair-zh.png +0 -0
- package/assets/ptc-plus-repl-workspace-en.png +0 -0
- package/assets/ptc-plus-repl-workspace-zh.png +0 -0
- package/assets/ptc-plus-settings-en.png +0 -0
- package/assets/ptc-plus-settings-zh.png +0 -0
- package/client.js +30596 -742
- package/compiler-amaro.cjs +5 -0
- package/compiler-amaro.cjs.map +6 -0
- package/compiler-asset-124f80c1bba92ff5fe79cfec6e8ad518f476de4a42514cae3919538887f510e8.txt +1 -0
- package/compiler-assets.json +3 -0
- package/compiler-core.cjs +554 -0
- package/compiler-core.cjs.map +6 -0
- package/compiler-platform.cjs +2 -0
- package/compiler-platform.cjs.map +6 -0
- package/compiler-typescript.cjs +388 -0
- package/compiler-typescript.cjs.map +6 -0
- package/docs/README.md +6 -0
- package/docs/adr/0004-declare-program-bindings-at-the-host.md +1 -1
- package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +5 -3
- package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +1 -1
- package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -0
- package/docs/adr/0010-session-log-derived-recovery-tips.md +59 -5
- package/docs/adr/0011-isolate-the-local-recovery-tip-provider.md +1 -1
- package/docs/adr/0012-preserve-mixed-destructuring-initialization.md +3 -1
- package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +3 -1
- package/docs/adr/0014-persist-cell-rewrite-policy.md +6 -2
- package/docs/adr/0015-preserve-source-positions-through-rewrites.md +1 -1
- package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
- package/docs/adr/0017-track-the-latest-dsh-public-surface.md +49 -0
- package/docs/adr/0018-use-versioned-fixture-for-ab-benchmark.md +3 -3
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +5 -3
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +12 -0
- package/docs/adr/0023-global-user-bindings.md +104 -0
- package/docs/adr/0024-repl-console-observation.md +33 -0
- package/docs/adr/0025-use-versioned-logical-binding-identities.md +119 -0
- package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +21 -0
- package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +37 -0
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +103 -0
- package/docs/adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md +27 -0
- package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +27 -0
- package/docs/architecture.md +73 -21
- package/docs/binding-review-placement-design.md +200 -0
- package/docs/capability-projection.md +1 -1
- package/docs/client-ui.md +95 -19
- package/docs/durability-design.md +25 -10
- package/docs/evaluation.md +78 -6
- package/docs/installation.md +41 -6
- package/docs/publishing.md +5 -3
- package/docs/runtime-reference.md +72 -25
- package/docs/semantic-validation.md +29 -0
- package/docs/stateful-binding-updates-proposal.md +755 -0
- package/docs/user-bindings.en.md +64 -0
- package/docs/user-bindings.md +64 -0
- package/docs/value-wire.md +3 -3
- package/docs/verification.md +57 -0
- package/index.js +369 -92
- package/internal/ast-traversal.js +5 -2
- package/internal/binding-descriptors.js +1 -0
- package/internal/binding-pattern-traversal.js +25 -0
- package/internal/binding-pattern.js +26 -29
- package/internal/binding-source-analysis.js +391 -0
- package/internal/binding-update-policy.js +66 -0
- package/internal/bounded-worker-round.js +56 -0
- package/internal/callable-definition-environment.js +42 -0
- package/internal/callable-source-catalog.js +129 -0
- package/internal/callable-source-encoding.js +47 -0
- package/internal/callable-source-facts.js +459 -0
- package/internal/callable-source-format.js +2 -0
- package/internal/callable-source-lowering.js +123 -0
- package/internal/cell-analysis-contract.js +25 -0
- package/internal/cell-analysis.js +240 -290
- package/internal/cell-completion.js +15 -0
- package/internal/cell-error.js +7 -0
- package/internal/cell-parser.js +66 -0
- package/internal/cell-rewriter.js +3 -677
- package/internal/commonjs-export-evidence.js +63 -0
- package/internal/commonjs-source-evidence.js +72 -0
- package/internal/compiler-amaro.cjs +2 -0
- package/internal/compiler-compression.js +12 -0
- package/internal/compiler-data.js +58 -0
- package/internal/compiler-descriptors.js +54 -0
- package/internal/compiler-dynamic-cache.js +48 -0
- package/internal/compiler-entry.js +35 -0
- package/internal/compiler-generated-names.js +17 -0
- package/internal/compiler-intrinsics.js +84 -0
- package/internal/compiler-module-links.js +34 -0
- package/internal/compiler-operations.js +61 -0
- package/internal/compiler-platform-factory.js +84 -0
- package/internal/compiler-platform.js +136 -0
- package/internal/compiler-record-roles.js +53 -0
- package/internal/compiler-region-output.js +259 -0
- package/internal/compiler-resource-lowering.js +103 -0
- package/internal/compiler-scope-facts.js +115 -0
- package/internal/compiler-service.js +109 -0
- package/internal/compiler-source-cache.js +32 -0
- package/internal/compiler-source-pieces.js +93 -0
- package/internal/compiler-source-regions.js +132 -0
- package/internal/compiler-storage-source.js +54 -0
- package/internal/compiler-text.js +15 -0
- package/internal/compiler-typescript.cjs +2 -0
- package/internal/config-spec.js +83 -27
- package/internal/cordis-tools-owner.js +130 -21
- package/internal/diagnostic.js +27 -1
- package/internal/direct-surface-owner.js +76 -23
- package/internal/dynamic-binding-evidence.js +16 -0
- package/internal/dynamic-environment-compiler.js +779 -0
- package/internal/dynamic-environment-integration.js +87 -0
- package/internal/dynamic-environment-runtime.js +362 -0
- package/internal/dynamic-native-calls.js +150 -0
- package/internal/dynamic-native-runtime.js +248 -0
- package/internal/dynamic-scope-analysis.js +90 -0
- package/internal/edit-transport-owner.js +27 -2
- package/internal/execution-seam-compat.js +193 -0
- package/internal/failure-reporting.js +118 -18
- package/internal/host-rpc.js +66 -0
- package/internal/isolated-worker.js +548 -0
- package/internal/kernel-child.js +139 -0
- package/internal/kernel-worker.js +904 -120
- package/internal/language-semantics.js +14 -0
- package/internal/legacy-binding-catalog.js +48 -0
- package/internal/legacy-cell-analysis.js +600 -0
- package/internal/legacy-cell-parser.js +50 -0
- package/internal/legacy-cell-rewriter.js +631 -0
- package/internal/legacy-repl-convenience.js +291 -0
- package/internal/managed-module-operations.js +170 -0
- package/internal/module-compilation.js +321 -0
- package/internal/module-runtime-platform.js +19 -0
- package/internal/module-transform-contract.js +12 -0
- package/internal/native-await-declarations.js +68 -0
- package/internal/native-root-compilation.js +54 -0
- package/internal/native-root-dynamic.js +67 -0
- package/internal/recovery-tips.js +27 -15
- package/internal/rejected-cell-editor.js +13 -8
- package/internal/repl-convenience.js +2 -289
- package/internal/repl-memory-projection.js +81 -12
- package/internal/repl-observation-interest.js +70 -0
- package/internal/repl-rewrite-contract.js +6 -0
- package/internal/repl-scope-normalizer.js +2091 -0
- package/internal/repl-value-observer.js +114 -0
- package/internal/rpc-contract.js +52 -0
- package/internal/runtime-bridge-owner.js +172 -28
- package/internal/runtime-config.js +48 -52
- package/internal/runtime-contexts.js +82 -10
- package/internal/runtime-messages.js +111 -0
- package/internal/session-cell-executor.js +401 -87
- package/internal/session-journal-recovery.js +684 -0
- package/internal/session-journal-schema.js +106 -0
- package/internal/session-journal.js +184 -654
- package/internal/session-log-view.js +42 -9
- package/internal/session-runtime.js +256 -95
- package/internal/session-state.js +247 -71
- package/internal/source-position-map.js +211 -71
- package/internal/stateful-module-compiler.js +154 -0
- package/internal/stateful-module-runtime.js +303 -0
- package/internal/stateful-root-compiler.js +598 -0
- package/internal/stateful-root-runtime.js +340 -0
- package/internal/typert-codec-compat.js +28 -0
- package/internal/typescript-transform.js +29 -0
- package/internal/user-binding-authoring.js +70 -0
- package/internal/user-binding-console-worker.js +184 -0
- package/internal/user-binding-console.js +148 -0
- package/internal/user-binding-draft-projection.js +335 -0
- package/internal/user-binding-model-context.js +31 -0
- package/internal/user-binding-runner.js +56 -0
- package/internal/user-bindings-owner.js +1065 -0
- package/internal/user-bindings-store.js +293 -0
- package/internal/user-bindings.js +353 -0
- package/internal/value-wire-schema.js +28 -0
- package/internal/value-wire.js +29 -20
- package/internal/worker-client.js +57 -67
- package/internal/worker-environment.js +45 -0
- package/internal/worker-output-relay.js +29 -0
- package/internal/worker-shutdown.js +14 -0
- package/package.json +75 -7
- package/src/client-activity.js +88 -53
- package/src/client-authoring-view.js +580 -0
- package/src/client-binding-review.js +230 -0
- package/src/client-bindings-data.js +29 -0
- package/src/client-catalog.js +127 -0
- package/src/client-code-editor.js +84 -0
- package/src/client-console.js +112 -0
- package/src/client-copy.js +370 -0
- package/src/client-feature-gates.js +62 -0
- package/src/client-host-compat.js +150 -0
- package/src/client-indicator-view.js +267 -0
- package/src/client-repl-view.js +292 -0
- package/src/client-rpc.js +20 -0
- package/src/client-settings-view.js +221 -0
- package/src/client-styles.js +106 -0
- package/src/client-tool-view.js +108 -0
- package/src/client-transport.js +159 -0
- package/src/client-workbench.js +521 -0
- package/src/client.js +233 -615
package/index.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Session-bound REPL for DeepSeek Harness PTC mode.
|
|
3
3
|
*
|
|
4
|
-
* DSH's run_code bridge does not pass session identity to
|
|
5
|
-
* The tools/execute around-hook carries that identity into the runtime
|
|
6
|
-
* which redirects only those runs to a persistent per-session kernel.
|
|
4
|
+
* DSH's run_code bridge does not pass session identity to the execution seam it
|
|
5
|
+
* calls. The tools/execute around-hook carries that identity into the runtime
|
|
6
|
+
* bridge, which redirects only those runs to a persistent per-session kernel.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import Schema from '@deepseek-ai/schemastery'
|
|
10
10
|
import { randomUUID } from 'node:crypto'
|
|
11
11
|
import { createDirectSurfaceOwner } from './internal/direct-surface-owner.js'
|
|
12
|
+
import { createRuntimeMessageOwner } from './internal/runtime-contexts.js'
|
|
13
|
+
import { PTC_DELIVERY_CONTEXT } from './internal/runtime-messages.js'
|
|
12
14
|
import { createCordisToolsOwner } from './internal/cordis-tools-owner.js'
|
|
13
15
|
import { createEditTransportOwner, EDIT_RUN_CODE } from './internal/edit-transport-owner.js'
|
|
14
16
|
import { createRuntimeBridgeOwner, RUN_CODE } from './internal/runtime-bridge-owner.js'
|
|
@@ -18,7 +20,14 @@ import {
|
|
|
18
20
|
SETTINGS_NAMESPACE,
|
|
19
21
|
} from './internal/config-spec.js'
|
|
20
22
|
import { installSettingsSectionCompat } from './internal/settings-compat.js'
|
|
23
|
+
import { installExecutionSeam } from './internal/execution-seam-compat.js'
|
|
24
|
+
import { valueLimitsFromConfig } from './internal/value-wire-schema.js'
|
|
21
25
|
import { createReplMemoryProjection } from './internal/repl-memory-projection.js'
|
|
26
|
+
import { createUserBindingDraftProjection } from './internal/user-binding-draft-projection.js'
|
|
27
|
+
import { createUserBindingsOwner } from './internal/user-bindings-owner.js'
|
|
28
|
+
import { createHostRpc } from './internal/host-rpc.js'
|
|
29
|
+
import { createReplObservationInterest } from './internal/repl-observation-interest.js'
|
|
30
|
+
import { guidancePolicies } from './internal/binding-update-policy.js'
|
|
22
31
|
import * as dshSettings from '@deepseek-ai/dsh-settings'
|
|
23
32
|
|
|
24
33
|
const INSTALL_CLEANUP = Symbol('ptc-plus install cleanup')
|
|
@@ -30,7 +39,10 @@ export const name = 'ptc-plus'
|
|
|
30
39
|
function configSchemaField(field) {
|
|
31
40
|
const base = field.type === 'boolean'
|
|
32
41
|
? Schema.boolean().default(field.default)
|
|
33
|
-
:
|
|
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)
|
|
34
46
|
return base.description(field.description)
|
|
35
47
|
}
|
|
36
48
|
|
|
@@ -38,18 +50,20 @@ export const Config = Schema.object(Object.fromEntries(
|
|
|
38
50
|
CONFIG_FIELDS.map(field => [field.key, configSchemaField(field)]),
|
|
39
51
|
))
|
|
40
52
|
|
|
41
|
-
/**
|
|
42
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Core services required by the plugin. Optional authoring services are
|
|
55
|
+
* injected on demand. The execution seam is not listed here: the host registers
|
|
56
|
+
* it under the service name of one generation, so `installExecutionSeam`
|
|
57
|
+
* selects the live one.
|
|
58
|
+
*/
|
|
59
|
+
export const inject = ['tools', 'systemPrompt', 'agents', 'llm']
|
|
43
60
|
|
|
44
|
-
function replGuidance(
|
|
45
|
-
looseTopLevelRedeclarations
|
|
46
|
-
looseTopLevelFunctionClassRedeclarations
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
autoSplitRedeclarations,
|
|
51
|
-
cordisToolsEnabled,
|
|
52
|
-
) {
|
|
61
|
+
function replGuidance({ bindingPolicy, rewritesEnabled, languageSemantics, durableReplay, cordisToolsEnabled }) {
|
|
62
|
+
const looseTopLevelRedeclarations = bindingPolicy.variableRedeclarations
|
|
63
|
+
const looseTopLevelFunctionClassRedeclarations = bindingPolicy.functionClassRedeclarations
|
|
64
|
+
const autoRewriteImports = rewritesEnabled.autoRewriteImports
|
|
65
|
+
const autoStripExports = rewritesEnabled.autoStripExports
|
|
66
|
+
const autoSplitRedeclarations = rewritesEnabled.autoSplitRedeclarations
|
|
53
67
|
const variableRedeclaration = looseTopLevelRedeclarations
|
|
54
68
|
? 'Repeated top-level `const`/`let` declarations replace existing bindings.'
|
|
55
69
|
: 'Repeated top-level variable declarations fail before execution, so reuse existing bindings or place one-off declarations inside a block.'
|
|
@@ -66,72 +80,133 @@ function replGuidance(
|
|
|
66
80
|
const splitSyntax = autoSplitRedeclarations
|
|
67
81
|
? 'Mixed new/existing top-level destructuring is split automatically while preserving assignment semantics.'
|
|
68
82
|
: 'Mixed new/existing top-level destructuring remains unsupported; separate the declaration from the assignment.'
|
|
83
|
+
const cellConventions = languageSemantics === 'legacy-v1'
|
|
84
|
+
? `Cells are async function bodies; ${moduleSyntax} Use dynamic import or require explicitly when static module syntax is unsupported. ${variableRedeclaration} ${functionClassRedeclaration} ${splitSyntax}`
|
|
85
|
+
: `Cells support TypeScript, top-level await, return, static import and top-level export. ${languageSemantics === 'stateful-v1'
|
|
86
|
+
? 'Declarations and assignments update the same logical binding within its scope, including const, functions, classes and imports. A bare declaration preserves an existing value. A declaration publishes its bindings after its whole pattern initializes; explicit assignments preserve actual partial writes. Imports follow their module until locally overwritten; importing again changes that same binding source. Existing closures observe later updates, while saved values retain their identity.'
|
|
87
|
+
: 'Redeclaration protection is enabled: existing session names cannot be redeclared, and newly created const and import bindings reject assignment. Other declarations follow native scope rules. Previously writable bindings stay writable.'}`
|
|
69
88
|
const recovery = durableReplay
|
|
70
89
|
? 'Direct Node/OS access remains live but is not replayed after a kernel restart.'
|
|
71
90
|
: 'Durable replay is disabled for this profile. Bindings remain reusable only in the current process; a new kernel starts empty.'
|
|
72
91
|
const cordisRecovery = cordisToolsEnabled
|
|
73
|
-
? '
|
|
92
|
+
? 'Keep large Cordis plugin source in a binding before the tool call so it can be reused after an error. A Cordis parse or validation failure can follow external effects; repeat the call only when its retry rules and current results establish that it is safe.'
|
|
74
93
|
: ''
|
|
75
94
|
return `\`run_code\` continues one persistent PTC REPL. Ordinary top-level bindings remain available to later cells, so reuse them instead of resending setup code. Choose the smallest cell that answers the request and return only the value the next step needs.
|
|
76
95
|
|
|
77
|
-
|
|
96
|
+
After an execution error, earlier statements may have taken effect. Inspect relevant values in a short cell and continue from them; repeat external operations only when their retry rules and current results establish that it is safe. \`edit_run_code\` edits and reruns a complete cell.
|
|
78
97
|
|
|
79
98
|
## Cell conventions
|
|
80
|
-
|
|
99
|
+
Use \`return\` for an explicit result and \`console\` for logs. Keep large inspection results in bindings or reduce them to targeted excerpts: \`tools.read\` is bounded inspection, not a lossless whole-file reader. ${cellConventions}
|
|
81
100
|
|
|
82
101
|
## Available capabilities
|
|
83
|
-
Use \`capabilities.tree()\`, \`capabilities.find()\`, and \`capabilities.inspect()\`
|
|
102
|
+
Use \`capabilities.tree()\`, \`capabilities.find()\`, and \`capabilities.inspect()\` for unfamiliar program APIs. Prefer direct current-cell work; reserve \`code.run\` for isolated execution of source held as data.
|
|
103
|
+
Program API references expire when their cell ends. Reusable helpers must read the current namespace when called, for example \`async function readNow(args) { return tools.read(args) }\`, rather than retaining an earlier \`tools\` object or method. Background callbacks from completed cells cannot call program APIs.
|
|
84
104
|
|
|
85
105
|
Native tool availability, executable names, shells, and path syntax depend on the current DSH profile and execution world; inspect them instead of assuming Windows, WSL, POSIX, or a particular shell. ${recovery}${cordisToolsEnabled ? ` ${cordisRecovery}` : ''}`
|
|
86
106
|
}
|
|
87
107
|
|
|
88
108
|
/** Register the session-bound REPL runtime. */
|
|
89
|
-
function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId) {
|
|
109
|
+
function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessionId) {
|
|
90
110
|
const presentationGeneration = randomUUID()
|
|
91
111
|
const replMemoryProjection = createReplMemoryProjection(presentationGeneration)
|
|
112
|
+
const userBindingDraftProjection = createUserBindingDraftProjection(presentationGeneration)
|
|
92
113
|
let activeConfig = resolvedConfig
|
|
93
114
|
let cordisTools
|
|
115
|
+
let cordisToolsReady = false
|
|
116
|
+
const cordisOwners = new Set()
|
|
94
117
|
let runtimeBridge
|
|
95
118
|
let editTransport
|
|
96
119
|
let directSurface
|
|
120
|
+
let userBindings
|
|
121
|
+
let observationInterest
|
|
97
122
|
let ready
|
|
98
123
|
let pendingCordisActivation
|
|
124
|
+
let draftProjectionRegistration
|
|
125
|
+
let draftProjectionAvailable = false
|
|
126
|
+
let projectionService
|
|
99
127
|
const disposers = []
|
|
128
|
+
const disposedOwners = new Set()
|
|
100
129
|
let disposed = false
|
|
101
|
-
|
|
102
|
-
|
|
130
|
+
let disposal
|
|
131
|
+
function dispose() {
|
|
103
132
|
disposed = true
|
|
104
133
|
pendingCordisActivation?.cancel()
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
134
|
+
if (disposal !== undefined) return disposal
|
|
135
|
+
const tasks = []
|
|
136
|
+
for (let index = disposers.length - 1; index >= 0; index -= 1) {
|
|
137
|
+
const cleanup = disposers[index]
|
|
138
|
+
if (typeof cleanup !== 'function') continue
|
|
139
|
+
tasks.push({
|
|
140
|
+
run: cleanup,
|
|
141
|
+
complete: () => { if (disposers[index] === cleanup) disposers[index] = undefined },
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
for (const owner of [directSurface, editTransport, runtimeBridge, userBindings]) {
|
|
145
|
+
if (typeof owner?.dispose !== 'function' || disposedOwners.has(owner)) continue
|
|
146
|
+
tasks.push({ run: () => owner.dispose(), complete: () => disposedOwners.add(owner) })
|
|
113
147
|
}
|
|
114
|
-
for (const owner of
|
|
148
|
+
for (const owner of cordisOwners) {
|
|
149
|
+
tasks.push({
|
|
150
|
+
run: () => owner.dispose(),
|
|
151
|
+
complete: () => {
|
|
152
|
+
cordisOwners.delete(owner)
|
|
153
|
+
if (cordisTools === owner) {
|
|
154
|
+
cordisTools = undefined
|
|
155
|
+
cordisToolsReady = false
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
const attempts = tasks.map(task => {
|
|
115
161
|
try {
|
|
116
|
-
|
|
162
|
+
return Promise.resolve(task.run())
|
|
117
163
|
} catch (error) {
|
|
118
|
-
|
|
164
|
+
return Promise.reject(error)
|
|
119
165
|
}
|
|
166
|
+
})
|
|
167
|
+
const operation = Promise.allSettled(attempts)
|
|
168
|
+
.then(results => {
|
|
169
|
+
const failures = []
|
|
170
|
+
for (let index = 0; index < results.length; index += 1) {
|
|
171
|
+
const result = results[index]
|
|
172
|
+
if (result.status === 'fulfilled') tasks[index].complete()
|
|
173
|
+
else failures.push(result.reason)
|
|
174
|
+
}
|
|
175
|
+
if (failures.length > 0) throw new AggregateError(failures, 'ptc-plus runtime disposal failed')
|
|
176
|
+
})
|
|
177
|
+
disposal = operation
|
|
178
|
+
operation.then(
|
|
179
|
+
() => { if (disposal === operation) disposal = undefined },
|
|
180
|
+
() => { if (disposal === operation) disposal = undefined },
|
|
181
|
+
)
|
|
182
|
+
return operation
|
|
183
|
+
}
|
|
184
|
+
const disposeCordisToolsOwner = async (owner) => {
|
|
185
|
+
await owner.dispose()
|
|
186
|
+
cordisOwners.delete(owner)
|
|
187
|
+
if (cordisTools === owner) {
|
|
188
|
+
cordisTools = undefined
|
|
189
|
+
cordisToolsReady = false
|
|
120
190
|
}
|
|
121
|
-
|
|
191
|
+
}
|
|
192
|
+
const disposeRetainedCordisTools = async () => {
|
|
193
|
+
const owners = [...cordisOwners]
|
|
194
|
+
const results = await Promise.allSettled(owners.map(owner => disposeCordisToolsOwner(owner)))
|
|
195
|
+
const failures = results.filter(result => result.status === 'rejected').map(result => result.reason)
|
|
196
|
+
if (failures.length > 0) throw new AggregateError(failures, 'ptc-plus: retained Cordis disposal failed')
|
|
122
197
|
}
|
|
123
198
|
const awaitCordisToolsOwner = async (owner, cancelled) => {
|
|
124
199
|
try {
|
|
125
200
|
await Promise.race([owner.ready, cancelled])
|
|
201
|
+
if (cordisTools === owner) cordisToolsReady = true
|
|
126
202
|
return owner
|
|
127
203
|
} catch (error) {
|
|
128
204
|
let rollbackError
|
|
129
205
|
try {
|
|
130
|
-
await owner
|
|
206
|
+
await disposeCordisToolsOwner(owner)
|
|
131
207
|
} catch (caught) {
|
|
132
208
|
rollbackError = caught
|
|
133
209
|
}
|
|
134
|
-
if (cordisTools === owner) cordisTools = undefined
|
|
135
210
|
if (rollbackError !== undefined) {
|
|
136
211
|
throw new AggregateError(
|
|
137
212
|
[error, rollbackError],
|
|
@@ -143,13 +218,17 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
|
|
|
143
218
|
}
|
|
144
219
|
}
|
|
145
220
|
const activateCordisToolsOwner = async () => {
|
|
221
|
+
await disposeRetainedCordisTools()
|
|
222
|
+
if (disposed) throw new Error('ptc-plus: Cordis activation cancelled')
|
|
146
223
|
const owner = createCordisToolsOwner(ctx)
|
|
224
|
+
cordisOwners.add(owner)
|
|
147
225
|
let cancel
|
|
148
226
|
const cancelled = new Promise((_resolve, reject) => {
|
|
149
227
|
cancel = () => reject(new Error('ptc-plus: Cordis activation cancelled'))
|
|
150
228
|
})
|
|
151
229
|
const activation = { owner, cancel }
|
|
152
230
|
cordisTools = owner
|
|
231
|
+
cordisToolsReady = false
|
|
153
232
|
pendingCordisActivation = activation
|
|
154
233
|
try {
|
|
155
234
|
return await awaitCordisToolsOwner(owner, cancelled)
|
|
@@ -158,19 +237,92 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
|
|
|
158
237
|
}
|
|
159
238
|
}
|
|
160
239
|
const cancelCordisActivation = () => pendingCordisActivation?.cancel()
|
|
240
|
+
const registerProjection = (projection, label) => {
|
|
241
|
+
try {
|
|
242
|
+
const unregister = projectionService?.register?.(projection)
|
|
243
|
+
if (typeof unregister !== 'function') {
|
|
244
|
+
throw new Error('ptc-plus: sessionProjections.register did not return a disposer')
|
|
245
|
+
}
|
|
246
|
+
return unregister
|
|
247
|
+
} catch (error) {
|
|
248
|
+
ctx.logger?.warn?.(`ptc-plus: ${label} projection unavailable`, error)
|
|
249
|
+
return undefined
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const setDraftProjectionEnabled = async (enabled) => {
|
|
253
|
+
if (!enabled) {
|
|
254
|
+
const unregister = draftProjectionRegistration
|
|
255
|
+
draftProjectionAvailable = false
|
|
256
|
+
const results = await Promise.allSettled([
|
|
257
|
+
Promise.resolve().then(() => userBindings?.setDraftProjectionAvailable(false)),
|
|
258
|
+
Promise.resolve().then(() => unregister?.()),
|
|
259
|
+
])
|
|
260
|
+
const failures = results.filter(result => result.status === 'rejected').map(result => result.reason)
|
|
261
|
+
if (failures.length > 0) {
|
|
262
|
+
throw new AggregateError(failures, 'ptc-plus: binding draft projection cleanup failed')
|
|
263
|
+
}
|
|
264
|
+
return
|
|
265
|
+
}
|
|
266
|
+
if (draftProjectionRegistration !== undefined) {
|
|
267
|
+
if (draftProjectionAvailable) return
|
|
268
|
+
await draftProjectionRegistration()
|
|
269
|
+
}
|
|
270
|
+
if (projectionService === undefined) return
|
|
271
|
+
const unregister = registerProjection(userBindingDraftProjection, 'binding draft')
|
|
272
|
+
if (unregister === undefined) return
|
|
273
|
+
let active = true
|
|
274
|
+
let releasing
|
|
275
|
+
const release = () => {
|
|
276
|
+
if (!active) return Promise.resolve()
|
|
277
|
+
if (releasing !== undefined) return releasing
|
|
278
|
+
const operation = Promise.resolve().then(unregister)
|
|
279
|
+
releasing = operation
|
|
280
|
+
operation.then(
|
|
281
|
+
() => {
|
|
282
|
+
active = false
|
|
283
|
+
if (draftProjectionRegistration === release) draftProjectionRegistration = undefined
|
|
284
|
+
if (releasing === operation) releasing = undefined
|
|
285
|
+
},
|
|
286
|
+
() => { if (releasing === operation) releasing = undefined },
|
|
287
|
+
)
|
|
288
|
+
return operation
|
|
289
|
+
}
|
|
290
|
+
draftProjectionRegistration = release
|
|
291
|
+
disposers.push(release)
|
|
292
|
+
draftProjectionAvailable = true
|
|
293
|
+
try {
|
|
294
|
+
await userBindings?.setDraftProjectionAvailable(true)
|
|
295
|
+
} catch (error) {
|
|
296
|
+
draftProjectionAvailable = false
|
|
297
|
+
await Promise.allSettled([
|
|
298
|
+
Promise.resolve().then(() => userBindings?.setDraftProjectionAvailable(false)),
|
|
299
|
+
release(),
|
|
300
|
+
])
|
|
301
|
+
throw error
|
|
302
|
+
}
|
|
303
|
+
}
|
|
161
304
|
try {
|
|
305
|
+
const hostRpc = createHostRpc(ctx)
|
|
306
|
+
disposers.push(() => hostRpc.dispose())
|
|
307
|
+
observationInterest = createReplObservationInterest(ctx, activeConfig.replViewEnabled,
|
|
308
|
+
(id, memory, signal) => runtimeBridge?.observeRepl(id, memory, signal))
|
|
309
|
+
disposers.push(() => observationInterest.dispose())
|
|
162
310
|
if (typeof ctx.inject === 'function') {
|
|
163
311
|
const projectionInjection = ctx.inject(['sessionProjections'], (scope) => {
|
|
164
312
|
if (disposed) return
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
313
|
+
const service = scope.sessionProjections
|
|
314
|
+
projectionService = service
|
|
315
|
+
draftProjectionAvailable = false
|
|
316
|
+
const unregisterMemory = registerProjection(replMemoryProjection, 'REPL memory')
|
|
317
|
+
if (unregisterMemory !== undefined) disposers.push(unregisterMemory)
|
|
318
|
+
void setDraftProjectionEnabled(activeConfig.userBindingsEnabled).catch(error => {
|
|
319
|
+
ctx.logger?.warn?.('ptc-plus: binding draft projection unavailable', error)
|
|
320
|
+
})
|
|
321
|
+
scope.effect?.(() => async () => {
|
|
322
|
+
if (projectionService !== service) return
|
|
323
|
+
projectionService = undefined
|
|
324
|
+
await setDraftProjectionEnabled(false)
|
|
325
|
+
}, 'ptc-plus: binding draft projection availability')
|
|
174
326
|
})
|
|
175
327
|
if (typeof projectionInjection === 'function') {
|
|
176
328
|
disposers.push(projectionInjection)
|
|
@@ -178,23 +330,32 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
|
|
|
178
330
|
disposers.push(() => projectionInjection.dispose())
|
|
179
331
|
}
|
|
180
332
|
}
|
|
181
|
-
cordisTools = activeConfig.cordisToolsEnabled
|
|
182
|
-
? createCordisToolsOwner(ctx)
|
|
183
|
-
: undefined
|
|
333
|
+
cordisTools = activeConfig.cordisToolsEnabled ? createCordisToolsOwner(ctx) : undefined
|
|
184
334
|
if (cordisTools !== undefined) {
|
|
185
335
|
const initialCordisTools = cordisTools
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
throw error
|
|
189
|
-
})
|
|
336
|
+
cordisOwners.add(initialCordisTools)
|
|
337
|
+
ready = awaitCordisToolsOwner(initialCordisTools, new Promise(() => {}))
|
|
190
338
|
}
|
|
339
|
+
userBindings = createUserBindingsOwner(ctx, {
|
|
340
|
+
enabled: activeConfig.userBindingsEnabled,
|
|
341
|
+
draftProjectionAvailable,
|
|
342
|
+
maxWallMs: activeConfig.maxWallMs,
|
|
343
|
+
maxOutputBytes: activeConfig.maxOutputBytes,
|
|
344
|
+
maxOldGenerationSizeMb: activeConfig.maxOldGenerationSizeMb,
|
|
345
|
+
valueLimits: valueLimitsFromConfig(activeConfig),
|
|
346
|
+
})
|
|
191
347
|
runtimeBridge = createRuntimeBridgeOwner({
|
|
192
348
|
ctx,
|
|
349
|
+
seam,
|
|
350
|
+
observeSession: id => observationInterest.has(id),
|
|
193
351
|
sessionConfig: activeConfig,
|
|
352
|
+
userBindingsCwd: userBindings.cwd,
|
|
194
353
|
maxNestedRunCodeDepth: activeConfig.maxNestedRunCodeDepth,
|
|
195
354
|
presentationGeneration,
|
|
196
355
|
sessionId,
|
|
197
356
|
toolSchemasForAgent,
|
|
357
|
+
userBindingDraftForAgent: agent => userBindings.draftCapabilityForAgent(agent),
|
|
358
|
+
bindingSubmissionForAgent: (agent, ensureLease, onAccepted) => userBindings.submissionForAgent(agent, ensureLease, onAccepted),
|
|
198
359
|
})
|
|
199
360
|
editTransport = createEditTransportOwner(ctx, {
|
|
200
361
|
durableReplay: activeConfig.durableReplay,
|
|
@@ -209,21 +370,17 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
|
|
|
209
370
|
canonicalizeToolCalls: activeConfig.canonicalizeToolCalls,
|
|
210
371
|
sessionId,
|
|
211
372
|
toolSchemasForAgent,
|
|
373
|
+
userBindingsForAgent: () => userBindings.snapshot(),
|
|
374
|
+
setAgentPresentation: (agent, presentation) => (
|
|
375
|
+
userBindings.setAgentPresentation(agent, presentation)
|
|
376
|
+
),
|
|
212
377
|
})
|
|
213
378
|
disposers.push(ctx.systemPrompt.section({
|
|
214
379
|
name: 'tools:ptc-plus-repl',
|
|
215
380
|
order: 98,
|
|
216
381
|
text: context => {
|
|
217
382
|
if (ctx.tools.get(RUN_CODE, context?.scope) === undefined) return ''
|
|
218
|
-
return replGuidance(
|
|
219
|
-
activeConfig.looseTopLevelRedeclarations,
|
|
220
|
-
activeConfig.looseTopLevelFunctionClassRedeclarations,
|
|
221
|
-
activeConfig.durableReplay,
|
|
222
|
-
activeConfig.autoRewriteImports,
|
|
223
|
-
activeConfig.autoStripExports,
|
|
224
|
-
activeConfig.autoSplitRedeclarations,
|
|
225
|
-
activeConfig.cordisToolsEnabled,
|
|
226
|
-
)
|
|
383
|
+
return replGuidance(guidancePolicies(activeConfig))
|
|
227
384
|
},
|
|
228
385
|
}))
|
|
229
386
|
disposers.push(ctx.on('system-prompt/assemble', (assembly, context, next) => (
|
|
@@ -235,7 +392,13 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
|
|
|
235
392
|
if (rejected !== undefined) return rejected
|
|
236
393
|
if (exec.name === RUN_CODE) {
|
|
237
394
|
const executionArguments = directSurface.executionArguments(exec)
|
|
238
|
-
|
|
395
|
+
const requestUserBindings = directSurface.executionUserBindings(exec)
|
|
396
|
+
return Promise.resolve(runtimeBridge.handleExecute(
|
|
397
|
+
exec,
|
|
398
|
+
next,
|
|
399
|
+
executionArguments,
|
|
400
|
+
requestUserBindings,
|
|
401
|
+
))
|
|
239
402
|
.then(result => directSurface.argumentDiagnostic(exec, result))
|
|
240
403
|
}
|
|
241
404
|
return next()
|
|
@@ -245,22 +408,48 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
|
|
|
245
408
|
if (exec.name === EDIT_RUN_CODE) return editTransport.handleResult(exec, result)
|
|
246
409
|
if (exec.name === RUN_CODE) return runtimeBridge.handleResult(exec, result)
|
|
247
410
|
}))
|
|
248
|
-
disposers.push(ctx.on('agent/disposed', ({ agent }) => {
|
|
411
|
+
disposers.push(ctx.on('agent/disposed', async ({ agent }) => {
|
|
249
412
|
directSurface.disposeAgent(agent)
|
|
250
413
|
editTransport.disposeAgent(agent)
|
|
251
|
-
|
|
414
|
+
await Promise.all([
|
|
415
|
+
runtimeBridge.disposeAgent(agent),
|
|
416
|
+
userBindings.clearAgentPresentation(agent),
|
|
417
|
+
])
|
|
252
418
|
}))
|
|
253
|
-
disposers.push(ctx.on('session/disposed', (session) => {
|
|
419
|
+
disposers.push(ctx.on('session/disposed', async (session) => {
|
|
254
420
|
directSurface.disposeSession(session)
|
|
255
421
|
editTransport.disposeSession(session)
|
|
256
|
-
|
|
422
|
+
await Promise.all([
|
|
423
|
+
runtimeBridge.disposeSession(session),
|
|
424
|
+
userBindings.clearSessionPresentation(session?.id ?? session),
|
|
425
|
+
])
|
|
426
|
+
}))
|
|
427
|
+
disposers.push(ctx.on('agent-preset/selected', (sessionId) => {
|
|
428
|
+
directSurface.resetSessionComposition(sessionId)
|
|
429
|
+
const agent = ctx.agents?.get?.(String(sessionId))
|
|
430
|
+
if (agent !== undefined) editTransport.disposeAgent(agent)
|
|
431
|
+
const cleanup = agent === undefined
|
|
432
|
+
? userBindings.clearSessionPresentation(sessionId)
|
|
433
|
+
: userBindings.setAgentPresentation(agent)
|
|
434
|
+
const report = ctx.logger?.warn?.bind(
|
|
435
|
+
ctx.logger,
|
|
436
|
+
'ptc-plus: failed to reconcile Global User Bindings authoring after preset selection',
|
|
437
|
+
) ?? console.warn
|
|
438
|
+
void cleanup.catch(report)
|
|
257
439
|
}))
|
|
258
440
|
} catch (error) {
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
441
|
+
const failedRuntime = Object.freeze({ dispose })
|
|
442
|
+
let failure = error
|
|
443
|
+
if (failure === null || (typeof failure !== 'object' && typeof failure !== 'function')) {
|
|
444
|
+
failure = new Error(String(error), { cause: error })
|
|
262
445
|
}
|
|
263
|
-
|
|
446
|
+
try {
|
|
447
|
+
Object.defineProperty(failure, INSTALL_CLEANUP, { value: failedRuntime })
|
|
448
|
+
} catch {
|
|
449
|
+
failure = new Error(failure.message ?? String(failure), { cause: failure })
|
|
450
|
+
Object.defineProperty(failure, INSTALL_CLEANUP, { value: failedRuntime })
|
|
451
|
+
}
|
|
452
|
+
throw failure
|
|
264
453
|
}
|
|
265
454
|
async function reconfigure(nextConfig) {
|
|
266
455
|
if (disposed) return
|
|
@@ -273,26 +462,34 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
|
|
|
273
462
|
rollbacks.push(() => editTransport.reconfigure(previousConfig))
|
|
274
463
|
directSurface.reconfigure(nextConfig)
|
|
275
464
|
rollbacks.push(() => directSurface.reconfigure(previousConfig))
|
|
465
|
+
await userBindings.reconfigure(nextConfig)
|
|
466
|
+
rollbacks.push(() => userBindings.reconfigure(previousConfig))
|
|
467
|
+
await setDraftProjectionEnabled(nextConfig.userBindingsEnabled)
|
|
468
|
+
rollbacks.push(() => setDraftProjectionEnabled(previousConfig.userBindingsEnabled))
|
|
276
469
|
|
|
470
|
+
if (nextConfig.cordisToolsEnabled && cordisTools !== undefined && !cordisToolsReady) {
|
|
471
|
+
await disposeRetainedCordisTools()
|
|
472
|
+
}
|
|
277
473
|
if (nextConfig.cordisToolsEnabled && cordisTools === undefined) {
|
|
278
474
|
await activateCordisToolsOwner()
|
|
279
475
|
} else if (!nextConfig.cordisToolsEnabled && cordisTools !== undefined) {
|
|
280
476
|
const currentCordis = cordisTools
|
|
477
|
+
cordisToolsReady = false
|
|
281
478
|
try {
|
|
282
|
-
await currentCordis
|
|
283
|
-
cordisTools = undefined
|
|
479
|
+
await disposeCordisToolsOwner(currentCordis)
|
|
284
480
|
} catch (error) {
|
|
285
481
|
try {
|
|
482
|
+
await disposeCordisToolsOwner(currentCordis)
|
|
286
483
|
await activateCordisToolsOwner()
|
|
287
484
|
/* c8 ignore next */
|
|
288
485
|
} catch (rollbackError) {
|
|
289
|
-
cordisTools = currentCordis
|
|
290
486
|
throw new AggregateError([error, rollbackError], 'ptc-plus: Cordis reconfiguration and rollback failed', { cause: error })
|
|
291
487
|
}
|
|
292
488
|
throw error
|
|
293
489
|
}
|
|
294
490
|
}
|
|
295
491
|
activeConfig = nextConfig
|
|
492
|
+
observationInterest.reconfigure(nextConfig.replViewEnabled)
|
|
296
493
|
} catch (error) {
|
|
297
494
|
const rollbackFailures = []
|
|
298
495
|
/* c8 ignore next -- the rollback loop's rejection branch is host-specific. */
|
|
@@ -308,12 +505,25 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
|
|
|
308
505
|
}
|
|
309
506
|
}
|
|
310
507
|
|
|
311
|
-
return Object.freeze({
|
|
508
|
+
return Object.freeze({
|
|
509
|
+
cancelCordisActivation, dispose, reconfigure, ready,
|
|
510
|
+
contextsForRequest: context => directSurface.contextsForRequest(context),
|
|
511
|
+
})
|
|
312
512
|
}
|
|
313
513
|
|
|
314
514
|
/** Register the session-bound REPL runtime. */
|
|
315
515
|
export function apply(ctx, config = {}) {
|
|
516
|
+
// Configuration is validated before the plugin waits for the host's seam, so
|
|
517
|
+
// an unusable configuration fails at the load call rather than later.
|
|
316
518
|
const resolvedConfig = resolveConfig(config)
|
|
519
|
+
return installExecutionSeam(ctx, {
|
|
520
|
+
services: inject,
|
|
521
|
+
attach: (scope, seam) => applyWithExecutionSeam(scope, resolvedConfig, seam),
|
|
522
|
+
})
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/** Install the plugin into the scope of the seam the host registered. */
|
|
526
|
+
function applyWithExecutionSeam(ctx, resolvedConfig, seam) {
|
|
317
527
|
const toolSchemasForAgent = agent => typeof ctx.tools.schemas === 'function'
|
|
318
528
|
? ctx.tools.schemas(agent)
|
|
319
529
|
: []
|
|
@@ -323,7 +533,9 @@ export function apply(ctx, config = {}) {
|
|
|
323
533
|
}
|
|
324
534
|
let committed
|
|
325
535
|
let activating
|
|
536
|
+
const retained = new Set()
|
|
326
537
|
let disposed = false
|
|
538
|
+
let controllerDisposal
|
|
327
539
|
let transitionTail = Promise.resolve()
|
|
328
540
|
let pendingTransitions = 0
|
|
329
541
|
const trackTransition = operation => {
|
|
@@ -348,9 +560,26 @@ export function apply(ctx, config = {}) {
|
|
|
348
560
|
return { promise, resolve, reject }
|
|
349
561
|
}
|
|
350
562
|
const disposeRuntime = record => {
|
|
563
|
+
if (record?.disposed === true) return Promise.resolve()
|
|
351
564
|
if (record?.disposal !== undefined) return record.disposal
|
|
352
|
-
|
|
353
|
-
|
|
565
|
+
retained.add(record)
|
|
566
|
+
const operation = Promise.resolve().then(() => record.runtime.dispose())
|
|
567
|
+
record.disposal = operation
|
|
568
|
+
operation.then(
|
|
569
|
+
() => {
|
|
570
|
+
record.disposed = true
|
|
571
|
+
retained.delete(record)
|
|
572
|
+
if (record.disposal === operation) record.disposal = undefined
|
|
573
|
+
},
|
|
574
|
+
() => { if (record.disposal === operation) record.disposal = undefined },
|
|
575
|
+
)
|
|
576
|
+
return operation
|
|
577
|
+
}
|
|
578
|
+
const disposeRetainedRuntimes = async () => {
|
|
579
|
+
const records = [...retained].filter(record => record.runtime !== undefined)
|
|
580
|
+
const results = await Promise.allSettled(records.map(record => disposeRuntime(record)))
|
|
581
|
+
const failures = results.filter(result => result.status === 'rejected').map(result => result.reason)
|
|
582
|
+
if (failures.length > 0) throw new AggregateError(failures, 'ptc-plus retained runtime disposal failed')
|
|
354
583
|
}
|
|
355
584
|
const activationError = (error, cleanupError) => new AggregateError(
|
|
356
585
|
[error, cleanupError],
|
|
@@ -376,17 +605,18 @@ export function apply(ctx, config = {}) {
|
|
|
376
605
|
record.completion.resolve({ status: 'superseded' })
|
|
377
606
|
return
|
|
378
607
|
}
|
|
379
|
-
if (
|
|
380
|
-
throw new Error('ptc-plus: unsupported code runtime language ' + JSON.stringify(
|
|
608
|
+
if (seam.language !== 'typescript') {
|
|
609
|
+
throw new Error('ptc-plus: unsupported code runtime language ' + JSON.stringify(seam.language) + '; only "typescript" is supported')
|
|
381
610
|
}
|
|
382
611
|
let candidate
|
|
383
612
|
try {
|
|
384
|
-
candidate = installPtCRuntime(ctx, record.config, toolSchemasForAgent, sessionId)
|
|
613
|
+
candidate = installPtCRuntime(ctx, record.config, seam, toolSchemasForAgent, sessionId)
|
|
385
614
|
record.runtime = candidate
|
|
386
615
|
} catch (error) {
|
|
387
|
-
const
|
|
388
|
-
if (
|
|
389
|
-
|
|
616
|
+
const failedRuntime = error?.[INSTALL_CLEANUP]
|
|
617
|
+
if (failedRuntime === undefined) throw error
|
|
618
|
+
record.runtime = failedRuntime
|
|
619
|
+
const trackedCleanup = trackTransition(disposeRuntime(record))
|
|
390
620
|
void trackedCleanup.then(
|
|
391
621
|
() => record.completion.reject(error),
|
|
392
622
|
cleanupError => record.completion.reject(activationError(error, cleanupError)),
|
|
@@ -427,6 +657,8 @@ export function apply(ctx, config = {}) {
|
|
|
427
657
|
generation,
|
|
428
658
|
runtime: undefined,
|
|
429
659
|
disposal: undefined,
|
|
660
|
+
disposed: false,
|
|
661
|
+
retiring: false,
|
|
430
662
|
trackedDisposal: undefined,
|
|
431
663
|
cancelled: false,
|
|
432
664
|
completion,
|
|
@@ -455,10 +687,22 @@ export function apply(ctx, config = {}) {
|
|
|
455
687
|
apply(nextConfig, generation) {
|
|
456
688
|
if (disposed) return Promise.resolve({ status: 'superseded' })
|
|
457
689
|
if (activating !== undefined) {
|
|
458
|
-
|
|
690
|
+
cancelActivation(activating)
|
|
691
|
+
const cleanup = disposeRetainedRuntimes()
|
|
692
|
+
return beginActivation(nextConfig, generation, cleanup)
|
|
693
|
+
}
|
|
694
|
+
if (committed === undefined) {
|
|
695
|
+
const cleanup = retained.size === 0 ? undefined : disposeRetainedRuntimes()
|
|
696
|
+
return beginActivation(nextConfig, generation, cleanup)
|
|
697
|
+
}
|
|
698
|
+
if (committed.retiring) {
|
|
699
|
+
const current = committed
|
|
700
|
+
void disposeRuntime(current)
|
|
701
|
+
const cleanup = disposeRetainedRuntimes().then(() => {
|
|
702
|
+
if (committed === current) committed = undefined
|
|
703
|
+
})
|
|
459
704
|
return beginActivation(nextConfig, generation, cleanup)
|
|
460
705
|
}
|
|
461
|
-
if (committed === undefined) return beginActivation(nextConfig, generation)
|
|
462
706
|
const current = committed
|
|
463
707
|
if (!nextConfig.cordisToolsEnabled) current.runtime.cancelCordisActivation()
|
|
464
708
|
return enqueueTransition(async () => {
|
|
@@ -469,27 +713,60 @@ export function apply(ctx, config = {}) {
|
|
|
469
713
|
return { status: 'applied' }
|
|
470
714
|
})
|
|
471
715
|
},
|
|
472
|
-
uninstall() {
|
|
473
|
-
const
|
|
716
|
+
async uninstall() {
|
|
717
|
+
const active = activating
|
|
718
|
+
const activationCleanup = active === undefined
|
|
474
719
|
? Promise.resolve()
|
|
475
|
-
: cancelActivation(
|
|
720
|
+
: cancelActivation(active)
|
|
476
721
|
const current = committed
|
|
477
|
-
|
|
722
|
+
if (current !== undefined) current.retiring = true
|
|
478
723
|
const committedCleanup = current === undefined
|
|
479
724
|
? Promise.resolve()
|
|
480
725
|
: trackTransition(disposeRuntime(current))
|
|
481
|
-
|
|
726
|
+
const records = [...retained].filter(record => (
|
|
727
|
+
record !== current && record !== active && record.runtime !== undefined
|
|
728
|
+
))
|
|
729
|
+
const results = await Promise.allSettled([
|
|
730
|
+
activationCleanup,
|
|
731
|
+
committedCleanup,
|
|
732
|
+
...records.map(record => disposeRuntime(record)),
|
|
733
|
+
])
|
|
734
|
+
const failures = results.filter(result => result.status === 'rejected').map(result => result.reason)
|
|
735
|
+
if (current?.disposed === true && committed === current) committed = undefined
|
|
736
|
+
if (failures.length > 0) throw new AggregateError(failures, 'ptc-plus runtime uninstall failed')
|
|
482
737
|
},
|
|
483
738
|
committedConfig() {
|
|
484
|
-
return committed?.config
|
|
739
|
+
return committed?.retiring ? undefined : committed?.config
|
|
485
740
|
},
|
|
486
|
-
|
|
741
|
+
dispose() {
|
|
487
742
|
disposed = true
|
|
488
|
-
|
|
743
|
+
if (controllerDisposal !== undefined) return controllerDisposal
|
|
744
|
+
const operation = controller.uninstall()
|
|
745
|
+
controllerDisposal = operation
|
|
746
|
+
operation.then(
|
|
747
|
+
() => { if (controllerDisposal === operation) controllerDisposal = undefined },
|
|
748
|
+
() => { if (controllerDisposal === operation) controllerDisposal = undefined },
|
|
749
|
+
)
|
|
750
|
+
return operation
|
|
489
751
|
},
|
|
490
752
|
}
|
|
491
753
|
ctx.effect(() => async () => controller.dispose(), 'ptc-plus runtime lifecycle')
|
|
492
754
|
|
|
755
|
+
const messageOwner = createRuntimeMessageOwner(context => (
|
|
756
|
+
committed?.retiring ? [] : committed?.runtime.contextsForRequest(context) ?? []
|
|
757
|
+
))
|
|
758
|
+
ctx.effect(() => ctx.systemPrompt.context({
|
|
759
|
+
name: PTC_DELIVERY_CONTEXT, order: 98, text: '',
|
|
760
|
+
}), 'ptc-plus dynamic message delivery witness')
|
|
761
|
+
ctx.effect(() => ctx.on('system-prompt/assemble', (assembly, context, next) => (
|
|
762
|
+
messageOwner.assemble(assembly, context, next)
|
|
763
|
+
)), 'ptc-plus message assembly')
|
|
764
|
+
ctx.effect(() => ctx.on('agent/pre-step', (payload, next) => (
|
|
765
|
+
messageOwner.preStep(payload, next)
|
|
766
|
+
)), 'ptc-plus accepted messages')
|
|
767
|
+
ctx.effect(() => ctx.on('agent/disposed', ({ agent }) => messageOwner.disposeAgent(agent)), 'ptc-plus message agent disposal')
|
|
768
|
+
ctx.effect(() => () => messageOwner.dispose(), 'ptc-plus dynamic message lifecycle')
|
|
769
|
+
|
|
493
770
|
let configSource = () => resolvedConfig
|
|
494
771
|
let configurationGeneration = 0
|
|
495
772
|
let settingsWriter
|