dsh-ptc-plus 0.2.4 → 0.3.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 +171 -0
- package/README.md +79 -69
- package/assets/ptc-plus-repair-en.png +0 -0
- package/assets/ptc-plus-settings-en.png +0 -0
- package/assets/ptc-plus-settings-zh.png +0 -0
- package/client.js +1134 -144
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +19 -4
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
- package/docs/adr/0022-stage-npm-releases-with-oidc.md +2 -2
- package/docs/architecture.md +10 -9
- package/docs/client-ui.md +29 -11
- package/docs/installation.md +11 -3
- package/docs/runtime-reference.md +17 -11
- package/docs/value-wire.md +1 -1
- package/index.js +30 -1
- package/internal/cell-analysis.js +20 -3
- package/internal/cell-rewriter.js +36 -2
- package/internal/config-spec.js +182 -117
- package/internal/direct-surface-owner.js +56 -5
- package/internal/edit-transport-owner.js +10 -1
- package/internal/failure-reporting.js +15 -0
- package/internal/rejected-cell-editor.js +21 -12
- package/internal/repl-memory-projection.js +277 -0
- package/internal/run-code-description.js +34 -0
- package/internal/runtime-bridge-owner.js +63 -15
- package/internal/runtime-config.js +2 -0
- package/internal/session-cell-executor.js +14 -4
- package/internal/session-events.js +7 -0
- package/internal/session-journal.js +3 -2
- package/internal/session-log-view.js +2 -1
- package/internal/session-runtime.js +19 -2
- package/internal/session-state.js +83 -4
- package/package.json +4 -3
- package/src/client-activity.js +472 -0
- package/src/client.js +456 -20
- package/README.zh.md +0 -161
- /package/assets/{ptc-plus-repair.png → ptc-plus-repair-zh.png} +0 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { isRecord } from './record-utils.js'
|
|
2
|
+
|
|
3
|
+
export const REPL_MEMORY_KEY = 'ptcPlusRepl'
|
|
4
|
+
export const REPL_MEMORY_META_KEY = 'dshPtcPlusBindings'
|
|
5
|
+
|
|
6
|
+
const BINDING_KINDS = new Set(['variable', 'function', 'class', 'import'])
|
|
7
|
+
const MAX_BINDINGS = 128
|
|
8
|
+
const MAX_BINDING_NAME_LENGTH = 128
|
|
9
|
+
const MAX_DEFINITION_SOURCE_LENGTH = 1024
|
|
10
|
+
const MAX_DEFINITION_SOURCE_TOTAL_LENGTH = 16 * 1024
|
|
11
|
+
const MAX_PENDING_REPL_CALLS = 256
|
|
12
|
+
const MAX_CALL_ID_LENGTH = 512
|
|
13
|
+
const MAX_GENERATION_LENGTH = 128
|
|
14
|
+
const SNAPSHOT_FIELDS = new Set(['available', 'entries', 'total', 'omitted'])
|
|
15
|
+
const ENTRY_FIELDS = new Set(['name', 'kind', 'definition'])
|
|
16
|
+
const DEFINITION_FIELDS = new Set(['source', 'line', 'column'])
|
|
17
|
+
const MEMORY_META_FIELDS = new Set(['version', 'generation', 'memory'])
|
|
18
|
+
const PROJECTION_STATE_FIELDS = new Set(['generation', 'memory', 'pendingReplCalls'])
|
|
19
|
+
const PENDING_CALL_FIELDS = new Set(['callId', 'seq'])
|
|
20
|
+
const REPL_TOOL_NAMES = new Set(['run_code', 'edit_run_code'])
|
|
21
|
+
const REPL_MEMORY_META_VERSION = 3
|
|
22
|
+
|
|
23
|
+
function exactFields(value, fields) {
|
|
24
|
+
if (!isRecord(value)) return false
|
|
25
|
+
const keys = Reflect.ownKeys(value)
|
|
26
|
+
return keys.length === fields.size && keys.every(key => (
|
|
27
|
+
typeof key === 'string' && fields.has(key)
|
|
28
|
+
&& Object.prototype.propertyIsEnumerable.call(value, key)
|
|
29
|
+
))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const EMPTY_REPL_MEMORY = Object.freeze({
|
|
33
|
+
available: false,
|
|
34
|
+
entries: Object.freeze([]),
|
|
35
|
+
total: 0,
|
|
36
|
+
omitted: 0,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
export function unavailableReplMemorySnapshot() {
|
|
40
|
+
return EMPTY_REPL_MEMORY
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function normalizeGeneration(value) {
|
|
44
|
+
if (typeof value !== 'string' || value.length === 0 || value.length > MAX_GENERATION_LENGTH) {
|
|
45
|
+
throw new Error('invalid dsh-ptc-plus REPL memory generation')
|
|
46
|
+
}
|
|
47
|
+
return value
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function normalizeDefinition(value) {
|
|
51
|
+
if (!exactFields(value, DEFINITION_FIELDS)
|
|
52
|
+
|| typeof value.source !== 'string' || value.source.length === 0
|
|
53
|
+
|| value.source.length > MAX_DEFINITION_SOURCE_LENGTH
|
|
54
|
+
|| !Number.isSafeInteger(value.line) || value.line < 1
|
|
55
|
+
|| !Number.isSafeInteger(value.column) || value.column < 1) {
|
|
56
|
+
throw new Error('invalid dsh-ptc-plus REPL binding definition')
|
|
57
|
+
}
|
|
58
|
+
return Object.freeze({ source: value.source, line: value.line, column: value.column })
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function normalizeBinding(value) {
|
|
62
|
+
if (!exactFields(value, ENTRY_FIELDS) || typeof value.name !== 'string'
|
|
63
|
+
|| value.name.length === 0 || value.name.length > MAX_BINDING_NAME_LENGTH
|
|
64
|
+
|| !BINDING_KINDS.has(value.kind)) {
|
|
65
|
+
throw new Error('invalid dsh-ptc-plus REPL memory binding')
|
|
66
|
+
}
|
|
67
|
+
return Object.freeze({
|
|
68
|
+
name: value.name,
|
|
69
|
+
kind: value.kind,
|
|
70
|
+
definition: normalizeDefinition(value.definition),
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Produce bounded, value-independent presentation metadata from the live binding catalog. */
|
|
75
|
+
export function createReplMemorySnapshot(bindings) {
|
|
76
|
+
const all = Array.isArray(bindings) ? bindings : []
|
|
77
|
+
const candidates = []
|
|
78
|
+
const names = new Set()
|
|
79
|
+
for (const binding of all) {
|
|
80
|
+
try {
|
|
81
|
+
const normalized = normalizeBinding(binding)
|
|
82
|
+
if (names.has(normalized.name)) continue
|
|
83
|
+
names.add(normalized.name)
|
|
84
|
+
candidates.push(normalized)
|
|
85
|
+
} catch {}
|
|
86
|
+
}
|
|
87
|
+
const entries = []
|
|
88
|
+
let sourceLength = 0
|
|
89
|
+
for (const binding of candidates) {
|
|
90
|
+
if (entries.length >= MAX_BINDINGS) break
|
|
91
|
+
if (sourceLength + binding.definition.source.length > MAX_DEFINITION_SOURCE_TOTAL_LENGTH) continue
|
|
92
|
+
entries.push(binding)
|
|
93
|
+
sourceLength += binding.definition.source.length
|
|
94
|
+
}
|
|
95
|
+
return Object.freeze({
|
|
96
|
+
available: true,
|
|
97
|
+
entries: Object.freeze(entries),
|
|
98
|
+
total: candidates.length,
|
|
99
|
+
omitted: candidates.length - entries.length,
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Validate one complete session-projection value without accepting unknown fields. */
|
|
104
|
+
export function normalizeReplMemorySnapshot(value) {
|
|
105
|
+
if (!exactFields(value, SNAPSHOT_FIELDS) || typeof value.available !== 'boolean'
|
|
106
|
+
|| !Array.isArray(value.entries) || value.entries.length > MAX_BINDINGS
|
|
107
|
+
|| !Number.isSafeInteger(value.total) || value.total < 0
|
|
108
|
+
|| !Number.isSafeInteger(value.omitted) || value.omitted < 0
|
|
109
|
+
|| value.total !== value.entries.length + value.omitted) {
|
|
110
|
+
throw new Error('invalid dsh-ptc-plus REPL memory snapshot')
|
|
111
|
+
}
|
|
112
|
+
if (!value.available && (value.total !== 0 || value.entries.length !== 0)) {
|
|
113
|
+
throw new Error('unavailable dsh-ptc-plus REPL memory snapshot must be empty')
|
|
114
|
+
}
|
|
115
|
+
const names = new Set()
|
|
116
|
+
let sourceLength = 0
|
|
117
|
+
const entries = value.entries.map((entry) => {
|
|
118
|
+
const normalized = normalizeBinding(entry)
|
|
119
|
+
if (names.has(normalized.name)) {
|
|
120
|
+
throw new Error('invalid dsh-ptc-plus REPL memory binding')
|
|
121
|
+
}
|
|
122
|
+
sourceLength += normalized.definition.source.length
|
|
123
|
+
if (sourceLength > MAX_DEFINITION_SOURCE_TOTAL_LENGTH) {
|
|
124
|
+
throw new Error('dsh-ptc-plus REPL binding definitions exceed the presentation budget')
|
|
125
|
+
}
|
|
126
|
+
names.add(normalized.name)
|
|
127
|
+
return normalized
|
|
128
|
+
})
|
|
129
|
+
return Object.freeze({
|
|
130
|
+
available: value.available,
|
|
131
|
+
entries: Object.freeze(entries),
|
|
132
|
+
total: value.total,
|
|
133
|
+
omitted: value.omitted,
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function normalizeReplMemoryMetadata(value) {
|
|
138
|
+
if (!exactFields(value, MEMORY_META_FIELDS) || value.version !== REPL_MEMORY_META_VERSION) {
|
|
139
|
+
throw new Error('invalid dsh-ptc-plus REPL memory metadata')
|
|
140
|
+
}
|
|
141
|
+
return Object.freeze({
|
|
142
|
+
version: REPL_MEMORY_META_VERSION,
|
|
143
|
+
generation: normalizeGeneration(value.generation),
|
|
144
|
+
memory: normalizeReplMemorySnapshot(value.memory),
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Read current-owner presentation metadata without affecting tool settlement. */
|
|
149
|
+
export function validatedReplMemorySnapshot(meta, generation) {
|
|
150
|
+
if (!isRecord(meta) || !Object.hasOwn(meta, REPL_MEMORY_META_KEY)) return undefined
|
|
151
|
+
try {
|
|
152
|
+
const normalized = normalizeReplMemoryMetadata(meta[REPL_MEMORY_META_KEY])
|
|
153
|
+
return normalized.generation === normalizeGeneration(generation)
|
|
154
|
+
? normalized.memory
|
|
155
|
+
: undefined
|
|
156
|
+
} catch {
|
|
157
|
+
return undefined
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function projectionState(generation, memory, pendingReplCalls) {
|
|
162
|
+
return Object.freeze({
|
|
163
|
+
generation,
|
|
164
|
+
memory,
|
|
165
|
+
pendingReplCalls: pendingReplCalls === null
|
|
166
|
+
? null
|
|
167
|
+
: Object.freeze(pendingReplCalls),
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function emptyProjectionState(generation) {
|
|
172
|
+
return projectionState(normalizeGeneration(generation), EMPTY_REPL_MEMORY, [])
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function normalizeProjectionState(value, generation) {
|
|
176
|
+
if (!exactFields(value, PROJECTION_STATE_FIELDS)) {
|
|
177
|
+
throw new Error('invalid dsh-ptc-plus REPL memory projection state')
|
|
178
|
+
}
|
|
179
|
+
const storedGeneration = normalizeGeneration(value.generation)
|
|
180
|
+
const currentGeneration = normalizeGeneration(generation)
|
|
181
|
+
if (storedGeneration !== currentGeneration) return emptyProjectionState(currentGeneration)
|
|
182
|
+
const memory = normalizeReplMemorySnapshot(value.memory)
|
|
183
|
+
if (value.pendingReplCalls === null) {
|
|
184
|
+
if (memory.available) {
|
|
185
|
+
throw new Error('untracked dsh-ptc-plus REPL calls require unavailable memory')
|
|
186
|
+
}
|
|
187
|
+
return projectionState(currentGeneration, memory, null)
|
|
188
|
+
}
|
|
189
|
+
if (!Array.isArray(value.pendingReplCalls)
|
|
190
|
+
|| value.pendingReplCalls.length > MAX_PENDING_REPL_CALLS) {
|
|
191
|
+
throw new Error('invalid dsh-ptc-plus pending REPL calls')
|
|
192
|
+
}
|
|
193
|
+
let previousSeq
|
|
194
|
+
const callIds = new Set()
|
|
195
|
+
const pendingReplCalls = value.pendingReplCalls.map((pending) => {
|
|
196
|
+
if (!exactFields(pending, PENDING_CALL_FIELDS)
|
|
197
|
+
|| typeof pending.callId !== 'string' || pending.callId.length === 0
|
|
198
|
+
|| pending.callId.length > MAX_CALL_ID_LENGTH || callIds.has(pending.callId)
|
|
199
|
+
|| !Number.isSafeInteger(pending.seq) || pending.seq < 0
|
|
200
|
+
|| (previousSeq !== undefined && previousSeq >= pending.seq)) {
|
|
201
|
+
throw new Error('invalid dsh-ptc-plus pending REPL call')
|
|
202
|
+
}
|
|
203
|
+
callIds.add(pending.callId)
|
|
204
|
+
previousSeq = pending.seq
|
|
205
|
+
return Object.freeze({ callId: pending.callId, seq: pending.seq })
|
|
206
|
+
})
|
|
207
|
+
return projectionState(currentGeneration, memory, pendingReplCalls)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function eventCallId(value) {
|
|
211
|
+
return typeof value === 'string' && value.length > 0 && value.length <= MAX_CALL_ID_LENGTH
|
|
212
|
+
? value
|
|
213
|
+
: undefined
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function trackReplCall(state, event) {
|
|
217
|
+
if (!REPL_TOOL_NAMES.has(event.data?.name)) return state
|
|
218
|
+
const seq = event.seq
|
|
219
|
+
const callId = eventCallId(event.data?.callId)
|
|
220
|
+
if (!Number.isSafeInteger(seq) || seq < 0 || callId === undefined) return state
|
|
221
|
+
const pending = state.pendingReplCalls
|
|
222
|
+
if (pending === null || pending.some(call => call.callId === callId || call.seq === seq)) return state
|
|
223
|
+
if (pending.length >= MAX_PENDING_REPL_CALLS) {
|
|
224
|
+
return projectionState(state.generation, EMPTY_REPL_MEMORY, null)
|
|
225
|
+
}
|
|
226
|
+
return projectionState(state.generation, state.memory, [
|
|
227
|
+
...pending,
|
|
228
|
+
Object.freeze({ callId, seq }),
|
|
229
|
+
].sort((left, right) => left.seq - right.seq))
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function settleReplCall(state, event, generation) {
|
|
233
|
+
if (event.surfaceOp !== undefined && event.surfaceOp !== 'append') return state
|
|
234
|
+
const pending = state.pendingReplCalls
|
|
235
|
+
if (pending === null) return state
|
|
236
|
+
const callId = eventCallId(event.data?.message?.source?.callId)
|
|
237
|
+
if (callId === undefined) return state
|
|
238
|
+
const index = pending.findIndex(call => call.callId === callId)
|
|
239
|
+
if (index < 0) return state
|
|
240
|
+
const remaining = pending.filter((_call, pendingIndex) => pendingIndex !== index)
|
|
241
|
+
const memory = validatedReplMemorySnapshot(event.data?.meta, generation) ?? state.memory
|
|
242
|
+
return projectionState(state.generation, memory, remaining)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** Merge the binding snapshot into private, model-invisible result presentation metadata. */
|
|
246
|
+
export function withReplMemorySnapshot(meta, snapshot, generation) {
|
|
247
|
+
const base = isRecord(meta) ? { ...meta } : meta === undefined ? {} : { value: meta }
|
|
248
|
+
base[REPL_MEMORY_META_KEY] = normalizeReplMemoryMetadata({
|
|
249
|
+
version: REPL_MEMORY_META_VERSION,
|
|
250
|
+
generation: normalizeGeneration(generation),
|
|
251
|
+
memory: snapshot,
|
|
252
|
+
})
|
|
253
|
+
return base
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Build the current runtime owner's value-independent session projection. */
|
|
257
|
+
export function createReplMemoryProjection(generation) {
|
|
258
|
+
const currentGeneration = normalizeGeneration(generation)
|
|
259
|
+
return Object.freeze({
|
|
260
|
+
key: REPL_MEMORY_KEY,
|
|
261
|
+
stateVersion: 3,
|
|
262
|
+
stateSchema: Object.freeze({
|
|
263
|
+
parse: value => normalizeProjectionState(value, currentGeneration),
|
|
264
|
+
}),
|
|
265
|
+
init: () => emptyProjectionState(currentGeneration),
|
|
266
|
+
apply(state, event) {
|
|
267
|
+
if (event?.type === 'session/end-seed') return emptyProjectionState(currentGeneration)
|
|
268
|
+
if (event?.type === 'tool/call') return trackReplCall(state, event)
|
|
269
|
+
if (event?.type === 'tool/result') return settleReplCall(state, event, currentGeneration)
|
|
270
|
+
return state
|
|
271
|
+
},
|
|
272
|
+
wire: Object.freeze({
|
|
273
|
+
viewSchema: Object.freeze({ parse: normalizeReplMemorySnapshot }),
|
|
274
|
+
view: state => state.memory,
|
|
275
|
+
}),
|
|
276
|
+
})
|
|
277
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const GENERATED_RUN_CODE_DESCRIPTION = 'Execute the next TypeScript cell in this session'
|
|
2
|
+
export const GENERATED_RUN_CODE_DESCRIPTION_KEY = 'dshPtcPlusRunCodeDescription'
|
|
3
|
+
|
|
4
|
+
// Keep presentation provenance outside the canonical JSON argument graph.
|
|
5
|
+
const generatedArguments = new WeakSet()
|
|
6
|
+
|
|
7
|
+
export function markGeneratedRunCodeArguments(argumentsValue) {
|
|
8
|
+
if (argumentsValue !== null
|
|
9
|
+
&& (typeof argumentsValue === 'object' || typeof argumentsValue === 'function')) {
|
|
10
|
+
generatedArguments.add(argumentsValue)
|
|
11
|
+
}
|
|
12
|
+
return argumentsValue
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function generatedRunCodeExecutionArguments(argumentsValue) {
|
|
16
|
+
markGeneratedRunCodeArguments(argumentsValue)
|
|
17
|
+
return markGeneratedRunCodeArguments({
|
|
18
|
+
...argumentsValue,
|
|
19
|
+
description: GENERATED_RUN_CODE_DESCRIPTION,
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function generatedRunCodeDescriptionMeta(argumentsValue, meta) {
|
|
24
|
+
if (!generatedArguments.has(argumentsValue)) return meta
|
|
25
|
+
const base = meta !== null && typeof meta === 'object' && !Array.isArray(meta)
|
|
26
|
+
? meta
|
|
27
|
+
: meta === undefined ? {} : { value: meta }
|
|
28
|
+
return {
|
|
29
|
+
...base,
|
|
30
|
+
[GENERATED_RUN_CODE_DESCRIPTION_KEY]: GENERATED_RUN_CODE_DESCRIPTION,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { GENERATED_RUN_CODE_DESCRIPTION }
|
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
toolCapabilityMetadata,
|
|
20
20
|
} from './program-bindings.js'
|
|
21
21
|
import { deepFreeze } from './record-utils.js'
|
|
22
|
+
import { withReplMemorySnapshot } from './repl-memory-projection.js'
|
|
23
|
+
import { generatedRunCodeDescriptionMeta } from './run-code-description.js'
|
|
22
24
|
|
|
23
25
|
export const RUN_CODE = 'run_code'
|
|
24
26
|
|
|
@@ -123,6 +125,7 @@ export function createRuntimeBridgeOwner({
|
|
|
123
125
|
ctx,
|
|
124
126
|
sessionConfig,
|
|
125
127
|
maxNestedRunCodeDepth,
|
|
128
|
+
presentationGeneration,
|
|
126
129
|
sessionId,
|
|
127
130
|
toolSchemasForAgent,
|
|
128
131
|
}) {
|
|
@@ -247,7 +250,7 @@ export function createRuntimeBridgeOwner({
|
|
|
247
250
|
return { request: { ...request, bindings: projected }, release }
|
|
248
251
|
}
|
|
249
252
|
|
|
250
|
-
const
|
|
253
|
+
const patchRunCodeDefinition = (agent) => {
|
|
251
254
|
const definition = ctx.tools.get(RUN_CODE, agent)
|
|
252
255
|
if (definition === undefined) {
|
|
253
256
|
throw new Error('ptc-plus: run_code definition is unavailable for the owning session')
|
|
@@ -257,28 +260,65 @@ export function createRuntimeBridgeOwner({
|
|
|
257
260
|
}
|
|
258
261
|
if (patchedDefinitions.has(definition)) return
|
|
259
262
|
const output = definition.output
|
|
260
|
-
const
|
|
261
|
-
const
|
|
262
|
-
if (!active)
|
|
263
|
-
|
|
263
|
+
const originalPresentationMeta = output.presentationMeta
|
|
264
|
+
const patchedPresentationMeta = (args, value) => {
|
|
265
|
+
if (!active) {
|
|
266
|
+
return originalPresentationMeta === undefined
|
|
267
|
+
? undefined
|
|
268
|
+
: originalPresentationMeta(args, value)
|
|
269
|
+
}
|
|
270
|
+
const base = originalPresentationMeta === undefined
|
|
271
|
+
? undefined
|
|
272
|
+
: originalPresentationMeta(args, value)
|
|
264
273
|
const current = scope.getStore()
|
|
265
274
|
const settlement = current?.settlement
|
|
266
|
-
if (settlement === undefined) return base
|
|
275
|
+
if (settlement === undefined) return generatedRunCodeDescriptionMeta(args, base)
|
|
267
276
|
let meta = withJournal(base, settlement.journal)
|
|
268
277
|
if (settlement.recoveryBoundaries !== undefined) {
|
|
269
278
|
meta = withRecoveryBoundaries(meta, settlement.recoveryBoundaries)
|
|
270
279
|
}
|
|
271
|
-
|
|
280
|
+
if (settlement.rewrites !== undefined) meta = withRewrites(meta, settlement.rewrites)
|
|
281
|
+
meta = generatedRunCodeDescriptionMeta(args, meta)
|
|
282
|
+
return withReplMemorySnapshot(meta, settlement.replMemory, presentationGeneration)
|
|
272
283
|
}
|
|
284
|
+
const ownExecute = Object.getOwnPropertyDescriptor(definition, 'execute')
|
|
285
|
+
const originalExecute = definition.execute
|
|
286
|
+
const patchedExecute = typeof originalExecute !== 'function'
|
|
287
|
+
? undefined
|
|
288
|
+
: function (args, exec) {
|
|
289
|
+
const executionArguments = scope.getStore()?.executionArguments ?? args
|
|
290
|
+
return originalExecute.call(this, executionArguments, exec)
|
|
291
|
+
}
|
|
292
|
+
let presentationPatched = false
|
|
273
293
|
try {
|
|
274
294
|
Object.defineProperty(output, 'presentationMeta', {
|
|
275
295
|
configurable: true,
|
|
276
296
|
enumerable: true,
|
|
277
297
|
writable: true,
|
|
278
|
-
value:
|
|
298
|
+
value: patchedPresentationMeta,
|
|
299
|
+
})
|
|
300
|
+
presentationPatched = true
|
|
301
|
+
if (patchedExecute !== undefined) {
|
|
302
|
+
Object.defineProperty(definition, 'execute', {
|
|
303
|
+
configurable: true,
|
|
304
|
+
enumerable: ownExecute?.enumerable ?? true,
|
|
305
|
+
writable: true,
|
|
306
|
+
value: patchedExecute,
|
|
307
|
+
})
|
|
308
|
+
}
|
|
309
|
+
patchedDefinitions.set(definition, {
|
|
310
|
+
definition,
|
|
311
|
+
output,
|
|
312
|
+
originalPresentationMeta,
|
|
313
|
+
patchedPresentationMeta,
|
|
314
|
+
ownExecute,
|
|
315
|
+
patchedExecute,
|
|
279
316
|
})
|
|
280
|
-
patchedDefinitions.set(definition, { output, original, patched })
|
|
281
317
|
} catch (error) {
|
|
318
|
+
if (presentationPatched && output.presentationMeta === patchedPresentationMeta) {
|
|
319
|
+
if (originalPresentationMeta === undefined) delete output.presentationMeta
|
|
320
|
+
else output.presentationMeta = originalPresentationMeta
|
|
321
|
+
}
|
|
282
322
|
throw new Error(`ptc-plus: cannot attach the session journal to run_code results: ${error.message}`)
|
|
283
323
|
}
|
|
284
324
|
}
|
|
@@ -333,11 +373,11 @@ export function createRuntimeBridgeOwner({
|
|
|
333
373
|
throw error
|
|
334
374
|
}
|
|
335
375
|
},
|
|
336
|
-
handleExecute(exec, next) {
|
|
376
|
+
handleExecute(exec, next, executionArguments = exec.arguments) {
|
|
337
377
|
if (exec.parent !== undefined) return scope.run(undefined, next)
|
|
338
378
|
const id = sessionId(exec.agent)
|
|
339
379
|
if (id === undefined) return next()
|
|
340
|
-
|
|
380
|
+
patchRunCodeDefinition(exec.agent)
|
|
341
381
|
const inherited = scope.getStore()
|
|
342
382
|
const persistedCallSeq = inherited?.persistedCallSeq
|
|
343
383
|
const current = {
|
|
@@ -349,6 +389,7 @@ export function createRuntimeBridgeOwner({
|
|
|
349
389
|
: { deferredSettlement: inherited.deferredSettlement }),
|
|
350
390
|
session: exec.agent?.session,
|
|
351
391
|
agent: exec.agent,
|
|
392
|
+
executionArguments,
|
|
352
393
|
}
|
|
353
394
|
if (current.deferredSettlement !== undefined) {
|
|
354
395
|
current.deferredSettlement.current = current
|
|
@@ -363,6 +404,7 @@ export function createRuntimeBridgeOwner({
|
|
|
363
404
|
meta = withRecoveryBoundaries(meta, settlement.recoveryBoundaries)
|
|
364
405
|
}
|
|
365
406
|
if (settlement.rewrites !== undefined) meta = withRewrites(meta, settlement.rewrites)
|
|
407
|
+
meta = withReplMemorySnapshot(meta, settlement.replMemory, presentationGeneration)
|
|
366
408
|
return { ...result, meta }
|
|
367
409
|
}
|
|
368
410
|
return result
|
|
@@ -405,10 +447,16 @@ export function createRuntimeBridgeOwner({
|
|
|
405
447
|
if (ownRun === undefined) delete runtime.run
|
|
406
448
|
else Object.defineProperty(runtime, 'run', ownRun)
|
|
407
449
|
}
|
|
408
|
-
for (const
|
|
409
|
-
if (output.presentationMeta
|
|
410
|
-
|
|
411
|
-
|
|
450
|
+
for (const patched of patchedDefinitions.values()) {
|
|
451
|
+
if (patched.output.presentationMeta === patched.patchedPresentationMeta) {
|
|
452
|
+
if (patched.originalPresentationMeta === undefined) delete patched.output.presentationMeta
|
|
453
|
+
else patched.output.presentationMeta = patched.originalPresentationMeta
|
|
454
|
+
}
|
|
455
|
+
if (patched.patchedExecute !== undefined
|
|
456
|
+
&& patched.definition.execute === patched.patchedExecute) {
|
|
457
|
+
if (patched.ownExecute === undefined) delete patched.definition.execute
|
|
458
|
+
else Object.defineProperty(patched.definition, 'execute', patched.ownExecute)
|
|
459
|
+
}
|
|
412
460
|
}
|
|
413
461
|
patchedDefinitions.clear()
|
|
414
462
|
await sessions.dispose()
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
LONG_CELL_CODE_UNITS,
|
|
13
13
|
markBindingFailure,
|
|
14
14
|
messageOf,
|
|
15
|
+
missingDescriptionPath,
|
|
15
16
|
oneLineMessage,
|
|
16
17
|
safeProperty,
|
|
17
18
|
} from './failure-reporting.js'
|
|
@@ -128,7 +129,11 @@ function exceptionDiagnostic({
|
|
|
128
129
|
declared,
|
|
129
130
|
longCellFailure = false,
|
|
130
131
|
}) {
|
|
131
|
-
const
|
|
132
|
+
const missingPath = error.name === 'ToolCallError' ? missingDescriptionPath(error) : undefined
|
|
133
|
+
const missingDescription = missingPath !== undefined
|
|
134
|
+
const message = missingDescription
|
|
135
|
+
? `nested native tool arguments are missing required \`description\` at JSON path $.${missingPath} (${error.toolName ?? 'unknown tool'}); this does not satisfy the outer run_code transport`
|
|
136
|
+
: firstLine(error.message, 'Unknown exception')
|
|
132
137
|
const rawName = typeof error.name === 'string' && error.name.length > 0
|
|
133
138
|
? error.name
|
|
134
139
|
/* c8 ignore next */
|
|
@@ -147,7 +152,9 @@ function exceptionDiagnostic({
|
|
|
147
152
|
...(cause === undefined ? {} : { cause }),
|
|
148
153
|
...(source === undefined ? {} : { source }),
|
|
149
154
|
help: [
|
|
150
|
-
|
|
155
|
+
...(missingDescription
|
|
156
|
+
? ['add a string `description` property to the nested native-tool argument object']
|
|
157
|
+
: ['inspect existing bindings and retry only the failing expression']),
|
|
151
158
|
...(error.name === 'ToolCallError'
|
|
152
159
|
&& typeof error.toolName === 'string'
|
|
153
160
|
&& error.toolName.startsWith('cordis_')
|
|
@@ -496,7 +503,7 @@ export class SessionCellExecutor {
|
|
|
496
503
|
})
|
|
497
504
|
active.appliedBindingCatalog = message.moduleLoadFailed === true
|
|
498
505
|
? active.priorBindingCatalog
|
|
499
|
-
: active.priorBindingCatalog.advance(active.prepared)
|
|
506
|
+
: active.priorBindingCatalog.advance(active.prepared, active.request.program)
|
|
500
507
|
const recordedFailure = active.replay?.diagnostics?.find(item => item.code === 'PTC-X001')
|
|
501
508
|
const failure = recordedFailure?.message === actualFailure.message ? recordedFailure : actualFailure
|
|
502
509
|
const error = {
|
|
@@ -525,7 +532,10 @@ export class SessionCellExecutor {
|
|
|
525
532
|
hasValue: message.hasValue,
|
|
526
533
|
...(message.hasValue ? { value } : {}),
|
|
527
534
|
}
|
|
528
|
-
active.appliedBindingCatalog = active.priorBindingCatalog.advance(
|
|
535
|
+
active.appliedBindingCatalog = active.priorBindingCatalog.advance(
|
|
536
|
+
active.prepared,
|
|
537
|
+
active.request.program,
|
|
538
|
+
)
|
|
529
539
|
if (active.replay?.completion?.kind === 'return'
|
|
530
540
|
&& (active.replay.completion.hasValue !== message.hasValue
|
|
531
541
|
|| (message.hasValue && !valueWiresEqual(active.replay.completion.value, value, active.valueLimits)))) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { decodeValue, encodeValue, normalizeValueWire } from './value-wire.js'
|
|
2
2
|
import { normalizeDiagnostic } from './diagnostic.js'
|
|
3
3
|
import { assertOwnFields, isRecord } from './record-utils.js'
|
|
4
|
+
import { sessionEvents } from './session-events.js'
|
|
4
5
|
|
|
5
6
|
export const JOURNAL_KEY = 'dshPtcPlus'
|
|
6
7
|
export const EDIT_TARGET_KEY = 'dshPtcPlusEdit'
|
|
@@ -365,7 +366,7 @@ export function migrateRecoveryBoundaryEvents(events) {
|
|
|
365
366
|
|
|
366
367
|
/** Resolve the persisted event identity for one named tool call being dispatched. */
|
|
367
368
|
export function liveToolCallSeq(session, callId, toolName) {
|
|
368
|
-
const events = session
|
|
369
|
+
const events = sessionEvents(session)
|
|
369
370
|
if (!Array.isArray(events) || typeof callId !== 'string' || callId.length === 0
|
|
370
371
|
|| typeof toolName !== 'string' || toolName.length === 0) return undefined
|
|
371
372
|
|
|
@@ -741,7 +742,7 @@ export function foldSessionTimeline(events) {
|
|
|
741
742
|
|
|
742
743
|
/** Fold the session log into the last exactly replayable frontier. */
|
|
743
744
|
export function recoverJournal(session, currentCallSeq, options = {}) {
|
|
744
|
-
const events = session
|
|
745
|
+
const events = sessionEvents(session)
|
|
745
746
|
if (!Array.isArray(events)) {
|
|
746
747
|
return { nodes: [], head: undefined, checkpoints: new Map(), volatileSuffix: [], available: true }
|
|
747
748
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
foldSessionTimeline,
|
|
3
3
|
} from './session-journal.js'
|
|
4
|
+
import { sessionEvents } from './session-events.js'
|
|
4
5
|
|
|
5
6
|
const SYSTEM_PROMPT_PLUGIN = '@deepseek-ai/dsh-system-prompt'
|
|
6
7
|
|
|
@@ -43,7 +44,7 @@ function cordisTranscriptFacts(timeline) {
|
|
|
43
44
|
|
|
44
45
|
/** Project session events into immutable facts consumed by prompt presentation. */
|
|
45
46
|
export function projectSessionLog(agent, requestedEdit) {
|
|
46
|
-
const events = agent?.session
|
|
47
|
+
const events = sessionEvents(agent?.session)
|
|
47
48
|
if (!Array.isArray(events)) {
|
|
48
49
|
return Object.freeze({
|
|
49
50
|
openTurn: false,
|
|
@@ -13,6 +13,10 @@ import { resolveConfig } from './runtime-config.js'
|
|
|
13
13
|
import { WorkerClient } from './worker-client.js'
|
|
14
14
|
import { BindingCatalog, durabilityState, transitionDurability } from './session-state.js'
|
|
15
15
|
import { SessionCellExecutor } from './session-cell-executor.js'
|
|
16
|
+
import {
|
|
17
|
+
createReplMemorySnapshot,
|
|
18
|
+
unavailableReplMemorySnapshot,
|
|
19
|
+
} from './repl-memory-projection.js'
|
|
16
20
|
|
|
17
21
|
const WORKER_URL = new URL('./kernel-worker.js', import.meta.url)
|
|
18
22
|
function recoveryDiagnostic(count) {
|
|
@@ -300,14 +304,14 @@ class SessionKernel {
|
|
|
300
304
|
callSeq: request.callSeq,
|
|
301
305
|
program: request.program,
|
|
302
306
|
bindingCatalog: active.appliedBindingCatalog
|
|
303
|
-
?? active.priorBindingCatalog.advance(prepared),
|
|
307
|
+
?? active.priorBindingCatalog.advance(prepared, request.program),
|
|
304
308
|
worker,
|
|
305
309
|
})
|
|
306
310
|
}
|
|
307
311
|
}
|
|
308
312
|
if (replay !== undefined && !terminate) {
|
|
309
313
|
this.bindingCatalog = active.appliedBindingCatalog
|
|
310
|
-
?? active.priorBindingCatalog.advance(prepared)
|
|
314
|
+
?? active.priorBindingCatalog.advance(prepared, request.program)
|
|
311
315
|
}
|
|
312
316
|
this.active = undefined
|
|
313
317
|
if (terminate) void this.client.reset(worker)
|
|
@@ -353,6 +357,18 @@ class SessionKernel {
|
|
|
353
357
|
}
|
|
354
358
|
}
|
|
355
359
|
|
|
360
|
+
replMemoryFor(journal) {
|
|
361
|
+
const tentative = this.tentatives.get(journal)
|
|
362
|
+
if (journal.status === 'discarded'
|
|
363
|
+
|| journal.operations.some(operation => operation.action === 'restore')) {
|
|
364
|
+
return unavailableReplMemorySnapshot()
|
|
365
|
+
}
|
|
366
|
+
if (tentative === undefined) {
|
|
367
|
+
return createReplMemorySnapshot(this.bindingCatalog.snapshot())
|
|
368
|
+
}
|
|
369
|
+
return createReplMemorySnapshot(tentative.bindingCatalog.snapshot())
|
|
370
|
+
}
|
|
371
|
+
|
|
356
372
|
finishStateOperations(operations, index, worker) {
|
|
357
373
|
const transition = reduceStateOperations(this.history, operations, index)
|
|
358
374
|
this.history.head = transition.head
|
|
@@ -487,6 +503,7 @@ export class SessionRuntime {
|
|
|
487
503
|
journal,
|
|
488
504
|
kernel,
|
|
489
505
|
sessionId,
|
|
506
|
+
replMemory: kernel.replMemoryFor(journal),
|
|
490
507
|
...(result.recoveryBoundaries === undefined
|
|
491
508
|
? {}
|
|
492
509
|
: { recoveryBoundaries: result.recoveryBoundaries }),
|