dsh-ptc-plus 0.4.8 → 0.5.0
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 +61 -123
- package/README.md +61 -123
- package/assets/dsh-ptc-plus-banner-en.webp +0 -0
- package/assets/dsh-ptc-plus-banner-zh.webp +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.cjs +42336 -0
- package/cordis.patch.yml +13 -2
- package/docs/README.md +7 -2
- package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +31 -179
- package/docs/adr/0008-define-agent-operating-contract.md +5 -1
- package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -2
- package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
- package/docs/adr/0017-track-the-latest-dsh-public-surface.md +2 -2
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +14 -3
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
- package/docs/adr/0022-stage-npm-releases-with-oidc.md +4 -2
- package/docs/adr/0023-global-user-bindings.md +3 -1
- package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +2 -0
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +101 -35
- package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +3 -3
- package/docs/adr/0031-close-semantic-obligations-by-owner.md +1 -1
- package/docs/adr/0032-own-session-log-facts-in-a-host-only-projection.md +27 -0
- package/docs/adr/0033-gate-client-visual-usability-in-a-browser.md +23 -0
- package/docs/adr/0034-keep-program-values-separate-from-output-projections.md +29 -0
- package/docs/adr/0035-run-coverage-only-before-a-new-release.md +27 -0
- package/docs/adr/0036-separate-focused-development-from-delivery-verification.md +29 -0
- package/docs/adr/0037-keep-tool-hover-previews-passive.md +52 -0
- package/docs/adr/0038-freeze-session-capability-plans.md +71 -0
- package/docs/adr/0039-verify-documentation-without-runtime-suites.md +29 -0
- package/docs/adr/0040-separate-capability-configurations-from-session-observations.md +39 -0
- package/docs/adr/0041-record-cell-execution-directories.md +65 -0
- package/docs/adr/0042-persist-provider-availability-and-handback.md +41 -0
- package/docs/adr/0043-compose-capability-and-context-policies.md +37 -0
- package/docs/adr/README.md +6 -0
- package/docs/architecture.md +25 -27
- package/docs/binding-review-placement-design.md +29 -21
- package/docs/capability-planning-components.md +93 -0
- package/docs/capability-projection.md +7 -0
- package/docs/client-ui.md +88 -29
- package/docs/client-visual-contract.md +65 -0
- package/docs/context-management.md +22 -0
- package/docs/context-optimization-plan.md +353 -0
- package/docs/context-public-interface-coverage.md +25 -0
- package/docs/durability-design.md +31 -14
- package/docs/evaluation.md +41 -6
- package/docs/evaluations/command-goat-deepseek-v4.1-flash-20261006.md +126 -0
- package/docs/evaluations/command-goat-deepseek-v4.1-flash.md +281 -0
- package/docs/installation.md +50 -8
- package/docs/program-data-plane.md +5 -3
- package/docs/publishing.md +5 -5
- package/docs/release-coverage.md +73 -0
- package/docs/release-notes.md +1 -1
- package/docs/review-procedure.md +56 -0
- package/docs/runtime-reference.md +48 -16
- package/docs/semantic-validation.md +14 -2
- package/docs/stateful-binding-updates-proposal.md +2 -2
- package/docs/user-bindings.en.md +2 -2
- package/docs/user-bindings.md +3 -3
- package/docs/value-wire.md +11 -1
- package/docs/verification-optimization-work.md +52 -15
- package/docs/verification.md +30 -86
- package/index.js +147 -23
- package/internal/binding-toggle-receipt.js +24 -0
- package/internal/capability-command-receipt.js +29 -0
- package/internal/capability-environment.js +1247 -0
- package/internal/capability-plan.js +285 -0
- package/internal/capability-policy.js +96 -0
- package/internal/capability-statistics-store.js +180 -0
- package/internal/capability-statistics.js +210 -0
- package/internal/compiler-platform.js +5 -3
- package/internal/compiler-service.js +2 -9
- package/internal/config-spec.js +79 -9
- package/internal/cordis-tools-owner.js +22 -19
- package/internal/direct-surface-owner.js +29 -29
- package/internal/dynamic-environment-runtime.js +9 -9
- package/internal/edit-transport-owner.js +183 -139
- package/internal/execution-provider-owner.js +160 -0
- package/internal/execution-seam-compat.js +76 -120
- package/internal/isolated-worker.js +12 -8
- package/internal/kernel-worker.js +104 -32
- package/internal/native-root-dynamic.js +12 -13
- package/internal/program-bindings.js +190 -0
- package/internal/recovery-tips.js +53 -23
- package/internal/rejected-cell-editor.js +8 -8
- package/internal/repl-observation-interest.js +11 -3
- package/internal/rpc-contract.js +1 -0
- package/internal/runtime-bridge-owner.js +351 -82
- package/internal/runtime-config.js +58 -0
- package/internal/runtime-contexts.js +85 -16
- package/internal/runtime-intrinsics.js +1 -0
- package/internal/runtime-messages.js +41 -0
- package/internal/sdk-projection.js +10 -82
- package/internal/session-cell-executor.js +42 -19
- package/internal/session-events.js +74 -4
- package/internal/session-journal-recovery.js +904 -184
- package/internal/session-journal-schema.js +6 -3
- package/internal/session-journal.js +31 -2
- package/internal/session-log-view.js +189 -43
- package/internal/session-runtime.js +175 -23
- package/internal/settings-compat.js +23 -6
- package/internal/stateful-module-compiler.js +3 -2
- package/internal/stateful-module-runtime.js +7 -3
- package/internal/stateful-root-runtime.js +9 -4
- package/internal/user-binding-authoring.js +2 -7
- package/internal/user-bindings-owner.js +47 -7
- package/internal/validated-parse-repair.js +1 -1
- package/internal/value-wire.js +68 -21
- package/internal/worker-cwd-virtualization.js +20 -18
- package/internal/worker-realm-surfaces.js +3 -3
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +55 -38
- package/src/client-authoring-view.js +286 -168
- package/src/client-binding-review.js +4 -1
- package/src/client-binding-toggle.js +18 -0
- package/src/client-capabilities-view.js +1341 -0
- package/src/client-capability-sort.js +29 -0
- package/src/client-code-editor.js +6 -0
- package/src/client-copy.js +881 -8
- package/src/client-feature-gates.js +15 -5
- package/src/client-host-compat.js +23 -2
- package/src/client-indicator-view.js +4 -6
- package/src/client-primitives.js +280 -0
- package/src/client-repl-view.js +23 -16
- package/src/client-settings-view.js +412 -69
- package/src/client-styles.js +193 -41
- package/src/client-tool-hover.js +185 -0
- package/src/client-tool-view.js +45 -11
- package/src/client-transport.js +44 -9
- package/src/client-workbench.js +9 -10
- package/src/client.js +148 -68
- 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/client.js +0 -31857
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
JOURNAL_KEY,
|
|
6
6
|
RECOVERY_BOUNDARY_KEY,
|
|
7
7
|
REWRITES_KEY,
|
|
8
|
-
|
|
8
|
+
isCanonicalSequence,
|
|
9
9
|
liveToolCallSeq,
|
|
10
10
|
normalizeJournal,
|
|
11
11
|
normalizeRecoveryBoundaries,
|
|
@@ -17,8 +17,12 @@ import {
|
|
|
17
17
|
editRejectedCell,
|
|
18
18
|
editRunCodeSchema,
|
|
19
19
|
} from './rejected-cell-editor.js'
|
|
20
|
-
import {
|
|
20
|
+
import { projectSessionLog } from './session-log-view.js'
|
|
21
|
+
import { sessionEvents } from './session-events.js'
|
|
22
|
+
import { deepEqualJson, snapshotJsonValue } from '@deepseek-ai/dsh-util-values'
|
|
23
|
+
import { scopeTarget } from '@deepseek-ai/dsh-scope'
|
|
21
24
|
import { RUN_CODE } from './runtime-bridge-owner.js'
|
|
25
|
+
import { CAPABILITY_EVIDENCE_KEY } from './capability-environment.js'
|
|
22
26
|
import { isRecord } from './record-utils.js'
|
|
23
27
|
import {
|
|
24
28
|
userBindingDraftCapabilityFromMeta,
|
|
@@ -35,6 +39,34 @@ import {
|
|
|
35
39
|
|
|
36
40
|
export const EDIT_RUN_CODE = 'edit_run_code'
|
|
37
41
|
|
|
42
|
+
function nestedEditStart(sessionLog, exec) {
|
|
43
|
+
const events = sessionEvents(sessionLog)
|
|
44
|
+
if (!Array.isArray(events) || typeof exec?.callId !== 'string'
|
|
45
|
+
|| typeof exec?.rootCallId !== 'string') return undefined
|
|
46
|
+
const rootCallSeq = liveToolCallSeq(sessionLog, exec.rootCallId, RUN_CODE)
|
|
47
|
+
if (rootCallSeq === undefined) return undefined
|
|
48
|
+
const roots = events.flatMap((event, index) => event?.seq === rootCallSeq
|
|
49
|
+
? [{ event, index }] : [])
|
|
50
|
+
if (roots.length !== 1 || roots[0].event?.type !== 'tool/call'
|
|
51
|
+
|| roots[0].event.data?.callId !== exec.rootCallId
|
|
52
|
+
|| roots[0].event.data?.name !== RUN_CODE) return undefined
|
|
53
|
+
const matches = events.flatMap((event, index) => (
|
|
54
|
+
index > roots[0].index && event?.type === 'tool/ptc-dispatch-start'
|
|
55
|
+
&& event.data?.subCallId === exec.callId
|
|
56
|
+
&& event.data?.name === EDIT_RUN_CODE
|
|
57
|
+
? [{ event, index }] : []
|
|
58
|
+
))
|
|
59
|
+
if (matches.length !== 1) return undefined
|
|
60
|
+
const { event, index } = matches[0]
|
|
61
|
+
if (!isCanonicalSequence(event.seq) || event.seq <= rootCallSeq
|
|
62
|
+
|| events.filter(candidate => candidate?.seq === event.seq).length !== 1
|
|
63
|
+
|| event.data.rootCallId !== exec.rootCallId
|
|
64
|
+
|| event.data.parentCallId !== exec.rootCallId
|
|
65
|
+
|| !deepEqualJson(event.data.arguments, exec.arguments)) return undefined
|
|
66
|
+
const target = projectSessionLog({ session: { events: events.slice(0, index + 1) } }).editableRun
|
|
67
|
+
return { startSeq: event.seq, rootCallSeq, target }
|
|
68
|
+
}
|
|
69
|
+
|
|
38
70
|
function unavailableDerivedResult(inner) {
|
|
39
71
|
if (inner?.isError !== true) {
|
|
40
72
|
throw new Error('ptc-plus: derived run_code result did not contain a valid execution journal')
|
|
@@ -61,17 +93,25 @@ function derivedEditResult(inner) {
|
|
|
61
93
|
return result
|
|
62
94
|
}
|
|
63
95
|
|
|
96
|
+
function derivedPresentation(inner) {
|
|
97
|
+
return {
|
|
98
|
+
content: inner.content,
|
|
99
|
+
...(inner.additionalContexts === undefined ? {} : { additionalContexts: inner.additionalContexts }),
|
|
100
|
+
...(inner.concludesTurn === true ? { concludesTurn: true } : {}),
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
64
104
|
export function createEditTransportOwner(ctx, {
|
|
65
|
-
|
|
105
|
+
canScheduleEdit,
|
|
66
106
|
executeTentative,
|
|
67
107
|
presentationGeneration,
|
|
108
|
+
scheduleEdit,
|
|
68
109
|
sessionId,
|
|
69
110
|
toolSchemasForAgent,
|
|
111
|
+
readSessionLog,
|
|
70
112
|
}) {
|
|
71
|
-
let currentDurableReplay = durableReplay
|
|
72
|
-
const editExecutionMetadata = new WeakMap()
|
|
73
113
|
const editClaims = new Map()
|
|
74
|
-
const
|
|
114
|
+
const pendingAcceptances = new Map()
|
|
75
115
|
const installedScopes = new Map()
|
|
76
116
|
|
|
77
117
|
const releaseClaim = (id, targetCallSeq) => {
|
|
@@ -81,10 +121,10 @@ export function createEditTransportOwner(ctx, {
|
|
|
81
121
|
}
|
|
82
122
|
|
|
83
123
|
const discardSettlements = (id) => {
|
|
84
|
-
for (const [exec, pending] of
|
|
124
|
+
for (const [exec, pending] of pendingAcceptances) {
|
|
85
125
|
if (id !== undefined && pending.id !== id) continue
|
|
86
|
-
|
|
87
|
-
pending.
|
|
126
|
+
pendingAcceptances.delete(exec)
|
|
127
|
+
pending.finish(false)
|
|
88
128
|
}
|
|
89
129
|
}
|
|
90
130
|
|
|
@@ -98,6 +138,7 @@ export function createEditTransportOwner(ctx, {
|
|
|
98
138
|
description: derived.description,
|
|
99
139
|
},
|
|
100
140
|
}
|
|
141
|
+
if (derived.capabilityEvidence !== undefined) meta[CAPABILITY_EVIDENCE_KEY] = derived.capabilityEvidence
|
|
101
142
|
if (derived.rewrites !== undefined) {
|
|
102
143
|
meta[REWRITES_KEY] = normalizeRewrites(derived.rewrites)
|
|
103
144
|
}
|
|
@@ -123,124 +164,162 @@ export function createEditTransportOwner(ctx, {
|
|
|
123
164
|
type: 'object',
|
|
124
165
|
additionalProperties: false,
|
|
125
166
|
properties: {
|
|
126
|
-
|
|
167
|
+
accepted: { type: 'boolean' },
|
|
168
|
+
scheduled: { type: 'boolean' },
|
|
169
|
+
target_call_seq: { type: 'integer' },
|
|
127
170
|
reason: { type: 'string' },
|
|
128
|
-
error: { type: 'string' },
|
|
129
|
-
value: {},
|
|
130
|
-
logs: { type: 'array', items: { type: 'string' } },
|
|
131
171
|
},
|
|
132
|
-
required: ['
|
|
172
|
+
required: ['accepted', 'scheduled'],
|
|
133
173
|
},
|
|
134
174
|
render: (_args, value) => [{ type: 'text', text: JSON.stringify(value ?? {}) }],
|
|
135
|
-
presentationMeta: (args) => {
|
|
136
|
-
const meta = {}
|
|
137
|
-
if (!isRecord(args)) return meta
|
|
138
|
-
const derived = editExecutionMetadata.get(args)
|
|
139
|
-
if (derived === undefined) return meta
|
|
140
|
-
return derivedMetadata(derived)
|
|
141
|
-
},
|
|
142
175
|
}
|
|
143
176
|
definition.execute = async (args, exec) => {
|
|
177
|
+
if (canScheduleEdit?.(exec) !== true) {
|
|
178
|
+
return { accepted: false, scheduled: false, reason: 'edit requires an active root run_code cell' }
|
|
179
|
+
}
|
|
144
180
|
const agent = exec?.agent
|
|
145
|
-
const
|
|
146
|
-
const
|
|
147
|
-
if (
|
|
148
|
-
throw new Error('ptc-plus: edit_run_code requires a unique persisted
|
|
181
|
+
const sessionLog = await readSessionLog(agent?.session, exec.signal)
|
|
182
|
+
const start = nestedEditStart(sessionLog, exec)
|
|
183
|
+
if (start === undefined) {
|
|
184
|
+
throw new Error('ptc-plus: edit_run_code requires a unique persisted PTC sub-call start')
|
|
149
185
|
}
|
|
150
|
-
const target =
|
|
151
|
-
? undefined
|
|
152
|
-
: editTargetForCall(agent, callId, persistedTargetCallSeq)
|
|
186
|
+
const target = start.target
|
|
153
187
|
if (target?.source === undefined || target.callSeq === undefined) {
|
|
154
|
-
return {
|
|
188
|
+
return { accepted: false, scheduled: false, reason: 'no run_code cell is currently eligible for safe editing' }
|
|
155
189
|
}
|
|
156
190
|
const edited = editRejectedCell(args, target.source)
|
|
157
|
-
if (!edited.edited) return edited
|
|
191
|
+
if (!edited.edited) return { accepted: false, scheduled: false, reason: edited.reason }
|
|
158
192
|
const expectedTargetCallSeq = args[EXPECTED_TARGET_CALL_SEQ]
|
|
159
193
|
if (expectedTargetCallSeq !== undefined && expectedTargetCallSeq !== target.callSeq) {
|
|
160
194
|
return {
|
|
161
|
-
|
|
195
|
+
accepted: false,
|
|
196
|
+
scheduled: false,
|
|
162
197
|
reason: `validated repair targets run_code call ${expectedTargetCallSeq}, but this edit captured call ${target.callSeq}`,
|
|
163
198
|
}
|
|
164
199
|
}
|
|
165
200
|
if (typeof ctx.tools.execute !== 'function') {
|
|
166
201
|
throw new Error('ptc-plus: DSH tools.execute is required for derived edit execution')
|
|
167
202
|
}
|
|
168
|
-
const persistedCallSeq = persistedTargetCallSeq
|
|
169
203
|
const id = sessionId(agent)
|
|
170
204
|
let claims = editClaims.get(id)
|
|
171
205
|
if (claims === undefined) {
|
|
172
206
|
claims = new Map()
|
|
173
207
|
editClaims.set(id, claims)
|
|
174
|
-
} else {
|
|
175
|
-
for (const [callSeq, phase] of claims) {
|
|
176
|
-
if (phase === 'settled' && callSeq !== target.callSeq) claims.delete(callSeq)
|
|
177
|
-
}
|
|
178
208
|
}
|
|
179
209
|
if (claims.has(target.callSeq)) {
|
|
180
|
-
return {
|
|
210
|
+
return { accepted: false, scheduled: false, reason: 'the current edit target already has a derived execution' }
|
|
181
211
|
}
|
|
182
|
-
claims.set(target.callSeq, '
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
212
|
+
claims.set(target.callSeq, 'scheduled')
|
|
213
|
+
const receipt = { accepted: true, scheduled: true, target_call_seq: target.callSeq }
|
|
214
|
+
const pending = {
|
|
215
|
+
id,
|
|
216
|
+
receipt,
|
|
217
|
+
confirmed: false,
|
|
218
|
+
startSeq: start.startSeq,
|
|
219
|
+
rootCallSeq: start.rootCallSeq,
|
|
220
|
+
subCallId: exec.callId,
|
|
221
|
+
targetCallSeq: target.callSeq,
|
|
222
|
+
finalized: false,
|
|
223
|
+
finalizeTentative: undefined,
|
|
224
|
+
finish(confirmed) {
|
|
225
|
+
if (this.finalized) return
|
|
226
|
+
this.finalized = true
|
|
227
|
+
pendingAcceptances.delete(exec)
|
|
228
|
+
this.finalizeTentative?.(confirmed)
|
|
229
|
+
releaseClaim(id, target.callSeq)
|
|
230
|
+
},
|
|
231
|
+
async dispatch(rootSignal, rootExec) {
|
|
232
|
+
if (!this.confirmed || this.finalized || rootSignal.aborted) {
|
|
233
|
+
this.finish(false)
|
|
234
|
+
return { outcome: { edited: false, reason: 'accepted edit was cancelled before derived execution' } }
|
|
235
|
+
}
|
|
236
|
+
if (rootExec?.name !== RUN_CODE || rootExec.callId !== exec.rootCallId
|
|
237
|
+
|| rootExec.agent !== agent || rootExec.token === undefined) {
|
|
238
|
+
throw new Error('ptc-plus: derived edit requires its actual root transport execution')
|
|
239
|
+
}
|
|
240
|
+
const argumentsValue = snapshotJsonValue({ code: edited.code, description: edited.description })
|
|
241
|
+
if (argumentsValue === undefined) throw new Error('ptc-plus: derived source is not losslessly JSON-serializable')
|
|
242
|
+
const executionLog = await readSessionLog(agent.session, rootSignal)
|
|
243
|
+
if (rootSignal.aborted || this.finalized) {
|
|
244
|
+
this.finish(false)
|
|
245
|
+
return { outcome: { edited: false, reason: 'accepted edit was cancelled before derived execution' } }
|
|
246
|
+
}
|
|
247
|
+
const data = { rootCallId: exec.rootCallId, parentCallId: rootExec.callId,
|
|
248
|
+
subCallId: `${String(exec.callId)}:derived`, name: RUN_CODE, arguments: argumentsValue }
|
|
249
|
+
const executionStart = agent.session.append('tool/ptc-dispatch-start', data)
|
|
250
|
+
const derivedExec = { callId: data.subCallId, rootCallId: data.rootCallId,
|
|
251
|
+
parent: rootExec.token, name: RUN_CODE, arguments: argumentsValue, agent, signal: rootSignal }
|
|
252
|
+
const dispatch = () => ctx.tools.execute(derivedExec)
|
|
253
|
+
const tentative = await executeTentative(executionStart.seq, start.rootCallSeq,
|
|
254
|
+
dispatch, executionLog, derivedExec)
|
|
255
|
+
const inner = tentative.result
|
|
256
|
+
const metadata = tentative.metadata ?? inner.meta
|
|
257
|
+
this.finalizeTentative = tentative.finalize
|
|
258
|
+
let content = inner.content
|
|
259
|
+
try {
|
|
260
|
+
content = await ctx.waterfall(scopeTarget(ctx.tools, agent), 'tools/ptc-dispatch-log', {
|
|
261
|
+
exec: rootExec, agent, subCallId: data.subCallId, name: RUN_CODE,
|
|
262
|
+
isError: inner.isError, content: inner.content,
|
|
263
|
+
}, () => Promise.resolve(inner.content))
|
|
264
|
+
} catch {
|
|
265
|
+
// A log projector cannot change the settled execution or omit its fact.
|
|
266
|
+
content = inner.content
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
agent.session.append('tool/ptc-dispatch', { ...data, isError: inner.isError,
|
|
270
|
+
...(inner.error?.info === undefined ? {} : { error: inner.error.info }),
|
|
271
|
+
content,
|
|
272
|
+
...(metadata === undefined ? {} : { meta: metadata }),
|
|
273
|
+
})
|
|
274
|
+
} catch (error) {
|
|
275
|
+
this.finish(false)
|
|
276
|
+
throw error
|
|
277
|
+
}
|
|
278
|
+
const journalValue = isRecord(metadata) && Object.hasOwn(metadata, JOURNAL_KEY)
|
|
279
|
+
? metadata[JOURNAL_KEY] : undefined
|
|
280
|
+
const journal = journalValue === undefined ? undefined : normalizeJournal(journalValue)
|
|
281
|
+
if (journal === undefined || journal.status === 'noop'
|
|
282
|
+
|| inner.isError !== (journal.completion?.kind === 'throw' || journal.status === 'discarded')) {
|
|
283
|
+
this.finish(false)
|
|
284
|
+
return { outcome: unavailableDerivedResult(inner), presentation: derivedPresentation(inner) }
|
|
285
|
+
}
|
|
286
|
+
const derived = {
|
|
287
|
+
targetCallSeq: target.callSeq,
|
|
288
|
+
journal,
|
|
289
|
+
rewrites: validatedRewrites(metadata),
|
|
290
|
+
recoveryBoundaries: isRecord(metadata) && Object.hasOwn(metadata, RECOVERY_BOUNDARY_KEY)
|
|
291
|
+
? normalizeRecoveryBoundaries(metadata[RECOVERY_BOUNDARY_KEY]) : undefined,
|
|
292
|
+
replMemory: validatedReplMemorySnapshot(metadata, presentationGeneration),
|
|
293
|
+
userBindings: userBindingsSnapshotFromMeta(metadata),
|
|
294
|
+
draftCapability: userBindingDraftCapabilityFromMeta(metadata, presentationGeneration),
|
|
295
|
+
capabilityEvidence: metadata?.[CAPABILITY_EVIDENCE_KEY],
|
|
296
|
+
code: edited.code,
|
|
297
|
+
description: edited.description,
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
outcome: derivedEditResult(inner),
|
|
301
|
+
presentation: derivedPresentation(inner),
|
|
302
|
+
provenance: {
|
|
303
|
+
startSeq: start.startSeq,
|
|
304
|
+
executionStartSeq: executionStart.seq,
|
|
305
|
+
rootCallSeq: start.rootCallSeq,
|
|
306
|
+
subCallId: exec.callId,
|
|
307
|
+
meta: derivedMetadata(derived),
|
|
308
|
+
},
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
}
|
|
312
|
+
if (scheduleEdit?.(exec, pending) !== true) {
|
|
313
|
+
pending.finish(false)
|
|
314
|
+
return { accepted: false, scheduled: false, reason: 'edit requires an active root run_code cell' }
|
|
238
315
|
}
|
|
316
|
+
pendingAcceptances.set(exec, pending)
|
|
317
|
+
return receipt
|
|
239
318
|
}
|
|
240
319
|
|
|
241
320
|
const disposeInstalled = (installed) => {
|
|
242
321
|
const failures = []
|
|
243
|
-
for (const key of ['
|
|
322
|
+
for (const key of ['disposeRegistration']) {
|
|
244
323
|
const dispose = installed[key]
|
|
245
324
|
if (dispose === undefined) continue
|
|
246
325
|
try {
|
|
@@ -261,8 +340,7 @@ export function createEditTransportOwner(ctx, {
|
|
|
261
340
|
try {
|
|
262
341
|
disposeInstalled(installed)
|
|
263
342
|
} finally {
|
|
264
|
-
if (installed.
|
|
265
|
-
&& installed.disposeRegistration === undefined) {
|
|
343
|
+
if (installed.disposeRegistration === undefined) {
|
|
266
344
|
installedScopes.delete(agent)
|
|
267
345
|
}
|
|
268
346
|
}
|
|
@@ -270,23 +348,13 @@ export function createEditTransportOwner(ctx, {
|
|
|
270
348
|
|
|
271
349
|
return Object.freeze({
|
|
272
350
|
definition,
|
|
273
|
-
reconfigure(nextConfig) {
|
|
274
|
-
currentDurableReplay = nextConfig.durableReplay
|
|
275
|
-
},
|
|
276
351
|
isInstalled(agent) {
|
|
277
352
|
const installed = installedScopes.get(agent)
|
|
278
|
-
return installed?.
|
|
279
|
-
&& installed.disposeRegistration !== undefined
|
|
353
|
+
return installed?.disposeRegistration !== undefined
|
|
280
354
|
},
|
|
281
355
|
ensureInstalled(agent) {
|
|
282
356
|
const installed = installedScopes.get(agent)
|
|
283
|
-
if (installed !== undefined)
|
|
284
|
-
if (installed.disposePresentation !== undefined
|
|
285
|
-
&& installed.disposeRegistration !== undefined) {
|
|
286
|
-
return installed.definition
|
|
287
|
-
}
|
|
288
|
-
uninstall(agent)
|
|
289
|
-
}
|
|
357
|
+
if (installed !== undefined) return installed.definition
|
|
290
358
|
const liveSchemas = typeof toolSchemasForAgent === 'function'
|
|
291
359
|
? toolSchemasForAgent(agent)
|
|
292
360
|
: []
|
|
@@ -295,8 +363,8 @@ export function createEditTransportOwner(ctx, {
|
|
|
295
363
|
throw new Error('ptc-plus: cannot install edit_run_code; the agent scope already owns that tool name')
|
|
296
364
|
}
|
|
297
365
|
const scopedTools = agent?.ctx?.tools
|
|
298
|
-
if (typeof scopedTools?.register !== 'function'
|
|
299
|
-
throw new Error('ptc-plus: DSH agent-scoped tools.register
|
|
366
|
+
if (typeof scopedTools?.register !== 'function') {
|
|
367
|
+
throw new Error('ptc-plus: DSH agent-scoped tools.register is required to expose edit_run_code')
|
|
300
368
|
}
|
|
301
369
|
const disposeRegistration = scopedTools.register(definition)
|
|
302
370
|
if (typeof disposeRegistration !== 'function') {
|
|
@@ -304,43 +372,19 @@ export function createEditTransportOwner(ctx, {
|
|
|
304
372
|
}
|
|
305
373
|
const nextInstalled = {
|
|
306
374
|
definition,
|
|
307
|
-
disposePresentation: undefined,
|
|
308
375
|
disposeRegistration,
|
|
309
376
|
sessionId: sessionId(agent),
|
|
310
377
|
}
|
|
311
378
|
installedScopes.set(agent, nextInstalled)
|
|
312
|
-
try {
|
|
313
|
-
const disposePresentation = scopedTools.presentAs('both')
|
|
314
|
-
if (typeof disposePresentation !== 'function') {
|
|
315
|
-
throw new Error('ptc-plus: DSH tools.presentAs did not return a disposer')
|
|
316
|
-
}
|
|
317
|
-
nextInstalled.disposePresentation = disposePresentation
|
|
318
|
-
} catch (error) {
|
|
319
|
-
try {
|
|
320
|
-
uninstall(agent)
|
|
321
|
-
} catch (cleanupError) {
|
|
322
|
-
throw new AggregateError(
|
|
323
|
-
[error, cleanupError],
|
|
324
|
-
'ptc-plus: edit_run_code installation and rollback failed',
|
|
325
|
-
{ cause: error },
|
|
326
|
-
)
|
|
327
|
-
}
|
|
328
|
-
throw error
|
|
329
|
-
}
|
|
330
379
|
return definition
|
|
331
380
|
},
|
|
332
381
|
handleResult(exec, result) {
|
|
333
|
-
const pending =
|
|
334
|
-
pendingSettlements.delete(exec)
|
|
382
|
+
const pending = pendingAcceptances.get(exec)
|
|
335
383
|
if (pending === undefined) return
|
|
336
|
-
const
|
|
337
|
-
result
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
)
|
|
341
|
-
pending.finalize(persisted)
|
|
342
|
-
if (persisted) return
|
|
343
|
-
releaseClaim(pending.id, pending.targetCallSeq)
|
|
384
|
+
const accepted = result?.isError === false
|
|
385
|
+
&& deepEqualJson(result.value, pending.receipt)
|
|
386
|
+
pending.confirmed = accepted
|
|
387
|
+
if (!accepted) pending.finish(false)
|
|
344
388
|
},
|
|
345
389
|
disposeAgent(agent) {
|
|
346
390
|
let failure
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { createExecutionSeam, EXECUTION_SEAM_SERVICE, LEGACY_EXECUTION_SEAM_SERVICE } from './execution-seam-compat.js'
|
|
2
|
+
|
|
3
|
+
export const name = 'ptc-plus-execution-provider'
|
|
4
|
+
export const inject = ['loader']
|
|
5
|
+
|
|
6
|
+
export function apply(ctx, config = {}) {
|
|
7
|
+
const ownerFiber = ctx.fiber
|
|
8
|
+
const { originalEntryId = 'ptc-runtime', isolation = 'ptc-plus-original', serviceName = EXECUTION_SEAM_SERVICE } = config
|
|
9
|
+
if (typeof originalEntryId !== 'string' || originalEntryId.length === 0
|
|
10
|
+
|| typeof isolation !== 'string' || isolation.length === 0) {
|
|
11
|
+
throw new TypeError('ptc-plus: execution provider requires an original entry id and named isolation')
|
|
12
|
+
}
|
|
13
|
+
if (![EXECUTION_SEAM_SERVICE, LEGACY_EXECUTION_SEAM_SERVICE].includes(serviceName)) {
|
|
14
|
+
throw new TypeError('ptc-plus: execution provider serviceName must be codeRuntime or ptcRuntime')
|
|
15
|
+
}
|
|
16
|
+
// Profile rows belong to the Include's tree; the global Loader resolves root IDs.
|
|
17
|
+
const entryTree = ownerFiber.entry?.parent.tree ?? ctx.loader
|
|
18
|
+
const originalEntry = entryTree.resolve(originalEntryId)
|
|
19
|
+
const isolated = () => originalEntry.options.isolate?.[serviceName] === isolation
|
|
20
|
+
if (!isolated()) throw new Error(`ptc-plus: original entry ${originalEntryId} must isolate ${serviceName} as ${isolation}`)
|
|
21
|
+
const originalContext = originalEntry.context.extend({ [originalEntry.constructor.key]: undefined })
|
|
22
|
+
let watcher
|
|
23
|
+
let live
|
|
24
|
+
let stopped = false
|
|
25
|
+
let suspended = false
|
|
26
|
+
let generation
|
|
27
|
+
const pending = new Set()
|
|
28
|
+
let resolveAttachment
|
|
29
|
+
let rejectAttachment
|
|
30
|
+
const attachment = new Promise((resolve, reject) => {
|
|
31
|
+
resolveAttachment = resolve
|
|
32
|
+
rejectAttachment = reject
|
|
33
|
+
})
|
|
34
|
+
const readOriginal = () => {
|
|
35
|
+
const original = isolated() ? originalContext.get(serviceName) : undefined
|
|
36
|
+
if (stopped || original === undefined) {
|
|
37
|
+
throw new Error(`ptc-plus: original ${serviceName} generation is unavailable`)
|
|
38
|
+
}
|
|
39
|
+
return original
|
|
40
|
+
}
|
|
41
|
+
const release = () => {
|
|
42
|
+
const entry = live
|
|
43
|
+
if (entry === undefined) return
|
|
44
|
+
live = undefined
|
|
45
|
+
entry.seam.retire()
|
|
46
|
+
const unregistration = entry.unregister?.()
|
|
47
|
+
const disposal = Promise.allSettled([unregistration, entry.disposeChild?.()]).then(results => {
|
|
48
|
+
const failures = results.filter(result => result.status === 'rejected').map(result => result.reason)
|
|
49
|
+
if (failures.length > 0) throw new AggregateError(failures, 'ptc-plus: execution provider disposal failed')
|
|
50
|
+
})
|
|
51
|
+
pending.add(disposal)
|
|
52
|
+
disposal.then(() => pending.delete(disposal), error => {
|
|
53
|
+
pending.delete(disposal)
|
|
54
|
+
ctx.logger.warn(error)
|
|
55
|
+
})
|
|
56
|
+
return disposal
|
|
57
|
+
}
|
|
58
|
+
const suspend = () => {
|
|
59
|
+
suspended = true
|
|
60
|
+
const disposal = release()
|
|
61
|
+
const previous = watcher
|
|
62
|
+
watcher = undefined
|
|
63
|
+
return Promise.all([disposal, previous?.dispose()])
|
|
64
|
+
}
|
|
65
|
+
const publish = () => {
|
|
66
|
+
const resolutions = new WeakMap()
|
|
67
|
+
const delegate = {
|
|
68
|
+
get language() { return readOriginal().language },
|
|
69
|
+
get isolation() { return readOriginal().isolation },
|
|
70
|
+
get executionInstructions() { return readOriginal().executionInstructions },
|
|
71
|
+
get sandboxMode() { return readOriginal().sandboxMode },
|
|
72
|
+
get timeout() { return readOriginal().timeout },
|
|
73
|
+
resolve(request) {
|
|
74
|
+
const original = readOriginal()
|
|
75
|
+
const spec = serviceName === EXECUTION_SEAM_SERVICE ? original.resolve(request) : request
|
|
76
|
+
if (spec !== null && typeof spec === 'object') {
|
|
77
|
+
resolutions.set(spec, { original, generation })
|
|
78
|
+
}
|
|
79
|
+
return spec
|
|
80
|
+
},
|
|
81
|
+
run(spec) {
|
|
82
|
+
const resolved = resolutions.get(spec)
|
|
83
|
+
if (resolved?.generation?.active === false) throw new Error('ptc-plus: resolved spec belongs to an unavailable original generation')
|
|
84
|
+
const original = resolved?.original ?? readOriginal()
|
|
85
|
+
return original.run(spec)
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
const seam = createExecutionSeam(delegate, serviceName)
|
|
89
|
+
const entry = { seam }
|
|
90
|
+
live = entry
|
|
91
|
+
entry.disposeChild = ctx.effect(() => {
|
|
92
|
+
const child = ctx.plugin({
|
|
93
|
+
name: 'ptc-plus-composed-runtime',
|
|
94
|
+
apply(childContext) {
|
|
95
|
+
childContext.effect(() => {
|
|
96
|
+
if (stopped || live !== entry || !isolated()) return () => {}
|
|
97
|
+
entry.unregister = childContext.provide(serviceName, seam.provider)
|
|
98
|
+
return () => entry.unregister()
|
|
99
|
+
}, 'ptc-plus owned execution registration')
|
|
100
|
+
},
|
|
101
|
+
})
|
|
102
|
+
entry.child = child
|
|
103
|
+
return () => child.dispose()
|
|
104
|
+
}, 'ptc-plus owned execution child')
|
|
105
|
+
return entry.child.await().then(() => {
|
|
106
|
+
if (!stopped && live === entry && entry.unregister !== undefined && entry.child.state === 2) resolveAttachment()
|
|
107
|
+
}, error => {
|
|
108
|
+
rejectAttachment(error)
|
|
109
|
+
throw error
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
const watch = () => {
|
|
113
|
+
if (stopped || watcher !== undefined || !isolated()) return
|
|
114
|
+
ctx.effect(() => {
|
|
115
|
+
const observer = originalContext.inject([serviceName], scope => {
|
|
116
|
+
if (stopped || !isolated()) return
|
|
117
|
+
createExecutionSeam(scope.get(serviceName), serviceName)
|
|
118
|
+
const currentGeneration = { active: true }
|
|
119
|
+
generation = currentGeneration
|
|
120
|
+
scope.effect(() => () => {
|
|
121
|
+
currentGeneration.active = false
|
|
122
|
+
if (generation === currentGeneration) generation = undefined
|
|
123
|
+
}, 'ptc-plus original generation scope')
|
|
124
|
+
if (live === undefined) return publish()
|
|
125
|
+
return live.seam.sourceChanged()
|
|
126
|
+
})
|
|
127
|
+
watcher = observer
|
|
128
|
+
return () => observer.dispose()
|
|
129
|
+
}, 'ptc-plus original execution observer')
|
|
130
|
+
}
|
|
131
|
+
ctx.on('loader/partial-dispose', (entry, _legacy, active) => {
|
|
132
|
+
if (entry === originalEntry && (!active || !isolated())) void suspend().catch(error => ctx.logger.warn(error))
|
|
133
|
+
}, { prepend: true, global: true })
|
|
134
|
+
ctx.on('loader/patch-context', (entry, next) => {
|
|
135
|
+
if (entry === originalEntry && !isolated()) void suspend().catch(error => ctx.logger.warn(error))
|
|
136
|
+
next()
|
|
137
|
+
if (entry === originalEntry && isolated() && suspended && !stopped && ownerFiber.state === 2) {
|
|
138
|
+
stopped = true
|
|
139
|
+
resolveAttachment()
|
|
140
|
+
void suspend().catch(error => ctx.logger.warn(error))
|
|
141
|
+
void ownerFiber.restart().catch(error => ctx.logger.warn(error))
|
|
142
|
+
}
|
|
143
|
+
}, { prepend: true, global: true })
|
|
144
|
+
ctx.effect(() => () => {
|
|
145
|
+
stopped = true
|
|
146
|
+
resolveAttachment()
|
|
147
|
+
const suspension = suspend()
|
|
148
|
+
return Promise.all([suspension, ...pending])
|
|
149
|
+
}, 'ptc-plus execution provider composition')
|
|
150
|
+
if (originalEntry.disabled) resolveAttachment()
|
|
151
|
+
const timer = setTimeout(() => rejectAttachment(new Error(
|
|
152
|
+
`ptc-plus: original execution entry ${originalEntryId} did not publish ${serviceName}`,
|
|
153
|
+
)), 30_000)
|
|
154
|
+
ctx.effect(() => () => clearTimeout(timer), 'ptc-plus original execution provider wait')
|
|
155
|
+
const originalReady = originalEntry.fiber?.await() ?? Promise.resolve()
|
|
156
|
+
return originalReady.then(() => {
|
|
157
|
+
watch()
|
|
158
|
+
return attachment
|
|
159
|
+
}).finally(() => clearTimeout(timer))
|
|
160
|
+
}
|