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.
Files changed (139) hide show
  1. package/README.en.md +61 -123
  2. package/README.md +61 -123
  3. package/assets/dsh-ptc-plus-banner-en.webp +0 -0
  4. package/assets/dsh-ptc-plus-banner-zh.webp +0 -0
  5. package/assets/ptc-plus-repl-workspace-en.png +0 -0
  6. package/assets/ptc-plus-repl-workspace-zh.png +0 -0
  7. package/assets/ptc-plus-settings-en.png +0 -0
  8. package/assets/ptc-plus-settings-zh.png +0 -0
  9. package/client.cjs +42336 -0
  10. package/cordis.patch.yml +13 -2
  11. package/docs/README.md +7 -2
  12. package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +31 -179
  13. package/docs/adr/0008-define-agent-operating-contract.md +5 -1
  14. package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -2
  15. package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
  16. package/docs/adr/0017-track-the-latest-dsh-public-surface.md +2 -2
  17. package/docs/adr/0019-plugin-settings-and-kill-switch.md +14 -3
  18. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
  19. package/docs/adr/0022-stage-npm-releases-with-oidc.md +4 -2
  20. package/docs/adr/0023-global-user-bindings.md +3 -1
  21. package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +2 -0
  22. package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +101 -35
  23. package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +3 -3
  24. package/docs/adr/0031-close-semantic-obligations-by-owner.md +1 -1
  25. package/docs/adr/0032-own-session-log-facts-in-a-host-only-projection.md +27 -0
  26. package/docs/adr/0033-gate-client-visual-usability-in-a-browser.md +23 -0
  27. package/docs/adr/0034-keep-program-values-separate-from-output-projections.md +29 -0
  28. package/docs/adr/0035-run-coverage-only-before-a-new-release.md +27 -0
  29. package/docs/adr/0036-separate-focused-development-from-delivery-verification.md +29 -0
  30. package/docs/adr/0037-keep-tool-hover-previews-passive.md +52 -0
  31. package/docs/adr/0038-freeze-session-capability-plans.md +71 -0
  32. package/docs/adr/0039-verify-documentation-without-runtime-suites.md +29 -0
  33. package/docs/adr/0040-separate-capability-configurations-from-session-observations.md +39 -0
  34. package/docs/adr/0041-record-cell-execution-directories.md +65 -0
  35. package/docs/adr/0042-persist-provider-availability-and-handback.md +41 -0
  36. package/docs/adr/0043-compose-capability-and-context-policies.md +37 -0
  37. package/docs/adr/README.md +6 -0
  38. package/docs/architecture.md +25 -27
  39. package/docs/binding-review-placement-design.md +29 -21
  40. package/docs/capability-planning-components.md +93 -0
  41. package/docs/capability-projection.md +7 -0
  42. package/docs/client-ui.md +88 -29
  43. package/docs/client-visual-contract.md +65 -0
  44. package/docs/context-management.md +22 -0
  45. package/docs/context-optimization-plan.md +353 -0
  46. package/docs/context-public-interface-coverage.md +25 -0
  47. package/docs/durability-design.md +31 -14
  48. package/docs/evaluation.md +41 -6
  49. package/docs/evaluations/command-goat-deepseek-v4.1-flash-20261006.md +126 -0
  50. package/docs/evaluations/command-goat-deepseek-v4.1-flash.md +281 -0
  51. package/docs/installation.md +50 -8
  52. package/docs/program-data-plane.md +5 -3
  53. package/docs/publishing.md +5 -5
  54. package/docs/release-coverage.md +73 -0
  55. package/docs/release-notes.md +1 -1
  56. package/docs/review-procedure.md +56 -0
  57. package/docs/runtime-reference.md +48 -16
  58. package/docs/semantic-validation.md +14 -2
  59. package/docs/stateful-binding-updates-proposal.md +2 -2
  60. package/docs/user-bindings.en.md +2 -2
  61. package/docs/user-bindings.md +3 -3
  62. package/docs/value-wire.md +11 -1
  63. package/docs/verification-optimization-work.md +52 -15
  64. package/docs/verification.md +30 -86
  65. package/index.js +147 -23
  66. package/internal/binding-toggle-receipt.js +24 -0
  67. package/internal/capability-command-receipt.js +29 -0
  68. package/internal/capability-environment.js +1247 -0
  69. package/internal/capability-plan.js +285 -0
  70. package/internal/capability-policy.js +96 -0
  71. package/internal/capability-statistics-store.js +180 -0
  72. package/internal/capability-statistics.js +210 -0
  73. package/internal/compiler-platform.js +5 -3
  74. package/internal/compiler-service.js +2 -9
  75. package/internal/config-spec.js +79 -9
  76. package/internal/cordis-tools-owner.js +22 -19
  77. package/internal/direct-surface-owner.js +29 -29
  78. package/internal/dynamic-environment-runtime.js +9 -9
  79. package/internal/edit-transport-owner.js +183 -139
  80. package/internal/execution-provider-owner.js +160 -0
  81. package/internal/execution-seam-compat.js +76 -120
  82. package/internal/isolated-worker.js +12 -8
  83. package/internal/kernel-worker.js +104 -32
  84. package/internal/native-root-dynamic.js +12 -13
  85. package/internal/program-bindings.js +190 -0
  86. package/internal/recovery-tips.js +53 -23
  87. package/internal/rejected-cell-editor.js +8 -8
  88. package/internal/repl-observation-interest.js +11 -3
  89. package/internal/rpc-contract.js +1 -0
  90. package/internal/runtime-bridge-owner.js +351 -82
  91. package/internal/runtime-config.js +58 -0
  92. package/internal/runtime-contexts.js +85 -16
  93. package/internal/runtime-intrinsics.js +1 -0
  94. package/internal/runtime-messages.js +41 -0
  95. package/internal/sdk-projection.js +10 -82
  96. package/internal/session-cell-executor.js +42 -19
  97. package/internal/session-events.js +74 -4
  98. package/internal/session-journal-recovery.js +904 -184
  99. package/internal/session-journal-schema.js +6 -3
  100. package/internal/session-journal.js +31 -2
  101. package/internal/session-log-view.js +189 -43
  102. package/internal/session-runtime.js +175 -23
  103. package/internal/settings-compat.js +23 -6
  104. package/internal/stateful-module-compiler.js +3 -2
  105. package/internal/stateful-module-runtime.js +7 -3
  106. package/internal/stateful-root-runtime.js +9 -4
  107. package/internal/user-binding-authoring.js +2 -7
  108. package/internal/user-bindings-owner.js +47 -7
  109. package/internal/validated-parse-repair.js +1 -1
  110. package/internal/value-wire.js +68 -21
  111. package/internal/worker-cwd-virtualization.js +20 -18
  112. package/internal/worker-realm-surfaces.js +3 -3
  113. package/locale/en.json +6 -0
  114. package/locale/zh.json +6 -0
  115. package/package.json +55 -38
  116. package/src/client-authoring-view.js +286 -168
  117. package/src/client-binding-review.js +4 -1
  118. package/src/client-binding-toggle.js +18 -0
  119. package/src/client-capabilities-view.js +1341 -0
  120. package/src/client-capability-sort.js +29 -0
  121. package/src/client-code-editor.js +6 -0
  122. package/src/client-copy.js +881 -8
  123. package/src/client-feature-gates.js +15 -5
  124. package/src/client-host-compat.js +23 -2
  125. package/src/client-indicator-view.js +4 -6
  126. package/src/client-primitives.js +280 -0
  127. package/src/client-repl-view.js +23 -16
  128. package/src/client-settings-view.js +412 -69
  129. package/src/client-styles.js +193 -41
  130. package/src/client-tool-hover.js +185 -0
  131. package/src/client-tool-view.js +45 -11
  132. package/src/client-transport.js +44 -9
  133. package/src/client-workbench.js +9 -10
  134. package/src/client.js +148 -68
  135. package/assets/ptc-plus-bindings-en.png +0 -0
  136. package/assets/ptc-plus-bindings-zh.png +0 -0
  137. package/assets/ptc-plus-repair-en.png +0 -0
  138. package/assets/ptc-plus-repair-zh.png +0 -0
  139. package/client.js +0 -31857
@@ -1,5 +1,8 @@
1
1
  /** Own the execution-seam takeover, per-cell lease, journal projection, and session settlement. */
2
2
  import { AsyncLocalStorage } from 'node:async_hooks'
3
+ import { deepEqualJson, snapshotJsonValue } from '@deepseek-ai/dsh-util-values'
4
+ import { decodeValue, renderValueWire } from './value-wire.js'
5
+ import { valueLimitsFromConfig } from './value-wire-schema.js'
3
6
  import { executionPolicies } from './binding-update-policy.js'
4
7
  import { assertObjectJsonSchema, validateJsonSchemaValue } from '@deepseek-ai/dsh-tools'
5
8
  import { normalizeBindingDescriptors } from './binding-descriptors.js'
@@ -17,11 +20,13 @@ import {
17
20
  capabilityFind,
18
21
  capabilityInspect,
19
22
  capabilityTree,
23
+ createCapabilitySearcher,
20
24
  toolCapabilityMetadata,
21
25
  } from './program-bindings.js'
22
26
  import { deepFreeze } from './record-utils.js'
23
- import { withReplMemorySnapshot } from './repl-memory-projection.js'
27
+ import { REPL_MEMORY_META_KEY, withReplMemorySnapshot } from './repl-memory-projection.js'
24
28
  import { withUserBindingDraftCapability } from './user-binding-draft-projection.js'
29
+ import { POST_EDIT_KEY } from './session-journal-schema.js'
25
30
  import { generatedRunCodeDescriptionMeta } from './run-code-description.js'
26
31
  import { BINDING_SUBMISSION } from './user-binding-authoring.js'
27
32
  import {
@@ -71,7 +76,7 @@ const PROGRAM_CAPABILITY_METADATA = deepFreeze([
71
76
  namespace: 'repl',
72
77
  members: [{
73
78
  name: 'state',
74
- description: 'Manage named REPL checkpoints. list returns checkpoint names, not variable names. Save captures the final durable state of this cell, including subsequent mutations; it is lost if the cell becomes volatile or is discarded. Save, restore and delete take effect after cell settlement and journal confirmation, not when the receipt is awaited. Restore changes bindings for subsequent cells and does not undo external effects.',
79
+ description: 'Manage named REPL checkpoints. list returns checkpoint names, not variable names. Save requires a durable cell; a volatile save rejection leaves live bindings usable. Save captures the final durable state of this cell, including subsequent mutations; it is lost if the cell becomes volatile or is discarded. Save, restore and delete take effect after cell settlement and journal confirmation, not when the receipt is awaited. Restore discards bindings beyond the selected durable state for subsequent cells; an omitted name selects the prior durable head, or an empty REPL if none exists. Restore does not undo external effects or change enabled binding configuration; subsequent cells may remain volatile.',
75
80
  parameters: {
76
81
  type: 'object',
77
82
  properties: {
@@ -128,7 +133,7 @@ const PROGRAM_CAPABILITY_METADATA = deepFreeze([
128
133
  description: 'Child console output in emission order.',
129
134
  },
130
135
  result: {
131
- description: 'Child return value; omitted when the child returns undefined.',
136
+ description: 'Plugin-owned isolated execution preserves supported PTC Value V1 types and reference relationships; omitted for no return, present for explicit undefined. Native delegation and legacy upstream execution retain their original result contracts.',
132
137
  },
133
138
  },
134
139
  required: ['logs'],
@@ -147,12 +152,14 @@ export function createRuntimeBridgeOwner({
147
152
  sessionConfig,
148
153
  userBindingsCwd,
149
154
  observeSession,
155
+ readSessionLog = async session => session,
150
156
  maxNestedRunCodeDepth,
151
157
  presentationGeneration,
152
158
  sessionId,
153
159
  toolSchemasForAgent,
154
160
  userBindingDraftForAgent,
155
161
  bindingSubmissionForAgent,
162
+ capabilityEnvironment,
156
163
  }) {
157
164
  const scope = new AsyncLocalStorage()
158
165
  const childScope = new AsyncLocalStorage()
@@ -172,22 +179,27 @@ export function createRuntimeBridgeOwner({
172
179
  const withInitiator = ctx.agents === undefined || typeof ctx.agents.withInitiator !== 'function'
173
180
  ? undefined
174
181
  : (agent, operation) => ctx.agents.withInitiator(agent, operation)
175
- const sessions = new SessionRuntime(sessionConfig, { withInitiator, userBindingsCwd, observeSession })
182
+ const sessions = new SessionRuntime(sessionConfig, {
183
+ withInitiator, userBindingsCwd, observeSession, readSessionLog,
184
+ sessionForId: id => ctx.get('sessions')?.get(id),
185
+ })
176
186
  let currentConfig = sessions.config
177
187
  const patchedDefinitions = new Map()
178
188
  const pending = new WeakMap()
179
189
  const nativeRunCodeHandoffs = new Set()
180
190
  let active = true
181
- const runIsolated = async (request, executionToken, depth, cellConfig, inheritedTools) => {
191
+ const runIsolated = async (request, executionToken, depth, cellConfig, inheritedTools, programCompletion = false) => {
182
192
  const child = new SessionRuntime({ ...cellConfig, durableReplay: false, userBindingsEnabled: false,
183
193
  replViewEnabled: false }, { withInitiator, userBindingsCwd })
184
194
  childRuntimes.add(child)
185
195
  const projected = projectBindings(request, depth, executionToken, inheritedTools, cellConfig)
186
196
  childFinalizers.add(child)
187
197
  try {
188
- return await child.run({ id: 'isolated-cell', session: { header: {
189
- cwd: executionToken?.session?.header?.cwd,
190
- } } }, { ...projected.request, executionToken })
198
+ const execution = await child.runTentative({ id: 'isolated-cell', session: { header: {
199
+ cwd: request.cwd ?? executionToken?.session?.header?.cwd,
200
+ } } }, { ...projected.request, executionToken }, { programCompletion })
201
+ if (execution.settlement !== undefined) child.finalize(execution.settlement, true)
202
+ return { result: execution.result, completion: execution.settlement?.journal.completion }
191
203
  } finally {
192
204
  projected.release()
193
205
  try {
@@ -262,11 +274,12 @@ export function createRuntimeBridgeOwner({
262
274
  }
263
275
  if (languageSemantics !== 'legacy-v1') {
264
276
  const child = await runIsolated({ ...request, program: args.code }, executionToken,
265
- depth + 1, cellConfig, functions)
266
- if (child.error !== undefined) {
267
- throw new Error(`nested run_code failed (${child.error.kind}): ${child.error.message}`)
277
+ depth + 1, cellConfig, functions, true)
278
+ if (child.result.error !== undefined) {
279
+ throw new Error(`nested run_code failed (${child.result.error.kind}): ${child.result.error.message}`)
268
280
  }
269
- return { logs: child.logs, ...(child.value === undefined ? {} : { result: child.value }) }
281
+ return { logs: child.result.logs, ...(child.completion.hasValue
282
+ ? { result: decodeValue(child.completion.value, valueLimitsFromConfig(cellConfig)) } : {}) }
270
283
  }
271
284
  const childProjected = projectBindings(
272
285
  { ...request, program: args.code }, depth + 1, executionToken, functions, cellConfig,
@@ -283,7 +296,8 @@ export function createRuntimeBridgeOwner({
283
296
  return { logs: child.logs, ...(child.value === undefined ? {} : { result: child.value }) }
284
297
  }
285
298
 
286
- const schemas = toolSchemasForAgent(executionToken?.agent)
299
+ const nativeSchemas = toolSchemasForAgent(executionToken?.agent)
300
+ const schemas = (capabilityEnvironment?.visibleSchemas(executionToken?.agent, nativeSchemas) ?? nativeSchemas)
287
301
  .filter(schema => schema?.name === RUN_CODE
288
302
  || (schema?.name !== EDIT_RUN_CODE && typeof functions[schema?.name] === 'function'))
289
303
  const emptyObjectTools = new Set(schemas.flatMap(schema => (
@@ -293,20 +307,30 @@ export function createRuntimeBridgeOwner({
293
307
  )))
294
308
  const projected = bindingDescriptors.namespaces.map((binding) => {
295
309
  const wrapped = Object.create(null)
296
- for (const key of binding.members) {
310
+ const members = binding.global === 'tools'
311
+ ? capabilityEnvironment?.projectMembers(executionToken?.agent, binding.members) ?? binding.members
312
+ : binding.members
313
+ for (const key of members) {
314
+ const original = (...args) => binding.functions[key](...args)
315
+ const invoke = binding.global === 'tools'
316
+ ? capabilityEnvironment?.adaptFunction(executionToken?.agent, key, original) ?? original
317
+ : original
297
318
  Object.defineProperty(wrapped, key, {
298
319
  enumerable: true,
299
320
  value: async (...args) => {
300
321
  ensureLease()
301
- return binding.functions[key](...args)
322
+ return binding.global === 'tools' && key === EDIT_RUN_CODE
323
+ ? scope.run(executionToken, () => invoke(...args))
324
+ : invoke(...args)
302
325
  },
303
326
  })
304
327
  }
305
328
  const emptyObjectMembers = binding.global === 'tools'
306
- ? binding.members.filter(member => emptyObjectTools.has(member))
329
+ ? members.filter(member => emptyObjectTools.has(member))
307
330
  : []
308
331
  return {
309
332
  ...binding,
333
+ members,
310
334
  functions: wrapped,
311
335
  ...(binding.global === 'tools' ? { emptyObjectMembers } : {}),
312
336
  }
@@ -317,12 +341,14 @@ export function createRuntimeBridgeOwner({
317
341
  : []
318
342
  )))
319
343
  const metadata = Object.freeze([
320
- ...toolCapabilityMetadata(schemas, annotations),
344
+ ...(capabilityEnvironment?.toolMetadata(executionToken?.agent, toolCapabilityMetadata(schemas, annotations), schemas) ?? toolCapabilityMetadata(schemas, annotations)),
345
+ ...(capabilityEnvironment?.metadata(executionToken?.agent) ?? []),
321
346
  ...PROGRAM_CAPABILITY_METADATA.map(entry => entry.namespace === 'code'
322
347
  && cellConfig.userBindingsEnabled === true
323
348
  ? { ...entry, members: [...entry.members, BINDING_SUBMISSION] }
324
349
  : entry),
325
350
  ])
351
+ let searchCapabilities
326
352
  projected.push(namespace('capabilities', {
327
353
  tree: async value => {
328
354
  if (value !== undefined) throw new TypeError('capabilities.tree does not accept arguments')
@@ -336,11 +362,24 @@ export function createRuntimeBridgeOwner({
336
362
  },
337
363
  inspect: async value => {
338
364
  ensureLease()
339
- if (value === undefined) return capabilityInspect(metadata)
365
+ if (value === undefined) {
366
+ const result = capabilityInspect(metadata)
367
+ capabilityEnvironment?.explored(executionToken?.agent, result)
368
+ return result
369
+ }
340
370
  if (value === null || typeof value !== 'object' || Array.isArray(value)) {
341
371
  throw new TypeError('capabilities.inspect expects an object')
342
372
  }
343
- return capabilityInspect(metadata, value.symbols, value.budget)
373
+ const result = capabilityInspect(metadata, value.symbols, value.budget)
374
+ capabilityEnvironment?.explored(executionToken?.agent, result)
375
+ return result
376
+ },
377
+ search: async value => {
378
+ ensureLease()
379
+ searchCapabilities ??= createCapabilitySearcher(metadata)
380
+ const result = searchCapabilities(value)
381
+ capabilityEnvironment?.explored(executionToken?.agent, result)
382
+ return result
344
383
  },
345
384
  }, 'CapabilityExplorationError', 'operation'))
346
385
  projected.push(namespace('code', {
@@ -364,16 +403,29 @@ export function createRuntimeBridgeOwner({
364
403
  }
365
404
  if (patchedDefinitions.has(definition)) return
366
405
  const output = definition.output
367
- const originalPresentationMeta = output.presentationMeta
406
+ const ownPresentationMeta = Object.getOwnPropertyDescriptor(output, 'presentationMeta')
368
407
  const patchedPresentationMeta = (args, value) => {
369
- if (!active) {
370
- return originalPresentationMeta === undefined
371
- ? undefined
372
- : originalPresentationMeta(args, value)
408
+ const projectedCurrent = scope.getStore()
409
+ if (projectedCurrent !== undefined) {
410
+ projectedCurrent.presentationMetaCalls = (projectedCurrent.presentationMetaCalls ?? 0) + 1
373
411
  }
412
+ const prototype = Object.getPrototypeOf(output)
413
+ const originalPresentationMeta = ownPresentationMeta === undefined
414
+ ? prototype === null ? undefined : Reflect.get(prototype, 'presentationMeta', output)
415
+ : Object.hasOwn(ownPresentationMeta, 'value')
416
+ ? ownPresentationMeta.value
417
+ : ownPresentationMeta.get?.call(output)
374
418
  const base = originalPresentationMeta === undefined
375
- ? undefined
376
- : originalPresentationMeta(args, value)
419
+ // Installing a projector replaces an optional Host callback with a
420
+ // required JSON-producing callback, including policy reprojection
421
+ // after the execution scope has ended. An empty object conveys no
422
+ // computation evidence; a stale settlement must never fill that gap.
423
+ ? {}
424
+ : snapshotJsonValue(originalPresentationMeta.call(output, args, value))
425
+ // An invalid upstream projection is still the Host's output failure;
426
+ // adding plugin metadata must not make it appear valid.
427
+ if (base === undefined) return undefined
428
+ if (!active) return base
377
429
  const current = scope.getStore()
378
430
  const settlement = current?.settlement
379
431
  if (settlement === undefined) return generatedRunCodeDescriptionMeta(args, base)
@@ -385,9 +437,50 @@ export function createRuntimeBridgeOwner({
385
437
  if (settlement.userBindings !== undefined) {
386
438
  meta = withUserBindingsSnapshot(meta, settlement.userBindings)
387
439
  }
440
+ if (current.postEdit?.provenance !== undefined) {
441
+ meta = { ...meta, [POST_EDIT_KEY]: current.postEdit.provenance }
442
+ }
388
443
  meta = generatedRunCodeDescriptionMeta(args, meta)
389
444
  meta = withReplMemorySnapshot(meta, settlement.replMemory, presentationGeneration)
390
- return withDraftCapability(meta, current)
445
+ if (current.postEdit?.provenance?.meta?.[REPL_MEMORY_META_KEY] !== undefined) {
446
+ meta = { ...meta,
447
+ [REPL_MEMORY_META_KEY]: current.postEdit.provenance.meta[REPL_MEMORY_META_KEY] }
448
+ }
449
+ meta = withDraftCapability(meta, current)
450
+ return capabilityEnvironment?.complete(current, { meta }).meta ?? meta
451
+ }
452
+ const ownProjectContent = Object.getOwnPropertyDescriptor(definition, 'projectContent')
453
+ const patchedProjectContent = (exec, result) => {
454
+ const prototype = Object.getPrototypeOf(definition)
455
+ const originalProjectContent = ownProjectContent === undefined
456
+ ? prototype === null ? undefined : Reflect.get(prototype, 'projectContent', definition)
457
+ : Object.hasOwn(ownProjectContent, 'value')
458
+ ? ownProjectContent.value : ownProjectContent.get?.call(definition)
459
+ const original = originalProjectContent?.call(definition, exec, result)
460
+ const current = active ? pending.get(exec) : undefined
461
+ const derived = current?.derivedExecution
462
+ // Some public Host implementations omit the metadata projector for a
463
+ // nested success. Capture its canonical value before policy; the private
464
+ // relation is admitted only if policy retains that same frozen value.
465
+ if (current?.deferredSettlement?.current === current && derived !== undefined
466
+ && current.exec === exec && exec.token !== undefined && exec.parent !== undefined
467
+ && current.id === sessionId(exec.agent) && derived.agent === exec.agent
468
+ && derived.callId === exec.callId && derived.rootCallId === exec.rootCallId
469
+ && derived.parent === exec.parent && deepEqualJson(derived.arguments, exec.arguments)
470
+ && current.presentationMetaCalls === undefined && result.isError === false
471
+ && result.value !== null && typeof result.value === 'object'
472
+ && Object.isFrozen(result.value) && result.meta?.[JOURNAL_KEY] === undefined
473
+ && current.settlement !== undefined) {
474
+ const metadata = scope.run(current, () => patchedPresentationMeta(exec.arguments, result.value))
475
+ if (metadata?.[JOURNAL_KEY] !== undefined) {
476
+ current.deferredSettlement.projection = { exec, token: exec.token,
477
+ value: result.value, meta: snapshotJsonValue(result.meta), metadata,
478
+ presentationMetaCalls: current.presentationMetaCalls }
479
+ }
480
+ }
481
+ const presentation = active ? pending.get(exec)?.postEdit?.presentation : undefined
482
+ if (presentation === undefined) return original
483
+ return [...original ?? result.content, ...presentation.content]
391
484
  }
392
485
  const ownExecute = Object.getOwnPropertyDescriptor(definition, 'execute')
393
486
  const originalExecute = definition.execute
@@ -406,6 +499,10 @@ export function createRuntimeBridgeOwner({
406
499
  value: patchedPresentationMeta,
407
500
  })
408
501
  presentationPatched = true
502
+ Object.defineProperty(definition, 'projectContent', {
503
+ configurable: true, enumerable: ownProjectContent?.enumerable ?? true,
504
+ writable: true, value: patchedProjectContent,
505
+ })
409
506
  if (patchedExecute !== undefined) {
410
507
  Object.defineProperty(definition, 'execute', {
411
508
  configurable: true,
@@ -417,15 +514,25 @@ export function createRuntimeBridgeOwner({
417
514
  patchedDefinitions.set(definition, {
418
515
  definition,
419
516
  output,
420
- originalPresentationMeta,
517
+ ownPresentationMeta,
421
518
  patchedPresentationMeta,
519
+ ownProjectContent,
520
+ patchedProjectContent,
422
521
  ownExecute,
423
522
  patchedExecute,
424
523
  })
425
524
  } catch (error) {
525
+ if (definition.projectContent === patchedProjectContent) {
526
+ if (ownProjectContent === undefined) delete definition.projectContent
527
+ else Object.defineProperty(definition, 'projectContent', ownProjectContent)
528
+ }
529
+ if (patchedExecute !== undefined && definition.execute === patchedExecute) {
530
+ if (ownExecute === undefined) delete definition.execute
531
+ else Object.defineProperty(definition, 'execute', ownExecute)
532
+ }
426
533
  if (presentationPatched && output.presentationMeta === patchedPresentationMeta) {
427
- if (originalPresentationMeta === undefined) delete output.presentationMeta
428
- else output.presentationMeta = originalPresentationMeta
534
+ if (ownPresentationMeta === undefined) delete output.presentationMeta
535
+ else Object.defineProperty(output, 'presentationMeta', ownPresentationMeta)
429
536
  }
430
537
  throw new Error(`ptc-plus: cannot attach the session journal to run_code results: ${error.message}`)
431
538
  }
@@ -437,25 +544,132 @@ export function createRuntimeBridgeOwner({
437
544
  if (!active || !child.active) return Promise.reject(new Error('PTC execution lease expired'))
438
545
  if (executionPolicies(child.cellConfig).languageSemantics !== 'legacy-v1') {
439
546
  return runIsolated(request, child.executionToken, child.depth, child.cellConfig, child.functions)
547
+ .then(execution => execution.result)
440
548
  }
441
549
  }
442
- if (!active) return seam.invokeUpstream(request)
550
+ if (!active) return seam.runUpstream(request)
443
551
  const current = scope.getStore()
444
- if (current === undefined) return seam.invokeUpstream(request)
445
- const projected = projectBindings(request, 0, current)
552
+ if (current === undefined) return seam.runUpstream(request)
553
+ const projected = projectBindings(request, 0, current, undefined, current.submission?.config ?? currentConfig)
446
554
  return sessions.runTentative(current, {
447
555
  ...projected.request,
448
556
  executionToken: current,
449
557
  ...(current.userBindings === undefined ? {} : { userBindings: current.userBindings }),
450
558
  })
451
- .then((execution) => {
559
+ .then(async (execution) => {
452
560
  current.settlement = execution.settlement
453
- return execution.result
561
+ const pendingEdit = current.pendingEdit
562
+ if (pendingEdit === undefined) return execution.result
563
+ if (!pendingEdit.confirmed || current.signal.aborted) {
564
+ pendingEdit.finish(false)
565
+ return execution.result
566
+ }
567
+ try {
568
+ const postEdit = await pendingEdit.dispatch(current.signal, current.exec)
569
+ current.postEdit = postEdit
570
+ for (const context of postEdit.presentation?.additionalContexts ?? []) {
571
+ current.exec.deferContext?.(context)
572
+ }
573
+ if (postEdit.presentation?.concludesTurn === true && execution.result.error === undefined) {
574
+ current.exec.concludeTurn?.()
575
+ }
576
+ const outcome = postEdit.outcome
577
+ const value = postEdit.provenance?.meta?.[JOURNAL_KEY]?.completion
578
+ const hasRenderedContent = postEdit.presentation?.content?.length > 0
579
+ let valueText = ''
580
+ if (!hasRenderedContent && value?.kind === 'return' && value.hasValue) {
581
+ try {
582
+ valueText = `; value: ${renderValueWire(value.value, {
583
+ ...valueLimitsFromConfig(current.submission?.config ?? currentConfig),
584
+ maxStringBytes: 2048,
585
+ })}`
586
+ } catch {
587
+ valueText = '; value omitted from preview'
588
+ }
589
+ }
590
+ const status = outcome.edited
591
+ ? `edit_run_code settled${outcome.error === undefined ? '' : ' with execution error'}`
592
+ : 'edit_run_code did not execute the accepted revision'
593
+ const detail = [
594
+ ...(hasRenderedContent || !Array.isArray(outcome.logs) ? [] : outcome.logs),
595
+ ...(!hasRenderedContent && typeof outcome.error === 'string' ? [outcome.error] : []),
596
+ ...(!hasRenderedContent && typeof outcome.reason === 'string' ? [outcome.reason] : []),
597
+ ].filter(item => typeof item === 'string').join('\n')
598
+ return { ...execution.result,
599
+ logs: [...execution.result.logs, `${status}${valueText}${detail ? `: ${detail}` : ''}`] }
600
+ } catch (error) {
601
+ pendingEdit.finish(false)
602
+ return { ...execution.result,
603
+ logs: [...execution.result.logs,
604
+ `edit_run_code failed after acceptance: ${error instanceof Error ? error.message : String(error)}`] }
605
+ }
454
606
  })
455
607
  .finally(projected.release)
456
608
  }
457
609
 
458
- const releaseSeam = seam.takeOver(patchedRun)
610
+ const releaseSeam = seam.installExecute(patchedRun)
611
+
612
+ const ownedDerivedScope = (exec, executionArguments = exec.arguments) => {
613
+ const inherited = scope.getStore()
614
+ const derived = inherited?.derivedExecution
615
+ if (inherited?.deferredSettlement === undefined
616
+ || inherited.id !== sessionId(exec.agent) || derived?.agent !== exec.agent
617
+ || derived.callId !== exec.callId || derived.rootCallId !== exec.rootCallId
618
+ || derived.parent !== exec.parent || exec.name !== RUN_CODE
619
+ || !deepEqualJson(derived.arguments, executionArguments)) return undefined
620
+ return inherited
621
+ }
622
+
623
+ const withSubmission = async (exec, operation) => {
624
+ const id = sessionId(exec.agent)
625
+ const inherited = scope.getStore()
626
+ const ownsDerived = ownedDerivedScope(exec) !== undefined
627
+ if ((exec.parent !== undefined && !ownsDerived) || id === undefined
628
+ || inherited?.submission?.sessionId === id) return operation()
629
+ const submission = sessions.beginSubmission({ id }, exec.signal)
630
+ const composite = inherited?.deferredSettlement !== undefined && inherited.id === id
631
+ ? {
632
+ persistedCallSeq: inherited.persistedCallSeq,
633
+ sourceCallSeq: inherited.sourceCallSeq,
634
+ sessionLog: inherited.sessionLog,
635
+ deferredSettlement: inherited.deferredSettlement,
636
+ derivedExecution: inherited.derivedExecution,
637
+ }
638
+ : {}
639
+ try {
640
+ // Derived edits retain their proved source under a fresh admission for
641
+ // the same session. Delegated agents own a fresh log and no child handoff.
642
+ return await scope.run({ ...composite, submission }, () => childScope.run(undefined, operation))
643
+ } finally {
644
+ sessions.releaseSubmission(submission)
645
+ }
646
+ }
647
+
648
+ const readSubmissionLog = async (session, signal) => {
649
+ const inherited = scope.getStore()
650
+ const sessionLog = inherited?.sessionLog ?? await readSessionLog(session, signal)
651
+ if (inherited?.submission !== undefined) sessions.assertSubmission(inherited.submission)
652
+ return sessionLog
653
+ }
654
+
655
+ const readNestedDispatchLog = async (session, signal) => {
656
+ const inherited = scope.getStore()
657
+ if (inherited === undefined || inherited.session !== session
658
+ || inherited.signal.aborted || signal?.aborted) {
659
+ throw new Error('ptc-plus: nested edit has no active root run_code cell')
660
+ }
661
+ return readSessionLog(session, signal)
662
+ }
663
+
664
+ const rootEditScope = exec => {
665
+ const current = scope.getStore()
666
+ if (!active || current === undefined || childScope.getStore() !== undefined
667
+ || current.deferredSettlement !== undefined
668
+ || current.callId !== exec.rootCallId || current.agent !== exec.agent
669
+ || current.session !== exec.agent?.session || current.signal.aborted
670
+ || exec.signal?.aborted) return undefined
671
+ return current
672
+ }
459
673
 
460
674
  return Object.freeze({
461
675
  config: sessions.config,
@@ -464,8 +678,20 @@ export function createRuntimeBridgeOwner({
464
678
  currentConfig = sessions.config
465
679
  },
466
680
  observeRepl(id, memory, signal) { return sessions.observe(id, memory, signal) },
681
+ withSubmission,
682
+ readSubmissionLog,
683
+ readNestedDispatchLog,
684
+ prepareAgent(agent) { patchRunCodeDefinition(agent) },
685
+ canScheduleEdit(exec) { return rootEditScope(exec) !== undefined },
686
+ scheduleEdit(exec, pendingEdit) {
687
+ const current = rootEditScope(exec)
688
+ if (current === undefined || current.pendingEdit !== undefined) return false
689
+ current.pendingEdit = pendingEdit
690
+ return true
691
+ },
467
692
  // A composite tool's outer result owns the final durability decision.
468
- async executeTentative(callSeq, operation) {
693
+ async executeTentative(callSeq, sourceCallSeq, operation, sessionLog, derivedExecution) {
694
+ const id = scope.getStore()?.id
469
695
  const settlement = {
470
696
  current: undefined,
471
697
  innerConfirmed: false,
@@ -480,69 +706,106 @@ export function createRuntimeBridgeOwner({
480
706
  }
481
707
  try {
482
708
  const result = await scope.run({
709
+ id,
483
710
  ...(callSeq === undefined ? {} : { persistedCallSeq: callSeq }),
711
+ ...(sourceCallSeq === undefined ? {} : { sourceCallSeq }),
712
+ ...(sessionLog === undefined ? {} : { sessionLog }),
484
713
  deferredSettlement: settlement,
714
+ derivedExecution,
485
715
  }, operation)
486
- return Object.freeze({ result, finalize })
716
+ return Object.freeze({ result, finalize,
717
+ ...(settlement.metadata === undefined ? {} : { metadata: settlement.metadata }) })
487
718
  } catch (error) {
488
719
  finalize(false)
489
720
  throw error
490
721
  }
491
722
  },
492
- handleExecute(exec, next, executionArguments = exec.arguments, userBindings = undefined) {
493
- if (exec.parent !== undefined) return scope.run(undefined, next)
494
- const id = sessionId(exec.agent)
495
- if (id === undefined) return next()
496
- patchRunCodeDefinition(exec.agent)
497
- const inherited = scope.getStore()
498
- const persistedCallSeq = inherited?.persistedCallSeq
499
- const current = {
500
- id,
501
- callId: String(exec.callId),
502
- ...(persistedCallSeq === undefined ? {} : { persistedCallSeq }),
503
- ...(inherited?.deferredSettlement === undefined
504
- ? {}
505
- : { deferredSettlement: inherited.deferredSettlement }),
506
- session: exec.agent?.session,
507
- agent: exec.agent,
508
- executionArguments,
509
- ...(userBindings === undefined ? {} : { userBindings }),
510
- }
511
- if (current.deferredSettlement !== undefined) {
512
- current.deferredSettlement.current = current
513
- }
514
- pending.set(exec, current)
515
- return scope.run(current, async () => {
516
- const result = await next()
517
- const settlement = current.settlement
518
- if (result?.isError === true && settlement !== undefined) {
519
- let meta = withJournal(result.meta, settlement.journal)
520
- if (settlement.recoveryBoundaries !== undefined) {
521
- meta = withRecoveryBoundaries(meta, settlement.recoveryBoundaries)
522
- }
523
- if (settlement.rewrites !== undefined) meta = withRewrites(meta, settlement.rewrites)
524
- if (settlement.userBindings !== undefined) {
525
- meta = withUserBindingsSnapshot(meta, settlement.userBindings)
526
- }
527
- meta = withReplMemorySnapshot(meta, settlement.replMemory, presentationGeneration)
528
- meta = withDraftCapability(meta, current)
529
- return { ...result, meta }
723
+ async handleExecute(exec, next, executionArguments = exec.arguments, userBindings = undefined) {
724
+ const ownsDerived = ownedDerivedScope(exec, executionArguments) !== undefined
725
+ if (exec.parent !== undefined && !ownsDerived) return scope.run(undefined, next)
726
+ return withSubmission(exec, async () => {
727
+ const id = sessionId(exec.agent)
728
+ if (id === undefined) return next()
729
+ await capabilityEnvironment?.prepare(exec.agent)
730
+ patchRunCodeDefinition(exec.agent)
731
+ const inherited = scope.getStore()
732
+ const persistedCallSeq = inherited?.persistedCallSeq
733
+ const current = {
734
+ id,
735
+ submission: inherited?.submission,
736
+ callId: String(exec.callId),
737
+ ...(persistedCallSeq === undefined ? {} : { persistedCallSeq }),
738
+ ...(inherited?.sourceCallSeq === undefined ? {} : { sourceCallSeq: inherited.sourceCallSeq }),
739
+ ...(inherited?.deferredSettlement === undefined
740
+ ? {}
741
+ : { deferredSettlement: inherited.deferredSettlement,
742
+ derivedExecution: inherited.derivedExecution }),
743
+ session: exec.agent?.session,
744
+ signal: exec.signal,
745
+ sessionLog: await readSubmissionLog(exec.agent?.session, exec.signal),
746
+ agent: exec.agent,
747
+ exec,
748
+ executionArguments,
749
+ ...(userBindings === undefined ? {} : { userBindings }),
530
750
  }
531
- return result
751
+ sessions.assertSubmission(current.submission)
752
+ if (current.deferredSettlement !== undefined) {
753
+ current.deferredSettlement.current = current
754
+ }
755
+ pending.set(exec, current)
756
+ return scope.run(current, async () => {
757
+ const result = await next()
758
+ const settlement = current.settlement
759
+ if (result?.isError === true && settlement !== undefined) {
760
+ let meta = withJournal(result.meta, settlement.journal)
761
+ if (settlement.recoveryBoundaries !== undefined) {
762
+ meta = withRecoveryBoundaries(meta, settlement.recoveryBoundaries)
763
+ }
764
+ if (settlement.rewrites !== undefined) meta = withRewrites(meta, settlement.rewrites)
765
+ if (settlement.userBindings !== undefined) {
766
+ meta = withUserBindingsSnapshot(meta, settlement.userBindings)
767
+ }
768
+ if (current.postEdit?.provenance !== undefined) {
769
+ meta = { ...meta, [POST_EDIT_KEY]: current.postEdit.provenance }
770
+ }
771
+ meta = withReplMemorySnapshot(meta, settlement.replMemory, presentationGeneration)
772
+ if (current.postEdit?.provenance?.meta?.[REPL_MEMORY_META_KEY] !== undefined) {
773
+ meta = { ...meta,
774
+ [REPL_MEMORY_META_KEY]: current.postEdit.provenance.meta[REPL_MEMORY_META_KEY] }
775
+ }
776
+ meta = withDraftCapability(meta, current)
777
+ return capabilityEnvironment?.complete(current, { ...result, meta }) ?? { ...result, meta }
778
+ }
779
+ return result
780
+ })
532
781
  })
533
782
  },
534
783
  handleResult(exec, result) {
535
- if (exec.parent !== undefined) return
784
+ if (exec.parent !== undefined && !pending.has(exec)) return
536
785
  const id = sessionId(exec.agent)
537
786
  if (id === undefined) return
538
787
  const current = pending.get(exec)
539
788
  pending.delete(exec)
540
789
  const settlement = current?.settlement
541
790
  if (settlement === undefined) {
542
- sessions.noteNoop(id, exec.agent?.session, exec.callId)
791
+ current?.pendingEdit?.finish(false)
792
+ sessions.noteNoop(id, current?.sessionLog, exec.callId)
543
793
  return
544
794
  }
545
- const meta = result?.meta
795
+ const deferred = current.deferredSettlement
796
+ const projection = deferred?.projection
797
+ const derived = current.derivedExecution
798
+ const privateMetadata = projection !== undefined && projection.exec === exec
799
+ && deferred.current === current && current.exec === exec && current.id === id
800
+ && derived?.agent === exec.agent && derived.callId === exec.callId
801
+ && derived.rootCallId === exec.rootCallId && derived.parent === exec.parent
802
+ && deepEqualJson(derived.arguments, exec.arguments)
803
+ && projection.token === exec.token && projection.value === result?.value
804
+ && result.isError === false && deepEqualJson(projection.meta, result.meta)
805
+ && projection.presentationMetaCalls === current.presentationMetaCalls
806
+ ? projection.metadata : undefined
807
+ if (deferred !== undefined) deferred.metadata = privateMetadata
808
+ const meta = privateMetadata ?? result?.meta
546
809
  const confirmed = meta !== null && typeof meta === 'object' && !Array.isArray(meta)
547
810
  && Object.hasOwn(meta, JOURNAL_KEY)
548
811
  && journalsEqual(meta[JOURNAL_KEY], settlement.journal)
@@ -561,6 +824,8 @@ export function createRuntimeBridgeOwner({
561
824
  return
562
825
  }
563
826
  sessions.finalize(settlement, confirmed)
827
+ current.pendingEdit?.finish(confirmed && current.postEdit?.provenance !== undefined
828
+ && deepEqualJson(meta?.[POST_EDIT_KEY], current.postEdit.provenance))
564
829
  },
565
830
  disposeAgent(agent) {
566
831
  return sessions.disposeSession(sessionId(agent) ?? String(agent.id))
@@ -586,8 +851,12 @@ export function createRuntimeBridgeOwner({
586
851
  releaseSeam()
587
852
  for (const patched of patchedDefinitions.values()) {
588
853
  if (patched.output.presentationMeta === patched.patchedPresentationMeta) {
589
- if (patched.originalPresentationMeta === undefined) delete patched.output.presentationMeta
590
- else patched.output.presentationMeta = patched.originalPresentationMeta
854
+ if (patched.ownPresentationMeta === undefined) delete patched.output.presentationMeta
855
+ else Object.defineProperty(patched.output, 'presentationMeta', patched.ownPresentationMeta)
856
+ }
857
+ if (patched.definition.projectContent === patched.patchedProjectContent) {
858
+ if (patched.ownProjectContent === undefined) delete patched.definition.projectContent
859
+ else Object.defineProperty(patched.definition, 'projectContent', patched.ownProjectContent)
591
860
  }
592
861
  if (patched.patchedExecute !== undefined
593
862
  && patched.definition.execute === patched.patchedExecute) {