dsh-ptc-plus 0.4.10 → 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.
Files changed (125) hide show
  1. package/README.en.md +65 -127
  2. package/README.md +65 -127
  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 +42702 -0
  10. package/cordis.patch.yml +1 -0
  11. package/docs/README.md +6 -2
  12. package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +31 -198
  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 +4 -4
  17. package/docs/adr/0019-plugin-settings-and-kill-switch.md +1 -1
  18. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +1 -1
  19. package/docs/adr/0022-stage-npm-releases-with-oidc.md +4 -2
  20. package/docs/adr/0023-global-user-bindings.md +2 -0
  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 +25 -23
  23. package/docs/adr/0031-close-semantic-obligations-by-owner.md +1 -1
  24. package/docs/adr/0034-keep-program-values-separate-from-output-projections.md +29 -0
  25. package/docs/adr/0035-run-coverage-only-before-a-new-release.md +27 -0
  26. package/docs/adr/0036-separate-focused-development-from-delivery-verification.md +29 -0
  27. package/docs/adr/0037-keep-tool-hover-previews-passive.md +52 -0
  28. package/docs/adr/0038-freeze-session-capability-plans.md +71 -0
  29. package/docs/adr/0039-verify-documentation-without-runtime-suites.md +29 -0
  30. package/docs/adr/0040-separate-capability-configurations-from-session-observations.md +39 -0
  31. package/docs/adr/0041-record-cell-execution-directories.md +65 -0
  32. package/docs/adr/0042-persist-provider-availability-and-handback.md +41 -0
  33. package/docs/adr/0043-compose-capability-and-context-policies.md +37 -0
  34. package/docs/adr/0044-coexist-with-the-official-persistent-repl.md +35 -0
  35. package/docs/adr/README.md +8 -0
  36. package/docs/architecture.md +23 -25
  37. package/docs/capability-planning-components.md +93 -0
  38. package/docs/capability-projection.md +7 -0
  39. package/docs/client-ui.md +70 -23
  40. package/docs/client-visual-contract.md +26 -9
  41. package/docs/context-management.md +22 -0
  42. package/docs/context-optimization-plan.md +353 -0
  43. package/docs/context-public-interface-coverage.md +25 -0
  44. package/docs/durability-design.md +20 -15
  45. package/docs/evaluation.md +41 -6
  46. package/docs/evaluations/command-goat-deepseek-v4.1-flash-20261006.md +126 -0
  47. package/docs/evaluations/command-goat-deepseek-v4.1-flash.md +281 -0
  48. package/docs/installation.md +33 -10
  49. package/docs/program-data-plane.md +5 -3
  50. package/docs/publishing.md +7 -7
  51. package/docs/release-coverage.md +73 -0
  52. package/docs/release-notes.md +1 -1
  53. package/docs/review-procedure.md +56 -0
  54. package/docs/runtime-reference.md +53 -16
  55. package/docs/semantic-validation.md +14 -2
  56. package/docs/stateful-binding-updates-proposal.md +2 -2
  57. package/docs/user-bindings.md +1 -1
  58. package/docs/value-wire.md +11 -1
  59. package/docs/verification-optimization-work.md +26 -15
  60. package/docs/verification.md +30 -88
  61. package/index.js +146 -20
  62. package/internal/binding-toggle-receipt.js +24 -0
  63. package/internal/capability-command-receipt.js +31 -0
  64. package/internal/capability-environment.js +1253 -0
  65. package/internal/capability-plan.js +285 -0
  66. package/internal/capability-policy.js +96 -0
  67. package/internal/capability-statistics-store.js +180 -0
  68. package/internal/capability-statistics.js +214 -0
  69. package/internal/config-spec.js +89 -10
  70. package/internal/direct-surface-owner.js +27 -29
  71. package/internal/dynamic-environment-runtime.js +9 -9
  72. package/internal/edit-transport-owner.js +195 -140
  73. package/internal/execution-provider-owner.js +40 -12
  74. package/internal/execution-seam-compat.js +26 -8
  75. package/internal/kernel-worker.js +104 -32
  76. package/internal/native-root-dynamic.js +12 -13
  77. package/internal/official-repl-priority.js +51 -0
  78. package/internal/program-bindings.js +190 -0
  79. package/internal/recovery-tips.js +3 -2
  80. package/internal/rejected-cell-editor.js +8 -8
  81. package/internal/repl-observation-interest.js +11 -3
  82. package/internal/rpc-contract.js +1 -0
  83. package/internal/runtime-bridge-owner.js +275 -34
  84. package/internal/runtime-config.js +58 -0
  85. package/internal/runtime-contexts.js +74 -5
  86. package/internal/runtime-intrinsics.js +1 -0
  87. package/internal/runtime-messages.js +41 -0
  88. package/internal/sdk-projection.js +10 -82
  89. package/internal/session-cell-executor.js +42 -19
  90. package/internal/session-events.js +34 -0
  91. package/internal/session-journal-recovery.js +234 -11
  92. package/internal/session-journal-schema.js +6 -3
  93. package/internal/session-journal.js +31 -2
  94. package/internal/session-log-view.js +1 -1
  95. package/internal/session-runtime.js +119 -15
  96. package/internal/settings-compat.js +12 -5
  97. package/internal/stateful-module-compiler.js +3 -2
  98. package/internal/stateful-module-runtime.js +7 -3
  99. package/internal/stateful-root-runtime.js +9 -4
  100. package/internal/user-binding-authoring.js +2 -7
  101. package/internal/user-bindings-owner.js +19 -2
  102. package/internal/validated-parse-repair.js +1 -1
  103. package/internal/value-wire.js +68 -21
  104. package/internal/worker-cwd-virtualization.js +20 -18
  105. package/internal/worker-realm-surfaces.js +3 -3
  106. package/package.json +49 -30
  107. package/src/client-activity.js +3 -2
  108. package/src/client-authoring-view.js +67 -30
  109. package/src/client-binding-review.js +2 -1
  110. package/src/client-binding-toggle.js +18 -0
  111. package/src/client-capabilities-view.js +1555 -0
  112. package/src/client-capability-sort.js +29 -0
  113. package/src/client-copy.js +923 -8
  114. package/src/client-feature-gates.js +3 -0
  115. package/src/client-indicator-view.js +4 -6
  116. package/src/client-primitives.js +33 -2
  117. package/src/client-repl-view.js +20 -13
  118. package/src/client-settings-view.js +411 -41
  119. package/src/client-styles.js +112 -15
  120. package/src/client-tool-hover.js +185 -0
  121. package/src/client-tool-view.js +45 -9
  122. package/src/client-transport.js +44 -9
  123. package/src/client-workbench.js +6 -7
  124. package/src/client.js +76 -22
  125. package/client.js +0 -32479
@@ -12,6 +12,11 @@ export function createReplObservationInterest(ctx, initiallyEnabled, observe = a
12
12
  for (const watcher of [...watchers]) watcher.release()
13
13
  for (const controller of inspections) controller.abort()
14
14
  }
15
+ const releaseEvents = typeof ctx.on === 'function' ? ctx.effect(() => ctx.on('session/event', session => {
16
+ for (const watcher of [...watchers]) {
17
+ if (watcher.sessionId === session.id) watcher.changed()
18
+ }
19
+ }), 'ptc-plus: model source observation changes') : undefined
15
20
  const handler = async (endpoint, payload, signal) => {
16
21
  if (!enabled || !['watch', 'observe'].includes(endpoint) || typeof payload?.sessionId !== 'string'
17
22
  || payload.sessionId.length === 0 || payload.sessionId.length > 256
@@ -32,11 +37,13 @@ export function createReplObservationInterest(ctx, initiallyEnabled, observe = a
32
37
  }
33
38
  }
34
39
  return new Promise(resolve => {
35
- const watcher = { sessionId: payload.sessionId, release() {
40
+ const finish = value => {
36
41
  signal.removeEventListener('abort', watcher.release)
37
42
  watchers.delete(watcher)
38
- resolve({ ok: true, value: null })
39
- } }
43
+ resolve({ ok: true, value })
44
+ }
45
+ const watcher = { sessionId: payload.sessionId,
46
+ release: () => finish(null), changed: () => finish({ changed: true }) }
40
47
  watchers.add(watcher)
41
48
  signal.addEventListener('abort', watcher.release, { once: true })
42
49
  if (signal.aborted) watcher.release()
@@ -62,6 +69,7 @@ export function createReplObservationInterest(ctx, initiallyEnabled, observe = a
62
69
  async dispose() {
63
70
  enabled = false
64
71
  clear()
72
+ await releaseEvents?.()
65
73
  for (const release of [...registrations]) await release()
66
74
  if (typeof injection === 'function') await injection()
67
75
  else await injection?.dispose?.()
@@ -17,6 +17,7 @@ const resultSchema = Schema.union([
17
17
  export const RPC_CONTRACTS = Object.freeze({
18
18
  bindings: Object.freeze({ service: 'ptcPlusBindings' }),
19
19
  repl: Object.freeze({ service: 'ptcPlusRepl' }),
20
+ capabilities: Object.freeze({ service: 'ptcPlusCapabilities' }),
20
21
  })
21
22
 
22
23
  export function rpcDescriptor({ service }) {
@@ -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'],
@@ -154,6 +159,7 @@ export function createRuntimeBridgeOwner({
154
159
  toolSchemasForAgent,
155
160
  userBindingDraftForAgent,
156
161
  bindingSubmissionForAgent,
162
+ capabilityEnvironment,
157
163
  }) {
158
164
  const scope = new AsyncLocalStorage()
159
165
  const childScope = new AsyncLocalStorage()
@@ -173,22 +179,27 @@ export function createRuntimeBridgeOwner({
173
179
  const withInitiator = ctx.agents === undefined || typeof ctx.agents.withInitiator !== 'function'
174
180
  ? undefined
175
181
  : (agent, operation) => ctx.agents.withInitiator(agent, operation)
176
- const sessions = new SessionRuntime(sessionConfig, { withInitiator, userBindingsCwd, observeSession, readSessionLog })
182
+ const sessions = new SessionRuntime(sessionConfig, {
183
+ withInitiator, userBindingsCwd, observeSession, readSessionLog,
184
+ sessionForId: id => ctx.get('sessions')?.get(id),
185
+ })
177
186
  let currentConfig = sessions.config
178
187
  const patchedDefinitions = new Map()
179
188
  const pending = new WeakMap()
180
189
  const nativeRunCodeHandoffs = new Set()
181
190
  let active = true
182
- const runIsolated = async (request, executionToken, depth, cellConfig, inheritedTools) => {
191
+ const runIsolated = async (request, executionToken, depth, cellConfig, inheritedTools, programCompletion = false) => {
183
192
  const child = new SessionRuntime({ ...cellConfig, durableReplay: false, userBindingsEnabled: false,
184
193
  replViewEnabled: false }, { withInitiator, userBindingsCwd })
185
194
  childRuntimes.add(child)
186
195
  const projected = projectBindings(request, depth, executionToken, inheritedTools, cellConfig)
187
196
  childFinalizers.add(child)
188
197
  try {
189
- return await child.run({ id: 'isolated-cell', session: { header: {
190
- cwd: executionToken?.session?.header?.cwd,
191
- } } }, { ...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 }
192
203
  } finally {
193
204
  projected.release()
194
205
  try {
@@ -263,11 +274,12 @@ export function createRuntimeBridgeOwner({
263
274
  }
264
275
  if (languageSemantics !== 'legacy-v1') {
265
276
  const child = await runIsolated({ ...request, program: args.code }, executionToken,
266
- depth + 1, cellConfig, functions)
267
- if (child.error !== undefined) {
268
- 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}`)
269
280
  }
270
- 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)) } : {}) }
271
283
  }
272
284
  const childProjected = projectBindings(
273
285
  { ...request, program: args.code }, depth + 1, executionToken, functions, cellConfig,
@@ -284,7 +296,8 @@ export function createRuntimeBridgeOwner({
284
296
  return { logs: child.logs, ...(child.value === undefined ? {} : { result: child.value }) }
285
297
  }
286
298
 
287
- const schemas = toolSchemasForAgent(executionToken?.agent)
299
+ const nativeSchemas = toolSchemasForAgent(executionToken?.agent)
300
+ const schemas = (capabilityEnvironment?.visibleSchemas(executionToken?.agent, nativeSchemas) ?? nativeSchemas)
288
301
  .filter(schema => schema?.name === RUN_CODE
289
302
  || (schema?.name !== EDIT_RUN_CODE && typeof functions[schema?.name] === 'function'))
290
303
  const emptyObjectTools = new Set(schemas.flatMap(schema => (
@@ -294,20 +307,30 @@ export function createRuntimeBridgeOwner({
294
307
  )))
295
308
  const projected = bindingDescriptors.namespaces.map((binding) => {
296
309
  const wrapped = Object.create(null)
297
- 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
298
318
  Object.defineProperty(wrapped, key, {
299
319
  enumerable: true,
300
320
  value: async (...args) => {
301
321
  ensureLease()
302
- return binding.functions[key](...args)
322
+ return binding.global === 'tools' && key === EDIT_RUN_CODE
323
+ ? scope.run(executionToken, () => invoke(...args))
324
+ : invoke(...args)
303
325
  },
304
326
  })
305
327
  }
306
328
  const emptyObjectMembers = binding.global === 'tools'
307
- ? binding.members.filter(member => emptyObjectTools.has(member))
329
+ ? members.filter(member => emptyObjectTools.has(member))
308
330
  : []
309
331
  return {
310
332
  ...binding,
333
+ members,
311
334
  functions: wrapped,
312
335
  ...(binding.global === 'tools' ? { emptyObjectMembers } : {}),
313
336
  }
@@ -318,12 +341,14 @@ export function createRuntimeBridgeOwner({
318
341
  : []
319
342
  )))
320
343
  const metadata = Object.freeze([
321
- ...toolCapabilityMetadata(schemas, annotations),
344
+ ...(capabilityEnvironment?.toolMetadata(executionToken?.agent, toolCapabilityMetadata(schemas, annotations), schemas) ?? toolCapabilityMetadata(schemas, annotations)),
345
+ ...(capabilityEnvironment?.metadata(executionToken?.agent) ?? []),
322
346
  ...PROGRAM_CAPABILITY_METADATA.map(entry => entry.namespace === 'code'
323
347
  && cellConfig.userBindingsEnabled === true
324
348
  ? { ...entry, members: [...entry.members, BINDING_SUBMISSION] }
325
349
  : entry),
326
350
  ])
351
+ let searchCapabilities
327
352
  projected.push(namespace('capabilities', {
328
353
  tree: async value => {
329
354
  if (value !== undefined) throw new TypeError('capabilities.tree does not accept arguments')
@@ -337,11 +362,24 @@ export function createRuntimeBridgeOwner({
337
362
  },
338
363
  inspect: async value => {
339
364
  ensureLease()
340
- 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
+ }
341
370
  if (value === null || typeof value !== 'object' || Array.isArray(value)) {
342
371
  throw new TypeError('capabilities.inspect expects an object')
343
372
  }
344
- 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
345
383
  },
346
384
  }, 'CapabilityExplorationError', 'operation'))
347
385
  projected.push(namespace('code', {
@@ -367,6 +405,10 @@ export function createRuntimeBridgeOwner({
367
405
  const output = definition.output
368
406
  const ownPresentationMeta = Object.getOwnPropertyDescriptor(output, 'presentationMeta')
369
407
  const patchedPresentationMeta = (args, value) => {
408
+ const projectedCurrent = scope.getStore()
409
+ if (projectedCurrent !== undefined) {
410
+ projectedCurrent.presentationMetaCalls = (projectedCurrent.presentationMetaCalls ?? 0) + 1
411
+ }
370
412
  const prototype = Object.getPrototypeOf(output)
371
413
  const originalPresentationMeta = ownPresentationMeta === undefined
372
414
  ? prototype === null ? undefined : Reflect.get(prototype, 'presentationMeta', output)
@@ -374,8 +416,15 @@ export function createRuntimeBridgeOwner({
374
416
  ? ownPresentationMeta.value
375
417
  : ownPresentationMeta.get?.call(output)
376
418
  const base = originalPresentationMeta === undefined
377
- ? undefined
378
- : originalPresentationMeta.call(output, 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
379
428
  if (!active) return base
380
429
  const current = scope.getStore()
381
430
  const settlement = current?.settlement
@@ -388,9 +437,50 @@ export function createRuntimeBridgeOwner({
388
437
  if (settlement.userBindings !== undefined) {
389
438
  meta = withUserBindingsSnapshot(meta, settlement.userBindings)
390
439
  }
440
+ if (current.postEdit?.provenance !== undefined) {
441
+ meta = { ...meta, [POST_EDIT_KEY]: current.postEdit.provenance }
442
+ }
391
443
  meta = generatedRunCodeDescriptionMeta(args, meta)
392
444
  meta = withReplMemorySnapshot(meta, settlement.replMemory, presentationGeneration)
393
- 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]
394
484
  }
395
485
  const ownExecute = Object.getOwnPropertyDescriptor(definition, 'execute')
396
486
  const originalExecute = definition.execute
@@ -409,6 +499,10 @@ export function createRuntimeBridgeOwner({
409
499
  value: patchedPresentationMeta,
410
500
  })
411
501
  presentationPatched = true
502
+ Object.defineProperty(definition, 'projectContent', {
503
+ configurable: true, enumerable: ownProjectContent?.enumerable ?? true,
504
+ writable: true, value: patchedProjectContent,
505
+ })
412
506
  if (patchedExecute !== undefined) {
413
507
  Object.defineProperty(definition, 'execute', {
414
508
  configurable: true,
@@ -422,10 +516,20 @@ export function createRuntimeBridgeOwner({
422
516
  output,
423
517
  ownPresentationMeta,
424
518
  patchedPresentationMeta,
519
+ ownProjectContent,
520
+ patchedProjectContent,
425
521
  ownExecute,
426
522
  patchedExecute,
427
523
  })
428
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
+ }
429
533
  if (presentationPatched && output.presentationMeta === patchedPresentationMeta) {
430
534
  if (ownPresentationMeta === undefined) delete output.presentationMeta
431
535
  else Object.defineProperty(output, 'presentationMeta', ownPresentationMeta)
@@ -440,6 +544,7 @@ export function createRuntimeBridgeOwner({
440
544
  if (!active || !child.active) return Promise.reject(new Error('PTC execution lease expired'))
441
545
  if (executionPolicies(child.cellConfig).languageSemantics !== 'legacy-v1') {
442
546
  return runIsolated(request, child.executionToken, child.depth, child.cellConfig, child.functions)
547
+ .then(execution => execution.result)
443
548
  }
444
549
  }
445
550
  if (!active) return seam.runUpstream(request)
@@ -451,22 +556,93 @@ export function createRuntimeBridgeOwner({
451
556
  executionToken: current,
452
557
  ...(current.userBindings === undefined ? {} : { userBindings: current.userBindings }),
453
558
  })
454
- .then((execution) => {
559
+ .then(async (execution) => {
455
560
  current.settlement = execution.settlement
456
- 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 targetDescription = pendingEdit.targetDescription ?? `run_code call ${pendingEdit.targetCallSeq}`
591
+ const status = outcome.edited
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})`
594
+ const detail = [
595
+ ...(hasRenderedContent || !Array.isArray(outcome.logs) ? [] : outcome.logs),
596
+ ...(!hasRenderedContent && typeof outcome.error === 'string' ? [outcome.error] : []),
597
+ ...(!hasRenderedContent && typeof outcome.reason === 'string' ? [outcome.reason] : []),
598
+ ].filter(item => typeof item === 'string').join('\n')
599
+ return { ...execution.result,
600
+ logs: [...execution.result.logs, `${status}${valueText}${detail ? `: ${detail}` : ''}`] }
601
+ } catch (error) {
602
+ pendingEdit.finish(false)
603
+ return { ...execution.result,
604
+ logs: [...execution.result.logs,
605
+ `edit_run_code failed after acceptance: ${error instanceof Error ? error.message : String(error)}`] }
606
+ }
457
607
  })
458
608
  .finally(projected.release)
459
609
  }
460
610
 
461
611
  const releaseSeam = seam.installExecute(patchedRun)
462
612
 
613
+ const ownedDerivedScope = (exec, executionArguments = exec.arguments) => {
614
+ const inherited = scope.getStore()
615
+ const derived = inherited?.derivedExecution
616
+ if (inherited?.deferredSettlement === undefined
617
+ || inherited.id !== sessionId(exec.agent) || derived?.agent !== exec.agent
618
+ || derived.callId !== exec.callId || derived.rootCallId !== exec.rootCallId
619
+ || derived.parent !== exec.parent || exec.name !== RUN_CODE
620
+ || !deepEqualJson(derived.arguments, executionArguments)) return undefined
621
+ return inherited
622
+ }
623
+
463
624
  const withSubmission = async (exec, operation) => {
464
625
  const id = sessionId(exec.agent)
465
626
  const inherited = scope.getStore()
466
- if (exec.parent !== undefined || id === undefined || inherited?.submission?.sessionId === id) return operation()
627
+ const ownsDerived = ownedDerivedScope(exec) !== undefined
628
+ if ((exec.parent !== undefined && !ownsDerived) || id === undefined
629
+ || (inherited?.exec === exec && inherited.submission?.sessionId === id)) return operation()
467
630
  const submission = sessions.beginSubmission({ id }, exec.signal)
631
+ const composite = ownsDerived
632
+ ? {
633
+ persistedCallSeq: inherited.persistedCallSeq,
634
+ sourceCallSeq: inherited.sourceCallSeq,
635
+ sessionLog: inherited.sessionLog,
636
+ deferredSettlement: inherited.deferredSettlement,
637
+ derivedExecution: inherited.derivedExecution,
638
+ }
639
+ : {}
468
640
  try {
469
- return await scope.run({ ...inherited, submission }, operation)
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))
470
646
  } finally {
471
647
  sessions.releaseSubmission(submission)
472
648
  }
@@ -479,6 +655,25 @@ export function createRuntimeBridgeOwner({
479
655
  return sessionLog
480
656
  }
481
657
 
658
+ const readNestedDispatchLog = async (session, signal) => {
659
+ const inherited = scope.getStore()
660
+ if (inherited === undefined || inherited.session !== session
661
+ || inherited.signal.aborted || signal?.aborted) {
662
+ throw new Error('ptc-plus: nested edit has no active root run_code cell')
663
+ }
664
+ return readSessionLog(session, signal)
665
+ }
666
+
667
+ const rootEditScope = exec => {
668
+ const current = scope.getStore()
669
+ if (!active || current === undefined || childScope.getStore() !== undefined
670
+ || current.deferredSettlement !== undefined
671
+ || current.callId !== exec.rootCallId || current.agent !== exec.agent
672
+ || current.session !== exec.agent?.session || current.signal.aborted
673
+ || exec.signal?.aborted) return undefined
674
+ return current
675
+ }
676
+
482
677
  return Object.freeze({
483
678
  config: sessions.config,
484
679
  reconfigure(nextConfig) {
@@ -488,8 +683,18 @@ export function createRuntimeBridgeOwner({
488
683
  observeRepl(id, memory, signal) { return sessions.observe(id, memory, signal) },
489
684
  withSubmission,
490
685
  readSubmissionLog,
686
+ readNestedDispatchLog,
687
+ prepareAgent(agent) { patchRunCodeDefinition(agent) },
688
+ canScheduleEdit(exec) { return rootEditScope(exec) !== undefined },
689
+ scheduleEdit(exec, pendingEdit) {
690
+ const current = rootEditScope(exec)
691
+ if (current === undefined || current.pendingEdit !== undefined) return false
692
+ current.pendingEdit = pendingEdit
693
+ return true
694
+ },
491
695
  // A composite tool's outer result owns the final durability decision.
492
- async executeTentative(callSeq, operation, sessionLog) {
696
+ async executeTentative(callSeq, sourceCallSeq, operation, sessionLog, derivedExecution) {
697
+ const id = scope.getStore()?.id
493
698
  const settlement = {
494
699
  current: undefined,
495
700
  innerConfirmed: false,
@@ -504,22 +709,27 @@ export function createRuntimeBridgeOwner({
504
709
  }
505
710
  try {
506
711
  const result = await scope.run({
507
- ...(scope.getStore()?.submission === undefined ? {} : { submission: scope.getStore().submission }),
712
+ id,
508
713
  ...(callSeq === undefined ? {} : { persistedCallSeq: callSeq }),
714
+ ...(sourceCallSeq === undefined ? {} : { sourceCallSeq }),
509
715
  ...(sessionLog === undefined ? {} : { sessionLog }),
510
716
  deferredSettlement: settlement,
717
+ derivedExecution,
511
718
  }, operation)
512
- return Object.freeze({ result, finalize })
719
+ return Object.freeze({ result, finalize,
720
+ ...(settlement.metadata === undefined ? {} : { metadata: settlement.metadata }) })
513
721
  } catch (error) {
514
722
  finalize(false)
515
723
  throw error
516
724
  }
517
725
  },
518
726
  async handleExecute(exec, next, executionArguments = exec.arguments, userBindings = undefined) {
519
- if (exec.parent !== undefined) return scope.run(undefined, next)
727
+ const ownsDerived = ownedDerivedScope(exec, executionArguments) !== undefined
728
+ if (exec.parent !== undefined && !ownsDerived) return scope.run(undefined, next)
520
729
  return withSubmission(exec, async () => {
521
730
  const id = sessionId(exec.agent)
522
731
  if (id === undefined) return next()
732
+ await capabilityEnvironment?.prepare(exec.agent)
523
733
  patchRunCodeDefinition(exec.agent)
524
734
  const inherited = scope.getStore()
525
735
  const persistedCallSeq = inherited?.persistedCallSeq
@@ -528,12 +738,16 @@ export function createRuntimeBridgeOwner({
528
738
  submission: inherited?.submission,
529
739
  callId: String(exec.callId),
530
740
  ...(persistedCallSeq === undefined ? {} : { persistedCallSeq }),
741
+ ...(inherited?.sourceCallSeq === undefined ? {} : { sourceCallSeq: inherited.sourceCallSeq }),
531
742
  ...(inherited?.deferredSettlement === undefined
532
743
  ? {}
533
- : { deferredSettlement: inherited.deferredSettlement }),
744
+ : { deferredSettlement: inherited.deferredSettlement,
745
+ derivedExecution: inherited.derivedExecution }),
534
746
  session: exec.agent?.session,
747
+ signal: exec.signal,
535
748
  sessionLog: await readSubmissionLog(exec.agent?.session, exec.signal),
536
749
  agent: exec.agent,
750
+ exec,
537
751
  executionArguments,
538
752
  ...(userBindings === undefined ? {} : { userBindings }),
539
753
  }
@@ -554,26 +768,47 @@ export function createRuntimeBridgeOwner({
554
768
  if (settlement.userBindings !== undefined) {
555
769
  meta = withUserBindingsSnapshot(meta, settlement.userBindings)
556
770
  }
771
+ if (current.postEdit?.provenance !== undefined) {
772
+ meta = { ...meta, [POST_EDIT_KEY]: current.postEdit.provenance }
773
+ }
557
774
  meta = withReplMemorySnapshot(meta, settlement.replMemory, presentationGeneration)
775
+ if (current.postEdit?.provenance?.meta?.[REPL_MEMORY_META_KEY] !== undefined) {
776
+ meta = { ...meta,
777
+ [REPL_MEMORY_META_KEY]: current.postEdit.provenance.meta[REPL_MEMORY_META_KEY] }
778
+ }
558
779
  meta = withDraftCapability(meta, current)
559
- return { ...result, meta }
780
+ return capabilityEnvironment?.complete(current, { ...result, meta }) ?? { ...result, meta }
560
781
  }
561
782
  return result
562
783
  })
563
784
  })
564
785
  },
565
786
  handleResult(exec, result) {
566
- if (exec.parent !== undefined) return
787
+ if (exec.parent !== undefined && !pending.has(exec)) return
567
788
  const id = sessionId(exec.agent)
568
789
  if (id === undefined) return
569
790
  const current = pending.get(exec)
570
791
  pending.delete(exec)
571
792
  const settlement = current?.settlement
572
793
  if (settlement === undefined) {
794
+ current?.pendingEdit?.finish(false)
573
795
  sessions.noteNoop(id, current?.sessionLog, exec.callId)
574
796
  return
575
797
  }
576
- const meta = result?.meta
798
+ const deferred = current.deferredSettlement
799
+ const projection = deferred?.projection
800
+ const derived = current.derivedExecution
801
+ const privateMetadata = projection !== undefined && projection.exec === exec
802
+ && deferred.current === current && current.exec === exec && current.id === id
803
+ && derived?.agent === exec.agent && derived.callId === exec.callId
804
+ && derived.rootCallId === exec.rootCallId && derived.parent === exec.parent
805
+ && deepEqualJson(derived.arguments, exec.arguments)
806
+ && projection.token === exec.token && projection.value === result?.value
807
+ && result.isError === false && deepEqualJson(projection.meta, result.meta)
808
+ && projection.presentationMetaCalls === current.presentationMetaCalls
809
+ ? projection.metadata : undefined
810
+ if (deferred !== undefined) deferred.metadata = privateMetadata
811
+ const meta = privateMetadata ?? result?.meta
577
812
  const confirmed = meta !== null && typeof meta === 'object' && !Array.isArray(meta)
578
813
  && Object.hasOwn(meta, JOURNAL_KEY)
579
814
  && journalsEqual(meta[JOURNAL_KEY], settlement.journal)
@@ -592,6 +827,8 @@ export function createRuntimeBridgeOwner({
592
827
  return
593
828
  }
594
829
  sessions.finalize(settlement, confirmed)
830
+ current.pendingEdit?.finish(confirmed && current.postEdit?.provenance !== undefined
831
+ && deepEqualJson(meta?.[POST_EDIT_KEY], current.postEdit.provenance))
595
832
  },
596
833
  disposeAgent(agent) {
597
834
  return sessions.disposeSession(sessionId(agent) ?? String(agent.id))
@@ -620,6 +857,10 @@ export function createRuntimeBridgeOwner({
620
857
  if (patched.ownPresentationMeta === undefined) delete patched.output.presentationMeta
621
858
  else Object.defineProperty(patched.output, 'presentationMeta', patched.ownPresentationMeta)
622
859
  }
860
+ if (patched.definition.projectContent === patched.patchedProjectContent) {
861
+ if (patched.ownProjectContent === undefined) delete patched.definition.projectContent
862
+ else Object.defineProperty(patched.definition, 'projectContent', patched.ownProjectContent)
863
+ }
623
864
  if (patched.patchedExecute !== undefined
624
865
  && patched.definition.execute === patched.patchedExecute) {
625
866
  if (patched.ownExecute === undefined) delete patched.definition.execute