dsh-ptc-plus 0.5.0 → 0.5.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 +9 -1
- package/README.md +9 -1
- package/client.cjs +517 -151
- package/docs/adr/0017-track-the-latest-dsh-public-surface.md +2 -2
- package/docs/adr/0023-global-user-bindings.md +1 -1
- package/docs/adr/0038-freeze-session-capability-plans.md +1 -1
- package/docs/adr/0044-coexist-with-the-official-persistent-repl.md +35 -0
- package/docs/adr/README.md +2 -0
- package/docs/capability-planning-components.md +2 -2
- package/docs/client-ui.md +8 -8
- package/docs/client-visual-contract.md +1 -1
- package/docs/installation.md +5 -3
- package/docs/publishing.md +2 -2
- package/docs/runtime-reference.md +6 -1
- package/index.js +27 -4
- package/internal/capability-command-receipt.js +3 -1
- package/internal/capability-environment.js +9 -3
- package/internal/capability-statistics.js +6 -2
- package/internal/config-spec.js +10 -1
- package/internal/edit-transport-owner.js +17 -3
- package/internal/execution-provider-owner.js +25 -0
- package/internal/execution-seam-compat.js +18 -0
- package/internal/official-repl-priority.js +51 -0
- package/internal/runtime-bridge-owner.js +10 -7
- package/internal/sdk-projection.js +2 -2
- package/internal/user-bindings-owner.js +5 -27
- package/package.json +3 -2
- package/src/client-activity.js +3 -2
- package/src/client-capabilities-view.js +315 -101
- package/src/client-copy.js +68 -16
- package/src/client-styles.js +4 -0
- package/src/client-tool-view.js +9 -2
|
@@ -39,6 +39,15 @@ import {
|
|
|
39
39
|
|
|
40
40
|
export const EDIT_RUN_CODE = 'edit_run_code'
|
|
41
41
|
|
|
42
|
+
const TARGET_FIRST_LINE_LIMIT = 120
|
|
43
|
+
|
|
44
|
+
/** Bounded identity of the cell an edit actually captured, for model-facing reasons. */
|
|
45
|
+
function targetSummary(target) {
|
|
46
|
+
const line = typeof target?.source === 'string' ? target.source.split(/\r?\n/, 1)[0].trim() : ''
|
|
47
|
+
const firstLine = line.length > TARGET_FIRST_LINE_LIMIT ? `${line.slice(0, TARGET_FIRST_LINE_LIMIT)}…` : line
|
|
48
|
+
return `run_code call ${target?.callSeq}${firstLine === '' ? '' : `, first line: ${firstLine}`}`
|
|
49
|
+
}
|
|
50
|
+
|
|
42
51
|
function nestedEditStart(sessionLog, exec) {
|
|
43
52
|
const events = sessionEvents(sessionLog)
|
|
44
53
|
if (!Array.isArray(events) || typeof exec?.callId !== 'string'
|
|
@@ -188,13 +197,16 @@ export function createEditTransportOwner(ctx, {
|
|
|
188
197
|
return { accepted: false, scheduled: false, reason: 'no run_code cell is currently eligible for safe editing' }
|
|
189
198
|
}
|
|
190
199
|
const edited = editRejectedCell(args, target.source)
|
|
191
|
-
if (!edited.edited)
|
|
200
|
+
if (!edited.edited) {
|
|
201
|
+
return { accepted: false, scheduled: false, reason: `${edited.reason} (edit target: ${targetSummary(target)})` }
|
|
202
|
+
}
|
|
192
203
|
const expectedTargetCallSeq = args[EXPECTED_TARGET_CALL_SEQ]
|
|
193
204
|
if (expectedTargetCallSeq !== undefined && expectedTargetCallSeq !== target.callSeq) {
|
|
194
205
|
return {
|
|
195
206
|
accepted: false,
|
|
196
207
|
scheduled: false,
|
|
197
|
-
reason: `validated repair targets run_code call ${expectedTargetCallSeq}, but this edit captured
|
|
208
|
+
reason: `validated repair targets run_code call ${expectedTargetCallSeq}, but this edit captured ${targetSummary(target)}.`
|
|
209
|
+
+ ' Only the most recent settled cell stays editable; if the intended cell is no longer editable, re-submit its corrected source with run_code instead.',
|
|
198
210
|
}
|
|
199
211
|
}
|
|
200
212
|
if (typeof ctx.tools.execute !== 'function') {
|
|
@@ -207,7 +219,8 @@ export function createEditTransportOwner(ctx, {
|
|
|
207
219
|
editClaims.set(id, claims)
|
|
208
220
|
}
|
|
209
221
|
if (claims.has(target.callSeq)) {
|
|
210
|
-
return { accepted: false, scheduled: false,
|
|
222
|
+
return { accepted: false, scheduled: false,
|
|
223
|
+
reason: `the current edit target already has a derived execution (edit target: ${targetSummary(target)})` }
|
|
211
224
|
}
|
|
212
225
|
claims.set(target.callSeq, 'scheduled')
|
|
213
226
|
const receipt = { accepted: true, scheduled: true, target_call_seq: target.callSeq }
|
|
@@ -219,6 +232,7 @@ export function createEditTransportOwner(ctx, {
|
|
|
219
232
|
rootCallSeq: start.rootCallSeq,
|
|
220
233
|
subCallId: exec.callId,
|
|
221
234
|
targetCallSeq: target.callSeq,
|
|
235
|
+
targetDescription: targetSummary(target),
|
|
222
236
|
finalized: false,
|
|
223
237
|
finalizeTentative: undefined,
|
|
224
238
|
finish(confirmed) {
|
|
@@ -64,12 +64,32 @@ export function apply(ctx, config = {}) {
|
|
|
64
64
|
}
|
|
65
65
|
const publish = () => {
|
|
66
66
|
const resolutions = new WeakMap()
|
|
67
|
+
const ownerOriginals = new WeakMap()
|
|
68
|
+
const isOwner = owner => owner !== null && (typeof owner === 'object' || typeof owner === 'function')
|
|
67
69
|
const delegate = {
|
|
68
70
|
get language() { return readOriginal().language },
|
|
69
71
|
get isolation() { return readOriginal().isolation },
|
|
72
|
+
get programFormat() { return readOriginal().programFormat ?? 'function-body' },
|
|
70
73
|
get executionInstructions() { return readOriginal().executionInstructions },
|
|
71
74
|
get sandboxMode() { return readOriginal().sandboxMode },
|
|
72
75
|
get timeout() { return readOriginal().timeout },
|
|
76
|
+
release(owner) {
|
|
77
|
+
const originals = isOwner(owner) ? ownerOriginals.get(owner) : undefined
|
|
78
|
+
if (originals !== undefined) {
|
|
79
|
+
return Promise.all([...originals].map(original => Promise.resolve()
|
|
80
|
+
.then(() => original.release(owner))
|
|
81
|
+
.then(() => originals.delete(original)))).then(() => undefined)
|
|
82
|
+
}
|
|
83
|
+
let original
|
|
84
|
+
try {
|
|
85
|
+
original = readOriginal()
|
|
86
|
+
} catch {
|
|
87
|
+
// No retained dispatch belongs to this owner and execution is unavailable.
|
|
88
|
+
return Promise.resolve()
|
|
89
|
+
}
|
|
90
|
+
if (typeof original.release !== 'function') return Promise.resolve()
|
|
91
|
+
return Promise.resolve(original.release.call(original, owner))
|
|
92
|
+
},
|
|
73
93
|
resolve(request) {
|
|
74
94
|
const original = readOriginal()
|
|
75
95
|
const spec = serviceName === EXECUTION_SEAM_SERVICE ? original.resolve(request) : request
|
|
@@ -82,6 +102,11 @@ export function apply(ctx, config = {}) {
|
|
|
82
102
|
const resolved = resolutions.get(spec)
|
|
83
103
|
if (resolved?.generation?.active === false) throw new Error('ptc-plus: resolved spec belongs to an unavailable original generation')
|
|
84
104
|
const original = resolved?.original ?? readOriginal()
|
|
105
|
+
if (isOwner(spec?.stateOwner) && typeof original.release === 'function') {
|
|
106
|
+
let originals = ownerOriginals.get(spec.stateOwner)
|
|
107
|
+
if (originals === undefined) ownerOriginals.set(spec.stateOwner, originals = new Set())
|
|
108
|
+
originals.add(original)
|
|
109
|
+
}
|
|
85
110
|
return original.run(spec)
|
|
86
111
|
},
|
|
87
112
|
}
|
|
@@ -60,6 +60,10 @@ export function createExecutionSeam(service, serviceName) {
|
|
|
60
60
|
const provider = Object.freeze({
|
|
61
61
|
get language() { requireCompatible(); return service.language },
|
|
62
62
|
get isolation() { requireCompatible(); return installed === undefined ? service.isolation : 'worker-thread' },
|
|
63
|
+
// Source evaluation form belongs to the provider that actually receives
|
|
64
|
+
// the program. Program-format consumers delegate to the original provider,
|
|
65
|
+
// so this descriptor always follows the original generation.
|
|
66
|
+
get programFormat() { requireCompatible(); return service.programFormat ?? 'function-body' },
|
|
63
67
|
get executionInstructions() { requireCompatible(); return installed === undefined ? service.executionInstructions ?? '' : '' },
|
|
64
68
|
get sandboxMode() { requireCompatible(); return installed === undefined ? service.sandboxMode : undefined },
|
|
65
69
|
get timeout() { requireCompatible(); return installed === undefined ? service.timeout : undefined },
|
|
@@ -71,6 +75,20 @@ export function createExecutionSeam(service, serviceName) {
|
|
|
71
75
|
requireCompatible()
|
|
72
76
|
return installed === undefined ? upstreamRun.call(service, input) : installed.execute(input)
|
|
73
77
|
},
|
|
78
|
+
/**
|
|
79
|
+
* Release one owner's retained environment. The resources live in the
|
|
80
|
+
* original generation (the session kernel releases its state through its
|
|
81
|
+
* own lifecycle). Adapter retirement only closes execution; the original
|
|
82
|
+
* still owns cleanup for references retained by its consumers.
|
|
83
|
+
*/
|
|
84
|
+
release(owner) {
|
|
85
|
+
if (typeof service.release !== 'function') return Promise.resolve()
|
|
86
|
+
try {
|
|
87
|
+
return Promise.resolve(service.release.call(service, owner))
|
|
88
|
+
} catch (error) {
|
|
89
|
+
return Promise.reject(error)
|
|
90
|
+
}
|
|
91
|
+
},
|
|
74
92
|
[PLUGIN_EXECUTION_SEAM]() { return seam },
|
|
75
93
|
})
|
|
76
94
|
const seam = Object.freeze({
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Official persistent-REPL priority.
|
|
3
|
+
*
|
|
4
|
+
* The official `persistent-in-memory` execution mode is a live provider
|
|
5
|
+
* capability, not a release fact: a provider reports `programFormat` `'repl'`
|
|
6
|
+
* exactly when it retains a REPL per state owner. While the user selects the
|
|
7
|
+
* official REPL priority and that provider is live, the plugin yields PTC
|
|
8
|
+
* session execution to the official implementation and installs no session
|
|
9
|
+
* runtime: `run_code`, its descriptors and its guidance stay official. The
|
|
10
|
+
* decision is re-evaluated on every configuration change and host source
|
|
11
|
+
* change, so either side can take over without restarting the host.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Decide whether this configuration yields PTC session execution to the
|
|
16
|
+
* official provider.
|
|
17
|
+
* @param {object} config - resolved plugin configuration.
|
|
18
|
+
* @param {object} provider - the live provider surface registered for the seam.
|
|
19
|
+
* @returns {boolean} true when the official persistent REPL owns execution.
|
|
20
|
+
*/
|
|
21
|
+
export function defersToOfficialRepl(config, provider) {
|
|
22
|
+
if (config?.officialReplPriority !== true) return false
|
|
23
|
+
try {
|
|
24
|
+
return provider?.programFormat === 'repl'
|
|
25
|
+
} catch {
|
|
26
|
+
// An unreadable capability descriptor cannot prove official ownership;
|
|
27
|
+
// the plugin keeps its execution responsibility instead of guessing.
|
|
28
|
+
return false
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Runtime record for an attachment that yields execution to the official
|
|
34
|
+
* provider. It owns no execution resources: the official provider releases
|
|
35
|
+
* anything it retained through its own lifecycle, and the plugin's settings,
|
|
36
|
+
* storage and UI owners remain available so the user can switch back.
|
|
37
|
+
*/
|
|
38
|
+
export function createDeferredRuntime() {
|
|
39
|
+
return Object.freeze({
|
|
40
|
+
cancelCordisActivation() {
|
|
41
|
+
// No Cordis companion is installed while execution is deferred.
|
|
42
|
+
},
|
|
43
|
+
contextsForRequest: () => [],
|
|
44
|
+
async reconfigure() {
|
|
45
|
+
// This attachment has no owners to reconfigure.
|
|
46
|
+
},
|
|
47
|
+
async dispose() {
|
|
48
|
+
// Nothing is owned here; disposal returns immediately.
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
}
|
|
@@ -587,9 +587,10 @@ export function createRuntimeBridgeOwner({
|
|
|
587
587
|
valueText = '; value omitted from preview'
|
|
588
588
|
}
|
|
589
589
|
}
|
|
590
|
+
const targetDescription = pendingEdit.targetDescription ?? `run_code call ${pendingEdit.targetCallSeq}`
|
|
590
591
|
const status = outcome.edited
|
|
591
|
-
? `edit_run_code settled${outcome.error === undefined ? '' : ' with execution error'}`
|
|
592
|
-
:
|
|
592
|
+
? `edit_run_code settled${outcome.error === undefined ? '' : ' with execution error'} (target ${targetDescription})`
|
|
593
|
+
: `edit_run_code did not execute the accepted revision (target ${targetDescription})`
|
|
593
594
|
const detail = [
|
|
594
595
|
...(hasRenderedContent || !Array.isArray(outcome.logs) ? [] : outcome.logs),
|
|
595
596
|
...(!hasRenderedContent && typeof outcome.error === 'string' ? [outcome.error] : []),
|
|
@@ -625,9 +626,9 @@ export function createRuntimeBridgeOwner({
|
|
|
625
626
|
const inherited = scope.getStore()
|
|
626
627
|
const ownsDerived = ownedDerivedScope(exec) !== undefined
|
|
627
628
|
if ((exec.parent !== undefined && !ownsDerived) || id === undefined
|
|
628
|
-
|| inherited?.submission?.sessionId === id) return operation()
|
|
629
|
+
|| (inherited?.exec === exec && inherited.submission?.sessionId === id)) return operation()
|
|
629
630
|
const submission = sessions.beginSubmission({ id }, exec.signal)
|
|
630
|
-
const composite =
|
|
631
|
+
const composite = ownsDerived
|
|
631
632
|
? {
|
|
632
633
|
persistedCallSeq: inherited.persistedCallSeq,
|
|
633
634
|
sourceCallSeq: inherited.sourceCallSeq,
|
|
@@ -637,9 +638,11 @@ export function createRuntimeBridgeOwner({
|
|
|
637
638
|
}
|
|
638
639
|
: {}
|
|
639
640
|
try {
|
|
640
|
-
//
|
|
641
|
-
//
|
|
642
|
-
|
|
641
|
+
// Only reentry for this exact execution shares admission. Background
|
|
642
|
+
// continuations can inherit a settled execution's asynchronous context.
|
|
643
|
+
// Only proved derived edits retain composite source and settlement under
|
|
644
|
+
// a fresh admission; other roots own a fresh log and no child handoff.
|
|
645
|
+
return await scope.run({ ...composite, exec, submission }, () => childScope.run(undefined, operation))
|
|
643
646
|
} finally {
|
|
644
647
|
sessions.releaseSubmission(submission)
|
|
645
648
|
}
|
|
@@ -10,7 +10,7 @@ export function projectProgramSdk(nativeSdk, { languageSemantics = 'legacy-v1' }
|
|
|
10
10
|
const fence = nativeSdk.indexOf(TYPESCRIPT_FENCE)
|
|
11
11
|
if (fence < 0) return nativeSdk
|
|
12
12
|
return nativeSdk.slice(0, fence).replace(
|
|
13
|
-
'
|
|
14
|
-
'
|
|
13
|
+
'the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped)',
|
|
14
|
+
'a PTC Plus TypeScript cell (runtime `enum` and namespaces are supported; type annotations are advisory)',
|
|
15
15
|
) + nativeSdk.slice(fence)
|
|
16
16
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PTC_MESSAGE_SOURCE_KIND } from './message-sources.js'
|
|
2
|
-
import { bindingToggleMessage
|
|
2
|
+
import { bindingToggleMessage } from './binding-toggle-receipt.js'
|
|
3
3
|
import { RPC_CONTRACTS } from './rpc-contract.js'
|
|
4
4
|
import { randomUUID } from 'node:crypto'
|
|
5
5
|
import { dirname } from 'node:path'
|
|
@@ -186,7 +186,6 @@ export function createUserBindingsOwner(ctx, options = {}) {
|
|
|
186
186
|
valueLimits: options.valueLimits,
|
|
187
187
|
}
|
|
188
188
|
let disposed = false
|
|
189
|
-
const pendingConfigurationReceipts = new WeakMap()
|
|
190
189
|
const codeConsole = new UserBindingConsole(currentOptions)
|
|
191
190
|
const commandRegistrations = new Map()
|
|
192
191
|
const sessionAgents = new Map()
|
|
@@ -387,15 +386,11 @@ export function createUserBindingsOwner(ctx, options = {}) {
|
|
|
387
386
|
const sessions = new Set((ctx.agents?.list?.() ?? []).map(agent => agent.session).filter(Boolean))
|
|
388
387
|
for (const session of sessions) {
|
|
389
388
|
try {
|
|
389
|
+
// Before the first accepted step these are initial settings, not changes
|
|
390
|
+
// to an environment the model has received. The catalog owns first delivery.
|
|
391
|
+
if (session.deriveMessages()[0]?.role !== 'system') continue
|
|
390
392
|
const message = createUserMessage(bindingToggleMessage(entry, next.revision))
|
|
391
|
-
|
|
392
|
-
// boundary, keep UI configuration facts pending without waking the Agent.
|
|
393
|
-
if (session.deriveMessages()[0]?.role === 'system') session.append('user/message', message, { surfaceOp: 'append' })
|
|
394
|
-
else {
|
|
395
|
-
const pending = pendingConfigurationReceipts.get(session) ?? []
|
|
396
|
-
pending.push(message)
|
|
397
|
-
pendingConfigurationReceipts.set(session, pending)
|
|
398
|
-
}
|
|
393
|
+
session.append('user/message', message, { surfaceOp: 'append' })
|
|
399
394
|
} catch (error) { reportContainedFailure('ptc-plus: binding configuration saved but its session receipt could not be appended', error) }
|
|
400
395
|
}
|
|
401
396
|
return next
|
|
@@ -916,23 +911,6 @@ export function createUserBindingsOwner(ctx, options = {}) {
|
|
|
916
911
|
mountAuthoring()
|
|
917
912
|
|
|
918
913
|
return Object.freeze({
|
|
919
|
-
committedEvent(session, event) {
|
|
920
|
-
if (event.type !== 'user/message' || !readBindingToggle(event.data)) return
|
|
921
|
-
const pending = pendingConfigurationReceipts.get(session)
|
|
922
|
-
if (pending) pendingConfigurationReceipts.set(session, pending.filter(message => message.id !== event.data.id))
|
|
923
|
-
},
|
|
924
|
-
async preStep(payload, next) {
|
|
925
|
-
const decision = await next()
|
|
926
|
-
if (disposed || decision?.kind !== 'enter' || payload.signal?.aborted || payload.agent?.signal?.aborted) return decision
|
|
927
|
-
const session = payload.agent?.session
|
|
928
|
-
const pending = pendingConfigurationReceipts.get(session)
|
|
929
|
-
if (!pending?.length) return decision
|
|
930
|
-
const visible = new Set(session.deriveMessages().map(message => message.id))
|
|
931
|
-
const remaining = pending.filter(message => !visible.has(message.id))
|
|
932
|
-
pendingConfigurationReceipts.set(session, remaining)
|
|
933
|
-
const proposed = new Set((decision.messages ?? []).map(message => message.id))
|
|
934
|
-
return { ...decision, messages: [...(decision.messages ?? []), ...remaining.filter(message => !proposed.has(message.id))] }
|
|
935
|
-
},
|
|
936
914
|
path: store.filename,
|
|
937
915
|
cwd: bindingsCwd,
|
|
938
916
|
async snapshot() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-ptc-plus",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"icon": "assets/ptc-plus-icon.svg",
|
|
5
5
|
"description": "A session-bound agent-native REPL for DeepSeek Harness PTC mode.",
|
|
6
6
|
"type": "module",
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"LICENSE"
|
|
43
43
|
],
|
|
44
44
|
"engines": {
|
|
45
|
-
"node": "^22.19.0 || >=24.0.0"
|
|
45
|
+
"node": "^22.19.0 || >=24.0.0",
|
|
46
|
+
"dsh": "*"
|
|
46
47
|
},
|
|
47
48
|
"allowScripts": {
|
|
48
49
|
"esbuild": false
|
package/src/client-activity.js
CHANGED
|
@@ -506,6 +506,7 @@ export function derivePtcToolView(block, toolName = undefined) {
|
|
|
506
506
|
? block.meta[GENERATED_RUN_CODE_DESCRIPTION_KEY]
|
|
507
507
|
: undefined
|
|
508
508
|
const description = (explicitDescription ?? generatedDescription)?.split(/\r?\n/, 1)[0] ?? ''
|
|
509
|
+
const descriptionGenerated = explicitDescription === undefined && generatedDescription !== undefined
|
|
509
510
|
const output = settled ? resultText(block) : ''
|
|
510
511
|
const state = !settled ? 'running'
|
|
511
512
|
: block.error?.code === 'interrupted' ? 'stopped'
|
|
@@ -514,7 +515,7 @@ export function derivePtcToolView(block, toolName = undefined) {
|
|
|
514
515
|
? block.meta.dshPtcPlus
|
|
515
516
|
: undefined
|
|
516
517
|
if (journal === undefined) {
|
|
517
|
-
return Object.freeze({ state, description, code, output, ptc: false, features: Object.freeze([]) })
|
|
518
|
+
return Object.freeze({ state, description, descriptionGenerated, code, output, ptc: false, features: Object.freeze([]) })
|
|
518
519
|
}
|
|
519
520
|
const rewrites = isValidRewrites(block.meta.dshPtcPlusRewrites, journal)
|
|
520
521
|
? block.meta.dshPtcPlusRewrites
|
|
@@ -542,5 +543,5 @@ export function derivePtcToolView(block, toolName = undefined) {
|
|
|
542
543
|
: undefined,
|
|
543
544
|
...operationFeatures(journal.operations),
|
|
544
545
|
])
|
|
545
|
-
return Object.freeze({ state, description, code, output, ptc: true, features: Object.freeze(features) })
|
|
546
|
+
return Object.freeze({ state, description, descriptionGenerated, code, output, ptc: true, features: Object.freeze(features) })
|
|
546
547
|
}
|