dsh-ptc-plus 0.4.10 → 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 +60 -130
- package/README.md +60 -130
- 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 +1 -0
- package/docs/README.md +6 -2
- package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +31 -198
- 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 +1 -1
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +1 -1
- package/docs/adr/0022-stage-npm-releases-with-oidc.md +4 -2
- package/docs/adr/0023-global-user-bindings.md +2 -0
- 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 +25 -23
- package/docs/adr/0031-close-semantic-obligations-by-owner.md +1 -1
- 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 +23 -25
- package/docs/capability-planning-components.md +93 -0
- package/docs/capability-projection.md +7 -0
- package/docs/client-ui.md +70 -23
- package/docs/client-visual-contract.md +26 -9
- 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 +20 -15
- 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 +31 -10
- 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.md +1 -1
- package/docs/value-wire.md +11 -1
- package/docs/verification-optimization-work.md +26 -15
- package/docs/verification.md +30 -88
- package/index.js +123 -20
- 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/config-spec.js +79 -9
- package/internal/direct-surface-owner.js +27 -29
- package/internal/dynamic-environment-runtime.js +9 -9
- package/internal/edit-transport-owner.js +180 -139
- package/internal/execution-provider-owner.js +15 -12
- package/internal/execution-seam-compat.js +8 -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 +3 -2
- 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 +272 -34
- package/internal/runtime-config.js +58 -0
- package/internal/runtime-contexts.js +74 -5
- 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 +34 -0
- package/internal/session-journal-recovery.js +234 -11
- package/internal/session-journal-schema.js +6 -3
- package/internal/session-journal.js +31 -2
- package/internal/session-log-view.js +1 -1
- package/internal/session-runtime.js +119 -15
- package/internal/settings-compat.js +12 -5
- 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 +41 -2
- 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/package.json +47 -29
- package/src/client-authoring-view.js +67 -30
- package/src/client-binding-review.js +2 -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-copy.js +871 -8
- package/src/client-feature-gates.js +3 -0
- package/src/client-indicator-view.js +4 -6
- package/src/client-primitives.js +33 -2
- package/src/client-repl-view.js +20 -13
- package/src/client-settings-view.js +411 -41
- package/src/client-styles.js +108 -15
- package/src/client-tool-hover.js +185 -0
- package/src/client-tool-view.js +37 -8
- package/src/client-transport.js +44 -9
- package/src/client-workbench.js +6 -7
- package/src/client.js +76 -22
- package/client.js +0 -32479
|
@@ -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,
|
|
@@ -18,7 +18,11 @@ import {
|
|
|
18
18
|
editRunCodeSchema,
|
|
19
19
|
} from './rejected-cell-editor.js'
|
|
20
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,19 +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,
|
|
70
111
|
readSessionLog,
|
|
71
|
-
sessionLogView = projectSessionLog,
|
|
72
112
|
}) {
|
|
73
|
-
let currentDurableReplay = durableReplay
|
|
74
|
-
const editExecutionMetadata = new WeakMap()
|
|
75
113
|
const editClaims = new Map()
|
|
76
|
-
const
|
|
114
|
+
const pendingAcceptances = new Map()
|
|
77
115
|
const installedScopes = new Map()
|
|
78
116
|
|
|
79
117
|
const releaseClaim = (id, targetCallSeq) => {
|
|
@@ -83,10 +121,10 @@ export function createEditTransportOwner(ctx, {
|
|
|
83
121
|
}
|
|
84
122
|
|
|
85
123
|
const discardSettlements = (id) => {
|
|
86
|
-
for (const [exec, pending] of
|
|
124
|
+
for (const [exec, pending] of pendingAcceptances) {
|
|
87
125
|
if (id !== undefined && pending.id !== id) continue
|
|
88
|
-
|
|
89
|
-
pending.
|
|
126
|
+
pendingAcceptances.delete(exec)
|
|
127
|
+
pending.finish(false)
|
|
90
128
|
}
|
|
91
129
|
}
|
|
92
130
|
|
|
@@ -100,6 +138,7 @@ export function createEditTransportOwner(ctx, {
|
|
|
100
138
|
description: derived.description,
|
|
101
139
|
},
|
|
102
140
|
}
|
|
141
|
+
if (derived.capabilityEvidence !== undefined) meta[CAPABILITY_EVIDENCE_KEY] = derived.capabilityEvidence
|
|
103
142
|
if (derived.rewrites !== undefined) {
|
|
104
143
|
meta[REWRITES_KEY] = normalizeRewrites(derived.rewrites)
|
|
105
144
|
}
|
|
@@ -125,125 +164,162 @@ export function createEditTransportOwner(ctx, {
|
|
|
125
164
|
type: 'object',
|
|
126
165
|
additionalProperties: false,
|
|
127
166
|
properties: {
|
|
128
|
-
|
|
167
|
+
accepted: { type: 'boolean' },
|
|
168
|
+
scheduled: { type: 'boolean' },
|
|
169
|
+
target_call_seq: { type: 'integer' },
|
|
129
170
|
reason: { type: 'string' },
|
|
130
|
-
error: { type: 'string' },
|
|
131
|
-
value: {},
|
|
132
|
-
logs: { type: 'array', items: { type: 'string' } },
|
|
133
171
|
},
|
|
134
|
-
required: ['
|
|
172
|
+
required: ['accepted', 'scheduled'],
|
|
135
173
|
},
|
|
136
174
|
render: (_args, value) => [{ type: 'text', text: JSON.stringify(value ?? {}) }],
|
|
137
|
-
presentationMeta: (args) => {
|
|
138
|
-
const meta = {}
|
|
139
|
-
if (!isRecord(args)) return meta
|
|
140
|
-
const derived = editExecutionMetadata.get(args)
|
|
141
|
-
if (derived === undefined) return meta
|
|
142
|
-
return derivedMetadata(derived)
|
|
143
|
-
},
|
|
144
175
|
}
|
|
145
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
|
+
}
|
|
146
180
|
const agent = exec?.agent
|
|
147
|
-
const callId = typeof exec?.callId === 'string' ? exec.callId : undefined
|
|
148
181
|
const sessionLog = await readSessionLog(agent?.session, exec.signal)
|
|
149
|
-
const
|
|
150
|
-
if (
|
|
151
|
-
throw new Error('ptc-plus: edit_run_code requires a unique persisted
|
|
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')
|
|
152
185
|
}
|
|
153
|
-
const target =
|
|
154
|
-
? undefined
|
|
155
|
-
: sessionLogView(agent, { callId, callSeq: persistedTargetCallSeq }).requestedEditTarget
|
|
186
|
+
const target = start.target
|
|
156
187
|
if (target?.source === undefined || target.callSeq === undefined) {
|
|
157
|
-
return {
|
|
188
|
+
return { accepted: false, scheduled: false, reason: 'no run_code cell is currently eligible for safe editing' }
|
|
158
189
|
}
|
|
159
190
|
const edited = editRejectedCell(args, target.source)
|
|
160
|
-
if (!edited.edited) return edited
|
|
191
|
+
if (!edited.edited) return { accepted: false, scheduled: false, reason: edited.reason }
|
|
161
192
|
const expectedTargetCallSeq = args[EXPECTED_TARGET_CALL_SEQ]
|
|
162
193
|
if (expectedTargetCallSeq !== undefined && expectedTargetCallSeq !== target.callSeq) {
|
|
163
194
|
return {
|
|
164
|
-
|
|
195
|
+
accepted: false,
|
|
196
|
+
scheduled: false,
|
|
165
197
|
reason: `validated repair targets run_code call ${expectedTargetCallSeq}, but this edit captured call ${target.callSeq}`,
|
|
166
198
|
}
|
|
167
199
|
}
|
|
168
200
|
if (typeof ctx.tools.execute !== 'function') {
|
|
169
201
|
throw new Error('ptc-plus: DSH tools.execute is required for derived edit execution')
|
|
170
202
|
}
|
|
171
|
-
const persistedCallSeq = persistedTargetCallSeq
|
|
172
203
|
const id = sessionId(agent)
|
|
173
204
|
let claims = editClaims.get(id)
|
|
174
205
|
if (claims === undefined) {
|
|
175
206
|
claims = new Map()
|
|
176
207
|
editClaims.set(id, claims)
|
|
177
|
-
} else {
|
|
178
|
-
for (const [callSeq, phase] of claims) {
|
|
179
|
-
if (phase === 'settled' && callSeq !== target.callSeq) claims.delete(callSeq)
|
|
180
|
-
}
|
|
181
208
|
}
|
|
182
209
|
if (claims.has(target.callSeq)) {
|
|
183
|
-
return {
|
|
210
|
+
return { accepted: false, scheduled: false, reason: 'the current edit target already has a derived execution' }
|
|
184
211
|
}
|
|
185
|
-
claims.set(target.callSeq, '
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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' }
|
|
241
315
|
}
|
|
316
|
+
pendingAcceptances.set(exec, pending)
|
|
317
|
+
return receipt
|
|
242
318
|
}
|
|
243
319
|
|
|
244
320
|
const disposeInstalled = (installed) => {
|
|
245
321
|
const failures = []
|
|
246
|
-
for (const key of ['
|
|
322
|
+
for (const key of ['disposeRegistration']) {
|
|
247
323
|
const dispose = installed[key]
|
|
248
324
|
if (dispose === undefined) continue
|
|
249
325
|
try {
|
|
@@ -264,8 +340,7 @@ export function createEditTransportOwner(ctx, {
|
|
|
264
340
|
try {
|
|
265
341
|
disposeInstalled(installed)
|
|
266
342
|
} finally {
|
|
267
|
-
if (installed.
|
|
268
|
-
&& installed.disposeRegistration === undefined) {
|
|
343
|
+
if (installed.disposeRegistration === undefined) {
|
|
269
344
|
installedScopes.delete(agent)
|
|
270
345
|
}
|
|
271
346
|
}
|
|
@@ -273,23 +348,13 @@ export function createEditTransportOwner(ctx, {
|
|
|
273
348
|
|
|
274
349
|
return Object.freeze({
|
|
275
350
|
definition,
|
|
276
|
-
reconfigure(nextConfig) {
|
|
277
|
-
currentDurableReplay = nextConfig.durableReplay
|
|
278
|
-
},
|
|
279
351
|
isInstalled(agent) {
|
|
280
352
|
const installed = installedScopes.get(agent)
|
|
281
|
-
return installed?.
|
|
282
|
-
&& installed.disposeRegistration !== undefined
|
|
353
|
+
return installed?.disposeRegistration !== undefined
|
|
283
354
|
},
|
|
284
355
|
ensureInstalled(agent) {
|
|
285
356
|
const installed = installedScopes.get(agent)
|
|
286
|
-
if (installed !== undefined)
|
|
287
|
-
if (installed.disposePresentation !== undefined
|
|
288
|
-
&& installed.disposeRegistration !== undefined) {
|
|
289
|
-
return installed.definition
|
|
290
|
-
}
|
|
291
|
-
uninstall(agent)
|
|
292
|
-
}
|
|
357
|
+
if (installed !== undefined) return installed.definition
|
|
293
358
|
const liveSchemas = typeof toolSchemasForAgent === 'function'
|
|
294
359
|
? toolSchemasForAgent(agent)
|
|
295
360
|
: []
|
|
@@ -298,8 +363,8 @@ export function createEditTransportOwner(ctx, {
|
|
|
298
363
|
throw new Error('ptc-plus: cannot install edit_run_code; the agent scope already owns that tool name')
|
|
299
364
|
}
|
|
300
365
|
const scopedTools = agent?.ctx?.tools
|
|
301
|
-
if (typeof scopedTools?.register !== 'function'
|
|
302
|
-
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')
|
|
303
368
|
}
|
|
304
369
|
const disposeRegistration = scopedTools.register(definition)
|
|
305
370
|
if (typeof disposeRegistration !== 'function') {
|
|
@@ -307,43 +372,19 @@ export function createEditTransportOwner(ctx, {
|
|
|
307
372
|
}
|
|
308
373
|
const nextInstalled = {
|
|
309
374
|
definition,
|
|
310
|
-
disposePresentation: undefined,
|
|
311
375
|
disposeRegistration,
|
|
312
376
|
sessionId: sessionId(agent),
|
|
313
377
|
}
|
|
314
378
|
installedScopes.set(agent, nextInstalled)
|
|
315
|
-
try {
|
|
316
|
-
const disposePresentation = scopedTools.presentAs('both')
|
|
317
|
-
if (typeof disposePresentation !== 'function') {
|
|
318
|
-
throw new Error('ptc-plus: DSH tools.presentAs did not return a disposer')
|
|
319
|
-
}
|
|
320
|
-
nextInstalled.disposePresentation = disposePresentation
|
|
321
|
-
} catch (error) {
|
|
322
|
-
try {
|
|
323
|
-
uninstall(agent)
|
|
324
|
-
} catch (cleanupError) {
|
|
325
|
-
throw new AggregateError(
|
|
326
|
-
[error, cleanupError],
|
|
327
|
-
'ptc-plus: edit_run_code installation and rollback failed',
|
|
328
|
-
{ cause: error },
|
|
329
|
-
)
|
|
330
|
-
}
|
|
331
|
-
throw error
|
|
332
|
-
}
|
|
333
379
|
return definition
|
|
334
380
|
},
|
|
335
381
|
handleResult(exec, result) {
|
|
336
|
-
const pending =
|
|
337
|
-
pendingSettlements.delete(exec)
|
|
382
|
+
const pending = pendingAcceptances.get(exec)
|
|
338
383
|
if (pending === undefined) return
|
|
339
|
-
const
|
|
340
|
-
result
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
)
|
|
344
|
-
pending.finalize(persisted)
|
|
345
|
-
if (persisted) return
|
|
346
|
-
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)
|
|
347
388
|
},
|
|
348
389
|
disposeAgent(agent) {
|
|
349
390
|
let failure
|
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
import { createExecutionSeam, EXECUTION_SEAM_SERVICE } from './execution-seam-compat.js'
|
|
1
|
+
import { createExecutionSeam, EXECUTION_SEAM_SERVICE, LEGACY_EXECUTION_SEAM_SERVICE } from './execution-seam-compat.js'
|
|
2
2
|
|
|
3
3
|
export const name = 'ptc-plus-execution-provider'
|
|
4
4
|
export const inject = ['loader']
|
|
5
5
|
|
|
6
6
|
export function apply(ctx, config = {}) {
|
|
7
7
|
const ownerFiber = ctx.fiber
|
|
8
|
-
const { originalEntryId = 'ptc-runtime', isolation = 'ptc-plus-original' } = config
|
|
8
|
+
const { originalEntryId = 'ptc-runtime', isolation = 'ptc-plus-original', serviceName = EXECUTION_SEAM_SERVICE } = config
|
|
9
9
|
if (typeof originalEntryId !== 'string' || originalEntryId.length === 0
|
|
10
10
|
|| typeof isolation !== 'string' || isolation.length === 0) {
|
|
11
11
|
throw new TypeError('ptc-plus: execution provider requires an original entry id and named isolation')
|
|
12
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
|
+
}
|
|
13
16
|
// Profile rows belong to the Include's tree; the global Loader resolves root IDs.
|
|
14
17
|
const entryTree = ownerFiber.entry?.parent.tree ?? ctx.loader
|
|
15
18
|
const originalEntry = entryTree.resolve(originalEntryId)
|
|
16
|
-
const isolated = () => originalEntry.options.isolate?.[
|
|
17
|
-
if (!isolated()) throw new Error(`ptc-plus: original entry ${originalEntryId} must isolate ${
|
|
19
|
+
const isolated = () => originalEntry.options.isolate?.[serviceName] === isolation
|
|
20
|
+
if (!isolated()) throw new Error(`ptc-plus: original entry ${originalEntryId} must isolate ${serviceName} as ${isolation}`)
|
|
18
21
|
const originalContext = originalEntry.context.extend({ [originalEntry.constructor.key]: undefined })
|
|
19
22
|
let watcher
|
|
20
23
|
let live
|
|
@@ -29,9 +32,9 @@ export function apply(ctx, config = {}) {
|
|
|
29
32
|
rejectAttachment = reject
|
|
30
33
|
})
|
|
31
34
|
const readOriginal = () => {
|
|
32
|
-
const original = isolated() ? originalContext.get(
|
|
35
|
+
const original = isolated() ? originalContext.get(serviceName) : undefined
|
|
33
36
|
if (stopped || original === undefined) {
|
|
34
|
-
throw new Error(`ptc-plus: original ${
|
|
37
|
+
throw new Error(`ptc-plus: original ${serviceName} generation is unavailable`)
|
|
35
38
|
}
|
|
36
39
|
return original
|
|
37
40
|
}
|
|
@@ -69,7 +72,7 @@ export function apply(ctx, config = {}) {
|
|
|
69
72
|
get timeout() { return readOriginal().timeout },
|
|
70
73
|
resolve(request) {
|
|
71
74
|
const original = readOriginal()
|
|
72
|
-
const spec = original.resolve(request)
|
|
75
|
+
const spec = serviceName === EXECUTION_SEAM_SERVICE ? original.resolve(request) : request
|
|
73
76
|
if (spec !== null && typeof spec === 'object') {
|
|
74
77
|
resolutions.set(spec, { original, generation })
|
|
75
78
|
}
|
|
@@ -82,7 +85,7 @@ export function apply(ctx, config = {}) {
|
|
|
82
85
|
return original.run(spec)
|
|
83
86
|
},
|
|
84
87
|
}
|
|
85
|
-
const seam = createExecutionSeam(delegate,
|
|
88
|
+
const seam = createExecutionSeam(delegate, serviceName)
|
|
86
89
|
const entry = { seam }
|
|
87
90
|
live = entry
|
|
88
91
|
entry.disposeChild = ctx.effect(() => {
|
|
@@ -91,7 +94,7 @@ export function apply(ctx, config = {}) {
|
|
|
91
94
|
apply(childContext) {
|
|
92
95
|
childContext.effect(() => {
|
|
93
96
|
if (stopped || live !== entry || !isolated()) return () => {}
|
|
94
|
-
entry.unregister = childContext.provide(
|
|
97
|
+
entry.unregister = childContext.provide(serviceName, seam.provider)
|
|
95
98
|
return () => entry.unregister()
|
|
96
99
|
}, 'ptc-plus owned execution registration')
|
|
97
100
|
},
|
|
@@ -109,9 +112,9 @@ export function apply(ctx, config = {}) {
|
|
|
109
112
|
const watch = () => {
|
|
110
113
|
if (stopped || watcher !== undefined || !isolated()) return
|
|
111
114
|
ctx.effect(() => {
|
|
112
|
-
const observer = originalContext.inject([
|
|
115
|
+
const observer = originalContext.inject([serviceName], scope => {
|
|
113
116
|
if (stopped || !isolated()) return
|
|
114
|
-
createExecutionSeam(scope.get(
|
|
117
|
+
createExecutionSeam(scope.get(serviceName), serviceName)
|
|
115
118
|
const currentGeneration = { active: true }
|
|
116
119
|
generation = currentGeneration
|
|
117
120
|
scope.effect(() => () => {
|
|
@@ -146,7 +149,7 @@ export function apply(ctx, config = {}) {
|
|
|
146
149
|
}, 'ptc-plus execution provider composition')
|
|
147
150
|
if (originalEntry.disabled) resolveAttachment()
|
|
148
151
|
const timer = setTimeout(() => rejectAttachment(new Error(
|
|
149
|
-
`ptc-plus: original execution entry ${originalEntryId} did not publish ${
|
|
152
|
+
`ptc-plus: original execution entry ${originalEntryId} did not publish ${serviceName}`,
|
|
150
153
|
)), 30_000)
|
|
151
154
|
ctx.effect(() => () => clearTimeout(timer), 'ptc-plus original execution provider wait')
|
|
152
155
|
const originalReady = originalEntry.fiber?.await() ?? Promise.resolve()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/** Cordis
|
|
1
|
+
/** Resolved-spec Cordis execution contract. */
|
|
2
2
|
export const EXECUTION_SEAM_SERVICE = 'ptcRuntime'
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/** Request-only Cordis execution contract. */
|
|
5
5
|
export const LEGACY_EXECUTION_SEAM_SERVICE = 'codeRuntime'
|
|
6
6
|
|
|
7
7
|
const SEAM_SERVICES = Object.freeze([EXECUTION_SEAM_SERVICE, LEGACY_EXECUTION_SEAM_SERVICE])
|
|
@@ -86,9 +86,9 @@ export function createExecutionSeam(service, serviceName) {
|
|
|
86
86
|
return Promise.all([...sourceListeners].map(listener => listener())).then(() => undefined)
|
|
87
87
|
},
|
|
88
88
|
/**
|
|
89
|
-
* Run one program through the provider this plugin wraps. The
|
|
90
|
-
*
|
|
91
|
-
*
|
|
89
|
+
* Run one program through the provider this plugin wraps. The resolved-spec
|
|
90
|
+
* contract resolves the request before running it, exactly as its consumer
|
|
91
|
+
* does; the request-only contract accepts the request directly.
|
|
92
92
|
*/
|
|
93
93
|
invokeUpstream(request) {
|
|
94
94
|
requireCompatible()
|
|
@@ -132,9 +132,9 @@ function registeredExecutionSeam(service, serviceName) {
|
|
|
132
132
|
/**
|
|
133
133
|
* Wait for the host's execution seam and attach the plugin to the live one.
|
|
134
134
|
*
|
|
135
|
-
* The
|
|
136
|
-
*
|
|
137
|
-
*
|
|
135
|
+
* The resolved-spec service is preferred whenever both contracts are live; the
|
|
136
|
+
* request-only service attaches when it is absent. A resolved-spec service
|
|
137
|
+
* registered later retires the request-only injection before attaching.
|
|
138
138
|
* The injected scope requires the plugin's own core
|
|
139
139
|
* services as well, so the callback receives a context the plugin can work in,
|
|
140
140
|
* and everything the plugin registers there belongs to the seam the host
|