dsh-ptc-plus 0.3.1 → 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 +66 -73
- package/README.md +66 -73
- 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 +30610 -722
- 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 +6 -4
- package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +15 -3
- 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 +9 -3
- package/docs/adr/0014-persist-cell-rewrite-policy.md +11 -5
- 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 +8 -6
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +17 -3
- 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 +82 -22
- package/docs/binding-review-placement-design.md +200 -0
- package/docs/capability-projection.md +1 -1
- package/docs/client-ui.md +96 -20
- package/docs/durability-design.md +61 -24
- package/docs/evaluation.md +78 -6
- package/docs/installation.md +41 -6
- package/docs/program-data-plane.md +6 -0
- package/docs/publishing.md +5 -3
- package/docs/runtime-reference.md +79 -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 +374 -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 +252 -242
- 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 -553
- 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 +91 -25
- 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 +28 -3
- 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 +911 -116
- 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 -201
- package/internal/repl-memory-projection.js +81 -12
- package/internal/repl-observation-interest.js +70 -0
- package/internal/repl-rewrite-contract.js +21 -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 -51
- package/internal/runtime-contexts.js +82 -10
- package/internal/runtime-messages.js +111 -0
- package/internal/session-cell-executor.js +455 -91
- package/internal/session-journal-recovery.js +684 -0
- package/internal/session-journal-schema.js +106 -0
- package/internal/session-journal.js +237 -440
- package/internal/session-log-view.js +42 -9
- package/internal/session-runtime.js +325 -80
- package/internal/session-state.js +249 -40
- 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 +81 -11
- package/src/client-activity.js +109 -45
- 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,20 +50,26 @@ 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
|
-
|
|
47
|
-
autoRewriteImports
|
|
48
|
-
autoStripExports
|
|
49
|
-
autoSplitRedeclarations
|
|
50
|
-
|
|
51
|
-
) {
|
|
52
|
-
const redeclaration = looseTopLevelRedeclarations
|
|
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
|
|
67
|
+
const variableRedeclaration = looseTopLevelRedeclarations
|
|
53
68
|
? 'Repeated top-level `const`/`let` declarations replace existing bindings.'
|
|
54
|
-
: '
|
|
69
|
+
: 'Repeated top-level variable declarations fail before execution, so reuse existing bindings or place one-off declarations inside a block.'
|
|
70
|
+
const functionClassRedeclaration = looseTopLevelFunctionClassRedeclarations
|
|
71
|
+
? 'Repeated top-level named `function`/`class` declarations replace existing writable bindings at their declaration position. Do not rely on function hoisting or class TDZ; define a replacement before using that name in the cell.'
|
|
72
|
+
: 'Repeated top-level `function`/`class` declarations remain unsupported; assign a function or class expression to an existing writable binding, or place one-off declarations inside a block.'
|
|
55
73
|
const moduleSyntax = autoRewriteImports && autoStripExports
|
|
56
74
|
? 'static `import` declarations are adapted with live, read-only bindings and top-level `export` modifiers are stripped automatically.'
|
|
57
75
|
: autoRewriteImports
|
|
@@ -62,72 +80,133 @@ function replGuidance(
|
|
|
62
80
|
const splitSyntax = autoSplitRedeclarations
|
|
63
81
|
? 'Mixed new/existing top-level destructuring is split automatically while preserving assignment semantics.'
|
|
64
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.'}`
|
|
65
88
|
const recovery = durableReplay
|
|
66
89
|
? 'Direct Node/OS access remains live but is not replayed after a kernel restart.'
|
|
67
90
|
: 'Durable replay is disabled for this profile. Bindings remain reusable only in the current process; a new kernel starts empty.'
|
|
68
91
|
const cordisRecovery = cordisToolsEnabled
|
|
69
|
-
? '
|
|
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.'
|
|
70
93
|
: ''
|
|
71
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.
|
|
72
95
|
|
|
73
|
-
|
|
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.
|
|
74
97
|
|
|
75
98
|
## Cell conventions
|
|
76
|
-
|
|
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}
|
|
77
100
|
|
|
78
101
|
## Available capabilities
|
|
79
|
-
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.
|
|
80
104
|
|
|
81
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}` : ''}`
|
|
82
106
|
}
|
|
83
107
|
|
|
84
108
|
/** Register the session-bound REPL runtime. */
|
|
85
|
-
function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId) {
|
|
109
|
+
function installPtCRuntime(ctx, resolvedConfig, seam, toolSchemasForAgent, sessionId) {
|
|
86
110
|
const presentationGeneration = randomUUID()
|
|
87
111
|
const replMemoryProjection = createReplMemoryProjection(presentationGeneration)
|
|
112
|
+
const userBindingDraftProjection = createUserBindingDraftProjection(presentationGeneration)
|
|
88
113
|
let activeConfig = resolvedConfig
|
|
89
114
|
let cordisTools
|
|
115
|
+
let cordisToolsReady = false
|
|
116
|
+
const cordisOwners = new Set()
|
|
90
117
|
let runtimeBridge
|
|
91
118
|
let editTransport
|
|
92
119
|
let directSurface
|
|
120
|
+
let userBindings
|
|
121
|
+
let observationInterest
|
|
93
122
|
let ready
|
|
94
123
|
let pendingCordisActivation
|
|
124
|
+
let draftProjectionRegistration
|
|
125
|
+
let draftProjectionAvailable = false
|
|
126
|
+
let projectionService
|
|
95
127
|
const disposers = []
|
|
128
|
+
const disposedOwners = new Set()
|
|
96
129
|
let disposed = false
|
|
97
|
-
|
|
98
|
-
|
|
130
|
+
let disposal
|
|
131
|
+
function dispose() {
|
|
99
132
|
disposed = true
|
|
100
133
|
pendingCordisActivation?.cancel()
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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) })
|
|
109
147
|
}
|
|
110
|
-
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 => {
|
|
111
161
|
try {
|
|
112
|
-
|
|
162
|
+
return Promise.resolve(task.run())
|
|
113
163
|
} catch (error) {
|
|
114
|
-
|
|
164
|
+
return Promise.reject(error)
|
|
115
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
|
|
116
190
|
}
|
|
117
|
-
|
|
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')
|
|
118
197
|
}
|
|
119
198
|
const awaitCordisToolsOwner = async (owner, cancelled) => {
|
|
120
199
|
try {
|
|
121
200
|
await Promise.race([owner.ready, cancelled])
|
|
201
|
+
if (cordisTools === owner) cordisToolsReady = true
|
|
122
202
|
return owner
|
|
123
203
|
} catch (error) {
|
|
124
204
|
let rollbackError
|
|
125
205
|
try {
|
|
126
|
-
await owner
|
|
206
|
+
await disposeCordisToolsOwner(owner)
|
|
127
207
|
} catch (caught) {
|
|
128
208
|
rollbackError = caught
|
|
129
209
|
}
|
|
130
|
-
if (cordisTools === owner) cordisTools = undefined
|
|
131
210
|
if (rollbackError !== undefined) {
|
|
132
211
|
throw new AggregateError(
|
|
133
212
|
[error, rollbackError],
|
|
@@ -139,13 +218,17 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
|
|
|
139
218
|
}
|
|
140
219
|
}
|
|
141
220
|
const activateCordisToolsOwner = async () => {
|
|
221
|
+
await disposeRetainedCordisTools()
|
|
222
|
+
if (disposed) throw new Error('ptc-plus: Cordis activation cancelled')
|
|
142
223
|
const owner = createCordisToolsOwner(ctx)
|
|
224
|
+
cordisOwners.add(owner)
|
|
143
225
|
let cancel
|
|
144
226
|
const cancelled = new Promise((_resolve, reject) => {
|
|
145
227
|
cancel = () => reject(new Error('ptc-plus: Cordis activation cancelled'))
|
|
146
228
|
})
|
|
147
229
|
const activation = { owner, cancel }
|
|
148
230
|
cordisTools = owner
|
|
231
|
+
cordisToolsReady = false
|
|
149
232
|
pendingCordisActivation = activation
|
|
150
233
|
try {
|
|
151
234
|
return await awaitCordisToolsOwner(owner, cancelled)
|
|
@@ -154,19 +237,92 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
|
|
|
154
237
|
}
|
|
155
238
|
}
|
|
156
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
|
+
}
|
|
157
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())
|
|
158
310
|
if (typeof ctx.inject === 'function') {
|
|
159
311
|
const projectionInjection = ctx.inject(['sessionProjections'], (scope) => {
|
|
160
312
|
if (disposed) return
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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')
|
|
170
326
|
})
|
|
171
327
|
if (typeof projectionInjection === 'function') {
|
|
172
328
|
disposers.push(projectionInjection)
|
|
@@ -174,23 +330,32 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
|
|
|
174
330
|
disposers.push(() => projectionInjection.dispose())
|
|
175
331
|
}
|
|
176
332
|
}
|
|
177
|
-
cordisTools = activeConfig.cordisToolsEnabled
|
|
178
|
-
? createCordisToolsOwner(ctx)
|
|
179
|
-
: undefined
|
|
333
|
+
cordisTools = activeConfig.cordisToolsEnabled ? createCordisToolsOwner(ctx) : undefined
|
|
180
334
|
if (cordisTools !== undefined) {
|
|
181
335
|
const initialCordisTools = cordisTools
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
throw error
|
|
185
|
-
})
|
|
336
|
+
cordisOwners.add(initialCordisTools)
|
|
337
|
+
ready = awaitCordisToolsOwner(initialCordisTools, new Promise(() => {}))
|
|
186
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
|
+
})
|
|
187
347
|
runtimeBridge = createRuntimeBridgeOwner({
|
|
188
348
|
ctx,
|
|
349
|
+
seam,
|
|
350
|
+
observeSession: id => observationInterest.has(id),
|
|
189
351
|
sessionConfig: activeConfig,
|
|
352
|
+
userBindingsCwd: userBindings.cwd,
|
|
190
353
|
maxNestedRunCodeDepth: activeConfig.maxNestedRunCodeDepth,
|
|
191
354
|
presentationGeneration,
|
|
192
355
|
sessionId,
|
|
193
356
|
toolSchemasForAgent,
|
|
357
|
+
userBindingDraftForAgent: agent => userBindings.draftCapabilityForAgent(agent),
|
|
358
|
+
bindingSubmissionForAgent: (agent, ensureLease, onAccepted) => userBindings.submissionForAgent(agent, ensureLease, onAccepted),
|
|
194
359
|
})
|
|
195
360
|
editTransport = createEditTransportOwner(ctx, {
|
|
196
361
|
durableReplay: activeConfig.durableReplay,
|
|
@@ -205,20 +370,17 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
|
|
|
205
370
|
canonicalizeToolCalls: activeConfig.canonicalizeToolCalls,
|
|
206
371
|
sessionId,
|
|
207
372
|
toolSchemasForAgent,
|
|
373
|
+
userBindingsForAgent: () => userBindings.snapshot(),
|
|
374
|
+
setAgentPresentation: (agent, presentation) => (
|
|
375
|
+
userBindings.setAgentPresentation(agent, presentation)
|
|
376
|
+
),
|
|
208
377
|
})
|
|
209
378
|
disposers.push(ctx.systemPrompt.section({
|
|
210
379
|
name: 'tools:ptc-plus-repl',
|
|
211
380
|
order: 98,
|
|
212
381
|
text: context => {
|
|
213
382
|
if (ctx.tools.get(RUN_CODE, context?.scope) === undefined) return ''
|
|
214
|
-
return replGuidance(
|
|
215
|
-
activeConfig.looseTopLevelRedeclarations,
|
|
216
|
-
activeConfig.durableReplay,
|
|
217
|
-
activeConfig.autoRewriteImports,
|
|
218
|
-
activeConfig.autoStripExports,
|
|
219
|
-
activeConfig.autoSplitRedeclarations,
|
|
220
|
-
activeConfig.cordisToolsEnabled,
|
|
221
|
-
)
|
|
383
|
+
return replGuidance(guidancePolicies(activeConfig))
|
|
222
384
|
},
|
|
223
385
|
}))
|
|
224
386
|
disposers.push(ctx.on('system-prompt/assemble', (assembly, context, next) => (
|
|
@@ -230,7 +392,13 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
|
|
|
230
392
|
if (rejected !== undefined) return rejected
|
|
231
393
|
if (exec.name === RUN_CODE) {
|
|
232
394
|
const executionArguments = directSurface.executionArguments(exec)
|
|
233
|
-
|
|
395
|
+
const requestUserBindings = directSurface.executionUserBindings(exec)
|
|
396
|
+
return Promise.resolve(runtimeBridge.handleExecute(
|
|
397
|
+
exec,
|
|
398
|
+
next,
|
|
399
|
+
executionArguments,
|
|
400
|
+
requestUserBindings,
|
|
401
|
+
))
|
|
234
402
|
.then(result => directSurface.argumentDiagnostic(exec, result))
|
|
235
403
|
}
|
|
236
404
|
return next()
|
|
@@ -240,22 +408,48 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
|
|
|
240
408
|
if (exec.name === EDIT_RUN_CODE) return editTransport.handleResult(exec, result)
|
|
241
409
|
if (exec.name === RUN_CODE) return runtimeBridge.handleResult(exec, result)
|
|
242
410
|
}))
|
|
243
|
-
disposers.push(ctx.on('agent/disposed', ({ agent }) => {
|
|
411
|
+
disposers.push(ctx.on('agent/disposed', async ({ agent }) => {
|
|
244
412
|
directSurface.disposeAgent(agent)
|
|
245
413
|
editTransport.disposeAgent(agent)
|
|
246
|
-
|
|
414
|
+
await Promise.all([
|
|
415
|
+
runtimeBridge.disposeAgent(agent),
|
|
416
|
+
userBindings.clearAgentPresentation(agent),
|
|
417
|
+
])
|
|
247
418
|
}))
|
|
248
|
-
disposers.push(ctx.on('session/disposed', (session) => {
|
|
419
|
+
disposers.push(ctx.on('session/disposed', async (session) => {
|
|
249
420
|
directSurface.disposeSession(session)
|
|
250
421
|
editTransport.disposeSession(session)
|
|
251
|
-
|
|
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)
|
|
252
439
|
}))
|
|
253
440
|
} catch (error) {
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
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 })
|
|
257
445
|
}
|
|
258
|
-
|
|
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
|
|
259
453
|
}
|
|
260
454
|
async function reconfigure(nextConfig) {
|
|
261
455
|
if (disposed) return
|
|
@@ -268,26 +462,34 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
|
|
|
268
462
|
rollbacks.push(() => editTransport.reconfigure(previousConfig))
|
|
269
463
|
directSurface.reconfigure(nextConfig)
|
|
270
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))
|
|
271
469
|
|
|
470
|
+
if (nextConfig.cordisToolsEnabled && cordisTools !== undefined && !cordisToolsReady) {
|
|
471
|
+
await disposeRetainedCordisTools()
|
|
472
|
+
}
|
|
272
473
|
if (nextConfig.cordisToolsEnabled && cordisTools === undefined) {
|
|
273
474
|
await activateCordisToolsOwner()
|
|
274
475
|
} else if (!nextConfig.cordisToolsEnabled && cordisTools !== undefined) {
|
|
275
476
|
const currentCordis = cordisTools
|
|
477
|
+
cordisToolsReady = false
|
|
276
478
|
try {
|
|
277
|
-
await currentCordis
|
|
278
|
-
cordisTools = undefined
|
|
479
|
+
await disposeCordisToolsOwner(currentCordis)
|
|
279
480
|
} catch (error) {
|
|
280
481
|
try {
|
|
482
|
+
await disposeCordisToolsOwner(currentCordis)
|
|
281
483
|
await activateCordisToolsOwner()
|
|
282
484
|
/* c8 ignore next */
|
|
283
485
|
} catch (rollbackError) {
|
|
284
|
-
cordisTools = currentCordis
|
|
285
486
|
throw new AggregateError([error, rollbackError], 'ptc-plus: Cordis reconfiguration and rollback failed', { cause: error })
|
|
286
487
|
}
|
|
287
488
|
throw error
|
|
288
489
|
}
|
|
289
490
|
}
|
|
290
491
|
activeConfig = nextConfig
|
|
492
|
+
observationInterest.reconfigure(nextConfig.replViewEnabled)
|
|
291
493
|
} catch (error) {
|
|
292
494
|
const rollbackFailures = []
|
|
293
495
|
/* c8 ignore next -- the rollback loop's rejection branch is host-specific. */
|
|
@@ -303,12 +505,25 @@ function installPtCRuntime(ctx, resolvedConfig, toolSchemasForAgent, sessionId)
|
|
|
303
505
|
}
|
|
304
506
|
}
|
|
305
507
|
|
|
306
|
-
return Object.freeze({
|
|
508
|
+
return Object.freeze({
|
|
509
|
+
cancelCordisActivation, dispose, reconfigure, ready,
|
|
510
|
+
contextsForRequest: context => directSurface.contextsForRequest(context),
|
|
511
|
+
})
|
|
307
512
|
}
|
|
308
513
|
|
|
309
514
|
/** Register the session-bound REPL runtime. */
|
|
310
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.
|
|
311
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) {
|
|
312
527
|
const toolSchemasForAgent = agent => typeof ctx.tools.schemas === 'function'
|
|
313
528
|
? ctx.tools.schemas(agent)
|
|
314
529
|
: []
|
|
@@ -318,7 +533,9 @@ export function apply(ctx, config = {}) {
|
|
|
318
533
|
}
|
|
319
534
|
let committed
|
|
320
535
|
let activating
|
|
536
|
+
const retained = new Set()
|
|
321
537
|
let disposed = false
|
|
538
|
+
let controllerDisposal
|
|
322
539
|
let transitionTail = Promise.resolve()
|
|
323
540
|
let pendingTransitions = 0
|
|
324
541
|
const trackTransition = operation => {
|
|
@@ -343,9 +560,26 @@ export function apply(ctx, config = {}) {
|
|
|
343
560
|
return { promise, resolve, reject }
|
|
344
561
|
}
|
|
345
562
|
const disposeRuntime = record => {
|
|
563
|
+
if (record?.disposed === true) return Promise.resolve()
|
|
346
564
|
if (record?.disposal !== undefined) return record.disposal
|
|
347
|
-
|
|
348
|
-
|
|
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')
|
|
349
583
|
}
|
|
350
584
|
const activationError = (error, cleanupError) => new AggregateError(
|
|
351
585
|
[error, cleanupError],
|
|
@@ -371,17 +605,18 @@ export function apply(ctx, config = {}) {
|
|
|
371
605
|
record.completion.resolve({ status: 'superseded' })
|
|
372
606
|
return
|
|
373
607
|
}
|
|
374
|
-
if (
|
|
375
|
-
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')
|
|
376
610
|
}
|
|
377
611
|
let candidate
|
|
378
612
|
try {
|
|
379
|
-
candidate = installPtCRuntime(ctx, record.config, toolSchemasForAgent, sessionId)
|
|
613
|
+
candidate = installPtCRuntime(ctx, record.config, seam, toolSchemasForAgent, sessionId)
|
|
380
614
|
record.runtime = candidate
|
|
381
615
|
} catch (error) {
|
|
382
|
-
const
|
|
383
|
-
if (
|
|
384
|
-
|
|
616
|
+
const failedRuntime = error?.[INSTALL_CLEANUP]
|
|
617
|
+
if (failedRuntime === undefined) throw error
|
|
618
|
+
record.runtime = failedRuntime
|
|
619
|
+
const trackedCleanup = trackTransition(disposeRuntime(record))
|
|
385
620
|
void trackedCleanup.then(
|
|
386
621
|
() => record.completion.reject(error),
|
|
387
622
|
cleanupError => record.completion.reject(activationError(error, cleanupError)),
|
|
@@ -422,6 +657,8 @@ export function apply(ctx, config = {}) {
|
|
|
422
657
|
generation,
|
|
423
658
|
runtime: undefined,
|
|
424
659
|
disposal: undefined,
|
|
660
|
+
disposed: false,
|
|
661
|
+
retiring: false,
|
|
425
662
|
trackedDisposal: undefined,
|
|
426
663
|
cancelled: false,
|
|
427
664
|
completion,
|
|
@@ -450,10 +687,22 @@ export function apply(ctx, config = {}) {
|
|
|
450
687
|
apply(nextConfig, generation) {
|
|
451
688
|
if (disposed) return Promise.resolve({ status: 'superseded' })
|
|
452
689
|
if (activating !== undefined) {
|
|
453
|
-
|
|
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
|
+
})
|
|
454
704
|
return beginActivation(nextConfig, generation, cleanup)
|
|
455
705
|
}
|
|
456
|
-
if (committed === undefined) return beginActivation(nextConfig, generation)
|
|
457
706
|
const current = committed
|
|
458
707
|
if (!nextConfig.cordisToolsEnabled) current.runtime.cancelCordisActivation()
|
|
459
708
|
return enqueueTransition(async () => {
|
|
@@ -464,27 +713,60 @@ export function apply(ctx, config = {}) {
|
|
|
464
713
|
return { status: 'applied' }
|
|
465
714
|
})
|
|
466
715
|
},
|
|
467
|
-
uninstall() {
|
|
468
|
-
const
|
|
716
|
+
async uninstall() {
|
|
717
|
+
const active = activating
|
|
718
|
+
const activationCleanup = active === undefined
|
|
469
719
|
? Promise.resolve()
|
|
470
|
-
: cancelActivation(
|
|
720
|
+
: cancelActivation(active)
|
|
471
721
|
const current = committed
|
|
472
|
-
|
|
722
|
+
if (current !== undefined) current.retiring = true
|
|
473
723
|
const committedCleanup = current === undefined
|
|
474
724
|
? Promise.resolve()
|
|
475
725
|
: trackTransition(disposeRuntime(current))
|
|
476
|
-
|
|
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')
|
|
477
737
|
},
|
|
478
738
|
committedConfig() {
|
|
479
|
-
return committed?.config
|
|
739
|
+
return committed?.retiring ? undefined : committed?.config
|
|
480
740
|
},
|
|
481
|
-
|
|
741
|
+
dispose() {
|
|
482
742
|
disposed = true
|
|
483
|
-
|
|
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
|
|
484
751
|
},
|
|
485
752
|
}
|
|
486
753
|
ctx.effect(() => async () => controller.dispose(), 'ptc-plus runtime lifecycle')
|
|
487
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
|
+
|
|
488
770
|
let configSource = () => resolvedConfig
|
|
489
771
|
let configurationGeneration = 0
|
|
490
772
|
let settingsWriter
|